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
=60 #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 class UnicodeBotError(Exception):
37 def bot_unicode(chain
):
40 except UnicodeDecodeError:
43 def log(channel
,auteur
=None,message
=None):
44 #f=open(config_logfile,"a")
45 #if auteur==message==None:
48 # chain="%s [%s:%s] %s"%(time.strftime("%T"),channel,auteur,message)
56 """Renvoie une regexp plus tolérante"""
57 reg
=unicode(regexp
,"utf8").lower()
58 reg
=reg
.replace(u
"á",u
"(á|a)").replace(u
"à",u
"(à|a)").replace(u
"â",u
"(â|a)").replace(u
"ä",u
"(ä|a)")
59 reg
=reg
.replace(u
"é",u
"(é|e)").replace(u
"è",u
"(è|e)").replace(u
"ê",u
"(ê|e)").replace(u
"ë",u
"(ë|e)")
60 reg
=reg
.replace(u
"í",u
"(í|i)").replace(u
"ì",u
"(ì|i)").replace(u
"î",u
"(î|i)").replace(u
"ï",u
"(ï|i)")
61 reg
=reg
.replace(u
"ó",u
"(ó|o)").replace(u
"ò",u
"(ò|o)").replace(u
"ô",u
"(ô|o)").replace(u
"ö",u
"(ö|o)")
62 reg
=reg
.replace(u
"ú",u
"(ú|u)").replace(u
"ù",u
"(ù|u)").replace(u
"û",u
"(û|u)").replace(u
"ü",u
"(ü|u)")
63 reg
=reg
.replace(u
"ý",u
"(ý|y)").replace(u
"ỳ",u
"(ỳ|y)").replace(u
"ŷ",u
"(ŷ|y)").replace(u
"ÿ",u
"(ÿ|y)")
64 reg
=reg
.replace(u
"œ",u
"(œ|oe)").replace(u
"æ",u
"(æ|ae)")
67 class RefuseError(Exception):
70 class Deconnaisseur(ircbot
.SingleServerIRCBot
):
71 def __init__(self
,serveur
,debug
=False):
72 temporary_pseudo
=config_pseudo
+str(random
.randrange(10000,100000))
73 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
74 temporary_pseudo
,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
77 self
.overops
=config_overops
78 self
.ops
=self
.overops
+config_ops
79 self
.chanlist
=config_chanlist
80 self
.stay_channels
=config_stay_channels
81 self
.play_channels
=config_play_channels
82 self
.play_status
={i
:[0] for i
in self
.play_channels
}
84 def give_me_my_pseudo(self
,serv
):
85 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
86 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
88 serv
.nick(config_pseudo
)
90 def on_welcome(self
, serv
, ev
):
91 self
.give_me_my_pseudo(serv
)
92 serv
.privmsg("NickServ","identify %s"%(config_password))
95 self
.chanlist
=["#bot"]
96 self
.play_channels
=["#bot"]
97 for c
in self
.chanlist
:
100 for c
in self
.play_channels
:
101 token
=time
.time()-3600
102 self
.play_status
[c
]=[0,token
]
103 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
105 def start_enigme(self
,serv
,channel
,token
=None):
106 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
108 if token
==self
.play_status
[channel
][-1]:
111 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
116 enigme
,indice
,answer_reg
,answer
=self
.get_enigme()
117 print "%s; %s; %s; %s"%(enigme
, indice
, answer_reg
, answer
)
118 serv
.privmsg(channel
,enigme
)
120 self
.play_status
[channel
]=[1,enigme
,indice
,answer_reg
,answer
,token
]
121 serv
.execute_delayed(random
.randrange(ttrig
*3,ttrig
*5),self
.give_indice
,(serv
,channel
,token
))
124 def give_indice(self
,serv
,channel
,token
):
125 if self
.play_status
[channel
][0]==1:
127 # c'est donc que l'indice a été demandé
128 if self
.play_status
[channel
][-1]+config_time_incompressible_clue
<time
.time():
129 token
=self
.play_status
[channel
][-1]
130 if self
.play_status
[channel
][-1]==token
:
131 indice
=self
.play_status
[channel
][2]
132 serv
.privmsg(channel
,"indice : %s"%(indice))
133 self
.play_status
[channel
][0]=2
134 serv
.execute_delayed(random
.randrange(ttrig
*1,ttrig
*3),self
.give_answer
,(serv
,channel
,token
))
135 def give_answer(self
,serv
,channel
,token
):
136 if self
.play_status
[channel
][0]==2 and self
.play_status
[channel
][-1]==token
:
137 answer
=self
.play_status
[channel
][4]
138 serv
.privmsg(channel
,"C'était : %s"%(answer))
140 self
.play_status
[channel
]=[0,token
]
141 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
143 def get_enigme(self
):
144 f
=open(get_config_source_file(self
.serveur
))
146 l
=re
.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t
)
147 l
=[list(i
) for i
in l
if len(i
)==5]
148 l
.sort(lambda x
,y
: cmp(int(x
[4]),int(y
[4])))
149 # on récupère le nombre d'occurrences le plus faible
151 # on garde que ceux qui ont le même nombre d'occurrences
152 l_mini
=[en
for en
in l
if en
[4]==mini
]
153 # on tire au hasard dedans
154 choisi
=random
.randrange(len(l_mini
))
155 enigme
,indice
,answer_reg
,answer
,_
=l_mini
[choisi
]
156 real_index
=l
.index(l_mini
[choisi
])
157 l
[real_index
][4]=str(int(l
[real_index
][4])+1)
158 f
=open(get_config_source_file(self
.serveur
),"w")
159 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%")
161 return enigme
,indice
,answer_reg
,answer
163 def pourmoi(self
, serv
, message
):
164 pseudo
=serv
.get_nickname()
166 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
167 return (True,message
[size
+1:].strip(" "))
169 return (False,message
)
171 def on_privmsg(self
, serv
, ev
):
172 message
=ev
.arguments()[0]
173 auteur
= irclib
.nm_to_n(ev
.source())
175 test
=bot_unicode(message
)
176 except UnicodeBotError
:
178 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
180 message
=message
.split()
181 cmd
=message
[0].lower()
184 helpmsg_default
="""Liste des commandes :
185 HELP Affiche ce message d'aide
186 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
187 SCORES Affiche les scores"""
189 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
190 LEAVE Faire quitter un channel
191 PLAY Passe un channel en mode "jouer"
192 NOPLAY Passe un channel en mode "ne pas jouer" """
194 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
195 SAY Fais envoyer un message sur un chan ou à une personne
196 STAY Ignorera les prochains LEAVE pour un chan
197 NOSTAY Opposé de STAY
198 STATUS Montre l'état courant
200 helpmsg
=helpmsg_default
201 if auteur
in self
.ops
:
203 if auteur
in self
.overops
:
204 helpmsg
+=helpmsg_overops
205 for ligne
in helpmsg
.split("\n"):
206 serv
.privmsg(auteur
,ligne
)
208 if auteur
in self
.ops
:
210 if message
[1] in self
.chanlist
:
211 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
213 serv
.join(message
[1])
214 self
.chanlist
.append(message
[1])
215 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
216 log("priv",auteur
," ".join(message
))
218 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
222 if auteur
in self
.ops
and len(message
)>1:
223 if message
[1] in self
.chanlist
:
224 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
225 serv
.part(message
[1])
226 self
.chanlist
.remove(message
[1])
227 log("priv",auteur
," ".join(message
)+"[successful]")
229 serv
.privmsg(auteur
,"Non, je reste !")
230 log("priv",auteur
," ".join(message
)+"[failed]")
232 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
236 if auteur
in self
.overops
:
238 if message
[1] in self
.stay_channels
:
239 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
240 log("priv",auteur
," ".join(message
)+"[failed]")
242 self
.stay_channels
.append(message
[1])
243 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
244 log("priv",auteur
," ".join(message
)+"[successful]")
246 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
250 if auteur
in self
.overops
:
252 if message
[1] in self
.stay_channels
:
253 self
.stay_channels
.remove(message
[1])
254 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
255 log("priv",auteur
," ".join(message
)+"[successful]")
257 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
258 log("priv",auteur
," ".join(message
)+"[failed]")
262 if auteur
in self
.ops
:
264 if message
[1] in self
.play_channels
:
265 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
266 log("priv",auteur
," ".join(message
)+"[failed]")
268 self
.play_channels
.append(message
[1])
269 self
.play_status
[message
[1]]=[0,time
.time()-3600]
270 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
271 log("priv",auteur
," ".join(message
)+"[successful]")
273 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
277 if auteur
in self
.ops
:
279 if message
[1] in self
.play_channels
:
280 self
.play_channels
.remove(message
[1])
281 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
282 log("priv",auteur
," ".join(message
)+"[successful]")
284 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
285 log("priv",auteur
," ".join(message
)+"[failed]")
288 elif cmd
in ["states","status"]:
289 if auteur
in self
.overops
:
290 for k
in self
.play_status
.keys():
291 serv
.privmsg(auteur
,"%s : %s"%(k
,"; ".join([str(i
) for i
in self
.play_status
[k
]])))
293 if auteur
in self
.overops
and len(message
)>2:
294 serv
.privmsg(message
[1]," ".join(message
[2:]))
295 log("priv",auteur
," ".join(message
))
296 elif len(message
)<=2:
297 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
301 if auteur
in self
.overops
:
305 if len(message
) in [3,4] and message
[1].lower()=="transfert":
306 scores
=self
.get_scores()
307 de
,to
=auteur
,message
[2]
308 value
=scores
.get(de
,0)
311 asked
=int(message
[3])
313 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
318 serv
.privmsg(auteur
,"Vous n'avez pas de points")
321 serv
.privmsg(auteur
,"Bien tenté…")
324 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
327 self
.add_score(de
,-asked
)
328 self
.add_score(to
,asked
)
329 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
331 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
333 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
336 scores
=self
.get_scores().items()
338 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
340 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
342 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
344 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
345 elif auteur
in self
.overops
:
346 souscmd
=message
[1].lower()
350 scores
=self
.get_scores()
351 if scores
.has_key(todelete
):
353 self
.save_scores(scores
)
354 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
356 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
358 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
359 elif souscmd
in ["add","sub"]:
361 toadd
,val
=message
[2],message
[3]
365 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
369 self
.add_score(toadd
,val
)
370 serv
.privmsg(auteur
,"Done")
372 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
374 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
380 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
382 def on_pubmsg(self
, serv
, ev
):
383 auteur
= irclib
.nm_to_n(ev
.source())
385 message
= ev
.arguments()[0]
387 test
=bot_unicode(message
)
388 except UnicodeBotError
:
389 if not canal
in self
.quiet_channels
:
391 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
394 pour_moi
,message
=self
.pourmoi(serv
,message
)
395 if pour_moi
and message
.split()!=[]:
396 cmd
=message
.split()[0].lower()
398 args
=" ".join(message
.split()[1:])
401 if cmd
in ["meurs","die","crève"]:
402 if auteur
in self
.overops
:
404 log(canal
,auteur
,message
+"[successful]")
406 serv
.privmsg(canal
,"%s: crève !"%(auteur))
407 log(canal
,auteur
,message
+"[failed]")
408 if cmd
in ["meur", "meurt","meurre","meurres"]:
409 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du pluriel : "meurs" (de rien)'%(auteur))
410 if cmd
in ["part","leave","dégage"]:
411 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
412 or auteur
in self
.overops
):
413 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
414 log(canal
,auteur
,message
+"[successful]")
415 self
.chanlist
.remove(canal
)
417 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
418 log(canal
,auteur
,message
+"[failed]")
420 if cmd
in ["deviens","pseudo"]:
421 if auteur
in self
.ops
:
424 log(canal
,auteur
,message
+"[successful]")
426 if cmd
in ["coucou"]:
427 serv
.privmsg(canal
,"%s: coucou"%(auteur))
429 serv
.privmsg(canal
,"%s: pong"%(auteur))
430 if cmd
in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
431 if canal
in self
.play_channels
:
432 if self
.play_status
.get(canal
,[-1])[0]==0:
434 self
.start_enigme(serv
,canal
)
436 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
438 serv
.privmsg(canal
,"%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1]))
440 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
441 if cmd
=="indice" and canal
in self
.play_channels
:
442 self
.give_indice(serv
,canal
,None)
448 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
449 answer_regexp
=self
.play_status
[canal
][3]
450 if re
.match(tolere(answer_regexp
),unicode(message
,"utf8").lower()):
451 answer
=self
.play_status
[canal
][4]
452 serv
.privmsg(canal
,"%s: bravo ! (C'était %s)"%(auteur
,answer
))
453 self
.add_score(auteur
,1)
455 self
.play_status
[canal
]=[0,token
]
456 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
457 def get_scores(self
):
458 f
=open(config_score_file
)
459 scores
=pickle
.load(f
)
463 def add_score(self
,pseudo
,value
):
464 scores
=self
.get_scores()
465 if scores
.has_key(pseudo
):
466 scores
[pseudo
]+=value
469 self
.save_scores(scores
)
471 def save_scores(self
,scores
):
472 f
=open(config_score_file
,"w")
473 pickle
.dump(scores
,f
)
476 if __name__
=="__main__":
479 print "Usage : deconnaisseur.py <serveur> [--debug]"
482 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
486 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
487 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
489 serveur
=serveurs
[serveur
]
491 print "Server Unknown : %s"%(serveur)
493 deco
=Deconnaisseur(serveur
,debug
)