]> gitweb.pimeys.fr Git - bots/skeleton.git/commitdiff
Skeleton.py un bot qui ne fait rien de spécial, mais qui n'attend que ça.
authorVincent Le Gallic <legallic@crans.org>
Tue, 26 Jun 2012 17:20:17 +0000 (19:20 +0200)
committerVincent Le Gallic <legallic@crans.org>
Tue, 26 Jun 2012 17:20:17 +0000 (19:20 +0200)
.gitignore [new file with mode: 0644]
config.py [new file with mode: 0644]
skeleton.py [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..a57fd2e
--- /dev/null
@@ -0,0 +1,31 @@
+# Custom #
+###################
+*~
+
+# Compiled source #
+###################
+*.pyc
+
+# Packages #
+############
+# it's better to unpack these files and commit the raw source
+# git has its own built in compression methods
+*.7z
+*.dmg
+*.gz
+*.iso
+*.jar
+*.rar
+*.tar
+*.zip
+
+# Logs #
+######################
+*.log
+
+# OS generated files #
+######################
+.DS_Store*
+*ehthumbs.db
+Icon?
+*Thumbs.db
diff --git a/config.py b/config.py
new file mode 100644 (file)
index 0000000..2cd8255
--- /dev/null
+++ b/config.py
@@ -0,0 +1,104 @@
+#!/usr/bin/python
+# -*- coding:utf8 -*-
+
+# Configuration d'un bot IRC (squelette de base vide)
+
+debug_stdout=True
+
+# la config irc-related
+irc_password="Mot de passe à envoyer à NickServ pour s'authentifier"
+irc_pseudo="Pseudo"
+chanlist=["#bot"] # liste des chans à rejoindre
+stay_channels=["#bot"] # liste des chans que le bot ne quitte pas, même sur demande d'un leave de la part d'un OP
+quiet_channels=[] # liste des chans sur lesquels le bot ne parle pas
+
+# les logs
+logfile_template="basile.%s.log" # fichier de log (%s est remplacé par le nom du serveur)
+
+# les ops
+overops=["[20-100]"] # liste des OVEROPs (meilleurs que les OP ^^)
+ops=[] # liste des OPs
+
+# config UTF8-fail
+utf8_fail_answers = [u"Ceci est une réponse en cas de <quelqu'un écrit en pas-utf8>"]
+utf8_trigger = True # râlé-je en cas de non-utf8 ?
+
+# config "tu m'traites ?"
+insultes=[u"conna(rd|sse)",u"pute",u"con(|ne)",u"enf(oiré|lure)",
+u"sal(op(|e(|rie)|ard)|aud)",u"p(e|')tite bite",u"imbécile",u"idiot",u"stupid(|e)",u"débile",u"crétin",
+u"pétasse",u"enculé",u"chagasse",u"cagole",u"abruti",u"ahuri",u"analphabète",u"andouille",
+u"atardé",u"avorton",u"bachibouzouk",u"(balais|brosse) (de|à) chiotte(|s)",
+u"batard",u"blaireau",u"bouffon",u"branque",u"bouseux",u"branleur",u"catin",u"chacal",
+u"charogne",u"chiant(|e)",u"chieur",u"cochon",u"coprophage",u"couillon",u"crapule",u"crevard",
+u"cruche",u"cuistre",u"ducon",u"décérébré",
+u"emmerdeur",u"feignasse",u"fainéant",u"fourbe",u"freluquet",u"frigide",
+u"garce",u"glandu",u"gogol",u"goujat",u"gourdasse",u"gredin",u"gringalet",u"grognasse",
+u"naze",u"truie",u"iconoclaste",
+u"peigne(-|)cul",u"ignare",u"illétré",u"lèche(|-)cul",u"malotru",u"motherfucker",u"nabot",u"nigaud",
+u"nul",u"escroc",u"pouffiasse",u"pourriture",u"raclure",u"relou",u"sagouin",u"putain",
+u"péripatéticienne"]
+insultes_answers=[
+u"Ceci est une réponse à une insulte."]
+
+# config "tais-toi"
+tag_triggers=[u"t(|a)g",u"ta gueule",u"la ferme",u"ferme( |-)la",u"tais-toi",u"chut",u"tu fais trop de bruit",u"tu parles trop"]
+tag_actions=[u"effectue une action en réponse à un \"tais-toi\"."]
+tag_answers=[
+u"Ceci est une réponse à un 'tais-toi' à quelqu'un qui n'en a pas le droit."]
+
+# config ping
+tesla_triggers=[u"t('|u )es là \?",u"\?",u"plop \?",u"plouf \?"]
+tesla_answers=[
+u"Ceci est une réponse à 't'es là ?'."
+]
+tesla_actions=[u"effectue une action en réponse à un \"t'es là ?\"."]
+
+# config en cas de non-insulte
+compliment_triggers=[u"gentil",u"cool",u"sympa",u"efficace"]
+compliment_answers=[
+u"Ceci est une réponse à une non-insulte."
+]
+
+# config merci
+merci_triggers=[u"merci",u"remercie",u"thx",u"thank(|s)"]
+merci_answers=[u"Ceci est une réponse à un remerciement."]
+
+# config "ta mère" 
+tamere_triggers=[u"ta mère"]
+tamere_answers=[u"Ceci est une réponse à un \"ta mère\"."]
+
+# config pour les actions désagréables
+bad_action_triggers=[u"(frappe|cogne|tape)(| sur)",u"(démolit|dégomme|fouette|agresse|tabasse)",
+u"(vomit|pisse|chie|crache) sur",u"slap(|s)"]
+bad_action_answers=[
+u"Ceci est une réponse en cas d'action désagréable. (Elle a été perpetrée par {})"
+]
+bad_action_actions=[u"effectue une action en réponse à une action désagréable."]
+
+# config pour les actions agréables
+good_action_triggers=[u"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u"embrasse",u"c(?:â|a)line",u"caresse"]
+good_action_answers=[u"Ceci est une réponse à une action agréable. (Prodiguée par {})"]
+good_action_actions=[u"effectue une action en réponse à une action agréable."]
+
+# config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
+bonjour_triggers=[u"(s|)(a|'|)lu(t|)",u"hello",u"pl(o|i)p",u"pr(ou|ü)t",u"bonjour",u"bonsoir",u"coucou"]
+bonjour_answers=[u"Ceci est un message bonjour. (En réponse à {})"]
+bonsoir_answers=[u"Ceci est un message bonsoir. (En réponse à {})"]
+night_answers=[u"Ceci est un message envoyé à {} si il dit bonjour en pleine nuit."]
+daytime = [7,18] # c'est quoi le "jour" ?
+nighttime = [3, 6] # c'est quoi la "nuit" ?
+
+# config dodo
+bonne_nuit_triggers=[u"bonne nuit",u"'?nite",u"'?nuit",u"'?night",u"good night",u"'?nenuit"]
+bonne_nuit_answers=[u"Ceci est un message de souhait de bonne nuit. (En réponse à {})"]
+
+# config quelqu'un s'est défoulé sur le bot
+kick_answers=[u"Ceci est un message suite à /kick (Perpetré par {})"]
+kick_actions=[u"effectue une action suite à un /kick."]
+
+# config on m'a demandé de mourir/partir
+quit_messages=[u"Ceci est un message de quit"]
+leave_messages=[u"Ceci est un message de leave"]
+
+quit_fail_messages = [u"Ceci est un message adressé à quelqu'un qui m'a demandé de quit alors qu'il n'en a pas le droit."]
+leave_fail_messages = [u"Ceci est un message adressé à quelqu'un qui m'a demandé de leave alors qu'il n'en a pas le droit."]
diff --git a/skeleton.py b/skeleton.py
new file mode 100755 (executable)
index 0000000..e04be5d
--- /dev/null
@@ -0,0 +1,521 @@
+#!/usr/bin/python
+# -*- encoding: utf-8 -*-
+
+# Codé par 20-100
+
+# Un bot IRC qui ne fait rien. Base pour en coder un autre.
+
+import irclib
+import ircbot
+import threading
+import random
+import time
+import socket, ssl, json
+import pickle
+import re
+import os
+from commands import getstatusoutput as ex
+
+# on récupère la config
+import config
+
+
+
+def get_config_logfile(serveur):
+    serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
+    return config.logfile_template%(serveurs[serveur])
+
+def get_filesize():
+    return ex("ls -s %s"%(config.thisfile))[1].split()[0]
+
+def log(serveur,channel,auteur=None,message=None):
+    f=open(get_config_logfile(serveur),"a")
+    if auteur==message==None:
+        # alors c'est que c'est pas un channel mais juste une ligne de log
+        chain="%s %s"%(time.strftime("%F %T"),channel)
+    else:
+        chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
+    f.write(chain+"\n")
+    if config.debug_stdout:
+        print chain
+    f.close()
+
+def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
+    if case_sensitive:
+        chain=unicode(chain,"utf8")
+    else:
+        chain=unicode(chain,"utf8").lower()
+    allmatches="("+"|".join(matches)+")"
+    reg=(avant+allmatches+apres).lower()
+    o=re.match(reg,chain)
+    return o
+
+def is_insult(chain,debug=True):
+    return is_something(chain,config.insultes,avant=".*(?:^| |')")
+def is_not_insult(chain):
+    chain=unicode(chain,"utf8")
+    insult_regexp=u"("+u"|".join(config.insultes)+u")"
+    middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
+    reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
+    if re.match(reg,chain):
+        return True
+    else:
+        return False
+def is_compliment(chain,debug=True):
+    return is_something(chain,config.compliment_triggers,avant=".*(?:^| |')")
+def is_tag(chain):
+    return is_something(chain,config.tag_triggers)
+def is_tesla(chain):
+    return is_something(chain,config.tesla_triggers,avant=u"^",apres=u"$",debug=True)
+def is_merci(chain):
+    return is_something(chain,config.merci_triggers)
+def is_tamere(chain):
+    return is_something(chain,config.tamere_triggers)
+def is_bad_action_trigger(chain,pseudo):
+    return is_something(chain,config.bad_action_triggers,avant=u"^",
+                            apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
+def is_good_action_trigger(chain,pseudo):
+    return is_something(chain,config.good_action_triggers,avant=u"^",
+                            apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
+def is_bonjour(chain):
+    return is_something(chain,config.bonjour_triggers,avant=u"^")
+def is_bonne_nuit(chain):
+    return is_something(chain,config.bonne_nuit_triggers,avant=u"^")
+def is_pan(chain):
+    return re.match(u"^(pan|bim|bang)( .*)?$",unicode(chain,"utf8").lower().strip())
+
+def is_time(conf):
+    _,_,_,h,m,s,_,_,_=time.localtime()
+    return (conf[0],0,0)<(h,m,s)<(conf[1],0,0)
+def is_day():
+    return is_time(config.daytime)
+def is_night():
+    return is_time(config.nighttime)
+
+      
+class UnicodeBotError(Exception):
+    pass
+def bot_unicode(chain):
+    try:
+        unicode(chain,"utf8")
+    except UnicodeDecodeError as exc:
+        raise UnicodeBotError
+
+class Skeleton(ircbot.SingleServerIRCBot):
+    def __init__(self,serveur,debug=False):
+        temporary_pseudo=config.irc_pseudo+str(random.randrange(10000,100000))
+        ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
+                              temporary_pseudo,"Ceci est l'ircname du bot", 10)
+        self.debug=debug
+        self.serveur=serveur
+        self.overops=config.overops
+        self.ops=self.overops+config.ops
+        self.chanlist=config.chanlist
+        self.stay_channels=config.stay_channels
+        self.quiet_channels=config.quiet_channels
+        self.last_perdu=0
+
+    def give_me_my_pseudo(self,serv):
+        serv.privmsg("NickServ","RECOVER %s %s"%(config.irc_pseudo,config.irc_password))
+        serv.privmsg("NickServ","RELEASE %s %s"%(config.irc_pseudo,config.irc_password))
+        time.sleep(0.3)
+        serv.nick(config.irc_pseudo)
+    
+    def on_welcome(self, serv, ev):
+        self.serv=serv # ça serv ira :)
+        self.give_me_my_pseudo(serv)
+        serv.privmsg("NickServ","identify %s"%(config.irc_password))
+        log(self.serveur,"Connected")
+        if self.debug:
+            self.chanlist=["#bot"]
+        for c in self.chanlist:
+            log(self.serveur,"JOIN %s"%(c))
+            serv.join(c)
+
+    def pourmoi(self, serv, message):
+        """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
+        pseudo=self.nick
+        size=len(pseudo)
+        if message[:size]==pseudo and len(message)>size and message[size]==":":
+            return (True,message[size+1:].lstrip(" "))
+        else:
+            return (False,message)
+
+    def on_privmsg(self, serv, ev):
+        message=ev.arguments()[0]
+        auteur = irclib.nm_to_n(ev.source())
+        try:
+            test=bot_unicode(message)
+        except UnicodeBotError:
+            if config.utf8_trigger:
+                serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
+            return
+        message=message.split()
+        cmd=message[0].lower()
+        notunderstood=False
+        if cmd=="help":
+            helpdico={"help":["""HELP <commande>
+ Affiche de l'aide sur la commande""",None,None],
+ "join": [None, """JOIN <channel>
+ Me fait rejoindre le channel""",None],
+ "leave": [None,"""LEAVE <channel>
+ Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
+ "quiet": [None,"""QUIET <channel>
+ Me rend silencieux sur le channel.""",None],
+ "noquiet": [None,"""NOQUIET <channel>
+ Me rend la parole sur le channel.""",None],
+ "lost": [None,"""LOST <channel>
+ Me fait perdre sur le channel.""",None],
+ "say": [None,None,"""SAY <channel> <message>
+ Me fait parler sur le channel."""],
+ "do": [None,None,"""DO <channel> <action>
+ Me fait faitre une action (/me) sur le channel."""],
+ "stay": [None,None,"""STAY <channel>
+ Ajoute le channel à ma stay_list."""],
+ "nostay": [None,None,"""NOSTAY <channel>
+ Retire le channel de ma stay_list."""],
+ "ops": [None,None,"""OPS
+ Affiche la liste des ops."""],
+ "overops": [None,None,"""OVEROPS
+ Affiche la liste des overops."""],
+ "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
+ Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
+ "die": [None,None,"""DIE
+ Me déconnecte du serveur IRC."""]
+ }
+            helpmsg_default="Liste des commandes disponibles :\nHELP"
+            helpmsg_ops=" JOIN LEAVE QUIET NOQUIET LOST RECONNECT"
+            helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
+            op,overop=auteur in self.ops, auteur in self.overops
+            if len(message)==1:
+                helpmsg=helpmsg_default
+                if op:
+                    helpmsg+=helpmsg_ops
+                if overop:
+                    helpmsg+=helpmsg_overops
+            else:
+                helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
+                helpmsg=helpmsgs[0]
+                if op and helpmsgs[1]:
+                    if helpmsg:
+                        helpmsg+="\n"+helpmsgs[1]
+                    else:
+                        helpmsg=helpmsgs[1]
+                if overop and helpmsgs[2]:
+                    if helpmsg:
+                        helpmsg+="\n"+helpmsgs[2]
+                    else:
+                        helpmsg=helpmsgs[2]
+            for ligne in helpmsg.split("\n"):
+                serv.privmsg(auteur,ligne)
+        elif cmd=="join":
+            if auteur in self.ops:
+                if len(message)>1:
+                    if message[1] in self.chanlist:
+                        serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
+                    else:
+                        serv.join(message[1])
+                        self.chanlist.append(message[1])
+                        serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
+                        log(self.serveur,"priv",auteur," ".join(message))
+                else:
+                    serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
+            else:
+                notunderstood=True
+        elif cmd=="leave":
+            if auteur in self.ops and len(message)>1:
+                if message[1] in self.chanlist:
+                    if not (message[1] in self.stay_channels) or auteur in self.overops:
+                        self.quitter(message[1]," ".join(message[2:]))
+                        self.chanlist.remove(message[1])
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                    else:
+                        serv.privmsg(auteur,"Non, je reste !")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                else:
+                    serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
+            else:
+                notunderstood=True
+        elif cmd=="stay":
+            if auteur in self.overops:
+                if len(message)>1:
+                    if message[1] in self.stay_channels:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                        serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
+                    else:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                        self.stay_channels.append(message[1])
+                        serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
+                else:
+                    serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
+            else:
+                notunderstood=True
+        elif cmd=="nostay":
+            if auteur in self.overops:
+                if len(message)>1:
+                    if message[1] in self.stay_channels:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                        self.stay_channels.remove(message[1])
+                        serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
+                    else:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                        serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
+
+            else:
+                notunderstood=True
+        elif cmd=="die":
+            if auteur in self.overops:
+                log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                self.mourir()
+            else:
+                notunderstood=True
+        elif cmd=="quiet":
+            if auteur in self.ops:
+                if len(message)>1:
+                    if message[1] in self.quiet_channels:
+                        serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                    else:
+                        self.quiet_channels.append(message[1])
+                        serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                else:
+                    serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
+            else:
+                notunderstood=True
+        elif cmd=="noquiet":
+            if auteur in self.ops:
+                if len(message)>1:
+                    if message[1] in self.quiet_channels:
+                        self.quiet_channels.remove(message[1])
+                        serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                    else:
+                        serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+            else:
+                notunderstood=True
+        elif cmd=="say":
+            if auteur in self.overops and len(message)>2:
+                serv.privmsg(message[1]," ".join(message[2:]))
+                log(self.serveur,"priv",auteur," ".join(message))
+            elif len(message)<=2:
+                serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
+            else:
+                notunderstood=True
+        elif cmd=="do":
+            if auteur in self.overops and len(message)>2:
+                serv.action(message[1]," ".join(message[2:]))
+                log(self.serveur,"priv",auteur," ".join(message))
+            elif len(message)<=2:
+                serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
+            else:
+                notunderstood=True
+        elif cmd=="kick":
+            if auteur in self.overops and len(message)>2:
+                serv.kick(message[1],message[2]," ".join(message[3:]))
+                log(self.serveur,"priv",auteur," ".join(message))
+            elif len(message)<=2:
+                serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
+            else:
+                notunderstood=True
+        elif cmd=="ops":
+            if auteur in self.overops:
+                serv.privmsg(auteur," ".join(self.ops))
+            else:
+                notunderstood=True
+        elif cmd=="overops":
+            if auteur in self.overops:
+                serv.privmsg(auteur," ".join(self.overops))
+            else:
+                notunderstood=True
+        else:
+            notunderstood=True
+        if notunderstood:
+            serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
+    
+    def on_pubmsg(self, serv, ev):
+        auteur = irclib.nm_to_n(ev.source())
+        canal = ev.target()
+        message = ev.arguments()[0]
+        try:
+            test=bot_unicode(message)
+        except UnicodeBotError:
+            if config.utf8_trigger and not canal in self.quiet_channels:
+                serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
+            return
+        pour_moi,message=self.pourmoi(serv,message)
+        if pour_moi and message.split()!=[]:
+            cmd=message.split()[0].lower()
+            try:
+                args=" ".join(message.split()[1:])
+            except:
+                args=""
+            if cmd in ["meurs","die","crève"]:
+                if auteur in self.overops:
+                    log(self.serveur,canal,auteur,message+"[successful]")
+                    self.mourir()
+                else:
+                    serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
+                    log(self.serveur,canal,auteur,message+"[failed]")
+    
+            elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
+                if auteur in self.ops and (not (canal in self.stay_channels)
+                                           or auteur in self.overops):
+                    self.quitter(canal)
+                    log(self.serveur,canal,auteur,message+"[successful]")
+                    if canal in self.chanlist:
+                        self.chanlist.remove(canal)
+                else:
+                    serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
+                    log(self.serveur,canal,auteur,message+"[failed]")
+            
+            elif cmd in ["deviens","pseudo"]:
+                if auteur in self.ops:
+                    become=args
+                    serv.nick(become)
+                    log(self.serveur,canal,auteur,message+"[successful]")
+    
+            if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
+                serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
+            elif cmd in ["ping"] and not canal in self.quiet_channels:
+                serv.privmsg(canal,"%s: pong"%(auteur))
+            if is_insult(message) and not canal in self.quiet_channels:
+                if is_not_insult(message):
+                    answer=random.choice(config.compliment_answers)
+                    for ligne in answer.split("\n"):
+                        serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
+                else:
+                    answer=random.choice(config.insultes_answers)
+                    for ligne in answer.split("\n"):
+                        serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
+            elif is_compliment(message) and not canal in self.quiet_channels:
+                answer=random.choice(config.compliment_answers)
+                for ligne in answer.split("\n"):
+                    serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
+            if is_tesla(message) and not canal in self.quiet_channels:
+                l1,l2=config.tesla_answers,config.tesla_actions
+                n1,n2=len(l1),len(l2)
+                i=random.randrange(n1+n2)
+                if i>=n1:
+                    serv.action(canal,l2[i-n1].encode("utf8"))
+                else:
+                    serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
+            if is_tamere(message) and not canal in self.quiet_channels:
+                answer=random.choice(config.tamere_answers)
+                for ligne in answer.split("\n"):
+                    serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
+            if is_tag(message) and not canal in self.quiet_channels:
+                if auteur in self.ops:
+                    action=random.choice(config.tag_actions)
+                    serv.action(canal,action.encode("utf8"))
+                    self.quiet_channels.append(canal)
+                else:
+                    answer=random.choice(config.tag_answers)
+                    for ligne in answer.split("\n"):
+                        serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
+            if is_merci(message):
+                answer=random.choice(config.merci_answers)
+                for ligne in answer.split("\n"):
+                    serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
+            if is_bonjour(message) and not canal in self.quiet_channels:
+                if is_night():
+                    answer=random.choice(config.night_answers)
+                elif is_day():
+                    answer=random.choice(config.bonjour_answers)
+                else:
+                    answer=random.choice(config.bonsoir_answers)
+                serv.privmsg(canal,answer.format(auteur).encode("utf8"))
+            if is_bonne_nuit(message) and not canal in self.quiet_channels:
+                answer=random.choice(config.bonne_nuit_answers)
+                serv.privmsg(canal,answer.format(auteur).encode("utf8"))
+        else:
+            if not canal in self.quiet_channels:
+                mypseudo=self.nick
+                if re.match((u"^("+u"|".join(config.bonjour_triggers)
+                                  +ur")( {}| all| tout le monde| (à )?tous)(\.| ?!)?$"
+                             ).format(mypseudo).lower(), message.decode("utf8").strip().lower()):
+                    answer = random.choice(config.bonjour_answers)
+                    serv.privmsg(canal,answer.format(auteur).encode("utf8"))
+
+    def on_action(self, serv, ev):
+        action = ev.arguments()[0]
+        auteur = irclib.nm_to_n(ev.source())
+        channel = ev.target()
+        try:
+            test=bot_unicode(action)
+        except UnicodeBotError:
+            if config.utf8_trigger and not channel in self.quiet_channels:
+                serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
+            return
+        mypseudo=self.nick
+        
+        if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
+            l1,l2=config.bad_action_answers,config.bad_action_actions
+            n1,n2=len(l1),len(l2)
+            i=random.randrange(n1+n2)
+            if i>=n1:
+                serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
+            else:
+                serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
+        if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
+            l1,l2=config.good_action_answers,config.good_action_actions
+            n1,n2=len(l1),len(l2)
+            i=random.randrange(n1+n2)
+            if i>=n1:
+                serv.action(channel,l2[i-n1].format(auteur).format(auteur).encode("utf8"))
+            else:
+                serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
+    
+    def on_kick(self,serv,ev):
+        auteur = irclib.nm_to_n(ev.source())
+        channel = ev.target()
+        victime = ev.arguments()[0]
+        raison = ev.arguments()[1]
+        if victime==self.nick:
+            log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
+            time.sleep(2)
+            serv.join(channel)
+            l1,l2=config.kick_answers,config.kick_actions
+            n1,n2=len(l1),len(l2)
+            i=random.randrange(n1+n2)
+            if i>=n1:
+                serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
+            else:
+                serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
+    
+    def quitter(self,chan,leave_message=None):
+        if leave_message==None:
+            leave_message=random.choice(config.leave_messages)
+        self.serv.part(chan,message=leave_message.encode("utf8"))
+    
+    def mourir(self):
+        quit_message=random.choice(config.quit_messages)
+        self.die(msg=quit_message.encode("utf8"))
+    
+    def _getnick(self):
+        return self.serv.get_nickname()
+    nick=property(_getnick)
+
+
+if __name__=="__main__":
+    import sys
+    if len(sys.argv)==1:
+        print "Usage : skeleton.py <serveur> [--debug]"
+        exit(1)
+    serveur=sys.argv[1]
+    if "debug" in sys.argv or "--debug" in sys.argv:
+        debug=True
+    else:
+        debug=False
+    if "--quiet" in sys.argv:
+        config.debug_stdout=False
+    serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
+              "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
+    try:
+        serveur=serveurs[serveur]
+    except KeyError:
+        print "Server Unknown : %s"%(serveur)
+        exit(404)
+    bot=Skeleton(serveur,debug)
+    bot.start()