]> gitweb.pimeys.fr Git - bots/deconnaisseur.git/blob - deconnaisseur.py
Scores pickle-mémorisés
[bots/deconnaisseur.git] / deconnaisseur.py
1 #!/usr/bin/python
2 # -*- coding:utf8 -*-
3
4 # Codé par 20-100 le 23/04/12
5
6 # Un bot IRC qui sort des déconnaissances
7
8 import irclib
9 import ircbot
10 import threading
11 import random
12 import time
13 import pickle
14 import re
15
16 config_password="PatrickSébastien"
17 config_pseudo="deconnaisseur"
18 config_chanlist=["#bot","#flood"]
19 config_play_channels=["#flood"]
20 config_stay_channels=["#flood","#bot"]
21 config_overops=["[20-100]","[20-100]_","PEB"]
22 config_ops=["Nit","Eguel","Harry"]
23
24 config_source_file_template="deconnaissances.%s.txt" #il faut rajouter le nom du serveur
25 def get_config_source_file(serveur):
26 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
27 return config_source_file_template%(serveurs[serveur])
28 ttrig=120 #time trigger (normalement 120, mais diminué pour les tests)
29 Ttrig=600 #between two enigms
30 config_time_incompressible=60 #on peut pas retrigger en dessous de ce temps (60)
31 config_time_incompressible_clue=60 #on peut pas forcer la demande d'indice en dessous
32
33 config_score_file="scores.pickle"
34
35 class UnicodeBotError(Exception):
36 pass
37 def bot_unicode(chain):
38 try:
39 unicode(chain,"utf8")
40 except UnicodeDecodeError:
41 raise UnicodeBotError
42
43 def log(channel,auteur=None,message=None):
44 #f=open(config_logfile,"a")
45 #if auteur==message==None:
46 # chain=channel
47 #else:
48 # chain="%s [%s:%s] %s"%(time.strftime("%T"),channel,auteur,message)
49 #f.write(chain+"\n")
50 #print chain
51 #f.close()
52 a=0 # does nothing
53
54
55 def tolere(regexp):
56 """Renvoie une regexp plus tolérante"""
57 reg=unicode(regexp,"utf8").lower()
58 reg=reg.replace(u"á",u"(á|a)").replace(u"à",u"(à|a)").replace(u"â",u"(â|a)").replace(u"ä",u"(ä|a)")
59 reg=reg.replace(u"é",u"(é|e)").replace(u"è",u"(è|e)").replace(u"ê",u"(ê|e)").replace(u"ë",u"(ë|e)")
60 reg=reg.replace(u"í",u"(í|i)").replace(u"ì",u"(ì|i)").replace(u"î",u"(î|i)").replace(u"ï",u"(ï|i)")
61 reg=reg.replace(u"ó",u"(ó|o)").replace(u"ò",u"(ò|o)").replace(u"ô",u"(ô|o)").replace(u"ö",u"(ö|o)")
62 reg=reg.replace(u"ú",u"(ú|u)").replace(u"ù",u"(ù|u)").replace(u"û",u"(û|u)").replace(u"ü",u"(ü|u)")
63 reg=reg.replace(u"ý",u"(ý|y)").replace(u"ỳ",u"(ỳ|y)").replace(u"ŷ",u"(ŷ|y)").replace(u"ÿ",u"(ÿ|y)")
64 reg=reg.replace(u"œ",u"(œ|oe)").replace(u"æ",u"(æ|ae)")
65 return reg
66
67 class RefuseError(Exception):
68 pass
69
70 class Deconnaisseur(ircbot.SingleServerIRCBot):
71 def __init__(self,serveur,debug=False):
72 temporary_pseudo=config_pseudo+str(random.randrange(10000,100000))
73 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
74 temporary_pseudo,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
75 self.debug=debug
76 self.serveur=serveur
77 self.overops=config_overops
78 self.ops=self.overops+config_ops
79 self.chanlist=config_chanlist
80 self.stay_channels=config_stay_channels
81 self.play_channels=config_play_channels
82 self.play_status={i:[0] for i in self.play_channels}
83 self.scores={}
84
85 def give_me_my_pseudo(self,serv):
86 serv.privmsg("NickServ","RECOVER %s %s"%(config_pseudo,config_password))
87 serv.privmsg("NickServ","RELEASE %s %s"%(config_pseudo,config_password))
88 time.sleep(0.3)
89 serv.nick(config_pseudo)
90
91 def on_welcome(self, serv, ev):
92 self.give_me_my_pseudo(serv)
93 serv.privmsg("NickServ","identify %s"%(config_password))
94 log("Connected")
95 if self.debug:
96 self.chanlist=["#bot"]
97 self.play_channels=["#bot"]
98 for c in self.chanlist:
99 log("JOIN %s"%(c))
100 serv.join(c)
101 for c in self.play_channels:
102 token=time.time()-3600
103 self.play_status[c]=[0,token]
104 serv.execute_delayed(random.randrange(ttrig),self.start_enigme,(serv,c,token))
105
106 def start_enigme(self,serv,channel,token=None):
107 if self.play_status[channel][0]==0 and channel in self.play_channels:
108 ok="skip"
109 if token==self.play_status[channel][-1]:
110 ok="do_it"
111 if token==None:
112 if time.time() > self.play_status[channel][-1]+config_time_incompressible:
113 ok="do_it"
114 else:
115 ok="refuse"
116 if ok=="do_it":
117 enigme,indice,answer_reg,answer=self.get_enigme()
118 print "%s; %s; %s; %s"%(enigme, indice, answer_reg, answer)
119 serv.privmsg(channel,enigme)
120 token=time.time()
121 self.play_status[channel]=[1,enigme,indice,answer_reg,answer,token]
122 serv.execute_delayed(random.randrange(ttrig*3,ttrig*5),self.give_indice,(serv,channel,token))
123 elif ok=="refuse":
124 raise RefuseError
125 def give_indice(self,serv,channel,token):
126 if self.play_status[channel][0]==1:
127 if token==None:
128 # c'est donc que l'indice a été demandé
129 if self.play_status[channe][-1]+config_time_incompressible_clue<time.time():
130 token=self.play_status[channel][-1]
131 if self.play_status[channel][-1]==token:
132 indice=self.play_status[channel][2]
133 serv.privmsg(channel,"indice : %s"%(indice))
134 self.play_status[channel][0]=2
135 serv.execute_delayed(random.randrange(ttrig*1,ttrig*3),self.give_answer,(serv,channel,token))
136 def give_answer(self,serv,channel,token):
137 if self.play_status[channel][0]==2 and self.play_status[channel][-1]==token:
138 answer=self.play_status[channel][4]
139 serv.privmsg(channel,"C'était : %s"%(answer))
140 token=time.time()
141 self.play_status[channel]=[0,token]
142 serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,channel,token))
143
144 def get_enigme(self):
145 f=open(get_config_source_file(self.serveur))
146 t=f.read()
147 l=re.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t)
148 l=[list(i) for i in l if len(i)==5]
149 l.sort(lambda x,y: cmp(int(x[4]),int(y[4])))
150 # on récupère le nombre d'occurrences le plus faible
151 mini=l[0][4]
152 # on garde que ceux qui ont le même nombre d'occurrences
153 l_mini=[en for en in l if en[4]==mini]
154 # on tire au hasard dedans
155 choisi=random.randrange(len(l_mini))
156 enigme,indice,answer_reg,answer,_=l_mini[choisi]
157 real_index=l.index(l_mini[choisi])
158 l[real_index][4]=str(int(l[real_index][4])+1)
159 f=open(get_config_source_file(self.serveur),"w")
160 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%")
161 f.close()
162 return enigme,indice,answer_reg,answer
163
164 def pourmoi(self, serv, message):
165 pseudo=serv.get_nickname()
166 size=len(pseudo)
167 if message[:size]==pseudo and len(message)>size and message[size]==":":
168 return (True,message[size+1:].strip(" "))
169 else:
170 return (False,message)
171
172 def on_privmsg(self, serv, ev):
173 message=ev.arguments()[0]
174 auteur = irclib.nm_to_n(ev.source())
175 try:
176 test=bot_unicode(message)
177 except UnicodeBotError:
178 serv.privmsg(auteur,
179 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
180 return
181 message=message.split()
182 cmd=message[0].lower()
183 notunderstood=False
184 if cmd=="help":
185 helpmsg_default="""Liste des commandes :
186 HELP Affiche ce message d'aide
187 SCORES Affiche les scores"""
188 helpmsg_ops="""
189 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
190 LEAVE Faire quitter un channel
191 PLAY Passe un channel en mode "jouer"
192 NOPLAY Passe un channel en mode "ne pas jouer" """
193 helpmsg_overops="""
194 SAY Fais envoyer un message sur un chan ou à une personne
195 STAY Ignorera les prochains LEAVE pour un chan
196 NOSTAY Opposé de STAY
197 STATUS Montre l'état courant
198 DIE Mourir"""
199 helpmsg=helpmsg_default
200 if auteur in self.ops:
201 helpmsg+=helpmsg_ops
202 if auteur in self.overops:
203 helpmsg+=helpmsg_overops
204 for ligne in helpmsg.split("\n"):
205 serv.privmsg(auteur,ligne)
206 elif cmd=="join":
207 if auteur in self.ops:
208 if len(message)>1:
209 if message[1] in self.chanlist:
210 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
211 else:
212 serv.join(message[1])
213 self.chanlist.append(message[1])
214 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
215 log("priv",auteur," ".join(message))
216 else:
217 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
218 else:
219 notunderstood=True
220 elif cmd=="leave":
221 if auteur in self.ops and len(message)>1:
222 if message[1] in self.chanlist:
223 if not (message[1] in self.stay_channels) or auteur in self.overops:
224 serv.part(message[1])
225 self.chanlist.remove(message[1])
226 log("priv",auteur," ".join(message)+"[successful]")
227 else:
228 serv.privmsg(auteur,"Non, je reste !")
229 log("priv",auteur," ".join(message)+"[failed]")
230 else:
231 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
232 else:
233 notunderstood=True
234 elif cmd=="stay":
235 if auteur in self.overops:
236 if len(message)>1:
237 if message[1] in self.stay_channels:
238 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
239 log("priv",auteur," ".join(message)+"[failed]")
240 else:
241 self.stay_channels.append(message[1])
242 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
243 log("priv",auteur," ".join(message)+"[successful]")
244 else:
245 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
246 else:
247 notunderstood=True
248 elif cmd=="nostay":
249 if auteur in self.overops:
250 if len(message)>1:
251 if message[1] in self.stay_channels:
252 self.stay_channels.remove(message[1])
253 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
254 log("priv",auteur," ".join(message)+"[successful]")
255 else:
256 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
257 log("priv",auteur," ".join(message)+"[failed]")
258 else:
259 notunderstood=True
260 elif cmd=="play":
261 if auteur in self.ops:
262 if len(message)>1:
263 if message[1] in self.play_channels:
264 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
265 log("priv",auteur," ".join(message)+"[failed]")
266 else:
267 self.play_channels.append(message[1])
268 self.play_status[message[1]]=[0,time.time()-3600]
269 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
270 log("priv",auteur," ".join(message)+"[successful]")
271 else:
272 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
273 else:
274 notunderstood=True
275 elif cmd=="noplay":
276 if auteur in self.ops:
277 if len(message)>1:
278 if message[1] in self.play_channels:
279 self.play_channels.remove(message[1])
280 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
281 log("priv",auteur," ".join(message)+"[successful]")
282 else:
283 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
284 log("priv",auteur," ".join(message)+"[failed]")
285 else:
286 notunderstood=True
287 elif cmd in ["states","status"]:
288 if auteur in self.overops:
289 for k in self.play_status.keys():
290 serv.privmsg(auteur,"%s : %s"%(k,"; ".join([str(i) for i in self.play_status[k]])))
291 elif cmd=="say":
292 if auteur in self.overops and len(message)>2:
293 serv.privmsg(message[1]," ".join(message[2:]))
294 log("priv",auteur," ".join(message))
295 elif len(message)<=2:
296 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
297 else:
298 notunderstood=True
299 elif cmd=="die":
300 if auteur in self.overops:
301 self.die()
302 elif cmd=="score":
303 serv.privmsg(auteur,"Votre score : %s"%(self.scores.get(auteur,0)) )
304 elif cmd=="scores":
305 scores=self.get_scores().items()
306 # trie par score
307 scores.sort(lambda x,y:cmp(x[1].lower(),y[1].lower()))
308 scores.reverse()
309 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
310 # trie par pseudo
311 scores.sort(lambda x,y:cmp(x[0],y[0]))
312 scores.reverse()
313 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
314 else:
315 notunderstood=True
316 if notunderstood:
317 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
318
319 def on_pubmsg(self, serv, ev):
320 auteur = irclib.nm_to_n(ev.source())
321 canal = ev.target()
322 message = ev.arguments()[0]
323 try:
324 test=bot_unicode(message)
325 except UnicodeBotError:
326 if not canal in self.quiet_channels:
327 serv.privmsg(canal,
328 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
329 return
330 tryother=False
331 pour_moi,message=self.pourmoi(serv,message)
332 if pour_moi and message.split()!=[]:
333 cmd=message.split()[0].lower()
334 try:
335 args=" ".join(message.split()[1:])
336 except:
337 args=""
338 if cmd in ["meurs","die","crève"]:
339 if auteur in self.overops:
340 self.die()
341 log(canal,auteur,message+"[successful]")
342 else:
343 serv.privmsg(canal,"%s: crève !"%(auteur))
344 log(canal,auteur,message+"[failed]")
345 if cmd in ["meur", "meurt","meurre","meurres"]:
346 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du pluriel : "meurs" (de rien)'%(auteur))
347 if cmd in ["part","leave","dégage"]:
348 if auteur in self.ops and (not (canal in self.stay_channels)
349 or auteur in self.overops):
350 serv.part(canal,message="Éjecté par %s"%(auteur))
351 log(canal,auteur,message+"[successful]")
352 self.chanlist.remove(canal)
353 else:
354 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
355 log(canal,auteur,message+"[failed]")
356
357 if cmd in ["deviens","pseudo"]:
358 if auteur in self.ops:
359 become=args
360 serv.nick(become)
361 log(canal,auteur,message+"[successful]")
362
363 if cmd in ["coucou"]:
364 serv.privmsg(canal,"%s: coucou"%(auteur))
365 if cmd in ["ping"]:
366 serv.privmsg(canal,"%s: pong"%(auteur))
367 if cmd in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
368 if canal in self.play_channels:
369 if self.play_status.get(canal,[-1])[0]==0:
370 try:
371 self.start_enigme(serv,canal)
372 except RefuseError:
373 serv.privmsg(canal,"%s: Je peux souffler une minute ?"%(auteur))
374 else:
375 serv.privmsg(canal,"%s: Rappel : %s"%(auteur,self.play_status[canal][1]))
376 else:
377 serv.privmsg(canal,"%s: pas ici…"%(auteur))
378 else:
379 tryother=True
380 else:
381 tryother=True
382 if tryother:
383 if self.play_status.get(canal,[-1])[0] in [1,2]:
384 answer_regexp=self.play_status[canal][3]
385 if re.match(tolere(answer_regexp),unicode(message,"utf8").lower()):
386 answer=self.play_status[canal][4]
387 serv.privmsg(canal,"%s: bravo ! (C'était %s)"%(auteur,answer))
388 self.scoreplus(auteur)
389 token=time.time()
390 self.play_status[canal]=[0,token]
391 serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,canal,token))
392 def get_scores(self):
393 f=open(config_score_file)
394 scores=pickle.load(f)
395 f.close()
396 return scores
397
398 def scoreplus(self,pseudo):
399 scores=self.get_scores()
400 if scores.has_key(pseudo):
401 scores[pseudo]+=1
402 else:
403 scores[pseudo]=1
404 self.save_scores(scores)
405
406 def save_scores(self,scores):
407 f=open(config_score_file,"w")
408 pickle.dump(scores,f)
409 f.close()
410
411 if __name__=="__main__":
412 import sys
413 if len(sys.argv)==1:
414 print "Usage : deconnaisseur.py <serveur> [--debug]"
415 exit(1)
416 serveur=sys.argv[1]
417 if "debug" in sys.argv or "--debug" in sys.argv:
418 debug=True
419 else:
420 debug=False
421 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
422 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
423 try:
424 serveur=serveurs[serveur]
425 except KeyError:
426 print "Server Unknown : %s"%(serveur)
427 exit(404)
428 deco=Deconnaisseur(serveur,debug)
429 deco.start()