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