]> gitweb.pimeys.fr Git - bots/hung.git/blob - hung.py
ea2a64c843dec47a3a0bd5ad2a333de08d8c8b1a
[bots/hung.git] / hung.py
1 #!/usr/bin/python
2 # -*- coding:utf8 -*-
3
4 # Codé par 20-100 le 23/04/12
5
6 # Un test de bot irc, parce que c'est cool
7
8 import irclib
9 import ircbot
10 import threading
11 import random
12 import time
13 import socket, ssl, json
14 import pickle
15 import re
16 import os
17 from commands import getstatusoutput as ex
18
19 import sys
20 config_debug_stdout=True
21 if "--quiet" in sys.argv:
22 config_debug_stdout=False
23
24 config_irc_password="I'mAHungMan"
25 config_irc_pseudo="Hung"
26 config_chanlist=["#bot","#flood"]
27 config_stay_channels=["#bot","#flood"]
28 config_play_channels=["#flood"]
29 config_quiet_channels=[]
30 config_logfile_template="hung.%s.log"
31 def get_config_logfile(serveur):
32 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
33 return config_logfile_template%(serveurs[serveur])
34 config_overops=["[20-100]","[20-100]_"]
35 config_ops=[]
36 config_report_bugs_to=["[20-100]"]
37
38 config_dico_mots="mots.txt"
39 config_dico_defs="definitions.txt"
40
41 config_scores_file="scores.pickle"
42
43
44 def log(serveur,channel,auteur=None,message=None):
45 f=open(get_config_logfile(serveur),"a")
46 if auteur==message==None:
47 # alors c'est que c'est pas un channel mais juste une ligne de log
48 chain="%s %s"%(time.strftime("%F %T"),channel)
49 else:
50 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
51 f.write(chain+"\n")
52 if config_debug_stdout:
53 print chain
54 f.close()
55
56 class UnicodeBotError(Exception):
57 pass
58 def bot_unicode(chain):
59 try:
60 unicode(chain,"utf8")
61 except UnicodeDecodeError as exc:
62 raise UnicodeBotError
63
64 def remplace_accents(chaine):
65 chaine=chaine.lower()
66 remplacements = {u"á":u"a",u"à":u"a",u"â":u"a",u"ä":u"a",u"é":u"e",u"è":u"e",u"ê":u"e",u"ë":u"e",u"í":u"i",u"ì":u"i",u"î":u"i",u"ï":u"i",u"ó":u"o", u"ò":u"o",u"ô":u"o",u"ö":u"o",u"ú":u"u",u"ù":u"u",u"û":u"u",u"ü":u"u",u"ý":u"y",u"ỳ":u"y",u"ŷ":u"y",u"ÿ":u"y",u"œ":u"oe",u"æ":u"ae"}
67 for avant,apres in remplacements.items():
68 chaine=chaine.replace(avant,apres)
69 return chaine
70
71
72 class Hung(ircbot.SingleServerIRCBot):
73 def __init__(self,serveur,debug=False):
74 temporary_pseudo=config_irc_pseudo+str(random.randrange(10000,100000))
75 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
76 temporary_pseudo,"Bot irc pour jouer au pendu", 10)
77 self.debug=debug
78 self.serveur=serveur
79 self.overops=config_overops
80 self.ops=self.overops+config_ops
81 self.report_bugs_to=config_report_bugs_to
82 self.chanlist=config_chanlist
83 self.stay_channels=config_stay_channels
84 self.play_channels=config_play_channels
85 self.play_status={}
86 self.quiet_channels=config_quiet_channels
87
88
89 def give_me_my_pseudo(self,serv):
90 serv.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo,config_irc_password))
91 serv.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo,config_irc_password))
92 time.sleep(0.3)
93 serv.nick(config_irc_pseudo)
94
95 def on_welcome(self, serv, ev):
96 self.give_me_my_pseudo(serv)
97 serv.privmsg("NickServ","IDENTIFY %s"%(config_irc_password))
98 log(self.serveur,"Connected")
99 if self.debug:
100 self.chanlist=["#bot"]
101 self.play_channels=["#bot"]
102 for c in self.chanlist:
103 log(self.serveur,"JOIN %s"%(c))
104 serv.join(c)
105
106 def pourmoi(self, serv, message):
107 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
108 pseudo=serv.get_nickname()
109 size=len(pseudo)
110 if message[:size]==pseudo and len(message)>size and message[size]==":":
111 return (True,message[size+1:].lstrip(" "))
112 else:
113 return (False,message)
114
115 def on_privmsg(self, serv, ev):
116 message=ev.arguments()[0]
117 auteur = irclib.nm_to_n(ev.source())
118 try:
119 test=bot_unicode(message)
120 except UnicodeBotError:
121 serv.privmsg(auteur,
122 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
123 return
124 message=message.split()
125 cmd=message[0].lower()
126 notunderstood=False
127 if cmd=="join":
128 if auteur in self.ops:
129 if len(message)>1:
130 if message[1] in self.chanlist:
131 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
132 else:
133 serv.join(message[1])
134 self.chanlist.append(message[1])
135 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
136 log(self.serveur,"priv",auteur," ".join(message))
137 else:
138 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
139 else:
140 notunderstood=True
141 elif cmd=="leave":
142 if auteur in self.ops and len(message)>1:
143 if message[1] in self.chanlist:
144 if not (message[1] in self.stay_channels) or auteur in self.overops:
145 serv.part(message[1])
146 self.chanlist.remove(message[1])
147 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
148 else:
149 serv.privmsg(auteur,"Non, je reste !")
150 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
151 else:
152 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
153 else:
154 notunderstood=True
155 elif cmd=="play":
156 if auteur in self.ops:
157 if len(message)>1:
158 if message[1] in self.play_channels:
159 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
160 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
161 else:
162 self.play_channels.append(message[1])
163 self.play_status[message[1]]=[None,None,None]
164 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
165 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
166 else:
167 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
168 else:
169 notunderstood=True
170 elif cmd=="noplay":
171 if auteur in self.ops:
172 if len(message)>1:
173 if message[1] in self.play_channels:
174 self.play_channels.remove(message[1])
175 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
176 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
177 else:
178 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
179 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
180 else:
181 notunderstood=True
182 elif cmd=="stay":
183 if auteur in self.overops:
184 if len(message)>1:
185 if message[1] in self.stay_channels:
186 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
187 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
188 else:
189 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
190 self.stay_channels.append(message[1])
191 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
192 else:
193 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
194 else:
195 notunderstood=True
196 elif cmd=="nostay":
197 if auteur in self.overops:
198 if len(message)>1:
199 if message[1] in self.stay_channels:
200 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
201 self.stay_channels.remove(message[1])
202 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
203 else:
204 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
205 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
206
207 else:
208 notunderstood=True
209 elif cmd in ["states","status"]:
210 if auteur in self.overops:
211 for k in self.play_status.keys():
212 if self.play_status[k]==[None,None,None]:
213 serv.privmsg(auteur,"None")
214 else:
215 serv.privmsg(auteur,"%s : %s (%s) [%s]"%(k,"".join([str(i[0]) for i in self.play_status[k][0]])
216 ,self.play_status[k][1], self.play_status[k][2]))
217 elif cmd=="die":
218 if auteur in self.overops:
219 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
220 self.die()
221 else:
222 notunderstood=True
223 elif cmd=="quiet":
224 if auteur in self.ops:
225 if len(message)>1:
226 if message[1] in self.quiet_channels:
227 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
228 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
229 else:
230 self.quiet_channels.append(message[1])
231 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
232 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
233 else:
234 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
235 else:
236 notunderstood=True
237 elif cmd=="noquiet":
238 if auteur in self.ops:
239 if len(message)>1:
240 if message[1] in self.quiet_channels:
241 self.quiet_channels.remove(message[1])
242 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
243 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
244 else:
245 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
246 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
247 else:
248 notunderstood=True
249 elif cmd=="say":
250 if auteur in self.overops and len(message)>2:
251 serv.privmsg(message[1]," ".join(message[2:]))
252 log(self.serveur,"priv",auteur," ".join(message))
253 elif len(message)<=2:
254 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
255 else:
256 notunderstood=True
257 elif cmd=="do":
258 if auteur in self.overops and len(message)>2:
259 serv.action(message[1]," ".join(message[2:]))
260 log(self.serveur,"priv",auteur," ".join(message))
261 elif len(message)<=2:
262 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
263 else:
264 notunderstood=True
265 elif cmd in ["score","scores"]:
266 self.send_scores(serv,auteur)
267 else:
268 notunderstood=True
269 if notunderstood:
270 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
271
272 def affiche_mot(self, serv, canal, begin="Mot courant"):
273 if self.play_status.has_key(canal):
274 mot = self.play_status[canal][0]
275 obfuskated=" ".join([lettre[0] if lettre[1] else "_" for lettre in mot])
276 serv.privmsg(canal,"%s : %s"%(begin,obfuskated))
277
278 def start_partie(self, serv, canal):
279 mots=[mot.strip() for mot in open(config_dico_mots).readlines()]
280 defs=[defi.strip() for defi in open(config_dico_defs).readlines()]
281 indice = random.randrange(0,len(mots))
282 mot,definition=mots[indice],defs[indice]
283 # ' et - sont considérés comme déjà devinés
284 mot = [(lettre,lettre in "'-") for lettre in list(mot)]
285 self.play_status[canal]=[mot,definition,{}]
286 self.affiche_mot(serv, canal, begin="Devinez")
287
288 def on_pubmsg(self, serv, ev):
289 auteur = irclib.nm_to_n(ev.source())
290 canal = ev.target()
291 message = ev.arguments()[0]
292 try:
293 test=bot_unicode(message)
294 except UnicodeBotError:
295 if not canal in self.quiet_channels:
296 serv.privmsg(canal,
297 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
298 return
299 pour_moi,message=self.pourmoi(serv,message)
300 if pour_moi and message.split()!=[]:
301 cmd=message.split()[0].lower()
302 try:
303 args=" ".join(message.split()[1:])
304 except:
305 args=""
306 if cmd in ["meurs","die","crève"]:
307 if auteur in self.overops:
308 log(self.serveur,canal,auteur,message+"[successful]")
309 self.die()
310 else:
311 serv.privmsg(canal,"%s: crève !"%(auteur))
312 log(self.serveur,canal,auteur,message+"[failed]")
313 elif cmd in ["part","leave","dégage"]:
314 if auteur in self.ops and (not (canal in self.stay_channels)
315 or auteur in self.overops):
316 serv.part(canal,message="Éjecté par %s"%(auteur))
317 log(self.serveur,canal,auteur,message+"[successful]")
318 if canal in self.chanlist:
319 self.chanlist.remove(canal)
320 else:
321 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
322 log(self.serveur,canal,auteur,message+"[failed]")
323 elif cmd in ["play","jeu","encore","again","partie","pendu","game","mot","go","allez"]:
324 if not canal in self.quiet_channels and canal in self.play_channels:
325 if self.play_status.has_key(canal):
326 if self.play_status[canal]==[None,None,None]:
327 self.start_partie(serv, canal)
328 else:
329 self.affiche_mot(serv, canal, begin="%s: Rappel"%(auteur))
330 else:
331 self.play_status[canal]=[None,None,None]
332 self.start_partie(serv, canal)
333 elif not canal in self.play_channels:
334 serv.privmsg(canal,"%s: pas ici…"%(auteur))
335 elif (cmd in list("azertyuiopqsdfghjklmwxcvbn") and canal in self.play_channels
336 and self.play_status.has_key(canal) and self.play_status[canal]!=[None,None,None]):
337 giv_let=cmd.upper()
338 liste=self.play_status[canal][0]
339 listeapres=[(lettre[0],lettre[1] or lettre[0]==giv_let) for lettre in liste]
340 if liste!=listeapres:
341 nbtrouvees=(sum([lettre[1] for lettre in listeapres if not lettre[0] in "'-"])
342 - sum([lettre[1] for lettre in liste if not lettre[0] in "'-"]))
343 if self.play_status[canal][2].has_key(auteur):
344 self.play_status[canal][2][auteur]+= nbtrouvees
345 else:
346 self.play_status[canal][2][auteur] = nbtrouvees
347 self.play_status[canal][0]=listeapres
348 self.affiche_mot(serv, canal, begin="%s placé"%(giv_let))
349 if all([lettre[1] for lettre in listeapres]):
350 self.gagne(serv, canal)
351
352 elif cmd in ["score","scores","!score","!scores"]:
353 self.send_scores(serv,auteur)
354 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
355 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
356 # on essaye de voir si le mot fourni matche la partie en cours
357 mot = cmd
358 # bon, ce teste merde et j'arrive pas à trouver pourquoi, alors j'ai craqué, je l'ai mis dans un try
359 # bouh ! beurk ! pas bien ! promis, j'irai me flageller…
360 try:
361 if remplace_accents(mot)==("".join([i[0] for i in self.play_status[canal][0]])).lower():
362 # on a trouvé le mot
363 # on regarde combien de lettre il manquait
364 manquait = sum([not lettre[1] for lettre in self.play_status[canal][0]])
365 self.add_score({auteur: manquait})
366 if self.play_status[canal][2].has_key(auteur):
367 self.play_status[canal][2][auteur]+=manquait
368 else:
369 self.play_status[canal][2][auteur]=manquait
370 self.gagne(serv, canal, bonus=auteur, bonusvalue=manquait)
371 except:
372 pass
373 else:
374 pass
375
376
377 def on_action(self, serv, ev):
378 action = ev.arguments()[0]
379 auteur = irclib.nm_to_n(ev.source())
380 channel = ev.target()
381 try:
382 test=bot_unicode(action)
383 except UnicodeBotError:
384 serv.privmsg(channel,
385 "%s : Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
386 return
387 mypseudo=serv.get_nickname()
388
389 def get_scores(self):
390 f=open(config_scores_file)
391 scores=pickle.load(f)
392 f.close()
393 return scores
394 def save_scores(self,scores):
395 f=open(config_scores_file,'w')
396 pickle.dump(scores,f)
397 f.close()
398 def add_score(self,dico):
399 scores=self.get_scores()
400 for k,v in dico.items():
401 if scores.has_key(k):
402 scores[k]+=v
403 else:
404 scores[k]=v
405 self.save_scores(scores)
406 def send_scores(self, serv, destinataire):
407 scores=self.get_scores()
408 scores=scores.items()
409 scores.sort(lambda x,y:cmp(x[1],y[1]))
410 scores.reverse()
411 serv.privmsg(destinataire,"Scores by score : "+" ; ".join(["%s %s"%(k,v) for (k,v) in scores]) )
412 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
413 serv.privmsg(destinataire,"Scores by pseudo : "+" ; ".join(["%s %s"%(k,v) for (k,v) in scores]) )
414
415 def gagne(self, serv, canal, bonus=None, bonusvalue=2):
416 realword="".join([lettre[0] for lettre in self.play_status[canal][0]])
417 definition = self.play_status[canal][1]
418 serv.privmsg(canal,"Bravo ! C'était %s"%(realword))
419 serv.privmsg(canal,definition)
420 nlettre=float(len(realword.replace("'","").replace("-","")))
421 contribs=["%s:%s%%%s"%(pseudo,str(int(100*contrib/nlettre)),("+bonus(%s)"%(bonusvalue))*(bonus==pseudo)) for pseudo,contrib in self.play_status[canal][2].items()]
422 contribs_score={pseudo:int(10*contrib/nlettre) for pseudo,contrib in self.play_status[canal][2].items()}
423 self.add_score(contribs_score)
424 serv.privmsg(canal,"Contributions : %s"%(" ".join(contribs)) )
425 self.play_status[canal]=[None,None,None]
426
427
428 if __name__=="__main__":
429 import sys
430 if len(sys.argv)==1:
431 print "Usage : hung.py <serveur> [--debug]"
432 exit(1)
433 serveur=sys.argv[1]
434 if "debug" in sys.argv or "--debug" in sys.argv:
435 debug=True
436 else:
437 debug=False
438 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
439 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
440 try:
441 serveur=serveurs[serveur]
442 except KeyError:
443 print "Server Unknown : %s"%(serveur)
444 exit(404)
445 hung=Hung(serveur,debug)
446 hung.start()