]> gitweb.pimeys.fr Git - bots/deconnaisseur.git/blobdiff - deconnaisseur.py
[brown paperbag] faute de frappe
[bots/deconnaisseur.git] / deconnaisseur.py
index 9444519bc7ca3eb60f9c2931b55f3464cb306309..ce0e7a16cc05277be3be76ef0510ca9eabfdaf80 100755 (executable)
@@ -5,32 +5,31 @@
 
 # Un bot IRC qui sort des déconnaissances
 
 
 # Un bot IRC qui sort des déconnaissances
 
-import irclib
-import ircbot
 import threading
 import random
 import time
 import pickle
 import re
 import threading
 import random
 import time
 import pickle
 import re
+import signal
+import sys
+import os
+from remplace_accents import remplace_accents
+
+# 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
 
 
-config_password="PatrickSébastien"
-config_pseudo="deconnaisseur"
-config_chanlist=["#bot","#flood"]
-config_play_channels=["#flood"]
-config_stay_channels=["#flood","#bot"]
-config_overops=["[20-100]","[20-100]_","PEB"]
-config_ops=["Nit","Eguel","Harry"]
+# Fichier de conf
+import config
 
 
-config_source_file_template="deconnaissances.%s.txt" #il faut rajouter le nom du serveur
-def get_config_source_file(serveur):
+def get_config_played_file(serveur):
     serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
     serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
-    return config_source_file_template%(serveurs[serveur])
-ttrig=120 #time trigger (normalement 120, mais diminué pour les tests)
-Ttrig=600 #between two enigms
-config_time_incompressible=60 #on peut pas retrigger en dessous de ce temps (60)
-config_time_incompressible_clue=60 #on peut pas forcer la demande d'indice en dessous
+    return config.played_file_template%(serveurs[serveur])
 
 
-config_score_file="scores.pickle"
+def get_config_logfile(serveur):
+    serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
+    return config.logfile_template%(serveurs[serveur])
 
 class UnicodeBotError(Exception):
     pass
 
 class UnicodeBotError(Exception):
     pass
@@ -40,128 +39,154 @@ def bot_unicode(chain):
     except UnicodeDecodeError:
         raise UnicodeBotError
 
     except UnicodeDecodeError:
         raise UnicodeBotError
 
-def log(channel,auteur=None,message=None):
-    #f=open(config_logfile,"a")
-    #if auteur==message==None:
-    #    chain=channel
-    #else:
-    #    chain="%s [%s:%s] %s"%(time.strftime("%T"),channel,auteur,message)
-    #f.write(chain+"\n")
-    #print chain
-    #f.close()
-    a=0 # does nothing
+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 reussi(message,answer,answer_regexp,auteur):
+    if auteur in config.level3:
+        return answer in message
+    if auteur in config.level2:
+        return remplace_accents(answer) in message
+    else:
+        if re.match(remplace_accents(answer_regexp).lower(),remplace_accents(message).lower()):
+            return True
+
+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 tolere(regexp):
-    """Renvoie une regexp plus tolérante"""
-    reg=unicode(regexp,"utf8").lower()
-    reg=reg.replace(u"á",u"(á|a)").replace(u"à",u"(à|a)").replace(u"â",u"(â|a)").replace(u"ä",u"(ä|a)")
-    reg=reg.replace(u"é",u"(é|e)").replace(u"è",u"(è|e)").replace(u"ê",u"(ê|e)").replace(u"ë",u"(ë|e)")
-    reg=reg.replace(u"í",u"(í|i)").replace(u"ì",u"(ì|i)").replace(u"î",u"(î|i)").replace(u"ï",u"(ï|i)")
-    reg=reg.replace(u"ó",u"(ó|o)").replace(u"ò",u"(ò|o)").replace(u"ô",u"(ô|o)").replace(u"ö",u"(ö|o)")
-    reg=reg.replace(u"ú",u"(ú|u)").replace(u"ù",u"(ù|u)").replace(u"û",u"(û|u)").replace(u"ü",u"(ü|u)")
-    reg=reg.replace(u"ý",u"(ý|y)").replace(u"ỳ",u"(ỳ|y)").replace(u"ŷ",u"(ŷ|y)").replace(u"ÿ",u"(ÿ|y)")
-    reg=reg.replace(u"œ",u"(œ|oe)").replace(u"æ",u"(æ|ae)")
-    return reg
+def is_tag(chain):
+    return is_something(chain,config.tag_triggers)
 
 class RefuseError(Exception):
     pass
 
 class Deconnaisseur(ircbot.SingleServerIRCBot):
     def __init__(self,serveur,debug=False):
 
 class RefuseError(Exception):
     pass
 
 class Deconnaisseur(ircbot.SingleServerIRCBot):
     def __init__(self,serveur,debug=False):
-        temporary_pseudo=config_pseudo+str(random.randrange(10000,100000))
+        temporary_pseudo=config.pseudo+str(random.randrange(10000,100000))
         ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
                                       temporary_pseudo,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
         self.debug=debug
         self.serveur=serveur
         ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
                                       temporary_pseudo,"Un bot irc.[flagellez 20-100, il le mérite]", 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.play_channels=config_play_channels
+        self.overops=config.overops
+        self.ops=self.overops+config.ops
+        self.chanlist=config.chanlist
+        self.stay_channels=config.stay_channels
+        self.play_channels=config.play_channels
         self.play_status={i:[0] for i in self.play_channels}
         self.play_status={i:[0] for i in self.play_channels}
+        self.last_activity={}
+        self.quiet_channels=[]
 
     def give_me_my_pseudo(self,serv):
 
     def give_me_my_pseudo(self,serv):
-        serv.privmsg("NickServ","RECOVER %s %s"%(config_pseudo,config_password))
-        serv.privmsg("NickServ","RELEASE %s %s"%(config_pseudo,config_password))
+        serv.privmsg("NickServ","RECOVER %s %s"%(config.pseudo,config.password))
+        serv.privmsg("NickServ","RELEASE %s %s"%(config.pseudo,config.password))
         time.sleep(0.3)
         time.sleep(0.3)
-        serv.nick(config_pseudo)
+        serv.nick(config.pseudo)
     
     def on_welcome(self, serv, ev):
     
     def on_welcome(self, serv, ev):
+        self.serv=serv # ça serv ira :)
         self.give_me_my_pseudo(serv)
         self.give_me_my_pseudo(serv)
-        serv.privmsg("NickServ","identify %s"%(config_password))
-        log("Connected")
+        serv.privmsg("NickServ","identify %s"%(config.password))
+        log(self.serveur,"Connected")
         if self.debug:
             self.chanlist=["#bot"]
             self.play_channels=["#bot"]
         for c in self.chanlist:
         if self.debug:
             self.chanlist=["#bot"]
             self.play_channels=["#bot"]
         for c in self.chanlist:
-            log("JOIN %s"%(c))
+            log(self.serveur,"JOIN %s"%(c))
             serv.join(c)
             serv.join(c)
+            self.update_activity(c,"") # la chaîne vide ne sera jamais un nom de bot et donc marchera toujours
         for c in self.play_channels:
             token=time.time()-3600
             self.play_status[c]=[0,token]
         for c in self.play_channels:
             token=time.time()-3600
             self.play_status[c]=[0,token]
-            serv.execute_delayed(random.randrange(ttrig),self.start_enigme,(serv,c,token))
+            serv.execute_delayed(random.randrange(config.ttrig),self.start_enigme,(serv,c,token))
 
     def start_enigme(self,serv,channel,token=None):
 
     def start_enigme(self,serv,channel,token=None):
+        # On reste silencieux si lechan n'est pas actif
+        if not self.is_active(channel):
+            serv.execute_delayed(config.ttrig*5,self.start_enigme,(serv,channel,token))
+            return
         if self.play_status[channel][0]==0 and channel in self.play_channels:
             ok="skip"
             if token==self.play_status[channel][-1]:
                 ok="do_it"
             if token==None:
         if self.play_status[channel][0]==0 and channel in self.play_channels:
             ok="skip"
             if token==self.play_status[channel][-1]:
                 ok="do_it"
             if token==None:
-                if time.time() > self.play_status[channel][-1]+config_time_incompressible:
+                if time.time() > self.play_status[channel][-1]+config.time_incompressible:
                     ok="do_it"
                 else:
                     ok="refuse"
             if ok=="do_it":
                 enigme,indice,answer_reg,answer=self.get_enigme()
                     ok="do_it"
                 else:
                     ok="refuse"
             if ok=="do_it":
                 enigme,indice,answer_reg,answer=self.get_enigme()
-                print "%s; %s; %s; %s"%(enigme, indice, answer_reg, answer)
-                serv.privmsg(channel,enigme)
+                log(self.serveur,channel,u"$Énigme$".encode("utf8"),("%s; %s; %s; %s"%(enigme, indice, answer_reg, answer)).encode("utf8"))
+                serv.privmsg(channel,enigme.encode("utf8"))
                 token=time.time()
                 self.play_status[channel]=[1,enigme,indice,answer_reg,answer,token]
                 token=time.time()
                 self.play_status[channel]=[1,enigme,indice,answer_reg,answer,token]
-                serv.execute_delayed(random.randrange(ttrig*3,ttrig*5),self.give_indice,(serv,channel,token))
+                serv.execute_delayed(random.randrange(config.ttrig*3,config.ttrig*5),self.give_indice,(serv,channel,token))
             elif ok=="refuse":
                 raise RefuseError
     def give_indice(self,serv,channel,token):
         if self.play_status[channel][0]==1:
             if token==None:
                 # c'est donc que l'indice a été demandé
             elif ok=="refuse":
                 raise RefuseError
     def give_indice(self,serv,channel,token):
         if self.play_status[channel][0]==1:
             if token==None:
                 # c'est donc que l'indice a été demandé
-                if self.play_status[channel][-1]+config_time_incompressible_clue<time.time():
+                if self.play_status[channel][-1]+config.time_incompressible_clue<time.time():
                     token=self.play_status[channel][-1]
             if self.play_status[channel][-1]==token:
                 indice=self.play_status[channel][2]
                     token=self.play_status[channel][-1]
             if self.play_status[channel][-1]==token:
                 indice=self.play_status[channel][2]
-                serv.privmsg(channel,"indice : %s"%(indice))
+                serv.privmsg(channel,"indice : %s"%(indice).encode("utf8"))
                 self.play_status[channel][0]=2
                 self.play_status[channel][0]=2
-                serv.execute_delayed(random.randrange(ttrig*1,ttrig*3),self.give_answer,(serv,channel,token))
+                serv.execute_delayed(random.randrange(config.ttrig*1,config.ttrig*3),self.give_answer,(serv,channel,token))
     def give_answer(self,serv,channel,token):
         if self.play_status[channel][0]==2 and self.play_status[channel][-1]==token:
             answer=self.play_status[channel][4]
     def give_answer(self,serv,channel,token):
         if self.play_status[channel][0]==2 and self.play_status[channel][-1]==token:
             answer=self.play_status[channel][4]
-            serv.privmsg(channel,"C'était : %s"%(answer))
+            serv.privmsg(channel,"C'était : %s"%(answer).encode("utf8"))
             token=time.time()
             self.play_status[channel]=[0,token]
             token=time.time()
             self.play_status[channel]=[0,token]
-            serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,channel,token))
+            serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,channel,token))
 
     def get_enigme(self):
 
     def get_enigme(self):
-        f=open(get_config_source_file(self.serveur))
+        # on récupère les déconnaissances
+        f=open(config.source_file)
         t=f.read()
         t=f.read()
+        f.close()
         l=re.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t)
         l=re.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t)
-        l=[list(i) for i in l if len(i)==5]
-        l.sort(lambda x,y: cmp(int(x[4]),int(y[4])))
+        dec={int(i[0]):list(i[1:]) for i in l if len(i)==5}
+        # on va chercher combien de fois elles ont été jouées
+        played_file=get_config_played_file(self.serveur)
+        f=open(played_file)
+        t=f.read()
+        f.close()
+        l=re.findall("(.*):(.*)",t)
+        played={int(i[0]):int(i[1]) for i in l}
         # on récupère le nombre d'occurrences le plus faible
         # on récupère le nombre d'occurrences le plus faible
-        mini=l[0][4]
-        # on garde que ceux qui ont le même nombre d'occurrences
-        l_mini=[en for en in l if en[4]==mini]
-        # on tire au hasard dedans
-        choisi=random.randrange(len(l_mini))
-        enigme,indice,answer_reg,answer,_=l_mini[choisi]
-        real_index=l.index(l_mini[choisi])
-        l[real_index][4]=str(int(l[real_index][4])+1)
-        f=open(get_config_source_file(self.serveur),"w")
-        f.write("%\n"+"\n%\n".join(["%s\n%s\n%s\n%s\n%s"%(i[0],i[1],i[2],i[3],i[4]) for i in l])+"\n%")
+        mini=min(played.values())
+        # on choisit un id dans ceux qui ont ce nombre d'occurences
+        id_choisi=random.choice([k for k,v in played.items() if v==mini])
+        enigme,indice,answer_reg,answer=dec[id_choisi]
+        # on incrémente la choisie
+        played[id_choisi]+=1
+        # on enregistre le played_file
+        f=open(played_file,"w")
+        f.write("\n".join(["%-3s : %s"%(k,v) for k,v in played.items()]))
         f.close()
         f.close()
-        return enigme,indice,answer_reg,answer
+        return enigme.decode("utf8"),indice.decode("utf8"),answer_reg.decode("utf8"),answer.decode("utf8")
 
     def pourmoi(self, serv, message):
 
     def pourmoi(self, serv, message):
-        pseudo=serv.get_nickname()
+        pseudo=self.nick
         size=len(pseudo)
         if message[:size]==pseudo and len(message)>size and message[size]==":":
             return (True,message[size+1:].strip(" "))
         size=len(pseudo)
         if message[:size]==pseudo and len(message)>size and message[size]==":":
             return (True,message[size+1:].strip(" "))
@@ -174,8 +199,8 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         try:
             test=bot_unicode(message)
         except UnicodeBotError:
         try:
             test=bot_unicode(message)
         except UnicodeBotError:
-            serv.privmsg(auteur,
-              "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
+            if config.utf8_trigger:
+                serv.privmsg(auteur, config.utf8_fail_answers)
             return
         message=message.split()
         cmd=message[0].lower()
             return
         message=message.split()
         cmd=message[0].lower()
@@ -189,7 +214,10 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
  JOIN       Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
  LEAVE      Faire quitter un channel
  PLAY       Passe un channel en mode "jouer"
  JOIN       Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
  LEAVE      Faire quitter un channel
  PLAY       Passe un channel en mode "jouer"
- NOPLAY     Passe un channel en mode "ne pas jouer" """
+ NOPLAY     Passe un channel en mode "ne pas jouer"
+ QUIET      Se taire sur un channel
+ NOQUIET    Opposé de QUIET
+ RELOAD     Recharge la config"""
             helpmsg_overops="""
  SCORES {DEL|ADD|SUB}   Tu veux un dessin ?
  SAY        Fais envoyer un message sur un chan ou à une personne
             helpmsg_overops="""
  SCORES {DEL|ADD|SUB}   Tu veux un dessin ?
  SAY        Fais envoyer un message sur un chan ou à une personne
@@ -212,8 +240,9 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                     else:
                         serv.join(message[1])
                         self.chanlist.append(message[1])
                     else:
                         serv.join(message[1])
                         self.chanlist.append(message[1])
+                        self.update_activity(message[1],"")
                         serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
                         serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
-                        log("priv",auteur," ".join(message))
+                        log(self.serveur,"priv",auteur," ".join(message))
                 else:
                     serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
             else:
                 else:
                     serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
             else:
@@ -222,12 +251,12 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
             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:
             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:
-                        serv.part(message[1])
+                        self.quitter(message[1]," ".join(message[2:]))
                         self.chanlist.remove(message[1])
                         self.chanlist.remove(message[1])
-                        log("priv",auteur," ".join(message)+"[successful]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
                     else:
                         serv.privmsg(auteur,"Non, je reste !")
                     else:
                         serv.privmsg(auteur,"Non, je reste !")
-                        log("priv",auteur," ".join(message)+"[failed]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
                 else:
                     serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
             else:
                 else:
                     serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
             else:
@@ -237,11 +266,11 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                 if len(message)>1:
                     if message[1] in self.stay_channels:
                         serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
                 if len(message)>1:
                     if message[1] in self.stay_channels:
                         serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
-                        log("priv",auteur," ".join(message)+"[failed]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
                     else:
                         self.stay_channels.append(message[1])
                         serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
                     else:
                         self.stay_channels.append(message[1])
                         serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
-                        log("priv",auteur," ".join(message)+"[successful]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
                 else:
                     serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
             else:
                 else:
                     serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
             else:
@@ -252,10 +281,10 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                     if message[1] in self.stay_channels:
                         self.stay_channels.remove(message[1])
                         serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
                     if message[1] in self.stay_channels:
                         self.stay_channels.remove(message[1])
                         serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
-                        log("priv",auteur," ".join(message)+"[successful]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
                     else:
                         serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
                     else:
                         serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
-                        log("priv",auteur," ".join(message)+"[failed]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
             else:
                 notunderstood=True
         elif cmd=="play":
             else:
                 notunderstood=True
         elif cmd=="play":
@@ -263,12 +292,12 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                 if len(message)>1:
                     if message[1] in self.play_channels:
                         serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
                 if len(message)>1:
                     if message[1] in self.play_channels:
                         serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
-                        log("priv",auteur," ".join(message)+"[failed]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
                     else:
                         self.play_channels.append(message[1])
                         self.play_status[message[1]]=[0,time.time()-3600]
                         serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
                     else:
                         self.play_channels.append(message[1])
                         self.play_status[message[1]]=[0,time.time()-3600]
                         serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
-                        log("priv",auteur," ".join(message)+"[successful]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
                 else:
                     serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
             else:
                 else:
                     serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
             else:
@@ -279,27 +308,60 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                     if message[1] in self.play_channels:
                         self.play_channels.remove(message[1])
                         serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
                     if message[1] in self.play_channels:
                         self.play_channels.remove(message[1])
                         serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
-                        log("priv",auteur," ".join(message)+"[successful]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
                     else:
                         serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
                     else:
                         serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
-                        log("priv",auteur," ".join(message)+"[failed]")
+                        log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
+            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=="reload":
+            if auteur in self.ops:
+                self.reload(auteur)
+                log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
             else:
                 notunderstood=True
         elif cmd in ["states","status"]:
             if auteur in self.overops:
                 for k in self.play_status.keys():
             else:
                 notunderstood=True
         elif cmd in ["states","status"]:
             if auteur in self.overops:
                 for k in self.play_status.keys():
-                    serv.privmsg(auteur,"%s : %s"%(k,"; ".join([str(i) for i in self.play_status[k]])))
+                    serv.privmsg(auteur,(u"%s : %s"%(k,"; ".join([unicode(i) for i in self.play_status[k]]))).encode("utf8") )
         elif cmd=="say":
             if auteur in self.overops and len(message)>2:
                 serv.privmsg(message[1]," ".join(message[2:]))
         elif cmd=="say":
             if auteur in self.overops and len(message)>2:
                 serv.privmsg(message[1]," ".join(message[2:]))
-                log("priv",auteur," ".join(message))
+                log(self.serveur,"priv",auteur," ".join(message))
             elif len(message)<=2:
                 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
             else:
                 notunderstood=True
         elif cmd=="die":
             if auteur in self.overops:
             elif len(message)<=2:
                 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
             else:
                 notunderstood=True
         elif cmd=="die":
             if auteur in self.overops:
-                self.die()
+                log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
+                self.mourir()
         elif cmd=="score":
             if len(message)>1:
                 if len(message) in [3,4] and message[1].lower()=="transfert":
         elif cmd=="score":
             if len(message)>1:
                 if len(message) in [3,4] and message[1].lower()=="transfert":
@@ -340,7 +402,6 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
                 # trie par pseudo
                 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
                 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
                 # trie par pseudo
                 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
-                scores.reverse()
                 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
             elif auteur in self.overops:
                 souscmd=message[1].lower()
                 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
             elif auteur in self.overops:
                 souscmd=message[1].lower()
@@ -383,13 +444,13 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         auteur = irclib.nm_to_n(ev.source())
         canal = ev.target()
         message = ev.arguments()[0]
         auteur = irclib.nm_to_n(ev.source())
         canal = ev.target()
         message = ev.arguments()[0]
+        self.update_activity(canal,auteur)
         try:
             test=bot_unicode(message)
         except UnicodeBotError:
         try:
             test=bot_unicode(message)
         except UnicodeBotError:
-            if not canal in self.quiet_channels:
-                serv.privmsg(canal,
-                   "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
-                return
+            if not canal in self.quiet_channels and config.utf8_trigger:
+                serv.privmsg(canal, "%s: %s"%(auteur,config.utf8_fail_answers))
+            return
         tryother=False
         pour_moi,message=self.pourmoi(serv,message)
         if pour_moi and message.split()!=[]:
         tryother=False
         pour_moi,message=self.pourmoi(serv,message)
         if pour_moi and message.split()!=[]:
@@ -400,34 +461,37 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                 args=""
             if cmd in ["meurs","die","crève"]:
                 if auteur in self.overops:
                 args=""
             if cmd in ["meurs","die","crève"]:
                 if auteur in self.overops:
-                    self.die()
-                    log(canal,auteur,message+"[successful]")
+                    self.mourir()
+                    log(self.serveur,canal,auteur,message+"[successful]")
                 else:
                     serv.privmsg(canal,"%s: crève !"%(auteur))
                 else:
                     serv.privmsg(canal,"%s: crève !"%(auteur))
-                    log(canal,auteur,message+"[failed]")
-            if cmd in ["meur", "meurt","meurre","meurres"]:
-                serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du pluriel : "meurs" (de rien)'%(auteur))
-            if cmd in ["part","leave","dégage"]:
+                    log(self.serveur,canal,auteur,message+"[failed]")
+            elif 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 == "reload":
+                if auteur in self.ops:
+                    log(self.serveur, canal, auteur, message+"[successful]")
+                    self.reload(canal)
+            elif cmd in ["part","leave","dégage"]:
                 if auteur in self.ops and (not (canal in self.stay_channels)
                                            or auteur in self.overops):
                 if auteur in self.ops and (not (canal in self.stay_channels)
                                            or auteur in self.overops):
-                    serv.part(canal,message="Éjecté par %s"%(auteur))
-                    log(canal,auteur,message+"[successful]")
+                    self.quitter(canal)
+                    log(self.serveur,canal,auteur,message+"[successful]")
                     self.chanlist.remove(canal)
                 else:
                     serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
                     self.chanlist.remove(canal)
                 else:
                     serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
-                    log(canal,auteur,message+"[failed]")
+                    log(self.serveur,canal,auteur,message+"[failed]")
     
     
-            if cmd in ["deviens","pseudo"]:
+            elif cmd in ["deviens","pseudo"]:
                 if auteur in self.ops:
                     become=args
                     serv.nick(become)
                 if auteur in self.ops:
                     become=args
                     serv.nick(become)
-                    log(canal,auteur,message+"[successful]")
-    
-            if cmd in ["coucou"]:
+                    log(self.serveur,canal,auteur,message+"[successful]")
+            elif cmd in ["coucou"] and not canal in self.quiet_channels:
                 serv.privmsg(canal,"%s: coucou"%(auteur))
                 serv.privmsg(canal,"%s: coucou"%(auteur))
-            if cmd in ["ping"]:
+            elif cmd in ["ping"] and not canal in self.quiet_channels:
                 serv.privmsg(canal,"%s: pong"%(auteur))
                 serv.privmsg(canal,"%s: pong"%(auteur))
-            if cmd in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
+            elif cmd in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
                 if canal in self.play_channels:
                     if self.play_status.get(canal,[-1])[0]==0:
                         try:
                 if canal in self.play_channels:
                     if self.play_status.get(canal,[-1])[0]==0:
                         try:
@@ -435,11 +499,33 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                         except RefuseError:
                             serv.privmsg(canal,"%s: Je peux souffler une minute ?"%(auteur))
                     else:
                         except RefuseError:
                             serv.privmsg(canal,"%s: Je peux souffler une minute ?"%(auteur))
                     else:
-                        serv.privmsg(canal,"%s: Rappel : %s"%(auteur,self.play_status[canal][1]))
+                        serv.privmsg(canal,("%s: Rappel : %s"%(auteur,self.play_status[canal][1])).encode("utf8") )
                 else:
                     serv.privmsg(canal,"%s: pas ici…"%(auteur))
                 else:
                     serv.privmsg(canal,"%s: pas ici…"%(auteur))
-            if cmd=="indice" and canal in self.play_channels:
+            elif cmd in ["score","!score"]:
+                serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
+            elif cmd in ["scores","!scores"]:
+                scores=self.get_scores().items()
+                # trie par score
+                scores.sort(lambda x,y:cmp(x[1],y[1]))
+                scores.reverse()
+                serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
+                # trie par pseudo
+                scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
+                serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
+            elif cmd=="indice" and canal in self.play_channels:
                 self.give_indice(serv,canal,None)
                 self.give_indice(serv,canal,None)
+            elif 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")))
+            elif "Bâille, cru aile ou orld" in message:
+                self.mourir(u"Un de mes easters eggs (non en fait j'en ai qu'un) a été découvert par %s !"%auteur)
             else:
                 tryother=True
         else:
             else:
                 tryother=True
         else:
@@ -447,15 +533,46 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         if tryother:
             if self.play_status.get(canal,[-1])[0] in [1,2]:
                 answer_regexp=self.play_status[canal][3]
         if tryother:
             if self.play_status.get(canal,[-1])[0] in [1,2]:
                 answer_regexp=self.play_status[canal][3]
-                if re.match(tolere(answer_regexp),unicode(message,"utf8").lower()):
-                    answer=self.play_status[canal][4]
-                    serv.privmsg(canal,"%s: bravo ! (C'était %s)"%(auteur,answer))
+                answer=self.play_status[canal][4]
+                if reussi(message.decode("utf8"),answer,answer_regexp,auteur):
+                    serv.privmsg(canal,(u"%s: bravo ! (C'était %s)"%(auteur,answer)).encode("utf8"))
+                    log(self.serveur,canal,auteur+"$win",message)
                     self.add_score(auteur,1)
                     token=time.time()
                     self.play_status[canal]=[0,token]
                     self.add_score(auteur,1)
                     token=time.time()
                     self.play_status[canal]=[0,token]
-                    serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,canal,token))
+                    serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,canal,token))
+    
+    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(5)
+            serv.join(channel)
+            self.update_activity(channel,"")
+            # on ne dit rien au rejoin
+            #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=None):
+        if quit_message==None:
+            quit_message=random.choice(config.quit_messages)
+        self.die(msg=quit_message.encode("utf8"))
+    
     def get_scores(self):
     def get_scores(self):
-        f=open(config_score_file)
+        f=open(config.score_file)
         scores=pickle.load(f)
         f.close()
         return scores
         scores=pickle.load(f)
         f.close()
         return scores
@@ -469,26 +586,97 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         self.save_scores(scores)
 
     def save_scores(self,scores):
         self.save_scores(scores)
 
     def save_scores(self,scores):
-        f=open(config_score_file,"w")
+        f=open(config.score_file,"w")
         pickle.dump(scores,f)
         f.close()
         pickle.dump(scores,f)
         f.close()
+    
+    def _getnick(self):
+        return self.serv.get_nickname()
+    nick = property(_getnick)
+    
+    def update_activity(self,canal,pseudo):
+        if not pseudo in config.idle_bots:
+            self.last_activity[canal]=time.time()
+    def is_active(self,canal):
+        return time.time()-self.last_activity[canal]<config.idle_time
+    
+    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 start_as_daemon(self, outfile):
+        sys.stderr = Logger(outfile)
+        self.start()
+
+
+class Logger(object):
+    """Pour écrire ailleurs que sur stdout"""
+    def __init__(self, filename="deconnaisseur.full.log"):
+        self.filename = filename
+
+    def write(self, message):
+        f = open(self.filename, "a")
+        f.write(message)
+        f.close()
+
 
 if __name__=="__main__":
     import sys
     if len(sys.argv)==1:
 
 if __name__=="__main__":
     import sys
     if len(sys.argv)==1:
-        print "Usage : deconnaisseur.py <serveur> [--debug]"
+        print "Usage : deconnaisseur.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]
         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
     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 "debug" in sys.argv or "--debug" in sys.argv:
         debug=True
     else:
         debug=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/deconnaisseur.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)
     try:
         serveur=serveurs[serveur]
     except KeyError:
         print "Server Unknown : %s"%(serveur)
         exit(404)
-    deco=Deconnaisseur(serveur,debug)
-    deco.start()
+    deconnaisseur=Deconnaisseur(serveur,debug)
+    # Si on reçoit un SIGHUP, on reload la config
+    def sighup_handler(signum, frame):
+        deconnaisseur.reload("SIGHUP")
+    signal.signal(signal.SIGHUP, sighup_handler)
+    if daemon:
+        child_pid = os.fork()
+        if child_pid == 0:
+            os.setsid()
+            deconnaisseur.start_as_daemon(outfile)
+        else:
+            # on enregistre le pid de deconnaisseur
+            pidfile = "/var/run/bots/deconnaisseur.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:
+        deconnaisseur.start()
\ No newline at end of file