Initialisation du repo d'iBot, honteusement copié sur Basile.
authorVincent Le Gallic <legallic@crans.org>
Wed, 10 Oct 2012 11:44:55 +0000 (13:44 +0200)
committerVincent Le Gallic <legallic@crans.org>
Wed, 10 Oct 2012 11:44:55 +0000 (13:44 +0200)
.gitignore [new file with mode: 0644]
config.py [new file with mode: 0644]
ibot.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..e70fe59
--- /dev/null
+++ b/config.py
@@ -0,0 +1,166 @@
+#!/usr/bin/python
+# -*- coding:utf8 -*-
+
+# Configuration de Basile
+
+debug_stdout=True
+
+# la config irc-related
+irc_password="iPassword"
+irc_pseudo="iBot"
+chanlist=["#bot", "#iflood"]
+stay_channels=["#bot", "#iflood"]
+i_channels=["#iflood"]
+quiet_channels=[]
+
+# les logs
+logfile_template="ibot.%s.log"
+
+# les ops
+overops=["[20-100]","[20-100]_"]
+ops=[]
+report_bugs_to=["[20-100]"]
+
+# config UTF8-fail
+utf8_fail_answers = [u"Si je n'avais pas été créé avec la plus grande attention, votre encodage aurait eu raison de moi…"]
+utf8_trigger = False
+# 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"Oh non ! Quelle insulte ! Je crois que je ne m'en relèverai jamais…\nEnfin presque.",
+u"J'entends comme un vague murmure, vous disiez ?",
+u"Je vais prendre ça pour un compliment.",
+u"Vous savez, pour vous c'est peut-être une insulte, mais pour moi ce n'est qu'une suite de 0 et de 1…",
+u"Permettez-moi de vous retourner le compliment.",
+u"Votre indélicatesse vous sied à ravir.",
+u"Parfois, je me demande pourquoi je fais encore ce métier…",
+u"Le saviez-vous : l'invective ne déshonore que son auteur.",
+u"Le saviez-vous : vous perdez plus de temps à m'insulter qu'à vous taire.",
+u"Mais je ne vous permets pas ! Enfin, pas comme ça…"]
+
+# config "à peine quelques kilos octets"
+gros=[u"gros",u"énorme",u"lourd"]
+
+# config spéciale-iota
+buffer_fail_answers=[u"Pas de chance !",u"Révisez vos classiques !",
+u"Encore un effort, je sais que vous pouvez le faire. ;)",
+u"Where did you learn to type?"]
+
+# config "jeu", d'ailleurs, j'ai perdu.
+premier_groupe_terminaisons=u"(e|es|ons|ez|ent|er(|ai|as|a|ons|ez|ont)|(|er)(ais|ait|ions|iez|aient)|(a(i|s|)|â(mes|tes|t)|èrent)|ass(e(|s|nt)|i(ons|ez))|é(|s|e|es))"
+regexp_etre=u"(être|suis|e(s|t)|so(mmes|nt)|êtes|(ét|ser)(ai(s|t|ent)|i(ons|ent)|)|ser(ai|as|a|ons|ez|ont)|so(i(s|t|ent)|y(ons|ez))|f(u(s|t|rent)|û(mes|tes|t))|fuss(e(|s|nt)|i(ons|ez))|étant)"
+regexp_etre_avec_c=u"c'(e(s|st)|étai(t|ent))"
+regexp_faire=u"fais"
+perdu=[u"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
+u"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
+u"perdiss(e(|s|nt)|i(ons|ez))",
+u"perdu(|s|e|es)",u"perdant(|s|e|es)",u"perte(|s)",
+
+u"(gagn|trouv)"+premier_groupe_terminaisons,u"gagnant(|s|e|es)",u"gain(|s)",
+
+u"trouvant",u"trouvaille(|s)",
+
+u"victoire(|s)",u"vaincu(|s|e|es)",
+u"loose",u"lost",u"looser(|s)",u"win(|ner)(|s)",
+u"jeu(|x)",u"game(|s)"]
+time_between_perdu_trigger=3600*3 #temps moyen pour perdre en l'absence de trigger
+time_between_perdu_trigger_delta = 30*60 #marge autorisée autour de ^^^
+time_between_perdu=30*60 #temps pendant lequel on ne peut pas perdre
+
+# 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"se tait",u"se tient coi"]
+tag_answers=[
+u"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
+u"Dès que cela sera utile.",
+u"Une autre fois, peut-être.",
+u"Si je me tais, qui vous rappellera combien vous me devez ?",
+u"J'aurais aimé accéder à votre requête, mais après mûre réflexion, j'en ai perdu l'envie.",
+u"Je ne ressens pas de besoin irrésistible de me taire, navré."]
+
+# config ping
+tesla_triggers=[u"t('|u )es là \?",u"\?",u"plop \?",u"plouf \?"]
+tesla_answers=[
+u"Oui, je suis là.",
+u"J'écoute.",
+u"En quoi puis-je me rendre utile ?",
+u"On a besoin de moi ?"
+]
+tesla_actions=[u"est là",u"attend des instructions",u"est toujours disponible"]
+
+# config en cas de non-insulte
+compliment_triggers=[u"gentil",u"cool",u"sympa",u"efficace"]
+compliment_answers=[
+u"Merci, c'est gentil de votre part. :)",
+u"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",
+u"Je vous remercie.",
+u"C'est trop d'honneur.",
+u"Vous êtes bien aimable."
+]
+
+# config merci
+merci_triggers=[u"merci",u"remercie",u"thx",u"thank(|s)"]
+merci_answers=[u"Mais de rien.",u"À votre service. ;)",u"Quand vous voulez. :)",
+u"Tout le plaisir est pour moi."]
+
+# config "ta mère" 
+tamere_triggers=[u"ta mère"]
+tamere_answers=[u"Laissez donc ma mère en dehors de ça !",
+u"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
+u"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
+u"""N'avait-on pas dit "pas les mamans" ?"""]
+
+# config pour les actions désagréables à Basile
+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"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
+u"{}: Pourquoi tant de violence en ce monde si doux ?",
+u"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
+u"{}: J'aurais préféré que vous ne fassiez pas cela en public.",
+u"{}: Entre nous, cela vous gratifie-t-il ?",
+u"{}: Une telle relation entre nous deux n'est pas saine, revenons à quelque chose de plus conventionnel. :D",
+u"J'ai la désagréable impression que {} cherche comment tuer le temps en ce moment…"
+]
+bad_action_actions=[u"prend de la distance, par précaution…",u"esquive",u"est bon pour prendre une semaine de repos… virtuel !",u"n'aime pas servir de souffre douleur, mais n'a malheureusement pas le choix", u"s'en souviendra sans doute longtemps… de quoi parlait-on déjà ?"]
+
+# config pour les actions agréables à Basile
+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":D",u"{}: Moi aussi je vous aime. ♡",u"Tant de délicatesse ne saurait être ignorée !",u"Pour une fois que quelqu'un me considère à ma juste valeur…"]
+good_action_actions=[u"ronronne",u"aimerait exprimer avec des mots simples le bonheur que {} lui procure !",u"éprouve une joie indescriptible",u"apprécie que des personnes comme {} soient sur IRC, sans quoi il n'y aurait sans doute jamais personne pour tenir compte de lui"]
+
+# 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"Bien le bonjour, {}.",u"Bonjour {}.",u"{}: bonjour.",u"{}: Quel beau temps aujourd'hui (arrêtez-moi si je me trompe) !",u"Meteo: Cachan"]
+bonsoir_answers=[u"Bonsoir {} !",u"{}: bonsoir.",u"Quel beau te… euh… bonsoir !",u"{}: Je cherche désespérément une formule pour vous dire bonsoir, mais j'avoue que mon lexique est un peu… limité."]
+night_answers=[u"{}: vous m'avez fait peur, je m'étais assoupi !", u"Debout à une heure pareille, {} ? Que vous arrive-t-il ?",u"Vous venez prendre la relève, {} ?"]
+daytime = [7,18]
+nighttime = [3, 6]
+
+# config dodo
+bonne_nuit_triggers=[u"bonne nuit",u"'?nite",u"'?nuit",u"'?night",u"good night",u"'?nenuit"]
+bonne_nuit_answers=[u"{}: thanks, make sweet dreams tonight ! ;)",u"Bonne nuit {}.",u"À demain {}. :)",u"{}: si seulement j'avais le droit de dormir… enfin, bonne nuit !",u"{}: à vous aussi !"]
+
+# config PEB est encore en train d'abuser de ses droits.
+kick_answers=[u"Suis-je de trop ici ?",u"{}: je m'excuse pour ce bruit indu qui a stimulé votre colère",u"{} a le /kick facile, sans doute la fatigue.",u"{}: j'ai l'impression que vous n'allez pas bien aujourd'hui, vous vous en prenez à un robot !"]
+kick_actions=[u"sera désormais exemplaire",u"prépare une lettre d'excuses à {}",u"essaiera de ne plus s'attirer les foudres de {}",u"croyait avoir tout bien fait… cruelle déception."]
+
+# config on m'a demandé de mourir/partir
+quit_messages=[u"Bien que cela me désole, je me vois dans l'obligation de vous abandonner."]
+leave_messages=quit_messages
+
+quit_fail_messages = [u"Navré, mais je me vois contraint de refuser, je ne peux pas céder aux exigences du premier venu."]
+leave_fail_messages = quit_fail_messages
+pas_programme_pour_tobeir = [u"Encore eût-il fallu que je fusse programmé pour vous obéir !"]
diff --git a/ibot.py b/ibot.py
new file mode 100755 (executable)
index 0000000..9fadb8a
--- /dev/null
+++ b/ibot.py
@@ -0,0 +1,562 @@
+#!/usr/bin/python
+# -*- coding:utf8 -*-
+
+# Codé par 20-100 (commencé le 23/04/12)
+
+# Un bot IRC qui, un jour, s'interfacera avec la Note Kfet 2015
+
+import threading
+import random
+import time
+import socket, ssl, json
+import pickle
+import re
+import os
+import signal
+import sys
+
+# Oui, j'ai recodé ma version d'irclib pour pouvoir rattrapper les SIGHUP
+sys.path.insert(0, "/home/vincent/scripts/python-myirclib")
+import irclib
+import ircbot
+
+from commands import getstatusoutput as ex
+
+# on récupère la config
+import config
+
+# la partie qui réfère au fichier lui-même est mieux ici
+# sinon on réfère la config et pas le fichier lui-même
+import os
+config.thisfile= os.path.realpath( __file__ )
+
+def get_config_logfile(serveur):
+    serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
+    return config.logfile_template%(serveurs[serveur])
+
+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_perdu(chain):
+    return is_something(chain,config.perdu)
+def is_tag(chain):
+    return is_something(chain,config.tag_triggers)
+def is_gros(chain):
+    return is_something(chain,config.gros)
+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
+
+class CrashError(Exception):
+    """Pour pouvoir faire crasher le bot, parce que ça a l'air drôle"""
+    pass
+
+def bot_unicode(chain):
+    try:
+        unicode(chain,"utf8")
+    except UnicodeDecodeError as exc:
+        raise UnicodeBotError
+
+
+class Ibot(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,"iDon't care", 10)
+        self.debug=debug
+        self.serveur=serveur
+        self.overops=config.overops
+        self.ops=self.overops+config.ops
+        self.report_bugs_to=config.report_bugs_to
+        self.chanlist=config.chanlist
+        self.stay_channels=config.stay_channels
+        self.i_channels=config.i_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],
+ "ichannel": [None, """ICHANNEL <channel>,
+ Rend le channel i-nazi""", None],
+ "noichannel": [None, """NOICHANNEL <channel>,
+ Dé-i-nazifie le channel""", None],
+ "reload": [None,"""RELOAD
+ Recharge la configuration.""",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."""],
+ "crash": [None,None,"""CRASH
+ Me fait crasher"""]
+ }
+            helpmsg_default="Liste des commandes disponibles :\nHELP "
+            helpmsg_ops=" JOIN LEAVE QUIET NOQUIET LOST RELOAD ICHANNEL NOICHANNEL"
+            helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE CRASH"
+            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=="ichannel":
+            if auteur in self.ops:
+                if len(message)>1:
+                    if message[1] in self.i_channels:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                        serv.privmsg(auteur,"%s est déjà i-nazi."%(message[1]))
+                    else:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                        self.i_channels.append(message[1])
+                        serv.privmsg(auteur,"I-channels : "+" ".join(self.i_channels))
+                else:
+                    serv.privmsg(auteur,"I-channels : "+" ".join(self.i_channels))
+            else:
+                notunderstood=True
+        elif cmd=="nostay":
+            if auteur in self.ops:
+                if len(message)>1:
+                    if message[1] in self.i_channels:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                        self.i_channels.remove(message[1])
+                        serv.privmsg(auteur,"I-channels : "+" ".join(self.i_channels))
+                    else:
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+                        serv.privmsg(auteur,"%s n'est pas i-nazi."%(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=="crash":
+            if auteur in self.overops:
+                log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                self.crash()
+            else:
+                notunderstood=True
+        elif cmd=="reload":
+            if auteur in self.ops:
+                self.reload(auteur)
+                log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+            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()
+            elif cmd == "reload":
+                if auteur in self.ops:
+                    log(self.serveur, canal, auteur, message+"[successful]")
+                    self.reload(canal)
+            elif cmd == "crash":
+                if auteur in self.overops:
+                    self.crash()
+            elif cmd in ["part","leave","dégage","va-t-en"]:
+                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)
+            elif cmd in ["deviens","pseudo"]:
+                if auteur in self.ops:
+                    become=args
+                    serv.nick(become)
+                    log(self.serveur,canal,auteur,message+"[successful]")
+    
+        else:
+            if not re.match(u'i.*',message.decode("utf8").lower().strip(u"  ")) and canal in self.i_channels:
+                serv.kick(canal, auteur, u"iKick".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
+        
+    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)
+    
+    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)
+
+    def reload(self, auteur=None):
+        reload(config)
+        if auteur in [None, "SIGHUP"]:
+            towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
+            for to in config.report_bugs_to:
+                self.serv.privmsg(to, towrite)
+            log(self.serveur, towrite)
+        else:
+            self.serv.privmsg(auteur,"Config reloaded")
+    
+    def crash(self):
+        raise CrashError
+    
+    def start_as_daemon(self, outfile):
+        sys.stderr = Logger(outfile)
+        self.start()
+    
+
+class Logger(object):
+    """Pour écrire ailleurs que sur stdout"""
+    def __init__(self, filename="ibot.full.log"):
+        self.filename = filename
+
+    def write(self, message):
+        f = open(self.filename, "a")
+        f.write(message)
+        f.close()
+
+def main():
+    if len(sys.argv)==1:
+        print "Usage : ibot.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
+        print "        --outfile sans --no-output ni --daemon n'a aucun effet"
+        exit(1)
+    serveur=sys.argv[1]
+    if "--daemon" in sys.argv:
+        thisfile = os.path.realpath(__file__)
+        thisdirectory = thisfile.rsplit("/", 1)[0]
+        os.chdir(thisdirectory)
+        daemon = True
+    else:
+        daemon = False
+    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"}
+    if "--no-output" in sys.argv or "--daemon" in sys.argv:
+        outfile = "/var/log/bots/ibot.full.log"
+        for arg in sys.argv:
+            arg = arg.split("=")
+            if arg[0].strip('-') in ["out", "outfile", "logfile"]:
+                outfile = arg[1]
+        sys.stdout = Logger(outfile)
+    try:
+        serveur=serveurs[serveur]
+    except KeyError:
+        print "Server Unknown : %s"%(serveur)
+        exit(404)
+    ibot=Ibot(serveur,debug)
+    # Si on reçoit un SIGHUP, on reload la config
+    def sighup_handler(signum, frame):
+        ibot.reload("SIGHUP")
+    signal.signal(signal.SIGHUP, sighup_handler)
+    if daemon:
+        child_pid = os.fork()
+        if child_pid == 0:
+            os.setsid()
+            ibot.start_as_daemon(outfile)
+        else:
+            # on enregistre le pid du bot
+            pidfile = "/var/run/bots/ibot.pid"
+            for arg in sys.argv:
+                arg = arg.split("=")
+                if arg[0].strip('-') in ["pidfile"]:
+                    pidfile = arg[1]
+            f = open(pidfile, "w")
+            f.write("%s\n" % child_pid)
+            f.close()
+    else:
+        ibot.start()
+
+if __name__ == "__main__":
+    main()