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
)
171 l
=re
.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t
)
172 dec
={int(i
[0]):list(i
[1:]) for i
in l
if len(i
)==5}
173 # on va chercher combien de fois elles ont été jouées
174 played_file
=get_config_played_file(self
.serveur
)
178 l
=re
.findall("(.*):(.*)",t
)
179 played
={int(i
[0]):int(i
[1]) for i
in l
}
180 # on récupère le nombre d'occurrences le plus faible
181 mini
=min(played
.values())
182 # on choisit un id dans ceux qui ont ce nombre d'occurences
183 id_choisi
=random
.choice([k
for k
,v
in played
.items() if v
==mini
])
184 enigme
,indice
,answer_reg
,answer
=dec
[id_choisi
]
185 # on incrémente la choisie
187 # on enregistre le played_file
188 f
=open(played_file
,"w")
189 f
.write("\n".join(["%-3s : %s"%(k
,v
) for k
,v
in played
.items()]))
191 return enigme
,indice
,answer_reg
,answer
193 def pourmoi(self
, serv
, message
):
194 pseudo
=serv
.get_nickname()
196 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
197 return (True,message
[size
+1:].strip(" "))
199 return (False,message
)
201 def on_privmsg(self
, serv
, ev
):
202 message
=ev
.arguments()[0]
203 auteur
= irclib
.nm_to_n(ev
.source())
205 test
=bot_unicode(message
)
206 except UnicodeBotError
:
208 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
210 message
=message
.split()
211 cmd
=message
[0].lower()
214 helpmsg_default
="""Liste des commandes :
215 HELP Affiche ce message d'aide
216 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
217 SCORES Affiche les scores"""
219 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
220 LEAVE Faire quitter un channel
221 PLAY Passe un channel en mode "jouer"
222 NOPLAY Passe un channel en mode "ne pas jouer"
223 QUIET Se taire sur un channel
224 NOQUIET Opposé de QUIET"""
226 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
227 SAY Fais envoyer un message sur un chan ou à une personne
228 STAY Ignorera les prochains LEAVE pour un chan
229 NOSTAY Opposé de STAY
230 STATUS Montre l'état courant
232 helpmsg
=helpmsg_default
233 if auteur
in self
.ops
:
235 if auteur
in self
.overops
:
236 helpmsg
+=helpmsg_overops
237 for ligne
in helpmsg
.split("\n"):
238 serv
.privmsg(auteur
,ligne
)
240 if auteur
in self
.ops
:
242 if message
[1] in self
.chanlist
:
243 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
245 serv
.join(message
[1])
246 self
.chanlist
.append(message
[1])
247 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
248 log("priv",auteur
," ".join(message
))
250 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
254 if auteur
in self
.ops
and len(message
)>1:
255 if message
[1] in self
.chanlist
:
256 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
257 serv
.part(message
[1])
258 self
.chanlist
.remove(message
[1])
259 log("priv",auteur
," ".join(message
)+"[successful]")
261 serv
.privmsg(auteur
,"Non, je reste !")
262 log("priv",auteur
," ".join(message
)+"[failed]")
264 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
268 if auteur
in self
.overops
:
270 if message
[1] in self
.stay_channels
:
271 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
272 log("priv",auteur
," ".join(message
)+"[failed]")
274 self
.stay_channels
.append(message
[1])
275 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
276 log("priv",auteur
," ".join(message
)+"[successful]")
278 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
282 if auteur
in self
.overops
:
284 if message
[1] in self
.stay_channels
:
285 self
.stay_channels
.remove(message
[1])
286 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
287 log("priv",auteur
," ".join(message
)+"[successful]")
289 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
290 log("priv",auteur
," ".join(message
)+"[failed]")
294 if auteur
in self
.ops
:
296 if message
[1] in self
.play_channels
:
297 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
298 log("priv",auteur
," ".join(message
)+"[failed]")
300 self
.play_channels
.append(message
[1])
301 self
.play_status
[message
[1]]=[0,time
.time()-3600]
302 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
303 log("priv",auteur
," ".join(message
)+"[successful]")
305 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
309 if auteur
in self
.ops
:
311 if message
[1] in self
.play_channels
:
312 self
.play_channels
.remove(message
[1])
313 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
314 log("priv",auteur
," ".join(message
)+"[successful]")
316 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
317 log("priv",auteur
," ".join(message
)+"[failed]")
321 if auteur
in self
.ops
:
323 if message
[1] in self
.quiet_channels
:
324 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
325 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
327 self
.quiet_channels
.append(message
[1])
328 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
329 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
331 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
335 if auteur
in self
.ops
:
337 if message
[1] in self
.quiet_channels
:
338 self
.quiet_channels
.remove(message
[1])
339 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
340 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
342 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
343 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
346 elif cmd
in ["states","status"]:
347 if auteur
in self
.overops
:
348 for k
in self
.play_status
.keys():
349 serv
.privmsg(auteur
,"%s : %s"%(k
,"; ".join([str(i
) for i
in self
.play_status
[k
]])))
351 if auteur
in self
.overops
and len(message
)>2:
352 serv
.privmsg(message
[1]," ".join(message
[2:]))
353 log("priv",auteur
," ".join(message
))
354 elif len(message
)<=2:
355 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
359 if auteur
in self
.overops
:
363 if len(message
) in [3,4] and message
[1].lower()=="transfert":
364 scores
=self
.get_scores()
365 de
,to
=auteur
,message
[2]
366 value
=scores
.get(de
,0)
369 asked
=int(message
[3])
371 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
376 serv
.privmsg(auteur
,"Vous n'avez pas de points")
379 serv
.privmsg(auteur
,"Bien tenté…")
382 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
385 self
.add_score(de
,-asked
)
386 self
.add_score(to
,asked
)
387 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
389 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
391 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
394 scores
=self
.get_scores().items()
396 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
398 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
400 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
401 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
402 elif auteur
in self
.overops
:
403 souscmd
=message
[1].lower()
407 scores
=self
.get_scores()
408 if scores
.has_key(todelete
):
410 self
.save_scores(scores
)
411 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
413 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
415 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
416 elif souscmd
in ["add","sub"]:
418 toadd
,val
=message
[2],message
[3]
422 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
426 self
.add_score(toadd
,val
)
427 serv
.privmsg(auteur
,"Done")
429 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
431 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
437 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
439 def on_pubmsg(self
, serv
, ev
):
440 auteur
= irclib
.nm_to_n(ev
.source())
442 message
= ev
.arguments()[0]
444 test
=bot_unicode(message
)
445 except UnicodeBotError
:
446 if not canal
in self
.quiet_channels
:
448 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
451 pour_moi
,message
=self
.pourmoi(serv
,message
)
452 if pour_moi
and message
.split()!=[]:
453 cmd
=message
.split()[0].lower()
455 args
=" ".join(message
.split()[1:])
458 if cmd
in ["meurs","die","crève"]:
459 if auteur
in self
.overops
:
461 log(canal
,auteur
,message
+"[successful]")
463 serv
.privmsg(canal
,"%s: crève !"%(auteur))
464 log(canal
,auteur
,message
+"[failed]")
465 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
466 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
467 if cmd
in ["part","leave","dégage"]:
468 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
469 or auteur
in self
.overops
):
470 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
471 log(canal
,auteur
,message
+"[successful]")
472 self
.chanlist
.remove(canal
)
474 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
475 log(canal
,auteur
,message
+"[failed]")
477 if cmd
in ["deviens","pseudo"]:
478 if auteur
in self
.ops
:
481 log(canal
,auteur
,message
+"[successful]")
483 if cmd
in ["coucou"] and not canal
in self
.quiet_channels
:
484 serv
.privmsg(canal
,"%s: coucou"%(auteur))
485 if cmd
in ["ping"] and not canal
in self
.quiet_channels
:
486 serv
.privmsg(canal
,"%s: pong"%(auteur))
487 if cmd
in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
488 if canal
in self
.play_channels
:
489 if self
.play_status
.get(canal
,[-1])[0]==0:
491 self
.start_enigme(serv
,canal
)
493 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
495 serv
.privmsg(canal
,"%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1]))
497 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
498 if cmd
=="indice" and canal
in self
.play_channels
:
499 self
.give_indice(serv
,canal
,None)
500 if is_tag(message
) and not canal
in self
.quiet_channels
:
501 if auteur
in self
.ops
:
502 action
=random
.choice(config_tag_actions
)
503 serv
.action(canal
,action
.encode("utf8"))
504 self
.quiet_channels
.append(canal
)
506 answer
=random
.choice(config_tag_answers
)
507 for ligne
in answer
.split("\n"):
508 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
514 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
515 answer_regexp
=self
.play_status
[canal
][3]
516 if re
.match(tolere(answer_regexp
),unicode(message
,"utf8").lower()):
517 answer
=self
.play_status
[canal
][4]
518 serv
.privmsg(canal
,"%s: bravo ! (C'était %s)"%(auteur
,answer
))
519 self
.add_score(auteur
,1)
521 self
.play_status
[canal
]=[0,token
]
522 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
523 def get_scores(self
):
524 f
=open(config_score_file
)
525 scores
=pickle
.load(f
)
529 def add_score(self
,pseudo
,value
):
530 scores
=self
.get_scores()
531 if scores
.has_key(pseudo
):
532 scores
[pseudo
]+=value
535 self
.save_scores(scores
)
537 def save_scores(self
,scores
):
538 f
=open(config_score_file
,"w")
539 pickle
.dump(scores
,f
)
542 if __name__
=="__main__":
545 print "Usage : deconnaisseur.py <serveur> [--debug]"
548 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
552 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
553 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
555 serveur
=serveurs
[serveur
]
557 print "Server Unknown : %s"%(serveur)
559 deco
=Deconnaisseur(serveur
,debug
)