4 # Codé par 20-100 le 23/04/12
6 # Un bot IRC qui sort des déconnaissances
15 from remplace_accents
import remplace_accents
17 config_password
="PatrickSébastien"
18 config_pseudo
="deconnaisseur"
19 config_chanlist
=["#bot","#flood"]
20 config_play_channels
=["#flood"]
21 config_stay_channels
=["#flood","#bot"]
22 config_overops
=["[20-100]","[20-100]_","PEB"]
23 config_ops
=["Nit","Eguel","Harry"]
25 config_source_file
="deconnaissances.txt"
26 config_played_file_template
="played.%s.txt" #il faut rajouter le nom du serveur
27 def get_config_played_file(serveur
):
28 serveurs
={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
29 return config_played_file_template
%(serveurs
[serveur
])
30 ttrig
=120 #time trigger (normalement 120, mais diminué pour les tests)
31 Ttrig
=600 #between two enigms
32 config_time_incompressible
=15 #on peut pas retrigger en dessous de ce temps (60)
33 config_time_incompressible_clue
=60 #on peut pas forcer la demande d'indice en dessous
35 config_score_file
="scores.pickle"
37 config_tag_triggers
=[u
"t(|a)g",u
"ta gueule",u
"la ferme",u
"ferme( |-)la",u
"tais-toi",u
"chut"]
38 config_tag_actions
=[u
"se tait",u
"ferme sa gueule",u
"se la ferme",u
"la ferme"]
39 config_tag_answers
=[u
"J'me tais si j'veux !",
40 u
"Je t'entends pas :°",
42 u
"Non, j'ai pas envie",
43 u
"Peut-être quand toi tu la fermeras, et encore…"]
46 config_level3
=["[20-100]","Petite-Peste","PEB"]
48 class UnicodeBotError(Exception):
50 def bot_unicode(chain
):
53 except UnicodeDecodeError:
56 def log(serveur
,channel
="prout",auteur
=None,message
=None):
57 #f=open(config_logfile,"a")
58 #if auteur==message==None:
61 # chain="%s [%s:%s] %s"%(time.strftime("%T"),channel,auteur,message)
68 def reussi(message
,answer
,answer_regexp
,auteur
):
69 if auteur
in config_level3
:
70 return answer
in message
71 if auteur
in config_level2
:
72 return remplace_accents(answer
) in message
74 if re
.match(remplace_accents(answer_regexp
).lower(),remplace_accents(message
).lower()):
77 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
79 chain
=unicode(chain
,"utf8")
81 chain
=unicode(chain
,"utf8").lower()
82 allmatches
="("+"|".join(matches
)+")"
83 reg
=(avant
+allmatches
+apres
).lower()
88 return is_something(chain
,config_tag_triggers
)
90 class RefuseError(Exception):
93 class Deconnaisseur(ircbot
.SingleServerIRCBot
):
94 def __init__(self
,serveur
,debug
=False):
95 temporary_pseudo
=config_pseudo
+str(random
.randrange(10000,100000))
96 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
97 temporary_pseudo
,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
100 self
.overops
=config_overops
101 self
.ops
=self
.overops
+config_ops
102 self
.chanlist
=config_chanlist
103 self
.stay_channels
=config_stay_channels
104 self
.play_channels
=config_play_channels
105 self
.play_status
={i
:[0] for i
in self
.play_channels
}
106 self
.quiet_channels
=[]
108 def give_me_my_pseudo(self
,serv
):
109 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
110 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
112 serv
.nick(config_pseudo
)
114 def on_welcome(self
, serv
, ev
):
115 self
.give_me_my_pseudo(serv
)
116 serv
.privmsg("NickServ","identify %s"%(config_password))
119 self
.chanlist
=["#bot"]
120 self
.play_channels
=["#bot"]
121 for c
in self
.chanlist
:
124 for c
in self
.play_channels
:
125 token
=time
.time()-3600
126 self
.play_status
[c
]=[0,token
]
127 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
129 def start_enigme(self
,serv
,channel
,token
=None):
130 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
132 if token
==self
.play_status
[channel
][-1]:
135 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
140 enigme
,indice
,answer_reg
,answer
=self
.get_enigme()
141 print "%s; %s; %s; %s"%(enigme
, indice
, answer_reg
, answer
)
142 serv
.privmsg(channel
,enigme
.encode("utf8"))
144 self
.play_status
[channel
]=[1,enigme
,indice
,answer_reg
,answer
,token
]
145 serv
.execute_delayed(random
.randrange(ttrig
*3,ttrig
*5),self
.give_indice
,(serv
,channel
,token
))
148 def give_indice(self
,serv
,channel
,token
):
149 if self
.play_status
[channel
][0]==1:
151 # c'est donc que l'indice a été demandé
152 if self
.play_status
[channel
][-1]+config_time_incompressible_clue
<time
.time():
153 token
=self
.play_status
[channel
][-1]
154 if self
.play_status
[channel
][-1]==token
:
155 indice
=self
.play_status
[channel
][2]
156 serv
.privmsg(channel
,"indice : %s"%(indice).encode("utf8"))
157 self
.play_status
[channel
][0]=2
158 serv
.execute_delayed(random
.randrange(ttrig
*1,ttrig
*3),self
.give_answer
,(serv
,channel
,token
))
159 def give_answer(self
,serv
,channel
,token
):
160 if self
.play_status
[channel
][0]==2 and self
.play_status
[channel
][-1]==token
:
161 answer
=self
.play_status
[channel
][4]
162 serv
.privmsg(channel
,"C'était : %s"%(answer).encode("utf8"))
164 self
.play_status
[channel
]=[0,token
]
165 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
167 def get_enigme(self
):
168 # on récupère les déconnaissances
169 f
=open(config_source_file
)
172 l
=re
.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t
)
173 dec
={int(i
[0]):list(i
[1:]) for i
in l
if len(i
)==5}
174 # on va chercher combien de fois elles ont été jouées
175 played_file
=get_config_played_file(self
.serveur
)
179 l
=re
.findall("(.*):(.*)",t
)
180 played
={int(i
[0]):int(i
[1]) for i
in l
}
181 # on récupère le nombre d'occurrences le plus faible
182 mini
=min(played
.values())
183 # on choisit un id dans ceux qui ont ce nombre d'occurences
184 id_choisi
=random
.choice([k
for k
,v
in played
.items() if v
==mini
])
185 enigme
,indice
,answer_reg
,answer
=dec
[id_choisi
]
186 # on incrémente la choisie
188 # on enregistre le played_file
189 f
=open(played_file
,"w")
190 f
.write("\n".join(["%-3s : %s"%(k
,v
) for k
,v
in played
.items()]))
192 return enigme
.decode("utf8"),indice
.decode("utf8"),answer_reg
.decode("utf8"),answer
.decode("utf8")
194 def pourmoi(self
, serv
, message
):
195 pseudo
=serv
.get_nickname()
197 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
198 return (True,message
[size
+1:].strip(" "))
200 return (False,message
)
202 def on_privmsg(self
, serv
, ev
):
203 message
=ev
.arguments()[0]
204 auteur
= irclib
.nm_to_n(ev
.source())
206 test
=bot_unicode(message
)
207 except UnicodeBotError
:
209 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
211 message
=message
.split()
212 cmd
=message
[0].lower()
215 helpmsg_default
="""Liste des commandes :
216 HELP Affiche ce message d'aide
217 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
218 SCORES Affiche les scores"""
220 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
221 LEAVE Faire quitter un channel
222 PLAY Passe un channel en mode "jouer"
223 NOPLAY Passe un channel en mode "ne pas jouer"
224 QUIET Se taire sur un channel
225 NOQUIET Opposé de QUIET"""
227 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
228 SAY Fais envoyer un message sur un chan ou à une personne
229 STAY Ignorera les prochains LEAVE pour un chan
230 NOSTAY Opposé de STAY
231 STATUS Montre l'état courant
233 helpmsg
=helpmsg_default
234 if auteur
in self
.ops
:
236 if auteur
in self
.overops
:
237 helpmsg
+=helpmsg_overops
238 for ligne
in helpmsg
.split("\n"):
239 serv
.privmsg(auteur
,ligne
)
241 if auteur
in self
.ops
:
243 if message
[1] in self
.chanlist
:
244 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
246 serv
.join(message
[1])
247 self
.chanlist
.append(message
[1])
248 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
249 log("priv",auteur
," ".join(message
))
251 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
255 if auteur
in self
.ops
and len(message
)>1:
256 if message
[1] in self
.chanlist
:
257 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
258 serv
.part(message
[1])
259 self
.chanlist
.remove(message
[1])
260 log("priv",auteur
," ".join(message
)+"[successful]")
262 serv
.privmsg(auteur
,"Non, je reste !")
263 log("priv",auteur
," ".join(message
)+"[failed]")
265 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
269 if auteur
in self
.overops
:
271 if message
[1] in self
.stay_channels
:
272 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
273 log("priv",auteur
," ".join(message
)+"[failed]")
275 self
.stay_channels
.append(message
[1])
276 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
277 log("priv",auteur
," ".join(message
)+"[successful]")
279 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
283 if auteur
in self
.overops
:
285 if message
[1] in self
.stay_channels
:
286 self
.stay_channels
.remove(message
[1])
287 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
288 log("priv",auteur
," ".join(message
)+"[successful]")
290 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
291 log("priv",auteur
," ".join(message
)+"[failed]")
295 if auteur
in self
.ops
:
297 if message
[1] in self
.play_channels
:
298 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
299 log("priv",auteur
," ".join(message
)+"[failed]")
301 self
.play_channels
.append(message
[1])
302 self
.play_status
[message
[1]]=[0,time
.time()-3600]
303 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
304 log("priv",auteur
," ".join(message
)+"[successful]")
306 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
310 if auteur
in self
.ops
:
312 if message
[1] in self
.play_channels
:
313 self
.play_channels
.remove(message
[1])
314 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
315 log("priv",auteur
," ".join(message
)+"[successful]")
317 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
318 log("priv",auteur
," ".join(message
)+"[failed]")
322 if auteur
in self
.ops
:
324 if message
[1] in self
.quiet_channels
:
325 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
326 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
328 self
.quiet_channels
.append(message
[1])
329 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
330 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
332 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
336 if auteur
in self
.ops
:
338 if message
[1] in self
.quiet_channels
:
339 self
.quiet_channels
.remove(message
[1])
340 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
341 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
343 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
344 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
347 elif cmd
in ["states","status"]:
348 if auteur
in self
.overops
:
349 for k
in self
.play_status
.keys():
350 serv
.privmsg(auteur
,(u
"%s : %s"%(k
,"; ".join([unicode(i
) for i
in self
.play_status
[k
]]))).encode("utf8") )
352 if auteur
in self
.overops
and len(message
)>2:
353 serv
.privmsg(message
[1]," ".join(message
[2:]))
354 log("priv",auteur
," ".join(message
))
355 elif len(message
)<=2:
356 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
360 if auteur
in self
.overops
:
364 if len(message
) in [3,4] and message
[1].lower()=="transfert":
365 scores
=self
.get_scores()
366 de
,to
=auteur
,message
[2]
367 value
=scores
.get(de
,0)
370 asked
=int(message
[3])
372 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
377 serv
.privmsg(auteur
,"Vous n'avez pas de points")
380 serv
.privmsg(auteur
,"Bien tenté…")
383 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
386 self
.add_score(de
,-asked
)
387 self
.add_score(to
,asked
)
388 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
390 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
392 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
395 scores
=self
.get_scores().items()
397 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
399 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
401 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
402 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
403 elif auteur
in self
.overops
:
404 souscmd
=message
[1].lower()
408 scores
=self
.get_scores()
409 if scores
.has_key(todelete
):
411 self
.save_scores(scores
)
412 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
414 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
416 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
417 elif souscmd
in ["add","sub"]:
419 toadd
,val
=message
[2],message
[3]
423 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
427 self
.add_score(toadd
,val
)
428 serv
.privmsg(auteur
,"Done")
430 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
432 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
438 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
440 def on_pubmsg(self
, serv
, ev
):
441 auteur
= irclib
.nm_to_n(ev
.source())
443 message
= ev
.arguments()[0]
445 test
=bot_unicode(message
)
446 except UnicodeBotError
:
447 if not canal
in self
.quiet_channels
:
449 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
452 pour_moi
,message
=self
.pourmoi(serv
,message
)
453 if pour_moi
and message
.split()!=[]:
454 cmd
=message
.split()[0].lower()
456 args
=" ".join(message
.split()[1:])
459 if cmd
in ["meurs","die","crève"]:
460 if auteur
in self
.overops
:
462 log(canal
,auteur
,message
+"[successful]")
464 serv
.privmsg(canal
,"%s: crève !"%(auteur))
465 log(canal
,auteur
,message
+"[failed]")
466 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
467 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
468 if cmd
in ["part","leave","dégage"]:
469 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
470 or auteur
in self
.overops
):
471 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
472 log(canal
,auteur
,message
+"[successful]")
473 self
.chanlist
.remove(canal
)
475 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
476 log(canal
,auteur
,message
+"[failed]")
478 if cmd
in ["deviens","pseudo"]:
479 if auteur
in self
.ops
:
482 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])).encode("utf8") )
497 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
498 if cmd
in ["score","!score"]:
499 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
500 if cmd
in ["scores","!scores"]:
501 scores
=self
.get_scores().items()
503 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
505 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
507 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
508 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
509 if cmd
=="indice" and canal
in self
.play_channels
:
510 self
.give_indice(serv
,canal
,None)
511 if is_tag(message
) and not canal
in self
.quiet_channels
:
512 if auteur
in self
.ops
:
513 action
=random
.choice(config_tag_actions
)
514 serv
.action(canal
,action
.encode("utf8"))
515 self
.quiet_channels
.append(canal
)
517 answer
=random
.choice(config_tag_answers
)
518 for ligne
in answer
.split("\n"):
519 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
525 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
526 answer_regexp
=self
.play_status
[canal
][3]
527 answer
=self
.play_status
[canal
][4]
528 if reussi(message
.decode("utf8"),answer
,answer_regexp
,auteur
):
529 serv
.privmsg(canal
,(u
"%s: bravo ! (C'était %s)"%(auteur
,answer
)).encode("utf8"))
530 self
.add_score(auteur
,1)
532 self
.play_status
[canal
]=[0,token
]
533 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
534 def get_scores(self
):
535 f
=open(config_score_file
)
536 scores
=pickle
.load(f
)
540 def add_score(self
,pseudo
,value
):
541 scores
=self
.get_scores()
542 if scores
.has_key(pseudo
):
543 scores
[pseudo
]+=value
546 self
.save_scores(scores
)
548 def save_scores(self
,scores
):
549 f
=open(config_score_file
,"w")
550 pickle
.dump(scores
,f
)
553 if __name__
=="__main__":
556 print "Usage : deconnaisseur.py <serveur> [--debug]"
559 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
563 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
564 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
566 serveur
=serveurs
[serveur
]
568 print "Server Unknown : %s"%(serveur)
570 deco
=Deconnaisseur(serveur
,debug
)