From: Vincent Le Gallic Date: Tue, 12 Jun 2012 04:36:50 +0000 (+0200) Subject: Pseudo en minuscules X-Git-Url: http://gitweb.pimeys.fr/?p=bots%2Fsalesman.git;a=commitdiff_plain;h=929ca4863d6da06c8f2ac6028b96552bb3ba2032 Pseudo en minuscules --- diff --git a/Salesman.py b/Salesman.py deleted file mode 100755 index ffb6be5..0000000 --- a/Salesman.py +++ /dev/null @@ -1,626 +0,0 @@ -#!/usr/bin/python -# -*- coding:utf8 -*- - -# Codé par 20-100 le 23/04/12 - -# Un bot IRC qui sort des déconnaissances - -import irclib -import ircbot -import threading -import random -import time -import pickle -import re -from remplace_accents import remplace_accents - -config_password="Yamoussoukro" -config_pseudo="Salesman" -config_chanlist=["#bot","#flood"] -config_play_channels=["#flood"] -config_stay_channels=["#flood","#bot"] -config_overops=["[20-100]","[20-100]_"] -config_ops=["PEB","Petite-Peste"] - -config_source_file="capitales.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"} - 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…"] - -config_level2=[] -config_level3=[] - -config_debug_stdout = True -config_logfile_template="Salesman.%s.log" -def get_config_logfile(serveur): - serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"} - return config_logfile_template%(serveurs[serveur]) - -config_quit_messages=[u"goto Tombouctou"] - -config_leave_messages=[u"On continuera à jouer plus tard ;)"] - -# Quand personne ne cause, on finit par se taire -# temps au bout duquel, si personne n'a parlé, on se tait -config_idle_time=20*60 -# liste des bots, qui ne sont pas considérés comme de l'activité -config_idle_bots=["deconnaisseur","Basile","historien","Hung","Salesman","Shadobot","Wen","___"] - -class UnicodeBotError(Exception): - pass -def bot_unicode(chain): - try: - unicode(chain,"utf8") - except UnicodeDecodeError: - raise UnicodeBotError - -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,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).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 is_tag(chain): - return is_something(chain,config_tag_triggers) - -class RefuseError(Exception): - pass - -class Salesman(ircbot.SingleServerIRCBot): - def __init__(self,serveur,debug=False): - 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 - 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.last_activity={} - 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)) - time.sleep(0.3) - serv.nick(config_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_password)) - log(self.serveur,"Connected") - if self.debug: - self.chanlist=["#bot"] - self.play_channels=["#bot"] - for c in self.chanlist: - log(self.serveur,"JOIN %s"%(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] - serv.execute_delayed(random.randrange(ttrig),self.start_enigme,(serv,c,token)) - - 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(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 time.time() > self.play_status[channel][-1]+config_time_incompressible: - ok="do_it" - else: - ok="refuse" - if ok=="do_it": - enigme,answer=self.get_enigme() - log(self.serveur,channel,u"$Énigme$".encode("utf8"),("%s | %s"%(enigme, answer)).encode("utf8")) - serv.privmsg(channel,enigme.encode("utf8")) - token=time.time() - self.play_status[channel]=[1,enigme,answer,token] - # ce bot n'a pas d'indices - serv.execute_delayed(random.randrange(ttrig*7,ttrig*10),self.give_answer,(serv,channel,token)) - elif ok=="refuse": - raise RefuseError - def give_answer(self,serv,channel,token): - if self.play_status[channel][0]==1 and self.play_status[channel][-1]==token: - answer=self.play_status[channel][2] - serv.privmsg(channel,"C'était : %s"%(answer).encode("utf8")) - token=time.time() - self.play_status[channel]=[0,token] - serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,channel,token)) - - def get_enigme(self): - # on récupère les capitales - f=open(config_source_file) - l=[i.strip("\n") for i in f.readlines()] - f.close() - l=[i.split(" | ") for i in l] - dec={int(i[0]):list(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) - 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 - 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]) - capitale,pays=dec[id_choisi] - # on peut jouer capitale -> pays ou pays -> capitale - enigme,answer=random.choice([[capitale,pays],[pays,capitale]]) - # 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() - return map(lambda x:x.decode("utf8"), [enigme,answer]) - - def pourmoi(self, serv, message): - pseudo=self.nick - size=len(pseudo) - if message[:size]==pseudo and len(message)>size and message[size]==":": - return (True,message[size+1:].strip(" ")) - 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: - serv.privmsg(auteur, - "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…") - return - message=message.split() - cmd=message[0].lower() - notunderstood=False - if cmd=="help": - helpmsg_default="""Liste des commandes : - HELP Affiche ce message d'aide - SCORE Affiche ton score (SCORE TRANSFERT [] pour transférer des points) - SCORES Affiche les scores""" - helpmsg_ops=""" - 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" - QUIET Se taire sur un channel - NOQUIET Opposé de QUIET""" - helpmsg_overops=""" - SCORES {DEL|ADD|SUB} Tu veux un dessin ? - SAY Fais envoyer un message sur un chan ou à une personne - STAY Ignorera les prochains LEAVE pour un chan - NOSTAY Opposé de STAY - STATUS Montre l'état courant - DIE Mourir""" - helpmsg=helpmsg_default - if auteur in self.ops: - helpmsg+=helpmsg_ops - if auteur in self.overops: - helpmsg+=helpmsg_overops - 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: - serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1])) - log(self.serveur,"priv",auteur," ".join(message)+"[failed]") - else: - self.stay_channels.append(message[1]) - serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels)) - log(self.serveur,"priv",auteur," ".join(message)+"[successful]") - 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: - self.stay_channels.remove(message[1]) - serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels)) - log(self.serveur,"priv",auteur," ".join(message)+"[successful]") - else: - serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1])) - log(self.serveur,"priv",auteur," ".join(message)+"[failed]") - else: - notunderstood=True - elif cmd=="play": - if auteur in self.ops: - if len(message)>1: - if message[1] in self.play_channels: - serv.privmsg(auteur,"Je play déjà sur %s."%(message[1])) - 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)) - log(self.serveur,"priv",auteur," ".join(message)+"[successful]") - else: - serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels)) - else: - notunderstood=True - elif cmd=="noplay": - if auteur in self.ops: - if len(message)>1: - if message[1] in self.play_channels: - self.play_channels.remove(message[1]) - serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels)) - log(self.serveur,"priv",auteur," ".join(message)+"[successful]") - else: - serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1])) - 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 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") ) - 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 ") - else: - notunderstood=True - elif cmd=="die": - if auteur in self.overops: - 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": - scores=self.get_scores() - de,to=auteur,message[2] - value=scores.get(de,0) - if len(message)==4: - try: - asked=int(message[3]) - except ValueError: - serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT []") - return - else: - asked=value - if value==0: - serv.privmsg(auteur,"Vous n'avez pas de points") - return - elif asked<=0: - serv.privmsg(auteur,"Bien tenté…") - return - elif asked>value: - serv.privmsg(auteur,"Vous n'avez que %s points"%(value)) - return - else: - self.add_score(de,-asked) - self.add_score(to,asked) - serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to)) - else: - serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT []") - else: - serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) ) - elif cmd=="scores": - if len(message)==1: - 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 auteur in self.overops: - souscmd=message[1].lower() - if souscmd=="del": - if len(message)==3: - todelete=message[2] - scores=self.get_scores() - if scores.has_key(todelete): - del scores[todelete] - self.save_scores(scores) - serv.privmsg(auteur,"Score de %s supprimé"%(todelete)) - else: - serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete)) - else: - serv.privmsg(auteur,"Syntaxe : SCORES DEL ") - elif souscmd in ["add","sub"]: - if len(message)==4: - toadd,val=message[2],message[3] - try: - val=int(val) - except ValueError: - serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} ") - return - if souscmd=="sub": - val=-val - self.add_score(toadd,val) - serv.privmsg(auteur,"Done") - else: - serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} ") - else: - serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} []") - else: - notunderstood=True - else: - notunderstood=True - if notunderstood: - serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…") - - def on_pubmsg(self, serv, ev): - 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: - 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 - tryother=False - 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: - self.mourir() - log(self.serveur,canal,auteur,message+"[successful]") - 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: - serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur)) - if 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) - log(self.serveur,canal,auteur,message+"[successful]") - self.chanlist.remove(canal) - else: - serv.privmsg(canal,"%s: Non, je reste !"%(auteur)) - log(self.serveur,canal,auteur,message+"[failed]") - - if cmd in ["deviens","pseudo"]: - 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: - serv.privmsg(canal,"%s: coucou"%(auteur)) - if cmd in ["ping"] and not canal in self.quiet_channels: - serv.privmsg(canal,"%s: pong"%(auteur)) - if cmd in ["ville","capitale","pays","énigme","enigme","encore"]: - if canal in self.play_channels: - if self.play_status.get(canal,[-1])[0]==0: - try: - self.start_enigme(serv,canal) - 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") ) - else: - serv.privmsg(canal,"%s: pas ici…"%(auteur)) - if cmd in ["score","!score"]: - serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) ) - if 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])) - if is_tag(message) and not canal in self.quiet_channels: - if auteur in self.ops: - action=random.choice(config_tag_actions) - serv.action(canal,action.encode("utf8")) - self.quiet_channels.append(canal) - else: - answer=random.choice(config_tag_answers) - for ligne in answer.split("\n"): - serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8"))) - else: - tryother=True - else: - tryother=True - if tryother: - if self.play_status.get(canal,[-1])[0]==1: - answer=self.play_status[canal][2] - if reussi(message.decode("utf8"),answer,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] - serv.execute_delayed(random.randrange(Ttrig*5,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(message[1],"") - # 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=random.choice(config_quit_messages) - self.die(msg=quit_message.encode("utf8")) - - def get_scores(self): - f=open(config_score_file) - scores=pickle.load(f) - f.close() - return scores - - def add_score(self,pseudo,value): - scores=self.get_scores() - if scores.has_key(pseudo): - scores[pseudo]+=value - else: - scores[pseudo]=value - self.save_scores(scores) - - def save_scores(self,scores): - f=open(config_score_file,"w") - 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] [--debug]" - exit(1) - serveur=sys.argv[1] - 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"} - try: - serveur=serveurs[serveur] - except KeyError: - print "Server Unknown : %s"%(serveur) - exit(404) - salesman=Salesman(serveur,debug) - salesman.start() diff --git a/salesman.py b/salesman.py new file mode 100755 index 0000000..e5523c2 --- /dev/null +++ b/salesman.py @@ -0,0 +1,626 @@ +#!/usr/bin/python +# -*- coding:utf8 -*- + +# Codé par 20-100 le 23/04/12 + +# Un bot IRC qui sort des déconnaissances + +import irclib +import ircbot +import threading +import random +import time +import pickle +import re +from remplace_accents import remplace_accents + +config_password="Yamoussoukro" +config_pseudo="salesman" +config_chanlist=["#bot","#flood"] +config_play_channels=["#flood"] +config_stay_channels=["#flood","#bot"] +config_overops=["[20-100]","[20-100]_"] +config_ops=["PEB","Petite-Peste"] + +config_source_file="capitales.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"} + 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…"] + +config_level2=[] +config_level3=[] + +config_debug_stdout = True +config_logfile_template="salesman.%s.log" +def get_config_logfile(serveur): + serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"} + return config_logfile_template%(serveurs[serveur]) + +config_quit_messages=[u"goto Tombouctou"] + +config_leave_messages=[u"On continuera à jouer plus tard ;)"] + +# Quand personne ne cause, on finit par se taire +# temps au bout duquel, si personne n'a parlé, on se tait +config_idle_time=20*60 +# liste des bots, qui ne sont pas considérés comme de l'activité +config_idle_bots=["deconnaisseur","Basile","historien","hung","salesman","Shadobot","Wen","___"] + +class UnicodeBotError(Exception): + pass +def bot_unicode(chain): + try: + unicode(chain,"utf8") + except UnicodeDecodeError: + raise UnicodeBotError + +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,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).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 is_tag(chain): + return is_something(chain,config_tag_triggers) + +class RefuseError(Exception): + pass + +class Salesman(ircbot.SingleServerIRCBot): + def __init__(self,serveur,debug=False): + 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 + 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.last_activity={} + 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)) + time.sleep(0.3) + serv.nick(config_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_password)) + log(self.serveur,"Connected") + if self.debug: + self.chanlist=["#bot"] + self.play_channels=["#bot"] + for c in self.chanlist: + log(self.serveur,"JOIN %s"%(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] + serv.execute_delayed(random.randrange(ttrig),self.start_enigme,(serv,c,token)) + + 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(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 time.time() > self.play_status[channel][-1]+config_time_incompressible: + ok="do_it" + else: + ok="refuse" + if ok=="do_it": + enigme,answer=self.get_enigme() + log(self.serveur,channel,u"$Énigme$".encode("utf8"),("%s | %s"%(enigme, answer)).encode("utf8")) + serv.privmsg(channel,enigme.encode("utf8")) + token=time.time() + self.play_status[channel]=[1,enigme,answer,token] + # ce bot n'a pas d'indices + serv.execute_delayed(random.randrange(ttrig*7,ttrig*10),self.give_answer,(serv,channel,token)) + elif ok=="refuse": + raise RefuseError + def give_answer(self,serv,channel,token): + if self.play_status[channel][0]==1 and self.play_status[channel][-1]==token: + answer=self.play_status[channel][2] + serv.privmsg(channel,"C'était : %s"%(answer).encode("utf8")) + token=time.time() + self.play_status[channel]=[0,token] + serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,channel,token)) + + def get_enigme(self): + # on récupère les capitales + f=open(config_source_file) + l=[i.strip("\n") for i in f.readlines()] + f.close() + l=[i.split(" | ") for i in l] + dec={int(i[0]):list(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) + 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 + 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]) + capitale,pays=dec[id_choisi] + # on peut jouer capitale -> pays ou pays -> capitale + enigme,answer=random.choice([[capitale,pays],[pays,capitale]]) + # 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() + return map(lambda x:x.decode("utf8"), [enigme,answer]) + + def pourmoi(self, serv, message): + pseudo=self.nick + size=len(pseudo) + if message[:size]==pseudo and len(message)>size and message[size]==":": + return (True,message[size+1:].strip(" ")) + 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: + serv.privmsg(auteur, + "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…") + return + message=message.split() + cmd=message[0].lower() + notunderstood=False + if cmd=="help": + helpmsg_default="""Liste des commandes : + HELP Affiche ce message d'aide + SCORE Affiche ton score (SCORE TRANSFERT [] pour transférer des points) + SCORES Affiche les scores""" + helpmsg_ops=""" + 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" + QUIET Se taire sur un channel + NOQUIET Opposé de QUIET""" + helpmsg_overops=""" + SCORES {DEL|ADD|SUB} Tu veux un dessin ? + SAY Fais envoyer un message sur un chan ou à une personne + STAY Ignorera les prochains LEAVE pour un chan + NOSTAY Opposé de STAY + STATUS Montre l'état courant + DIE Mourir""" + helpmsg=helpmsg_default + if auteur in self.ops: + helpmsg+=helpmsg_ops + if auteur in self.overops: + helpmsg+=helpmsg_overops + 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: + serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1])) + log(self.serveur,"priv",auteur," ".join(message)+"[failed]") + else: + self.stay_channels.append(message[1]) + serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels)) + log(self.serveur,"priv",auteur," ".join(message)+"[successful]") + 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: + self.stay_channels.remove(message[1]) + serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels)) + log(self.serveur,"priv",auteur," ".join(message)+"[successful]") + else: + serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1])) + log(self.serveur,"priv",auteur," ".join(message)+"[failed]") + else: + notunderstood=True + elif cmd=="play": + if auteur in self.ops: + if len(message)>1: + if message[1] in self.play_channels: + serv.privmsg(auteur,"Je play déjà sur %s."%(message[1])) + 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)) + log(self.serveur,"priv",auteur," ".join(message)+"[successful]") + else: + serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels)) + else: + notunderstood=True + elif cmd=="noplay": + if auteur in self.ops: + if len(message)>1: + if message[1] in self.play_channels: + self.play_channels.remove(message[1]) + serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels)) + log(self.serveur,"priv",auteur," ".join(message)+"[successful]") + else: + serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1])) + 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 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") ) + 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 ") + else: + notunderstood=True + elif cmd=="die": + if auteur in self.overops: + 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": + scores=self.get_scores() + de,to=auteur,message[2] + value=scores.get(de,0) + if len(message)==4: + try: + asked=int(message[3]) + except ValueError: + serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT []") + return + else: + asked=value + if value==0: + serv.privmsg(auteur,"Vous n'avez pas de points") + return + elif asked<=0: + serv.privmsg(auteur,"Bien tenté…") + return + elif asked>value: + serv.privmsg(auteur,"Vous n'avez que %s points"%(value)) + return + else: + self.add_score(de,-asked) + self.add_score(to,asked) + serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to)) + else: + serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT []") + else: + serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) ) + elif cmd=="scores": + if len(message)==1: + 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 auteur in self.overops: + souscmd=message[1].lower() + if souscmd=="del": + if len(message)==3: + todelete=message[2] + scores=self.get_scores() + if scores.has_key(todelete): + del scores[todelete] + self.save_scores(scores) + serv.privmsg(auteur,"Score de %s supprimé"%(todelete)) + else: + serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete)) + else: + serv.privmsg(auteur,"Syntaxe : SCORES DEL ") + elif souscmd in ["add","sub"]: + if len(message)==4: + toadd,val=message[2],message[3] + try: + val=int(val) + except ValueError: + serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} ") + return + if souscmd=="sub": + val=-val + self.add_score(toadd,val) + serv.privmsg(auteur,"Done") + else: + serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} ") + else: + serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} []") + else: + notunderstood=True + else: + notunderstood=True + if notunderstood: + serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…") + + def on_pubmsg(self, serv, ev): + 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: + 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 + tryother=False + 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: + self.mourir() + log(self.serveur,canal,auteur,message+"[successful]") + 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: + serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur)) + if 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) + log(self.serveur,canal,auteur,message+"[successful]") + self.chanlist.remove(canal) + else: + serv.privmsg(canal,"%s: Non, je reste !"%(auteur)) + log(self.serveur,canal,auteur,message+"[failed]") + + if cmd in ["deviens","pseudo"]: + 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: + serv.privmsg(canal,"%s: coucou"%(auteur)) + if cmd in ["ping"] and not canal in self.quiet_channels: + serv.privmsg(canal,"%s: pong"%(auteur)) + if cmd in ["ville","capitale","pays","énigme","enigme","encore"]: + if canal in self.play_channels: + if self.play_status.get(canal,[-1])[0]==0: + try: + self.start_enigme(serv,canal) + 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") ) + else: + serv.privmsg(canal,"%s: pas ici…"%(auteur)) + if cmd in ["score","!score"]: + serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) ) + if 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])) + if is_tag(message) and not canal in self.quiet_channels: + if auteur in self.ops: + action=random.choice(config_tag_actions) + serv.action(canal,action.encode("utf8")) + self.quiet_channels.append(canal) + else: + answer=random.choice(config_tag_answers) + for ligne in answer.split("\n"): + serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8"))) + else: + tryother=True + else: + tryother=True + if tryother: + if self.play_status.get(canal,[-1])[0]==1: + answer=self.play_status[canal][2] + if reussi(message.decode("utf8"),answer,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] + serv.execute_delayed(random.randrange(Ttrig*5,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(message[1],"") + # 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=random.choice(config_quit_messages) + self.die(msg=quit_message.encode("utf8")) + + def get_scores(self): + f=open(config_score_file) + scores=pickle.load(f) + f.close() + return scores + + def add_score(self,pseudo,value): + scores=self.get_scores() + if scores.has_key(pseudo): + scores[pseudo]+=value + else: + scores[pseudo]=value + self.save_scores(scores) + + def save_scores(self,scores): + f=open(config_score_file,"w") + 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] [--debug]" + exit(1) + serveur=sys.argv[1] + 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"} + try: + serveur=serveurs[serveur] + except KeyError: + print "Server Unknown : %s"%(serveur) + exit(404) + salesman=Salesman(serveur,debug) + salesman.start()