4 # Codé par 20-100 le 23/04/12
6 # Un bot IRC qui sort des déconnaissances
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"]
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
=15 #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
33 config_score_file
="scores.pickle"
35 config_tag_triggers
=[u
"t(|a)g",u
"ta gueule",u
"la ferme",u
"ferme( |-)la",u
"tais-toi",u
"chut"]
36 config_tag_actions
=[u
"se tait",u
"ferme sa gueule",u
"se la ferme",u
"la ferme"]
37 config_tag_answers
=[u
"J'me tais si j'veux !",
38 u
"Je t'entends pas :°",
40 u
"Non, j'ai pas envie",
41 u
"Peut-être quand toi tu la fermeras, et encore…"]
43 class UnicodeBotError(Exception):
45 def bot_unicode(chain
):
48 except UnicodeDecodeError:
51 def log(channel
,auteur
=None,message
=None):
52 #f=open(config_logfile,"a")
53 #if auteur==message==None:
56 # chain="%s [%s:%s] %s"%(time.strftime("%T"),channel,auteur,message)
64 """Renvoie une regexp plus tolérante"""
65 reg
=unicode(regexp
,"utf8").lower()
66 reg
=reg
.replace(u
"á",u
"(á|a)").replace(u
"à",u
"(à|a)").replace(u
"â",u
"(â|a)").replace(u
"ä",u
"(ä|a)")
67 reg
=reg
.replace(u
"é",u
"(é|e)").replace(u
"è",u
"(è|e)").replace(u
"ê",u
"(ê|e)").replace(u
"ë",u
"(ë|e)")
68 reg
=reg
.replace(u
"í",u
"(í|i)").replace(u
"ì",u
"(ì|i)").replace(u
"î",u
"(î|i)").replace(u
"ï",u
"(ï|i)")
69 reg
=reg
.replace(u
"ó",u
"(ó|o)").replace(u
"ò",u
"(ò|o)").replace(u
"ô",u
"(ô|o)").replace(u
"ö",u
"(ö|o)")
70 reg
=reg
.replace(u
"ú",u
"(ú|u)").replace(u
"ù",u
"(ù|u)").replace(u
"û",u
"(û|u)").replace(u
"ü",u
"(ü|u)")
71 reg
=reg
.replace(u
"ý",u
"(ý|y)").replace(u
"ỳ",u
"(ỳ|y)").replace(u
"ŷ",u
"(ŷ|y)").replace(u
"ÿ",u
"(ÿ|y)")
72 reg
=reg
.replace(u
"œ",u
"(œ|oe)").replace(u
"æ",u
"(æ|ae)")
75 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
77 chain
=unicode(chain
,"utf8")
79 chain
=unicode(chain
,"utf8").lower()
80 allmatches
="("+"|".join(matches
)+")"
81 reg
=(avant
+allmatches
+apres
).lower()
86 return is_something(chain
,config_tag_triggers
)
88 class RefuseError(Exception):
91 class Deconnaisseur(ircbot
.SingleServerIRCBot
):
92 def __init__(self
,serveur
,debug
=False):
93 temporary_pseudo
=config_pseudo
+str(random
.randrange(10000,100000))
94 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
95 temporary_pseudo
,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
98 self
.overops
=config_overops
99 self
.ops
=self
.overops
+config_ops
100 self
.chanlist
=config_chanlist
101 self
.stay_channels
=config_stay_channels
102 self
.play_channels
=config_play_channels
103 self
.play_status
={i
:[0] for i
in self
.play_channels
}
104 self
.quiet_channels
=[]
106 def give_me_my_pseudo(self
,serv
):
107 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
108 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
110 serv
.nick(config_pseudo
)
112 def on_welcome(self
, serv
, ev
):
113 self
.give_me_my_pseudo(serv
)
114 serv
.privmsg("NickServ","identify %s"%(config_password))
117 self
.chanlist
=["#bot"]
118 self
.play_channels
=["#bot"]
119 for c
in self
.chanlist
:
122 for c
in self
.play_channels
:
123 token
=time
.time()-3600
124 self
.play_status
[c
]=[0,token
]
125 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
127 def start_enigme(self
,serv
,channel
,token
=None):
128 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
130 if token
==self
.play_status
[channel
][-1]:
133 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
138 enigme
,indice
,answer_reg
,answer
=self
.get_enigme()
139 print "%s; %s; %s; %s"%(enigme
, indice
, answer_reg
, answer
)
140 serv
.privmsg(channel
,enigme
)
142 self
.play_status
[channel
]=[1,enigme
,indice
,answer_reg
,answer
,token
]
143 serv
.execute_delayed(random
.randrange(ttrig
*3,ttrig
*5),self
.give_indice
,(serv
,channel
,token
))
146 def give_indice(self
,serv
,channel
,token
):
147 if self
.play_status
[channel
][0]==1:
149 # c'est donc que l'indice a été demandé
150 if self
.play_status
[channel
][-1]+config_time_incompressible_clue
<time
.time():
151 token
=self
.play_status
[channel
][-1]
152 if self
.play_status
[channel
][-1]==token
:
153 indice
=self
.play_status
[channel
][2]
154 serv
.privmsg(channel
,"indice : %s"%(indice))
155 self
.play_status
[channel
][0]=2
156 serv
.execute_delayed(random
.randrange(ttrig
*1,ttrig
*3),self
.give_answer
,(serv
,channel
,token
))
157 def give_answer(self
,serv
,channel
,token
):
158 if self
.play_status
[channel
][0]==2 and self
.play_status
[channel
][-1]==token
:
159 answer
=self
.play_status
[channel
][4]
160 serv
.privmsg(channel
,"C'était : %s"%(answer))
162 self
.play_status
[channel
]=[0,token
]
163 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
165 def get_enigme(self
):
166 f
=open(get_config_source_file(self
.serveur
))
168 l
=re
.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t
)
169 l
=[list(i
) for i
in l
if len(i
)==5]
170 l
.sort(lambda x
,y
: cmp(int(x
[4]),int(y
[4])))
171 # on récupère le nombre d'occurrences le plus faible
173 # on garde que ceux qui ont le même nombre d'occurrences
174 l_mini
=[en
for en
in l
if en
[4]==mini
]
175 # on tire au hasard dedans
176 choisi
=random
.randrange(len(l_mini
))
177 enigme
,indice
,answer_reg
,answer
,_
=l_mini
[choisi
]
178 real_index
=l
.index(l_mini
[choisi
])
179 l
[real_index
][4]=str(int(l
[real_index
][4])+1)
180 f
=open(get_config_source_file(self
.serveur
),"w")
181 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%")
183 return enigme
,indice
,answer_reg
,answer
185 def pourmoi(self
, serv
, message
):
186 pseudo
=serv
.get_nickname()
188 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
189 return (True,message
[size
+1:].strip(" "))
191 return (False,message
)
193 def on_privmsg(self
, serv
, ev
):
194 message
=ev
.arguments()[0]
195 auteur
= irclib
.nm_to_n(ev
.source())
197 test
=bot_unicode(message
)
198 except UnicodeBotError
:
200 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
202 message
=message
.split()
203 cmd
=message
[0].lower()
206 helpmsg_default
="""Liste des commandes :
207 HELP Affiche ce message d'aide
208 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
209 SCORES Affiche les scores"""
211 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
212 LEAVE Faire quitter un channel
213 PLAY Passe un channel en mode "jouer"
214 NOPLAY Passe un channel en mode "ne pas jouer"
215 QUIET Se taire sur un channel
216 NOQUIET Opposé de QUIET"""
218 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
219 SAY Fais envoyer un message sur un chan ou à une personne
220 STAY Ignorera les prochains LEAVE pour un chan
221 NOSTAY Opposé de STAY
222 STATUS Montre l'état courant
224 helpmsg
=helpmsg_default
225 if auteur
in self
.ops
:
227 if auteur
in self
.overops
:
228 helpmsg
+=helpmsg_overops
229 for ligne
in helpmsg
.split("\n"):
230 serv
.privmsg(auteur
,ligne
)
232 if auteur
in self
.ops
:
234 if message
[1] in self
.chanlist
:
235 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
237 serv
.join(message
[1])
238 self
.chanlist
.append(message
[1])
239 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
240 log("priv",auteur
," ".join(message
))
242 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
246 if auteur
in self
.ops
and len(message
)>1:
247 if message
[1] in self
.chanlist
:
248 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
249 serv
.part(message
[1])
250 self
.chanlist
.remove(message
[1])
251 log("priv",auteur
," ".join(message
)+"[successful]")
253 serv
.privmsg(auteur
,"Non, je reste !")
254 log("priv",auteur
," ".join(message
)+"[failed]")
256 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
260 if auteur
in self
.overops
:
262 if message
[1] in self
.stay_channels
:
263 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
264 log("priv",auteur
," ".join(message
)+"[failed]")
266 self
.stay_channels
.append(message
[1])
267 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
268 log("priv",auteur
," ".join(message
)+"[successful]")
270 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
274 if auteur
in self
.overops
:
276 if message
[1] in self
.stay_channels
:
277 self
.stay_channels
.remove(message
[1])
278 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
279 log("priv",auteur
," ".join(message
)+"[successful]")
281 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
282 log("priv",auteur
," ".join(message
)+"[failed]")
286 if auteur
in self
.ops
:
288 if message
[1] in self
.play_channels
:
289 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
290 log("priv",auteur
," ".join(message
)+"[failed]")
292 self
.play_channels
.append(message
[1])
293 self
.play_status
[message
[1]]=[0,time
.time()-3600]
294 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
295 log("priv",auteur
," ".join(message
)+"[successful]")
297 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
301 if auteur
in self
.ops
:
303 if message
[1] in self
.play_channels
:
304 self
.play_channels
.remove(message
[1])
305 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
306 log("priv",auteur
," ".join(message
)+"[successful]")
308 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
309 log("priv",auteur
," ".join(message
)+"[failed]")
313 if auteur
in self
.ops
:
315 if message
[1] in self
.quiet_channels
:
316 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
317 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
319 self
.quiet_channels
.append(message
[1])
320 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
321 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
323 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
327 if auteur
in self
.ops
:
329 if message
[1] in self
.quiet_channels
:
330 self
.quiet_channels
.remove(message
[1])
331 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
332 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
334 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
335 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
338 elif cmd
in ["states","status"]:
339 if auteur
in self
.overops
:
340 for k
in self
.play_status
.keys():
341 serv
.privmsg(auteur
,"%s : %s"%(k
,"; ".join([str(i
) for i
in self
.play_status
[k
]])))
343 if auteur
in self
.overops
and len(message
)>2:
344 serv
.privmsg(message
[1]," ".join(message
[2:]))
345 log("priv",auteur
," ".join(message
))
346 elif len(message
)<=2:
347 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
351 if auteur
in self
.overops
:
355 if len(message
) in [3,4] and message
[1].lower()=="transfert":
356 scores
=self
.get_scores()
357 de
,to
=auteur
,message
[2]
358 value
=scores
.get(de
,0)
361 asked
=int(message
[3])
363 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
368 serv
.privmsg(auteur
,"Vous n'avez pas de points")
371 serv
.privmsg(auteur
,"Bien tenté…")
374 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
377 self
.add_score(de
,-asked
)
378 self
.add_score(to
,asked
)
379 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
381 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
383 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
386 scores
=self
.get_scores().items()
388 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
390 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
392 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
393 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
394 elif auteur
in self
.overops
:
395 souscmd
=message
[1].lower()
399 scores
=self
.get_scores()
400 if scores
.has_key(todelete
):
402 self
.save_scores(scores
)
403 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
405 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
407 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
408 elif souscmd
in ["add","sub"]:
410 toadd
,val
=message
[2],message
[3]
414 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
418 self
.add_score(toadd
,val
)
419 serv
.privmsg(auteur
,"Done")
421 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
423 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
429 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
431 def on_pubmsg(self
, serv
, ev
):
432 auteur
= irclib
.nm_to_n(ev
.source())
434 message
= ev
.arguments()[0]
436 test
=bot_unicode(message
)
437 except UnicodeBotError
:
438 if not canal
in self
.quiet_channels
:
440 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
443 pour_moi
,message
=self
.pourmoi(serv
,message
)
444 if pour_moi
and message
.split()!=[]:
445 cmd
=message
.split()[0].lower()
447 args
=" ".join(message
.split()[1:])
450 if cmd
in ["meurs","die","crève"]:
451 if auteur
in self
.overops
:
453 log(canal
,auteur
,message
+"[successful]")
455 serv
.privmsg(canal
,"%s: crève !"%(auteur))
456 log(canal
,auteur
,message
+"[failed]")
457 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
458 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
459 if cmd
in ["part","leave","dégage"]:
460 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
461 or auteur
in self
.overops
):
462 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
463 log(canal
,auteur
,message
+"[successful]")
464 self
.chanlist
.remove(canal
)
466 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
467 log(canal
,auteur
,message
+"[failed]")
469 if cmd
in ["deviens","pseudo"]:
470 if auteur
in self
.ops
:
473 log(canal
,auteur
,message
+"[successful]")
475 if cmd
in ["coucou"] and not canal
in self
.quiet_channels
:
476 serv
.privmsg(canal
,"%s: coucou"%(auteur))
477 if cmd
in ["ping"] and not canal
in self
.quiet_channels
:
478 serv
.privmsg(canal
,"%s: pong"%(auteur))
479 if cmd
in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
480 if canal
in self
.play_channels
:
481 if self
.play_status
.get(canal
,[-1])[0]==0:
483 self
.start_enigme(serv
,canal
)
485 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
487 serv
.privmsg(canal
,"%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1]))
489 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
490 if cmd
=="indice" and canal
in self
.play_channels
:
491 self
.give_indice(serv
,canal
,None)
492 if is_tag(message
) and not canal
in self
.quiet_channels
:
493 if auteur
in self
.ops
:
494 action
=random
.choice(config_tag_actions
)
495 serv
.action(canal
,action
.encode("utf8"))
496 self
.quiet_channels
.append(canal
)
498 answer
=random
.choice(config_tag_answers
)
499 for ligne
in answer
.split("\n"):
500 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
506 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
507 answer_regexp
=self
.play_status
[canal
][3]
508 if re
.match(tolere(answer_regexp
),unicode(message
,"utf8").lower()):
509 answer
=self
.play_status
[canal
][4]
510 serv
.privmsg(canal
,"%s: bravo ! (C'était %s)"%(auteur
,answer
))
511 self
.add_score(auteur
,1)
513 self
.play_status
[canal
]=[0,token
]
514 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
515 def get_scores(self
):
516 f
=open(config_score_file
)
517 scores
=pickle
.load(f
)
521 def add_score(self
,pseudo
,value
):
522 scores
=self
.get_scores()
523 if scores
.has_key(pseudo
):
524 scores
[pseudo
]+=value
527 self
.save_scores(scores
)
529 def save_scores(self
,scores
):
530 f
=open(config_score_file
,"w")
531 pickle
.dump(scores
,f
)
534 if __name__
=="__main__":
537 print "Usage : deconnaisseur.py <serveur> [--debug]"
540 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
544 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
545 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
547 serveur
=serveurs
[serveur
]
549 print "Server Unknown : %s"%(serveur)
551 deco
=Deconnaisseur(serveur
,debug
)