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…"]
45 config_level2
=["Petite-Peste"]
46 config_level3
=["[20-100]"]
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 print answer_regexp
.lower(), remplace_accents(message
).lower()
75 if re
.match(remplace_accents(answer_regexp
).lower(),remplace_accents(message
).lower()):
78 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
80 chain
=unicode(chain
,"utf8")
82 chain
=unicode(chain
,"utf8").lower()
83 allmatches
="("+"|".join(matches
)+")"
84 reg
=(avant
+allmatches
+apres
).lower()
89 return is_something(chain
,config_tag_triggers
)
91 class RefuseError(Exception):
94 class Deconnaisseur(ircbot
.SingleServerIRCBot
):
95 def __init__(self
,serveur
,debug
=False):
96 temporary_pseudo
=config_pseudo
+str(random
.randrange(10000,100000))
97 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
98 temporary_pseudo
,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
101 self
.overops
=config_overops
102 self
.ops
=self
.overops
+config_ops
103 self
.chanlist
=config_chanlist
104 self
.stay_channels
=config_stay_channels
105 self
.play_channels
=config_play_channels
106 self
.play_status
={i
:[0] for i
in self
.play_channels
}
107 self
.quiet_channels
=[]
109 def give_me_my_pseudo(self
,serv
):
110 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
111 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
113 serv
.nick(config_pseudo
)
115 def on_welcome(self
, serv
, ev
):
116 self
.give_me_my_pseudo(serv
)
117 serv
.privmsg("NickServ","identify %s"%(config_password))
120 self
.chanlist
=["#bot"]
121 self
.play_channels
=["#bot"]
122 for c
in self
.chanlist
:
125 for c
in self
.play_channels
:
126 token
=time
.time()-3600
127 self
.play_status
[c
]=[0,token
]
128 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
130 def start_enigme(self
,serv
,channel
,token
=None):
131 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
133 if token
==self
.play_status
[channel
][-1]:
136 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
141 enigme
,indice
,answer_reg
,answer
=self
.get_enigme()
142 print "%s; %s; %s; %s"%(enigme
, indice
, answer_reg
, answer
)
143 serv
.privmsg(channel
,enigme
.encode("utf8"))
145 self
.play_status
[channel
]=[1,enigme
,indice
,answer_reg
,answer
,token
]
146 serv
.execute_delayed(random
.randrange(ttrig
*3,ttrig
*5),self
.give_indice
,(serv
,channel
,token
))
149 def give_indice(self
,serv
,channel
,token
):
150 if self
.play_status
[channel
][0]==1:
152 # c'est donc que l'indice a été demandé
153 if self
.play_status
[channel
][-1]+config_time_incompressible_clue
<time
.time():
154 token
=self
.play_status
[channel
][-1]
155 if self
.play_status
[channel
][-1]==token
:
156 indice
=self
.play_status
[channel
][2]
157 serv
.privmsg(channel
,"indice : %s"%(indice).encode("utf8"))
158 self
.play_status
[channel
][0]=2
159 serv
.execute_delayed(random
.randrange(ttrig
*1,ttrig
*3),self
.give_answer
,(serv
,channel
,token
))
160 def give_answer(self
,serv
,channel
,token
):
161 if self
.play_status
[channel
][0]==2 and self
.play_status
[channel
][-1]==token
:
162 answer
=self
.play_status
[channel
][4]
163 serv
.privmsg(channel
,"C'était : %s"%(answer).encode("utf8"))
165 self
.play_status
[channel
]=[0,token
]
166 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
168 def get_enigme(self
):
169 # on récupère les déconnaissances
170 f
=open(config_source_file
)
173 l
=re
.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t
)
174 dec
={int(i
[0]):list(i
[1:]) for i
in l
if len(i
)==5}
175 # on va chercher combien de fois elles ont été jouées
176 played_file
=get_config_played_file(self
.serveur
)
180 l
=re
.findall("(.*):(.*)",t
)
181 played
={int(i
[0]):int(i
[1]) for i
in l
}
182 # on récupère le nombre d'occurrences le plus faible
183 mini
=min(played
.values())
184 # on choisit un id dans ceux qui ont ce nombre d'occurences
185 id_choisi
=random
.choice([k
for k
,v
in played
.items() if v
==mini
])
186 enigme
,indice
,answer_reg
,answer
=dec
[id_choisi
]
187 # on incrémente la choisie
189 # on enregistre le played_file
190 f
=open(played_file
,"w")
191 f
.write("\n".join(["%-3s : %s"%(k
,v
) for k
,v
in played
.items()]))
193 return enigme
.decode("utf8"),indice
.decode("utf8"),answer_reg
.decode("utf8"),answer
.decode("utf8")
195 def pourmoi(self
, serv
, message
):
196 pseudo
=serv
.get_nickname()
198 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
199 return (True,message
[size
+1:].strip(" "))
201 return (False,message
)
203 def on_privmsg(self
, serv
, ev
):
204 message
=ev
.arguments()[0]
205 auteur
= irclib
.nm_to_n(ev
.source())
207 test
=bot_unicode(message
)
208 except UnicodeBotError
:
210 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
212 message
=message
.split()
213 cmd
=message
[0].lower()
216 helpmsg_default
="""Liste des commandes :
217 HELP Affiche ce message d'aide
218 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
219 SCORES Affiche les scores"""
221 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
222 LEAVE Faire quitter un channel
223 PLAY Passe un channel en mode "jouer"
224 NOPLAY Passe un channel en mode "ne pas jouer"
225 QUIET Se taire sur un channel
226 NOQUIET Opposé de QUIET"""
228 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
229 SAY Fais envoyer un message sur un chan ou à une personne
230 STAY Ignorera les prochains LEAVE pour un chan
231 NOSTAY Opposé de STAY
232 STATUS Montre l'état courant
234 helpmsg
=helpmsg_default
235 if auteur
in self
.ops
:
237 if auteur
in self
.overops
:
238 helpmsg
+=helpmsg_overops
239 for ligne
in helpmsg
.split("\n"):
240 serv
.privmsg(auteur
,ligne
)
242 if auteur
in self
.ops
:
244 if message
[1] in self
.chanlist
:
245 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
247 serv
.join(message
[1])
248 self
.chanlist
.append(message
[1])
249 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
250 log("priv",auteur
," ".join(message
))
252 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
256 if auteur
in self
.ops
and len(message
)>1:
257 if message
[1] in self
.chanlist
:
258 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
259 serv
.part(message
[1])
260 self
.chanlist
.remove(message
[1])
261 log("priv",auteur
," ".join(message
)+"[successful]")
263 serv
.privmsg(auteur
,"Non, je reste !")
264 log("priv",auteur
," ".join(message
)+"[failed]")
266 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
270 if auteur
in self
.overops
:
272 if message
[1] in self
.stay_channels
:
273 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
274 log("priv",auteur
," ".join(message
)+"[failed]")
276 self
.stay_channels
.append(message
[1])
277 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
278 log("priv",auteur
," ".join(message
)+"[successful]")
280 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
284 if auteur
in self
.overops
:
286 if message
[1] in self
.stay_channels
:
287 self
.stay_channels
.remove(message
[1])
288 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
289 log("priv",auteur
," ".join(message
)+"[successful]")
291 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
292 log("priv",auteur
," ".join(message
)+"[failed]")
296 if auteur
in self
.ops
:
298 if message
[1] in self
.play_channels
:
299 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
300 log("priv",auteur
," ".join(message
)+"[failed]")
302 self
.play_channels
.append(message
[1])
303 self
.play_status
[message
[1]]=[0,time
.time()-3600]
304 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
305 log("priv",auteur
," ".join(message
)+"[successful]")
307 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
311 if auteur
in self
.ops
:
313 if message
[1] in self
.play_channels
:
314 self
.play_channels
.remove(message
[1])
315 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
316 log("priv",auteur
," ".join(message
)+"[successful]")
318 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
319 log("priv",auteur
," ".join(message
)+"[failed]")
323 if auteur
in self
.ops
:
325 if message
[1] in self
.quiet_channels
:
326 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
327 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
329 self
.quiet_channels
.append(message
[1])
330 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
331 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
333 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
337 if auteur
in self
.ops
:
339 if message
[1] in self
.quiet_channels
:
340 self
.quiet_channels
.remove(message
[1])
341 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
342 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
344 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
345 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
348 elif cmd
in ["states","status"]:
349 if auteur
in self
.overops
:
350 for k
in self
.play_status
.keys():
351 serv
.privmsg(auteur
,(u
"%s : %s"%(k
,"; ".join([unicode(i
) for i
in self
.play_status
[k
]]))).encode("utf8") )
353 if auteur
in self
.overops
and len(message
)>2:
354 serv
.privmsg(message
[1]," ".join(message
[2:]))
355 log("priv",auteur
," ".join(message
))
356 elif len(message
)<=2:
357 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
361 if auteur
in self
.overops
:
365 if len(message
) in [3,4] and message
[1].lower()=="transfert":
366 scores
=self
.get_scores()
367 de
,to
=auteur
,message
[2]
368 value
=scores
.get(de
,0)
371 asked
=int(message
[3])
373 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
378 serv
.privmsg(auteur
,"Vous n'avez pas de points")
381 serv
.privmsg(auteur
,"Bien tenté…")
384 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
387 self
.add_score(de
,-asked
)
388 self
.add_score(to
,asked
)
389 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
391 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
393 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
396 scores
=self
.get_scores().items()
398 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
400 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
402 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
403 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
404 elif auteur
in self
.overops
:
405 souscmd
=message
[1].lower()
409 scores
=self
.get_scores()
410 if scores
.has_key(todelete
):
412 self
.save_scores(scores
)
413 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
415 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
417 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
418 elif souscmd
in ["add","sub"]:
420 toadd
,val
=message
[2],message
[3]
424 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
428 self
.add_score(toadd
,val
)
429 serv
.privmsg(auteur
,"Done")
431 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
433 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
439 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
441 def on_pubmsg(self
, serv
, ev
):
442 auteur
= irclib
.nm_to_n(ev
.source())
444 message
= ev
.arguments()[0]
446 test
=bot_unicode(message
)
447 except UnicodeBotError
:
448 if not canal
in self
.quiet_channels
:
450 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
453 pour_moi
,message
=self
.pourmoi(serv
,message
)
454 if pour_moi
and message
.split()!=[]:
455 cmd
=message
.split()[0].lower()
457 args
=" ".join(message
.split()[1:])
460 if cmd
in ["meurs","die","crève"]:
461 if auteur
in self
.overops
:
463 log(canal
,auteur
,message
+"[successful]")
465 serv
.privmsg(canal
,"%s: crève !"%(auteur))
466 log(canal
,auteur
,message
+"[failed]")
467 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
468 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
469 if cmd
in ["part","leave","dégage"]:
470 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
471 or auteur
in self
.overops
):
472 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
473 log(canal
,auteur
,message
+"[successful]")
474 self
.chanlist
.remove(canal
)
476 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
477 log(canal
,auteur
,message
+"[failed]")
479 if cmd
in ["deviens","pseudo"]:
480 if auteur
in self
.ops
:
483 log(canal
,auteur
,message
+"[successful]")
485 if cmd
in ["coucou"] and not canal
in self
.quiet_channels
:
486 serv
.privmsg(canal
,"%s: coucou"%(auteur))
487 if cmd
in ["ping"] and not canal
in self
.quiet_channels
:
488 serv
.privmsg(canal
,"%s: pong"%(auteur))
489 if cmd
in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
490 if canal
in self
.play_channels
:
491 if self
.play_status
.get(canal
,[-1])[0]==0:
493 self
.start_enigme(serv
,canal
)
495 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
497 serv
.privmsg(canal
,"%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1]))
499 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
500 if cmd
=="indice" and canal
in self
.play_channels
:
501 self
.give_indice(serv
,canal
,None)
502 if is_tag(message
) and not canal
in self
.quiet_channels
:
503 if auteur
in self
.ops
:
504 action
=random
.choice(config_tag_actions
)
505 serv
.action(canal
,action
.encode("utf8"))
506 self
.quiet_channels
.append(canal
)
508 answer
=random
.choice(config_tag_answers
)
509 for ligne
in answer
.split("\n"):
510 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
516 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
517 answer_regexp
=self
.play_status
[canal
][3]
518 answer
=self
.play_status
[canal
][4]
519 if reussi(message
.decode("utf8"),answer
,answer_regexp
,auteur
):
520 serv
.privmsg(canal
,(u
"%s: bravo ! (C'était %s)"%(auteur
,answer
)).encode("utf8"))
521 self
.add_score(auteur
,1)
523 self
.play_status
[canal
]=[0,token
]
524 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
525 def get_scores(self
):
526 f
=open(config_score_file
)
527 scores
=pickle
.load(f
)
531 def add_score(self
,pseudo
,value
):
532 scores
=self
.get_scores()
533 if scores
.has_key(pseudo
):
534 scores
[pseudo
]+=value
537 self
.save_scores(scores
)
539 def save_scores(self
,scores
):
540 f
=open(config_score_file
,"w")
541 pickle
.dump(scores
,f
)
544 if __name__
=="__main__":
547 print "Usage : deconnaisseur.py <serveur> [--debug]"
550 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
554 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
555 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
557 serveur
=serveurs
[serveur
]
559 print "Server Unknown : %s"%(serveur)
561 deco
=Deconnaisseur(serveur
,debug
)