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
="deconnaissances.txt"
25 config_played_file_template
="played.%s.txt" #il faut rajouter le nom du serveur
26 def get_config_played_file(serveur
):
27 serveurs
={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
28 return config_played_file_template
%(serveurs
[serveur
])
29 ttrig
=120 #time trigger (normalement 120, mais diminué pour les tests)
30 Ttrig
=600 #between two enigms
31 config_time_incompressible
=15 #on peut pas retrigger en dessous de ce temps (60)
32 config_time_incompressible_clue
=60 #on peut pas forcer la demande d'indice en dessous
34 config_score_file
="scores.pickle"
36 config_tag_triggers
=[u
"t(|a)g",u
"ta gueule",u
"la ferme",u
"ferme( |-)la",u
"tais-toi",u
"chut"]
37 config_tag_actions
=[u
"se tait",u
"ferme sa gueule",u
"se la ferme",u
"la ferme"]
38 config_tag_answers
=[u
"J'me tais si j'veux !",
39 u
"Je t'entends pas :°",
41 u
"Non, j'ai pas envie",
42 u
"Peut-être quand toi tu la fermeras, et encore…"]
44 class UnicodeBotError(Exception):
46 def bot_unicode(chain
):
49 except UnicodeDecodeError:
52 def log(serveur
,channel
="prout",auteur
=None,message
=None):
53 #f=open(config_logfile,"a")
54 #if auteur==message==None:
57 # chain="%s [%s:%s] %s"%(time.strftime("%T"),channel,auteur,message)
65 """Renvoie une regexp plus tolérante"""
66 reg
=unicode(regexp
,"utf8").lower()
67 reg
=reg
.replace(u
"á",u
"(á|a)").replace(u
"à",u
"(à|a)").replace(u
"â",u
"(â|a)").replace(u
"ä",u
"(ä|a)")
68 reg
=reg
.replace(u
"é",u
"(é|e)").replace(u
"è",u
"(è|e)").replace(u
"ê",u
"(ê|e)").replace(u
"ë",u
"(ë|e)")
69 reg
=reg
.replace(u
"í",u
"(í|i)").replace(u
"ì",u
"(ì|i)").replace(u
"î",u
"(î|i)").replace(u
"ï",u
"(ï|i)")
70 reg
=reg
.replace(u
"ó",u
"(ó|o)").replace(u
"ò",u
"(ò|o)").replace(u
"ô",u
"(ô|o)").replace(u
"ö",u
"(ö|o)")
71 reg
=reg
.replace(u
"ú",u
"(ú|u)").replace(u
"ù",u
"(ù|u)").replace(u
"û",u
"(û|u)").replace(u
"ü",u
"(ü|u)")
72 reg
=reg
.replace(u
"ý",u
"(ý|y)").replace(u
"ỳ",u
"(ỳ|y)").replace(u
"ŷ",u
"(ŷ|y)").replace(u
"ÿ",u
"(ÿ|y)")
73 reg
=reg
.replace(u
"œ",u
"(œ|oe)").replace(u
"æ",u
"(æ|ae)")
76 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
78 chain
=unicode(chain
,"utf8")
80 chain
=unicode(chain
,"utf8").lower()
81 allmatches
="("+"|".join(matches
)+")"
82 reg
=(avant
+allmatches
+apres
).lower()
87 return is_something(chain
,config_tag_triggers
)
89 class RefuseError(Exception):
92 class Deconnaisseur(ircbot
.SingleServerIRCBot
):
93 def __init__(self
,serveur
,debug
=False):
94 temporary_pseudo
=config_pseudo
+str(random
.randrange(10000,100000))
95 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
96 temporary_pseudo
,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
99 self
.overops
=config_overops
100 self
.ops
=self
.overops
+config_ops
101 self
.chanlist
=config_chanlist
102 self
.stay_channels
=config_stay_channels
103 self
.play_channels
=config_play_channels
104 self
.play_status
={i
:[0] for i
in self
.play_channels
}
105 self
.quiet_channels
=[]
107 def give_me_my_pseudo(self
,serv
):
108 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
109 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
111 serv
.nick(config_pseudo
)
113 def on_welcome(self
, serv
, ev
):
114 self
.give_me_my_pseudo(serv
)
115 serv
.privmsg("NickServ","identify %s"%(config_password))
118 self
.chanlist
=["#bot"]
119 self
.play_channels
=["#bot"]
120 for c
in self
.chanlist
:
123 for c
in self
.play_channels
:
124 token
=time
.time()-3600
125 self
.play_status
[c
]=[0,token
]
126 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
128 def start_enigme(self
,serv
,channel
,token
=None):
129 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
131 if token
==self
.play_status
[channel
][-1]:
134 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
139 enigme
,indice
,answer_reg
,answer
=self
.get_enigme()
140 print "%s; %s; %s; %s"%(enigme
, indice
, answer_reg
, answer
)
141 serv
.privmsg(channel
,enigme
)
143 self
.play_status
[channel
]=[1,enigme
,indice
,answer_reg
,answer
,token
]
144 serv
.execute_delayed(random
.randrange(ttrig
*3,ttrig
*5),self
.give_indice
,(serv
,channel
,token
))
147 def give_indice(self
,serv
,channel
,token
):
148 if self
.play_status
[channel
][0]==1:
150 # c'est donc que l'indice a été demandé
151 if self
.play_status
[channel
][-1]+config_time_incompressible_clue
<time
.time():
152 token
=self
.play_status
[channel
][-1]
153 if self
.play_status
[channel
][-1]==token
:
154 indice
=self
.play_status
[channel
][2]
155 serv
.privmsg(channel
,"indice : %s"%(indice))
156 self
.play_status
[channel
][0]=2
157 serv
.execute_delayed(random
.randrange(ttrig
*1,ttrig
*3),self
.give_answer
,(serv
,channel
,token
))
158 def give_answer(self
,serv
,channel
,token
):
159 if self
.play_status
[channel
][0]==2 and self
.play_status
[channel
][-1]==token
:
160 answer
=self
.play_status
[channel
][4]
161 serv
.privmsg(channel
,"C'était : %s"%(answer))
163 self
.play_status
[channel
]=[0,token
]
164 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
166 def get_enigme(self
):
167 # on récupère les déconnaissances
168 f
=open(config_source_file
)
170 l
=re
.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t
)
171 dec
={int(i
[0]):list(i
[1:]) for i
in l
if len(i
)==5}
172 # on va chercher combien de fois elles ont été jouées
173 played_file
=get_config_played_file(self
.serveur
)
176 l
=re
.findall("(.*):(.*)",t
)
177 played
={int(i
[0]):int(i
[1]) for i
in l
}
178 # on récupère le nombre d'occurrences le plus faible
179 mini
=min(played
.values())
180 # on choisit un id dans ceux qui ont ce nombre d'occurences
181 id_choisi
=random
.choice([k
for k
,v
in played
.items() if v
==mini
])
182 enigme
,indice
,answer_reg
,answer
=dec
[id_choisi
]
183 # on incrémente la choisie
185 # on enregistre le played_file
186 f
=open(played_file
,"w")
187 f
.write("\n".join(["%-3s : %s"%(k
,v
) for k
,v
in played
.items()]))
189 return enigme
,indice
,answer_reg
,answer
191 def pourmoi(self
, serv
, message
):
192 pseudo
=serv
.get_nickname()
194 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
195 return (True,message
[size
+1:].strip(" "))
197 return (False,message
)
199 def on_privmsg(self
, serv
, ev
):
200 message
=ev
.arguments()[0]
201 auteur
= irclib
.nm_to_n(ev
.source())
203 test
=bot_unicode(message
)
204 except UnicodeBotError
:
206 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
208 message
=message
.split()
209 cmd
=message
[0].lower()
212 helpmsg_default
="""Liste des commandes :
213 HELP Affiche ce message d'aide
214 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
215 SCORES Affiche les scores"""
217 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
218 LEAVE Faire quitter un channel
219 PLAY Passe un channel en mode "jouer"
220 NOPLAY Passe un channel en mode "ne pas jouer"
221 QUIET Se taire sur un channel
222 NOQUIET Opposé de QUIET"""
224 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
225 SAY Fais envoyer un message sur un chan ou à une personne
226 STAY Ignorera les prochains LEAVE pour un chan
227 NOSTAY Opposé de STAY
228 STATUS Montre l'état courant
230 helpmsg
=helpmsg_default
231 if auteur
in self
.ops
:
233 if auteur
in self
.overops
:
234 helpmsg
+=helpmsg_overops
235 for ligne
in helpmsg
.split("\n"):
236 serv
.privmsg(auteur
,ligne
)
238 if auteur
in self
.ops
:
240 if message
[1] in self
.chanlist
:
241 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
243 serv
.join(message
[1])
244 self
.chanlist
.append(message
[1])
245 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
246 log("priv",auteur
," ".join(message
))
248 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
252 if auteur
in self
.ops
and len(message
)>1:
253 if message
[1] in self
.chanlist
:
254 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
255 serv
.part(message
[1])
256 self
.chanlist
.remove(message
[1])
257 log("priv",auteur
," ".join(message
)+"[successful]")
259 serv
.privmsg(auteur
,"Non, je reste !")
260 log("priv",auteur
," ".join(message
)+"[failed]")
262 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
266 if auteur
in self
.overops
:
268 if message
[1] in self
.stay_channels
:
269 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
270 log("priv",auteur
," ".join(message
)+"[failed]")
272 self
.stay_channels
.append(message
[1])
273 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
274 log("priv",auteur
," ".join(message
)+"[successful]")
276 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
280 if auteur
in self
.overops
:
282 if message
[1] in self
.stay_channels
:
283 self
.stay_channels
.remove(message
[1])
284 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
285 log("priv",auteur
," ".join(message
)+"[successful]")
287 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
288 log("priv",auteur
," ".join(message
)+"[failed]")
292 if auteur
in self
.ops
:
294 if message
[1] in self
.play_channels
:
295 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
296 log("priv",auteur
," ".join(message
)+"[failed]")
298 self
.play_channels
.append(message
[1])
299 self
.play_status
[message
[1]]=[0,time
.time()-3600]
300 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
301 log("priv",auteur
," ".join(message
)+"[successful]")
303 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
307 if auteur
in self
.ops
:
309 if message
[1] in self
.play_channels
:
310 self
.play_channels
.remove(message
[1])
311 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
312 log("priv",auteur
," ".join(message
)+"[successful]")
314 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
315 log("priv",auteur
," ".join(message
)+"[failed]")
319 if auteur
in self
.ops
:
321 if message
[1] in self
.quiet_channels
:
322 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
323 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
325 self
.quiet_channels
.append(message
[1])
326 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
327 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
329 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
333 if auteur
in self
.ops
:
335 if message
[1] in self
.quiet_channels
:
336 self
.quiet_channels
.remove(message
[1])
337 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
338 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
340 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
341 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
344 elif cmd
in ["states","status"]:
345 if auteur
in self
.overops
:
346 for k
in self
.play_status
.keys():
347 serv
.privmsg(auteur
,"%s : %s"%(k
,"; ".join([str(i
) for i
in self
.play_status
[k
]])))
349 if auteur
in self
.overops
and len(message
)>2:
350 serv
.privmsg(message
[1]," ".join(message
[2:]))
351 log("priv",auteur
," ".join(message
))
352 elif len(message
)<=2:
353 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
357 if auteur
in self
.overops
:
361 if len(message
) in [3,4] and message
[1].lower()=="transfert":
362 scores
=self
.get_scores()
363 de
,to
=auteur
,message
[2]
364 value
=scores
.get(de
,0)
367 asked
=int(message
[3])
369 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
374 serv
.privmsg(auteur
,"Vous n'avez pas de points")
377 serv
.privmsg(auteur
,"Bien tenté…")
380 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
383 self
.add_score(de
,-asked
)
384 self
.add_score(to
,asked
)
385 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
387 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
389 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
392 scores
=self
.get_scores().items()
394 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
396 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
398 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
399 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
400 elif auteur
in self
.overops
:
401 souscmd
=message
[1].lower()
405 scores
=self
.get_scores()
406 if scores
.has_key(todelete
):
408 self
.save_scores(scores
)
409 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
411 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
413 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
414 elif souscmd
in ["add","sub"]:
416 toadd
,val
=message
[2],message
[3]
420 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
424 self
.add_score(toadd
,val
)
425 serv
.privmsg(auteur
,"Done")
427 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
429 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
435 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
437 def on_pubmsg(self
, serv
, ev
):
438 auteur
= irclib
.nm_to_n(ev
.source())
440 message
= ev
.arguments()[0]
442 test
=bot_unicode(message
)
443 except UnicodeBotError
:
444 if not canal
in self
.quiet_channels
:
446 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
449 pour_moi
,message
=self
.pourmoi(serv
,message
)
450 if pour_moi
and message
.split()!=[]:
451 cmd
=message
.split()[0].lower()
453 args
=" ".join(message
.split()[1:])
456 if cmd
in ["meurs","die","crève"]:
457 if auteur
in self
.overops
:
459 log(canal
,auteur
,message
+"[successful]")
461 serv
.privmsg(canal
,"%s: crève !"%(auteur))
462 log(canal
,auteur
,message
+"[failed]")
463 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
464 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
465 if cmd
in ["part","leave","dégage"]:
466 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
467 or auteur
in self
.overops
):
468 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
469 log(canal
,auteur
,message
+"[successful]")
470 self
.chanlist
.remove(canal
)
472 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
473 log(canal
,auteur
,message
+"[failed]")
475 if cmd
in ["deviens","pseudo"]:
476 if auteur
in self
.ops
:
479 log(canal
,auteur
,message
+"[successful]")
481 if cmd
in ["coucou"] and not canal
in self
.quiet_channels
:
482 serv
.privmsg(canal
,"%s: coucou"%(auteur))
483 if cmd
in ["ping"] and not canal
in self
.quiet_channels
:
484 serv
.privmsg(canal
,"%s: pong"%(auteur))
485 if cmd
in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
486 if canal
in self
.play_channels
:
487 if self
.play_status
.get(canal
,[-1])[0]==0:
489 self
.start_enigme(serv
,canal
)
491 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
493 serv
.privmsg(canal
,"%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1]))
495 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
496 if cmd
=="indice" and canal
in self
.play_channels
:
497 self
.give_indice(serv
,canal
,None)
498 if is_tag(message
) and not canal
in self
.quiet_channels
:
499 if auteur
in self
.ops
:
500 action
=random
.choice(config_tag_actions
)
501 serv
.action(canal
,action
.encode("utf8"))
502 self
.quiet_channels
.append(canal
)
504 answer
=random
.choice(config_tag_answers
)
505 for ligne
in answer
.split("\n"):
506 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
512 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
513 answer_regexp
=self
.play_status
[canal
][3]
514 if re
.match(tolere(answer_regexp
),unicode(message
,"utf8").lower()):
515 answer
=self
.play_status
[canal
][4]
516 serv
.privmsg(canal
,"%s: bravo ! (C'était %s)"%(auteur
,answer
))
517 self
.add_score(auteur
,1)
519 self
.play_status
[canal
]=[0,token
]
520 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
521 def get_scores(self
):
522 f
=open(config_score_file
)
523 scores
=pickle
.load(f
)
527 def add_score(self
,pseudo
,value
):
528 scores
=self
.get_scores()
529 if scores
.has_key(pseudo
):
530 scores
[pseudo
]+=value
533 self
.save_scores(scores
)
535 def save_scores(self
,scores
):
536 f
=open(config_score_file
,"w")
537 pickle
.dump(scores
,f
)
540 if __name__
=="__main__":
543 print "Usage : deconnaisseur.py <serveur> [--debug]"
546 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
550 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
551 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
553 serveur
=serveurs
[serveur
]
555 print "Server Unknown : %s"%(serveur)
557 deco
=Deconnaisseur(serveur
,debug
)