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
}
85 def give_me_my_pseudo(self
,serv
):
86 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
87 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
89 serv
.nick(config_pseudo
)
91 def on_welcome(self
, serv
, ev
):
92 self
.give_me_my_pseudo(serv
)
93 serv
.privmsg("NickServ","identify %s"%(config_password))
96 self
.chanlist
=["#bot"]
97 self
.play_channels
=["#bot"]
98 for c
in self
.chanlist
:
101 for c
in self
.play_channels
:
102 token
=time
.time()-3600
103 self
.play_status
[c
]=[0,token
]
104 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
106 def start_enigme(self
,serv
,channel
,token
=None):
107 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
109 if token
==self
.play_status
[channel
][-1]:
112 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
117 enigme
,indice
,answer_reg
,answer
=self
.get_enigme()
118 print "%s; %s; %s; %s"%(enigme
, indice
, answer_reg
, answer
)
119 serv
.privmsg(channel
,enigme
)
121 self
.play_status
[channel
]=[1,enigme
,indice
,answer_reg
,answer
,token
]
122 serv
.execute_delayed(random
.randrange(ttrig
*3,ttrig
*5),self
.give_indice
,(serv
,channel
,token
))
125 def give_indice(self
,serv
,channel
,token
):
126 if self
.play_status
[channel
][0]==1:
128 # c'est donc que l'indice a été demandé
129 if self
.play_status
[channe
][-1]+config_time_incompressible_clue
<time
.time():
130 token
=self
.play_status
[channel
][-1]
131 if self
.play_status
[channel
][-1]==token
:
132 indice
=self
.play_status
[channel
][2]
133 serv
.privmsg(channel
,"indice : %s"%(indice))
134 self
.play_status
[channel
][0]=2
135 serv
.execute_delayed(random
.randrange(ttrig
*1,ttrig
*3),self
.give_answer
,(serv
,channel
,token
))
136 def give_answer(self
,serv
,channel
,token
):
137 if self
.play_status
[channel
][0]==2 and self
.play_status
[channel
][-1]==token
:
138 answer
=self
.play_status
[channel
][4]
139 serv
.privmsg(channel
,"C'était : %s"%(answer))
141 self
.play_status
[channel
]=[0,token
]
142 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
144 def get_enigme(self
):
145 f
=open(get_config_source_file(self
.serveur
))
147 l
=re
.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t
)
148 l
=[list(i
) for i
in l
if len(i
)==5]
149 l
.sort(lambda x
,y
: cmp(int(x
[4]),int(y
[4])))
150 # on récupère le nombre d'occurrences le plus faible
152 # on garde que ceux qui ont le même nombre d'occurrences
153 l_mini
=[en
for en
in l
if en
[4]==mini
]
154 # on tire au hasard dedans
155 choisi
=random
.randrange(len(l_mini
))
156 enigme
,indice
,answer_reg
,answer
,_
=l_mini
[choisi
]
157 real_index
=l
.index(l_mini
[choisi
])
158 l
[real_index
][4]=str(int(l
[real_index
][4])+1)
159 f
=open(get_config_source_file(self
.serveur
),"w")
160 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%")
162 return enigme
,indice
,answer_reg
,answer
164 def pourmoi(self
, serv
, message
):
165 pseudo
=serv
.get_nickname()
167 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
168 return (True,message
[size
+1:].strip(" "))
170 return (False,message
)
172 def on_privmsg(self
, serv
, ev
):
173 message
=ev
.arguments()[0]
174 auteur
= irclib
.nm_to_n(ev
.source())
176 test
=bot_unicode(message
)
177 except UnicodeBotError
:
179 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
181 message
=message
.split()
182 cmd
=message
[0].lower()
185 helpmsg_default
="""Liste des commandes :
186 HELP Affiche ce message d'aide
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 SAY Fais envoyer un message sur un chan ou à une personne
195 STAY Ignorera les prochains LEAVE pour un chan
196 NOSTAY Opposé de STAY
197 STATUS Montre l'état courant
199 helpmsg
=helpmsg_default
200 if auteur
in self
.ops
:
202 if auteur
in self
.overops
:
203 helpmsg
+=helpmsg_overops
204 for ligne
in helpmsg
.split("\n"):
205 serv
.privmsg(auteur
,ligne
)
207 if auteur
in self
.ops
:
209 if message
[1] in self
.chanlist
:
210 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
212 serv
.join(message
[1])
213 self
.chanlist
.append(message
[1])
214 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
215 log("priv",auteur
," ".join(message
))
217 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
221 if auteur
in self
.ops
and len(message
)>1:
222 if message
[1] in self
.chanlist
:
223 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
224 serv
.part(message
[1])
225 self
.chanlist
.remove(message
[1])
226 log("priv",auteur
," ".join(message
)+"[successful]")
228 serv
.privmsg(auteur
,"Non, je reste !")
229 log("priv",auteur
," ".join(message
)+"[failed]")
231 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
235 if auteur
in self
.overops
:
237 if message
[1] in self
.stay_channels
:
238 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
239 log("priv",auteur
," ".join(message
)+"[failed]")
241 self
.stay_channels
.append(message
[1])
242 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
243 log("priv",auteur
," ".join(message
)+"[successful]")
245 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
249 if auteur
in self
.overops
:
251 if message
[1] in self
.stay_channels
:
252 self
.stay_channels
.remove(message
[1])
253 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
254 log("priv",auteur
," ".join(message
)+"[successful]")
256 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
257 log("priv",auteur
," ".join(message
)+"[failed]")
261 if auteur
in self
.ops
:
263 if message
[1] in self
.play_channels
:
264 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
265 log("priv",auteur
," ".join(message
)+"[failed]")
267 self
.play_channels
.append(message
[1])
268 self
.play_status
[message
[1]]=[0,time
.time()-3600]
269 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
270 log("priv",auteur
," ".join(message
)+"[successful]")
272 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
276 if auteur
in self
.ops
:
278 if message
[1] in self
.play_channels
:
279 self
.play_channels
.remove(message
[1])
280 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
281 log("priv",auteur
," ".join(message
)+"[successful]")
283 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
284 log("priv",auteur
," ".join(message
)+"[failed]")
287 elif cmd
in ["states","status"]:
288 if auteur
in self
.overops
:
289 for k
in self
.play_status
.keys():
290 serv
.privmsg(auteur
,"%s : %s"%(k
,"; ".join([str(i
) for i
in self
.play_status
[k
]])))
292 if auteur
in self
.overops
and len(message
)>2:
293 serv
.privmsg(message
[1]," ".join(message
[2:]))
294 log("priv",auteur
," ".join(message
))
295 elif len(message
)<=2:
296 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
300 if auteur
in self
.overops
:
303 serv
.privmsg(auteur
,"Votre score : %s"%(self
.scores
.get(auteur
,0)) )
305 scores
=self
.get_scores().items()
307 scores
.sort(lambda x
,y
:cmp(x
[1].lower(),y
[1].lower()))
309 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
311 scores
.sort(lambda x
,y
:cmp(x
[0],y
[0]))
313 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
317 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
319 def on_pubmsg(self
, serv
, ev
):
320 auteur
= irclib
.nm_to_n(ev
.source())
322 message
= ev
.arguments()[0]
324 test
=bot_unicode(message
)
325 except UnicodeBotError
:
326 if not canal
in self
.quiet_channels
:
328 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
331 pour_moi
,message
=self
.pourmoi(serv
,message
)
332 if pour_moi
and message
.split()!=[]:
333 cmd
=message
.split()[0].lower()
335 args
=" ".join(message
.split()[1:])
338 if cmd
in ["meurs","die","crève"]:
339 if auteur
in self
.overops
:
341 log(canal
,auteur
,message
+"[successful]")
343 serv
.privmsg(canal
,"%s: crève !"%(auteur))
344 log(canal
,auteur
,message
+"[failed]")
345 if cmd
in ["meur", "meurt","meurre","meurres"]:
346 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du pluriel : "meurs" (de rien)'%(auteur))
347 if cmd
in ["part","leave","dégage"]:
348 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
349 or auteur
in self
.overops
):
350 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
351 log(canal
,auteur
,message
+"[successful]")
352 self
.chanlist
.remove(canal
)
354 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
355 log(canal
,auteur
,message
+"[failed]")
357 if cmd
in ["deviens","pseudo"]:
358 if auteur
in self
.ops
:
361 log(canal
,auteur
,message
+"[successful]")
363 if cmd
in ["coucou"]:
364 serv
.privmsg(canal
,"%s: coucou"%(auteur))
366 serv
.privmsg(canal
,"%s: pong"%(auteur))
367 if cmd
in ["déconnaissance","deconnaissance","énigme","enigme","encore"]:
368 if canal
in self
.play_channels
:
369 if self
.play_status
.get(canal
,[-1])[0]==0:
371 self
.start_enigme(serv
,canal
)
373 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
375 serv
.privmsg(canal
,"%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1]))
377 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
383 if self
.play_status
.get(canal
,[-1])[0] in [1,2]:
384 answer_regexp
=self
.play_status
[canal
][3]
385 if re
.match(tolere(answer_regexp
),unicode(message
,"utf8").lower()):
386 answer
=self
.play_status
[canal
][4]
387 serv
.privmsg(canal
,"%s: bravo ! (C'était %s)"%(auteur
,answer
))
388 self
.scoreplus(auteur
)
390 self
.play_status
[canal
]=[0,token
]
391 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
392 def get_scores(self
):
393 f
=open(config_score_file
)
394 scores
=pickle
.load(f
)
398 def scoreplus(self
,pseudo
):
399 scores
=self
.get_scores()
400 if scores
.has_key(pseudo
):
404 self
.save_scores(scores
)
406 def save_scores(self
,scores
):
407 f
=open(config_score_file
,"w")
408 pickle
.dump(scores
,f
)
411 if __name__
=="__main__":
414 print "Usage : deconnaisseur.py <serveur> [--debug]"
417 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
421 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
422 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
424 serveur
=serveurs
[serveur
]
426 print "Server Unknown : %s"%(serveur)
428 deco
=Deconnaisseur(serveur
,debug
)