]> gitweb.pimeys.fr Git - bots/historien.git/blobdiff - historien.py
Ignorer marcel
[bots/historien.git] / historien.py
index bc8e3fe85d81dad74e524f981253f1f91f74f621..66a4ff6a9b604b2d7d79793c1da72445a6e0d32d 100755 (executable)
@@ -5,55 +5,31 @@
 
 # Un bot IRC qui pose des questions d'histoire
 
 
 # Un bot IRC qui pose des questions d'histoire
 
-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 cast_as_date import *
 
 
+# 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="EtTaMère,ElleEstNéeQuand?"
-config_pseudo="historien"
-config_chanlist=["#bot","#flood"]
-config_play_channels=["#flood"]
-config_stay_channels=["#flood","#bot"]
-config_overops=["[20-100]","[20-100]_"]
-config_ops=["PEB","Petite-Peste"]
+# Fichier de conf
+import config
 
 
-config_source_file="dates.txt"
-config_played_file_template="played.%s.txt" #il faut rajouter le nom du serveur
 def get_config_played_file(serveur):
     serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
 def get_config_played_file(serveur):
     serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
-    return config_played_file_template%(serveurs[serveur])
-ttrig=120 #time trigger (normalement 120, mais diminué pour les tests)
-Ttrig=600 #between two enigms
-config_time_incompressible=15 #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
-
-config_score_file="scores.pickle"
-
-config_tag_triggers=[u"t(|a)g",u"ta gueule",u"la ferme",u"ferme( |-)la",u"tais-toi",u"chut"]
-config_tag_actions=[u"se tait",u"ferme sa gueule",u"se la ferme",u"la ferme"]
-config_tag_answers=[u"J'me tais si j'veux !",
-u"Je t'entends pas :°",
-u"Héhé, try again",
-u"Non, j'ai pas envie",
-u"Peut-être quand toi tu la fermeras, et encore…"]
+    return config.played_file_template%(serveurs[serveur])
 
 
-config_level2=[]
-config_level3=[]
-
-config_debug_stdout = True
-config_logfile_template="historien.%s.log"
 def get_config_logfile(serveur):
     serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
 def get_config_logfile(serveur):
     serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
-    return config_logfile_template%(serveurs[serveur])
-
-config_quit_messages=[u"%s : %s quitte le serveur IRC"]
-
-config_leave_messages=[u"%s : %s quitte le channel"]
+    return config.logfile_template%(serveurs[serveur])
 
 class UnicodeBotError(Exception):
     pass
 
 class UnicodeBotError(Exception):
     pass
@@ -71,19 +47,52 @@ def log(serveur,channel,auteur=None,message=None):
     else:
         chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
     f.write(chain+"\n")
     else:
         chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
     f.write(chain+"\n")
-    if config_debug_stdout:
+    if config.debug_stdout:
         print chain
     f.close()
 
         print chain
     f.close()
 
-## 20-100
-def reussi(message,answer,answer_regexp,auteur):
-    if auteur in config_level3:
+def ignore_event(serv, ev):
+    """Retourne ``True`` si il faut ignorer cet évènement."""
+    for (blackmask, exceptmask) in config.blacklisted_masks:
+        usermask = ev.source()
+        if exceptmask is None:
+            exceptit = False
+        else:
+            exceptit = bool(irclib.mask_matches(usermask, exceptmask))
+        blackit = bool(irclib.mask_matches(usermask, blackmask))
+        if blackit and not exceptit:
+            return True
+
+class GoodCentury(Exception):
+    pass
+
+class GoodDeceny(Exception):
+    pass
+
+def reussi(message,answer,auteur):
+    if auteur in config.level3:
+        return answer in message
+    if auteur in config.level2:
         return answer in message
         return answer in message
-    if auteur in config_level2:
-        return remplace_accents(answer) in message
     else:
     else:
-        if re.match(remplace_accents(answer_regexp).lower(),remplace_accents(message).lower()):
-            return True
+        try:
+            date=cast_as_date(message.lower().strip())
+        except ThisIsNotADate:
+            return False
+        realdate=map(lambda x:int(x), answer.split('/'))
+        realdate.reverse()
+        score=0
+        if date[0]==realdate[0]:
+            score=config.score_annee
+            if date[1]==realdate[1]:
+                score+=config.score_mois
+                if date[2]==realdate[2]:
+                    score+=config.score_jour
+        elif date[0]/10 == realdate[0]/10:
+            raise GoodDeceny
+        elif date[0]/100 == realdate[0]/100:
+            raise GoodCentury
+        return score
 
 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
     if case_sensitive:
 
 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
     if case_sensitive:
@@ -96,36 +105,37 @@ def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case
     return o
 
 def is_tag(chain):
     return o
 
 def is_tag(chain):
-    return is_something(chain,config_tag_triggers)
+    return is_something(chain,config.tag_triggers)
 
 class RefuseError(Exception):
     pass
 
 
 class RefuseError(Exception):
     pass
 
-class Deconnaisseur(ircbot.SingleServerIRCBot):
+class Historien(ircbot.SingleServerIRCBot):
     def __init__(self,serveur,debug=False):
     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 qui a au moins l'agreg d'histoire", 10)
         self.debug=debug
         self.serveur=serveur
         ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
                                       temporary_pseudo,"Un bot irc qui a au moins l'agreg d'histoire", 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):
         self.quiet_channels=[]
 
     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):
         self.serv=serv # ça serv ira :)
         self.give_me_my_pseudo(serv)
     
     def on_welcome(self, serv, ev):
         self.serv=serv # ça serv ira :)
         self.give_me_my_pseudo(serv)
-        serv.privmsg("NickServ","identify %s"%(config_password))
+        serv.privmsg("NickServ","identify %s"%(config.password))
         log(self.serveur,"Connected")
         if self.debug:
             self.chanlist=["#bot"]
         log(self.serveur,"Connected")
         if self.debug:
             self.chanlist=["#bot"]
@@ -136,53 +146,62 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         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))
+            if config.auto_trigger:
+                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):
+            if config.auto_trigger:
+                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":
                     ok="do_it"
                 else:
                     ok="refuse"
             if ok=="do_it":
-                enigme,indice,answer_reg,answer=self.get_enigme()
-                log(self.serveur,channel,u"$Date$".encode("utf8"),("%s; %s; %s; %s"%(enigme, indice, answer_reg, answer)).encode("utf8"))
-                serv.privmsg(channel,enigme.encode("utf8"))
+                date,evenement=self.get_enigme()
+                log(self.serveur,channel,u"$Date$".encode("utf8"),("%s : %s"%(date, evenement)).encode("utf8"))
+                serv.privmsg(channel,evenement.encode("utf8"))
                 token=time.time()
                 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))
+                # le 0 est le flag "bon siècle" n'a pas encore été dit
+                self.play_status[channel]=[1,date,evenement,0,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:
                     token=self.play_status[channel][-1]
             if self.play_status[channel][-1]==token:
-                indice=self.play_status[channel][2]
+                date=self.play_status[channel][1]
+                indice=date[:5]
                 serv.privmsg(channel,"indice : %s"%(indice).encode("utf8"))
                 self.play_status[channel][0]=2
                 serv.privmsg(channel,"indice : %s"%(indice).encode("utf8"))
                 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:
     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).encode("utf8"))
+            date=self.play_status[channel][1]
+            serv.privmsg(channel,"C'était le %s"%(date).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))
+            if config.auto_trigger:
+                serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,channel,token))
 
     def get_enigme(self):
         # on récupère les dates
 
     def get_enigme(self):
         # on récupère les dates
-        f=open(config_source_file)
-        t=f.read()
+        f=open(config.source_file)
+        l=f.readlines()
         f.close()
         f.close()
-        l=re.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t)
-        dec={int(i[0]):list(i[1:]) for i in l if len(i)==5}
+        l=[i.split(" : ",2) for i in l]
+        dates={int(i[0]):i[1:] for i in l}
         # on va chercher combien de fois elles ont été jouées
         played_file=get_config_played_file(self.serveur)
         f=open(played_file)
         # on va chercher combien de fois elles ont été jouées
         played_file=get_config_played_file(self.serveur)
         f=open(played_file)
@@ -194,14 +213,15 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         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])
         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]
+        date,evenement=dates[id_choisi]
+        evenement=evenement.replace("\n","")
         # on incrémente la choisie
         played[id_choisi]+=1
         # on enregistre le played_file
         f=open(played_file,"w")
         # 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.write("\n".join(["%-4s : %s"%(k,v) for k,v in played.items()]))
         f.close()
         f.close()
-        return enigme.decode("utf8"),indice.decode("utf8"),answer_reg.decode("utf8"),answer.decode("utf8")
+        return map(lambda x:x.decode("utf8"), [date,evenement])
 
     def pourmoi(self, serv, message):
         pseudo=self.nick
 
     def pourmoi(self, serv, message):
         pseudo=self.nick
@@ -212,13 +232,15 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
             return (False,message)
 
     def on_privmsg(self, serv, ev):
             return (False,message)
 
     def on_privmsg(self, serv, ev):
+        if ignore_event(serv, ev):
+            return
         message=ev.arguments()[0]
         auteur = irclib.nm_to_n(ev.source())
         try:
             test=bot_unicode(message)
         except UnicodeBotError:
         message=ev.arguments()[0]
         auteur = irclib.nm_to_n(ev.source())
         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, random.choice(config.utf8_fail_answers).encode("utf8"))
             return
         message=message.split()
         cmd=message[0].lower()
             return
         message=message.split()
         cmd=message[0].lower()
@@ -234,7 +256,8 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
  PLAY       Passe un channel en mode "jouer"
  NOPLAY     Passe un channel en mode "ne pas jouer"
  QUIET      Se taire sur un channel
  PLAY       Passe un channel en mode "jouer"
  NOPLAY     Passe un channel en mode "ne pas jouer"
  QUIET      Se taire sur un channel
- NOQUIET    Opposé de QUIET"""
+ NOQUIET    Opposé de QUIET
+ RELOAD     Me fait recharger la conf"""
             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
@@ -356,10 +379,16 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                         log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
             else:
                 notunderstood=True
                         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():
         elif cmd in ["states","status"]:
             if auteur in self.overops:
                 for k in self.play_status.keys():
-                    serv.privmsg(auteur,(u"%s : %s"%(k,"; ".join([unicode(i) for i in self.play_status[k]]))).encode("utf8") )
+                    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:]))
@@ -451,15 +480,17 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
             serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
     
     def on_pubmsg(self, serv, ev):
             serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
     
     def on_pubmsg(self, serv, ev):
+        if ignore_event(serv, ev):
+            return
         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,message)
         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))
+            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
         tryother=False
         pour_moi,message=self.pourmoi(serv,message)
             return
         tryother=False
         pour_moi,message=self.pourmoi(serv,message)
@@ -476,9 +507,13 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                 else:
                     serv.privmsg(canal,"%s: crève !"%(auteur))
                     log(self.serveur,canal,auteur,message+"[failed]")
                 else:
                     serv.privmsg(canal,"%s: crève !"%(auteur))
                     log(self.serveur,canal,auteur,message+"[failed]")
-            if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
+            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))
                 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
-            if cmd in ["part","leave","dégage"]:
+            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):
                     self.quitter(canal)
                 if auteur in self.ops and (not (canal in self.stay_channels)
                                            or auteur in self.overops):
                     self.quitter(canal)
@@ -488,16 +523,16 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                     serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
                     log(self.serveur,canal,auteur,message+"[failed]")
     
                     serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
                     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)
                     log(self.serveur,canal,auteur,message+"[successful]")
                 if auteur in self.ops:
                     become=args
                     serv.nick(become)
                     log(self.serveur,canal,auteur,message+"[successful]")
-            if cmd in ["coucou"] and not canal in self.quiet_channels:
+            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"] and not canal in self.quiet_channels:
+            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 ["date","dates","histoire","é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:
@@ -505,12 +540,12 @@ 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])).encode("utf8") )
+                        serv.privmsg(canal,("%s: Rappel : %s"%(auteur,self.play_status[canal][2])).encode("utf8") )
                 else:
                     serv.privmsg(canal,"%s: pas ici…"%(auteur))
                 else:
                     serv.privmsg(canal,"%s: pas ici…"%(auteur))
-            if cmd in ["score","!score"]:
+            elif cmd in ["score","!score"]:
                 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
                 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
-            if cmd in ["scores","!scores"]:
+            elif cmd in ["scores","!scores"]:
                 scores=self.get_scores().items()
                 # trie par score
                 scores.sort(lambda x,y:cmp(x[1],y[1]))
                 scores=self.get_scores().items()
                 # trie par score
                 scores.sort(lambda x,y:cmp(x[1],y[1]))
@@ -519,15 +554,15 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
                 # 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]))
                 # 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]))
-            if cmd=="indice" and canal in self.play_channels:
+            elif cmd == "indice" and canal in self.play_channels:
                 self.give_indice(serv,canal,None)
                 self.give_indice(serv,canal,None)
-            if is_tag(message) and not canal in self.quiet_channels:
+            elif is_tag(message) and not canal in self.quiet_channels:
                 if auteur in self.ops:
                 if auteur in self.ops:
-                    action=random.choice(config_tag_actions)
+                    action=random.choice(config.tag_actions)
                     serv.action(canal,action.encode("utf8"))
                     self.quiet_channels.append(canal)
                 else:
                     serv.action(canal,action.encode("utf8"))
                     self.quiet_channels.append(canal)
                 else:
-                    answer=random.choice(config_tag_answers)
+                    answer=random.choice(config.tag_answers)
                     for ligne in answer.split("\n"):
                         serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
             else:
                     for ligne in answer.split("\n"):
                         serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
             else:
@@ -536,15 +571,37 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
             tryother=True
         if tryother:
             if self.play_status.get(canal,[-1])[0] in [1,2]:
             tryother=True
         if tryother:
             if self.play_status.get(canal,[-1])[0] in [1,2]:
-                answer_regexp=self.play_status[canal][3]
-                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"))
+                answer=self.play_status[canal][1]
+                flag_century=self.play_status[canal][3]
+                try:
+                    score_obtenu=reussi(message.decode("utf8"),answer,auteur)
+                except GoodCentury:
+                    if not flag_century:
+                        serv.privmsg(canal,"%s: C'est le bon siècle, mais pas la bonne année, cherche encore ;)"%(auteur))
+                        self.play_status[canal][3]=1
+                    return
+                except GoodDeceny:
+                    if flag_century in [0,1]:
+                        serv.privmsg(canal,"%s: C'est la bonne décennie, mais pas la bonne année, encore un effort ;)"%(auteur))
+                        self.play_status[canal][3]=2
+                    return
+                if score_obtenu:
+                    if self.play_status[canal][0]==1:
+                        bonusmsg=u" [+bonus_mois"*(score_obtenu>config.score_annee)+u"+bonus_jour"*(score_obtenu>config.score_annee+config.score_mois)
+                    else:
+                        bonusmsg=""
+                        score_obtenu=1
+                    if bonusmsg:
+                        bonusmsg+=u"]"
+                    serv.privmsg(canal,(u"%s: bravo ! (C'était le %s)%s"%(auteur,answer,bonusmsg)).encode("utf8"))
                     log(self.serveur,canal,auteur+"$win",message)
                     log(self.serveur,canal,auteur+"$win",message)
-                    self.add_score(auteur,1)
+                    if auteur in config.noscore:
+                        score_obtenu=0
+                    self.add_score(auteur,score_obtenu)
                     token=time.time()
                     self.play_status[canal]=[0,token]
                     token=time.time()
                     self.play_status[canal]=[0,token]
-                    serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,canal,token))
+                    if config.auto_trigger:
+                        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())
     
     def on_kick(self,serv,ev):
         auteur = irclib.nm_to_n(ev.source())
@@ -552,11 +609,12 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         victime = ev.arguments()[0]
         raison = ev.arguments()[1]
         if victime==self.nick:
         victime = ev.arguments()[0]
         raison = ev.arguments()[1]
         if victime==self.nick:
-            log(self.serveur,"%s kické par %s (raison : %s)" %(victime,auteur,raison))
+            log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
             time.sleep(5)
             serv.join(channel)
             time.sleep(5)
             serv.join(channel)
+            self.update_activity(channel,force=True)
             # on ne dit rien au rejoin
             # on ne dit rien au rejoin
-            #l1,l2=config_kick_answers,config_kick_actions
+            #l1,l2=config.kick_answers,config.kick_actions
             #n1,n2=len(l1),len(l2)
             #i=random.randrange(n1+n2)
             #if i>=n1:
             #n1,n2=len(l1),len(l2)
             #i=random.randrange(n1+n2)
             #if i>=n1:
@@ -566,7 +624,7 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
     
     def quitter(self,chan,leave_message=None):
         if leave_message==None:
     
     def quitter(self,chan,leave_message=None):
         if leave_message==None:
-            leave_message=random.choice(config_leave_messages)
+            leave_message=random.choice(config.leave_messages)
         try:
             leave_message=leave_message%(time.strftime("le %d/%m/%Y à %T").decode("utf8"),self.nick)
         except:
         try:
             leave_message=leave_message%(time.strftime("le %d/%m/%Y à %T").decode("utf8"),self.nick)
         except:
@@ -574,7 +632,7 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         self.serv.part(chan,message=leave_message.encode("utf8"))
     
     def mourir(self):
         self.serv.part(chan,message=leave_message.encode("utf8"))
     
     def mourir(self):
-        quit_message=random.choice(config_quit_messages)
+        quit_message=random.choice(config.quit_messages)
         try:
             quit_message=quit_message%(time.strftime("le %d/%m/%Y à %T").decode("utf8"),self.nick)
         except:
         try:
             quit_message=quit_message%(time.strftime("le %d/%m/%Y à %T").decode("utf8"),self.nick)
         except:
@@ -582,7 +640,7 @@ class Deconnaisseur(ircbot.SingleServerIRCBot):
         self.die(msg=quit_message.encode("utf8"))
     
     def get_scores(self):
         self.die(msg=quit_message.encode("utf8"))
     
     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
@@ -596,24 +654,72 @@ 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()
     
     def _getnick(self):
         return self.serv.get_nickname()
     nick = property(_getnick)
         pickle.dump(scores,f)
         f.close()
     
     def _getnick(self):
         return self.serv.get_nickname()
     nick = property(_getnick)
+    
+    def update_activity(self,canal="",pseudo="",message="",force=False):
+        if force or (not pseudo in config.idle_bots and all([not re.match(ignore, message) for ignore in config.idle_messages])):
+            self.last_activity[canal]=time.time()
+    def is_active(self,canal):
+        # Si on n'a pas d'info sur le chan, il est inactif
+        return time.time()-self.last_activity.get(canal, config.idle_time)<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="historien.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 : historien.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
     if "debug" in sys.argv or "--debug" in sys.argv:
         debug=True
     else:
         debug=False
+    if "--no-output" in sys.argv or "--daemon" in sys.argv:
+        outfile = "/var/log/bots/historien.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)
     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:
     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:
@@ -621,5 +727,26 @@ if __name__=="__main__":
     except KeyError:
         print "Server Unknown : %s"%(serveur)
         exit(404)
     except KeyError:
         print "Server Unknown : %s"%(serveur)
         exit(404)
-    deco=Deconnaisseur(serveur,debug)
-    deco.start()
+    historien=Historien(serveur,debug)
+    # Si on reçoit un SIGHUP, on reload la config
+    def sighup_handler(signum, frame):
+        historien.reload("SIGHUP")
+    signal.signal(signal.SIGHUP, sighup_handler)
+    if daemon:
+        child_pid = os.fork()
+        if child_pid == 0:
+            os.setsid()
+            historien.start_as_daemon(outfile)
+        else:
+            # on enregistre le pid de historien
+            pidfile = "/var/run/bots/historien.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:
+        historien.start()
+