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(serveur
,channel
="prout",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
:
199 if not canal
in self
.quiet_channels
:
201 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
203 message
=message
.split()
204 cmd
=message
[0].lower()
207 helpmsg_default
="""Liste des commandes :
208 HELP Affiche ce message d'aide
209 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
210 SCORES Affiche les scores"""
212 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
213 LEAVE Faire quitter un channel
214 PLAY Passe un channel en mode "jouer"
215 NOPLAY Passe un channel en mode "ne pas jouer"
216 QUIET Se taire sur un channel
217 NOQUIET Opposé de QUIET"""
219 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
220 SAY Fais envoyer un message sur un chan ou à une personne
221 STAY Ignorera les prochains LEAVE pour un chan
222 NOSTAY Opposé de STAY
223 STATUS Montre l'état courant
225 helpmsg
=helpmsg_default
226 if auteur
in self
.ops
:
228 if auteur
in self
.overops
:
229 helpmsg
+=helpmsg_overops
230 for ligne
in helpmsg
.split("\n"):
231 serv
.privmsg(auteur
,ligne
)
233 if auteur
in self
.ops
:
235 if message
[1] in self
.chanlist
:
236 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
238 serv
.join(message
[1])
239 self
.chanlist
.append(message
[1])
240 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
241 log("priv",auteur
," ".join(message
))
243 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
247 if auteur
in self
.ops
and len(message
)>1:
248 if message
[1] in self
.chanlist
:
249 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
250 serv
.part(message
[1])
251 self
.chanlist
.remove(message
[1])
252 log("priv",auteur
," ".join(message
)+"[successful]")
254 serv
.privmsg(auteur
,"Non, je reste !")
255 log("priv",auteur
," ".join(message
)+"[failed]")
257 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
261 if auteur
in self
.overops
:
263 if message
[1] in self
.stay_channels
:
264 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
265 log("priv",auteur
," ".join(message
)+"[failed]")
267 self
.stay_channels
.append(message
[1])
268 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
269 log("priv",auteur
," ".join(message
)+"[successful]")
271 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
275 if auteur
in self
.overops
:
277 if message
[1] in self
.stay_channels
:
278 self
.stay_channels
.remove(message
[1])
279 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
280 log("priv",auteur
," ".join(message
)+"[successful]")
282 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
283 log("priv",auteur
," ".join(message
)+"[failed]")
287 if auteur
in self
.ops
:
289 if message
[1] in self
.play_channels
:
290 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
291 log("priv",auteur
," ".join(message
)+"[failed]")
293 self
.play_channels
.append(message
[1])
294 self
.play_status
[message
[1]]=[0,time
.time()-3600]
295 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
296 log("priv",auteur
," ".join(message
)+"[successful]")
298 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
302 if auteur
in self
.ops
:
304 if message
[1] in self
.play_channels
:
305 self
.play_channels
.remove(message
[1])
306 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
307 log("priv",auteur
," ".join(message
)+"[successful]")
309 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
310 log("priv",auteur
," ".join(message
)+"[failed]")
314 if auteur
in self
.ops
:
316 if message
[1] in self
.quiet_channels
:
317 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
318 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
320 self
.quiet_channels
.append(message
[1])
321 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
322 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
324 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
328 if auteur
in self
.ops
:
330 if message
[1] in self
.quiet_channels
:
331 self
.quiet_channels
.remove(message
[1])
332 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
333 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
335 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
336 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
339 elif cmd
in ["states","status"]:
340 if auteur
in self
.overops
:
341 for k
in self
.play_status
.keys():
342 serv
.privmsg(auteur
,"%s : %s"%(k
,"; ".join([str(i
) for i
in self
.play_status
[k
]])))
344 if auteur
in self
.overops
and len(message
)>2:
345 serv
.privmsg(message
[1]," ".join(message
[2:]))
346 log("priv",auteur
," ".join(message
))
347 elif len(message
)<=2:
348 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
352 if auteur
in self
.overops
:
356 if len(message
) in [3,4] and message
[1].lower()=="transfert":
357 scores
=self
.get_scores()
358 de
,to
=auteur
,message
[2]
359 value
=scores
.get(de
,0)
362 asked
=int(message
[3])
364 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
369 serv
.privmsg(auteur
,"Vous n'avez pas de points")
372 serv
.privmsg(auteur
,"Bien tenté…")
375 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
378 self
.add_score(de
,-asked
)
379 self
.add_score(to
,asked
)
380 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
382 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
384 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
387 scores
=self
.get_scores().items()
389 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
391 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
393 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
394 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
395 elif auteur
in self
.overops
:
396 souscmd
=message
[1].lower()
400 scores
=self
.get_scores()
401 if scores
.has_key(todelete
):
403 self
.save_scores(scores
)
404 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
406 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
408 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
409 elif souscmd
in ["add","sub"]:
411 toadd
,val
=message
[2],message
[3]
415 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
419 self
.add_score(toadd
,val
)
420 serv
.privmsg(auteur
,"Done")
422 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
424 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
430 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
432 def on_pubmsg(self
, serv
, ev
):
433 auteur
= irclib
.nm_to_n(ev
.source())
435 message
= ev
.arguments()[0]
437 test
=bot_unicode(message
)
438 except UnicodeBotError
:
439 if not canal
in self
.quiet_channels
:
441 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
444 pour_moi
,message
=self
.pourmoi(serv
,message
)
445 if pour_moi
and message
.split()!=[]:
446 cmd
=message
.split()[0].lower()
448 args
=" ".join(message
.split()[1:])
451 if cmd
in ["meurs","die","crève"]:
452 if auteur
in self
.overops
:
454 log(canal
,auteur
,message
+"[successful]")
456 serv
.privmsg(canal
,"%s: crève !"%(auteur))
457 log(canal
,auteur
,message
+"[failed]")
458 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
459 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
460 if cmd
in ["part","leave","dégage"]:
461 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
462 or auteur
in self
.overops
):
463 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
464 log(canal
,auteur
,message
+"[successful]")
465 self
.chanlist
.remove(canal
)
467 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
468 log(canal
,auteur
,message
+"[failed]")
470 if cmd
in ["deviens","pseudo"]:
471 if auteur
in self
.ops
:
474 log(canal
,auteur
,message
+"[successful]")
476 if cmd
in ["coucou"] and not canal
in self
.quiet_channels
:
477 serv
.privmsg(canal
,"%s: coucou"%(auteur))
478 if cmd
in ["ping"] and not canal
in self
.quiet_channels
:
479 serv
.privmsg(canal
,"%s: pong"%(auteur))
480 if cmd
in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
481 if canal
in self
.play_channels
:
482 if self
.play_status
.get(canal
,[-1])[0]==0:
484 self
.start_enigme(serv
,canal
)
486 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
488 serv
.privmsg(canal
,"%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1]))
490 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
491 if cmd
=="indice" and canal
in self
.play_channels
:
492 self
.give_indice(serv
,canal
,None)
493 if is_tag(message
) and not canal
in self
.quiet_channels
:
494 if auteur
in self
.ops
:
495 action
=random
.choice(config_tag_actions
)
496 serv
.action(canal
,action
.encode("utf8"))
497 self
.quiet_channels
.append(canal
)
499 answer
=random
.choice(config_tag_answers
)
500 for ligne
in answer
.split("\n"):
501 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
507 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
508 answer_regexp
=self
.play_status
[canal
][3]
509 if re
.match(tolere(answer_regexp
),unicode(message
,"utf8").lower()):
510 answer
=self
.play_status
[canal
][4]
511 serv
.privmsg(canal
,"%s: bravo ! (C'était %s)"%(auteur
,answer
))
512 self
.add_score(auteur
,1)
514 self
.play_status
[canal
]=[0,token
]
515 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
516 def get_scores(self
):
517 f
=open(config_score_file
)
518 scores
=pickle
.load(f
)
522 def add_score(self
,pseudo
,value
):
523 scores
=self
.get_scores()
524 if scores
.has_key(pseudo
):
525 scores
[pseudo
]+=value
528 self
.save_scores(scores
)
530 def save_scores(self
,scores
):
531 f
=open(config_score_file
,"w")
532 pickle
.dump(scores
,f
)
535 if __name__
=="__main__":
538 print "Usage : deconnaisseur.py <serveur> [--debug]"
541 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
545 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
546 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
548 serveur
=serveurs
[serveur
]
550 print "Server Unknown : %s"%(serveur)
552 deco
=Deconnaisseur(serveur
,debug
)