]> gitweb.pimeys.fr Git - bots/hung.git/blob - hung.py
+scores (dans un fichier)
[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 class Hung(ircbot.SingleServerIRCBot):
65 def __init__(self,serveur,debug=False):
66 temporary_pseudo=config_irc_pseudo+str(random.randrange(10000,100000))
67 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
68 temporary_pseudo,"Bot irc pour jouer au pendu", 10)
69 self.debug=debug
70 self.serveur=serveur
71 self.overops=config_overops
72 self.ops=self.overops+config_ops
73 self.report_bugs_to=config_report_bugs_to
74 self.chanlist=config_chanlist
75 self.stay_channels=config_stay_channels
76 self.play_channels=config_play_channels
77 self.play_status={}
78 self.quiet_channels=config_quiet_channels
79
80
81 def give_me_my_pseudo(self,serv):
82 serv.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo,config_irc_password))
83 serv.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo,config_irc_password))
84 time.sleep(0.3)
85 serv.nick(config_irc_pseudo)
86
87 def on_welcome(self, serv, ev):
88 self.give_me_my_pseudo(serv)
89 serv.privmsg("NickServ","IDENTIFY %s"%(config_irc_password))
90 log(self.serveur,"Connected")
91 if self.debug:
92 self.chanlist=["#bot"]
93 self.play_channels=["#bot"]
94 for c in self.chanlist:
95 log(self.serveur,"JOIN %s"%(c))
96 serv.join(c)
97
98 def pourmoi(self, serv, message):
99 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
100 pseudo=serv.get_nickname()
101 size=len(pseudo)
102 if message[:size]==pseudo and len(message)>size and message[size]==":":
103 return (True,message[size+1:].lstrip(" "))
104 else:
105 return (False,message)
106
107 def on_privmsg(self, serv, ev):
108 message=ev.arguments()[0]
109 auteur = irclib.nm_to_n(ev.source())
110 try:
111 test=bot_unicode(message)
112 except UnicodeBotError:
113 serv.privmsg(auteur,
114 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
115 return
116 message=message.split()
117 cmd=message[0].lower()
118 notunderstood=False
119 if cmd=="join":
120 if auteur in self.ops:
121 if len(message)>1:
122 if message[1] in self.chanlist:
123 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
124 else:
125 serv.join(message[1])
126 self.chanlist.append(message[1])
127 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
128 log(self.serveur,"priv",auteur," ".join(message))
129 else:
130 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
131 else:
132 notunderstood=True
133 elif cmd=="leave":
134 if auteur in self.ops and len(message)>1:
135 if message[1] in self.chanlist:
136 if not (message[1] in self.stay_channels) or auteur in self.overops:
137 serv.part(message[1])
138 self.chanlist.remove(message[1])
139 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
140 else:
141 serv.privmsg(auteur,"Non, je reste !")
142 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
143 else:
144 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
145 else:
146 notunderstood=True
147 elif cmd=="play":
148 if auteur in self.ops:
149 if len(message)>1:
150 if message[1] in self.play_channels:
151 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
152 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
153 else:
154 self.play_channels.append(message[1])
155 self.play_status[message[1]]=[[None,None,None]]
156 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
157 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
158 else:
159 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
160 else:
161 notunderstood=True
162 elif cmd=="noplay":
163 if auteur in self.ops:
164 if len(message)>1:
165 if message[1] in self.play_channels:
166 self.play_channels.remove(message[1])
167 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
168 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
169 else:
170 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
171 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
172 else:
173 notunderstood=True
174 elif cmd=="stay":
175 if auteur in self.overops:
176 if len(message)>1:
177 if message[1] in self.stay_channels:
178 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
179 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
180 else:
181 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
182 self.stay_channels.append(message[1])
183 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
184 else:
185 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
186 else:
187 notunderstood=True
188 elif cmd=="nostay":
189 if auteur in self.overops:
190 if len(message)>1:
191 if message[1] in self.stay_channels:
192 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
193 self.stay_channels.remove(message[1])
194 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
195 else:
196 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
197 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
198
199 else:
200 notunderstood=True
201 elif cmd in ["states","status"]:
202 if auteur in self.overops:
203 for k in self.play_status.keys():
204 serv.privmsg(auteur,"%s : %s (%s) [%s]"%(k,"".join([str(i[0]) for i in self.play_status[k][0]])
205 ,self.play_status[k][1], self.play_status[k][2]))
206 elif cmd=="die":
207 if auteur in self.overops:
208 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
209 self.die()
210 else:
211 notunderstood=True
212 elif cmd=="quiet":
213 if auteur in self.ops:
214 if len(message)>1:
215 if message[1] in self.quiet_channels:
216 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
217 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
218 else:
219 self.quiet_channels.append(message[1])
220 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
221 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
222 else:
223 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
224 else:
225 notunderstood=True
226 elif cmd=="noquiet":
227 if auteur in self.ops:
228 if len(message)>1:
229 if message[1] in self.quiet_channels:
230 self.quiet_channels.remove(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,"Je ne me la ferme pas sur %s."%(message[1]))
235 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
236 else:
237 notunderstood=True
238 elif cmd=="say":
239 if auteur in self.overops and len(message)>2:
240 serv.privmsg(message[1]," ".join(message[2:]))
241 log(self.serveur,"priv",auteur," ".join(message))
242 elif len(message)<=2:
243 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
244 else:
245 notunderstood=True
246 elif cmd=="do":
247 if auteur in self.overops and len(message)>2:
248 serv.action(message[1]," ".join(message[2:]))
249 log(self.serveur,"priv",auteur," ".join(message))
250 elif len(message)<=2:
251 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
252 else:
253 notunderstood=True
254 elif cmd in ["score","scores"]:
255 self.send_scores(serv,auteur)
256 else:
257 notunderstood=True
258 if notunderstood:
259 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
260
261 def affiche_mot(self, serv, canal, begin="Mot courant"):
262 if self.play_status.has_key(canal):
263 mot = self.play_status[canal][0]
264 obfuskated=" ".join([lettre[0] if lettre[1] else "_" for lettre in mot])
265 serv.privmsg(canal,"%s : %s"%(begin,obfuskated))
266
267 def start_partie(self, serv, canal):
268 mots=[mot.strip() for mot in open(config_dico_mots).readlines()]
269 defs=[defi.strip() for defi in open(config_dico_defs).readlines()]
270 indice = random.randrange(0,len(mots))
271 mot,definition=mots[indice],defs[indice]
272 # ' et - sont considérés comme déjà devinés
273 mot = [(lettre,lettre in "'-") for lettre in list(mot)]
274 self.play_status[canal]=[mot,definition,{}]
275 self.affiche_mot(serv, canal, begin="Devinez")
276
277 def on_pubmsg(self, serv, ev):
278 auteur = irclib.nm_to_n(ev.source())
279 canal = ev.target()
280 message = ev.arguments()[0]
281 try:
282 test=bot_unicode(message)
283 except UnicodeBotError:
284 if not canal in self.quiet_channels:
285 serv.privmsg(canal,
286 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
287 return
288 pour_moi,message=self.pourmoi(serv,message)
289 if pour_moi and message.split()!=[]:
290 cmd=message.split()[0].lower()
291 try:
292 args=" ".join(message.split()[1:])
293 except:
294 args=""
295 if cmd in ["meurs","die","crève"]:
296 if auteur in self.overops:
297 log(self.serveur,canal,auteur,message+"[successful]")
298 self.die()
299 else:
300 serv.privmsg(canal,"%s: crève !"%(auteur))
301 log(self.serveur,canal,auteur,message+"[failed]")
302 elif cmd in ["part","leave","dégage"]:
303 if auteur in self.ops and (not (canal in self.stay_channels)
304 or auteur in self.overops):
305 serv.part(canal,message="Éjecté par %s"%(auteur))
306 log(self.serveur,canal,auteur,message+"[successful]")
307 if canal in self.chanlist:
308 self.chanlist.remove(canal)
309 else:
310 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
311 log(self.serveur,canal,auteur,message+"[failed]")
312 elif cmd in ["play","jeu","encore","again","partie","pendu","game","mot","go","allez"]:
313 if not canal in self.quiet_channels and canal in self.play_channels:
314 if self.play_status.has_key(canal):
315 if self.play_status[canal]==[[None,None,None]]:
316 self.start_partie(serv, canal)
317 else:
318 self.affiche_mot(serv, canal, begin="%s: Rappel"%(auteur))
319 else:
320 self.play_status[canal]=[[None,None,None]]
321 self.start_partie(serv, canal)
322 elif not canal in self.play_channels:
323 serv.privmsg(canal,"%s: pas ici…"%(auteur))
324 elif (cmd in list("azertyuiopqsdfghjklmwxcvbn") and canal in self.play_channels
325 and self.play_status.has_key(canal) and self.play_status[canal]!=[[None,None,None]]):
326 giv_let=cmd.upper()
327 liste=self.play_status[canal][0]
328 listeapres=[(lettre[0],lettre[1] or lettre[0]==giv_let) for lettre in liste]
329 if liste!=listeapres:
330 nbtrouvees=(sum([lettre[1] for lettre in listeapres if not lettre[0] in "'-"])
331 - sum([lettre[1] for lettre in liste if not lettre[0] in "'-"]))
332 if self.play_status[canal][2].has_key(auteur):
333 self.play_status[canal][2][auteur]+= nbtrouvees
334 else:
335 self.play_status[canal][2][auteur] = nbtrouvees
336 self.play_status[canal][0]=listeapres
337 self.affiche_mot(serv, canal, begin="%s placé"%(giv_let))
338 if all([lettre[1] for lettre in listeapres]):
339 realword="".join([lettre[0] for lettre in self.play_status[canal][0]])
340 definition = self.play_status[canal][1]
341 serv.privmsg(canal,"Bravo ! C'était %s"%(realword))
342 serv.privmsg(canal,definition)
343 nlettre=float(len(realword.replace("'","").replace("-","")))
344 contribs=["%s:%s%%"%(pseudo,int(100*contrib/nlettre)) for pseudo,contrib in self.play_status[canal][2].items()]
345 contribs_score={pseudo:int(10*contrib/nlettre) for pseudo,contrib in self.play_status[canal][2].items()}
346 self.add_score(contribs_score)
347 serv.privmsg(canal,"Contributions : %s"%(" ".join(contribs)) )
348 self.play_status[canal]=[[None,None,None]]
349
350 elif cmd in ["score","scores","!score","!scores"]:
351 self.send_scores(serv,auteur)
352 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
353 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
354 else:
355 pass
356
357 def on_action(self, serv, ev):
358 action = ev.arguments()[0]
359 auteur = irclib.nm_to_n(ev.source())
360 channel = ev.target()
361 try:
362 test=bot_unicode(action)
363 except UnicodeBotError:
364 serv.privmsg(channel,
365 "%s : Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
366 return
367 mypseudo=serv.get_nickname()
368
369 def get_scores(self):
370 f=open(config_scores_file)
371 scores=pickle.load(f)
372 f.close()
373 return scores
374 def save_scores(self,scores):
375 f=open(config_scores_file,'w')
376 pickle.dump(scores,f)
377 f.close()
378 def add_score(self,dico):
379 scores=self.get_scores()
380 for k,v in dico.items():
381 if scores.has_key(k):
382 scores[k]+=v
383 else:
384 scores[k]=v
385 self.save_scores(scores)
386 def send_scores(self, serv, destinataire):
387 scores=self.get_scores()
388 scores=scores.items()
389 scores.sort(lambda x,y:cmp(x[1],y[1]))
390 scores.reverse()
391 serv.privmsg(destinataire,"Scores by score : "+" ; ".join(["%s %s"%(k,v) for (k,v) in scores]) )
392 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
393 serv.privmsg(destinataire,"Scores by pseudo : "+" ; ".join(["%s %s"%(k,v) for (k,v) in scores]) )
394
395
396 if __name__=="__main__":
397 import sys
398 if len(sys.argv)==1:
399 print "Usage : hung.py <serveur> [--debug]"
400 exit(1)
401 serveur=sys.argv[1]
402 if "debug" in sys.argv or "--debug" in sys.argv:
403 debug=True
404 else:
405 debug=False
406 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
407 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
408 try:
409 serveur=serveurs[serveur]
410 except KeyError:
411 print "Server Unknown : %s"%(serveur)
412 exit(404)
413 hung=Hung(serveur,debug)
414 hung.start()