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
="Yamoussoukro"
18 config_pseudo
="Salesman"
19 config_chanlist
=["#bot","#flood"]
20 config_play_channels
=["#flood"]
21 config_stay_channels
=["#flood","#bot"]
22 config_overops
=["[20-100]","[20-100]_"]
23 config_ops
=["PEB","Petite-Peste"]
25 config_source_file
="capitales.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…"]
48 config_debug_stdout
= True
49 config_logfile_template
="Salesman.%s.log"
50 def get_config_logfile(serveur
):
51 serveurs
={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
52 return config_logfile_template
%(serveurs
[serveur
])
54 config_quit_messages
=[u
"goto Tombouctou"]
56 config_leave_messages
=[u
"On continuera à jouer plus tard ;)"]
58 class UnicodeBotError(Exception):
60 def bot_unicode(chain
):
63 except UnicodeDecodeError:
66 def log(serveur
,channel
,auteur
=None,message
=None):
67 f
=open(get_config_logfile(serveur
),"a")
68 if auteur
==message
==None:
69 # alors c'est que c'est pas un channel mais juste une ligne de log
70 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
72 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
74 if config_debug_stdout
:
79 def reussi(message
,answer
,auteur
):
80 if auteur
in config_level3
:
81 return answer
in message
82 if auteur
in config_level2
:
83 return remplace_accents(answer
) in message
85 if re
.match(remplace_accents(answer
).lower(),remplace_accents(message
).lower()):
88 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
90 chain
=unicode(chain
,"utf8")
92 chain
=unicode(chain
,"utf8").lower()
93 allmatches
="("+"|".join(matches
)+")"
94 reg
=(avant
+allmatches
+apres
).lower()
99 return is_something(chain
,config_tag_triggers
)
101 class RefuseError(Exception):
104 class Deconnaisseur(ircbot
.SingleServerIRCBot
):
105 def __init__(self
,serveur
,debug
=False):
106 temporary_pseudo
=config_pseudo
+str(random
.randrange(10000,100000))
107 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
108 temporary_pseudo
,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
111 self
.overops
=config_overops
112 self
.ops
=self
.overops
+config_ops
113 self
.chanlist
=config_chanlist
114 self
.stay_channels
=config_stay_channels
115 self
.play_channels
=config_play_channels
116 self
.play_status
={i
:[0] for i
in self
.play_channels
}
117 self
.quiet_channels
=[]
119 def give_me_my_pseudo(self
,serv
):
120 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
121 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
123 serv
.nick(config_pseudo
)
125 def on_welcome(self
, serv
, ev
):
126 self
.serv
=serv
# ça serv ira :)
127 self
.give_me_my_pseudo(serv
)
128 serv
.privmsg("NickServ","identify %s"%(config_password))
129 log(self
.serveur
,"Connected")
131 self
.chanlist
=["#bot"]
132 self
.play_channels
=["#bot"]
133 for c
in self
.chanlist
:
134 log(self
.serveur
,"JOIN %s"%(c))
136 for c
in self
.play_channels
:
137 token
=time
.time()-3600
138 self
.play_status
[c
]=[0,token
]
139 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
141 def start_enigme(self
,serv
,channel
,token
=None):
142 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
144 if token
==self
.play_status
[channel
][-1]:
147 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
152 enigme
,answer
=self
.get_enigme()
153 log(self
.serveur
,channel
,u
"$Énigme$".encode("utf8"),("%s | %s"%(enigme
, answer
)).encode("utf8"))
154 serv
.privmsg(channel
,enigme
.encode("utf8"))
156 self
.play_status
[channel
]=[1,enigme
,answer
,token
]
157 # ce bot n'a pas d'indices
158 serv
.execute_delayed(random
.randrange(ttrig
*7,ttrig
*10),self
.give_answer
,(serv
,channel
,token
))
161 def give_answer(self
,serv
,channel
,token
):
162 if self
.play_status
[channel
][0]==1 and self
.play_status
[channel
][-1]==token
:
163 answer
=self
.play_status
[channel
][2]
164 serv
.privmsg(channel
,"C'était : %s"%(answer).encode("utf8"))
166 self
.play_status
[channel
]=[0,token
]
167 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
169 def get_enigme(self
):
170 # on récupère les capitales
171 f
=open(config_source_file
)
174 l
=[i
.split(" | ") for i
in l
]
175 dec
={int(i
[0]):list(i
[1:]) for i
in l
}
176 # on va chercher combien de fois elles ont été jouées
177 played_file
=get_config_played_file(self
.serveur
)
181 l
=re
.findall("(.*):(.*)",t
)
182 played
={int(i
[0]):int(i
[1]) for i
in l
}
183 # on récupère le nombre d'occurrences le plus faible
184 mini
=min(played
.values())
185 # on choisit un id dans ceux qui ont ce nombre d'occurences
186 id_choisi
=random
.choice([k
for k
,v
in played
.items() if v
==mini
])
187 capitale
,pays
=dec
[id_choisi
]
188 # on peut jouer capitale -> pays ou pays -> capitale
189 enigme
,answer
=random
.choice([[capitale
,pays
],[pays
,capitale
]])
190 # on incrémente la choisie
192 # on enregistre le played_file
193 f
=open(played_file
,"w")
194 f
.write("\n".join(["%-3s : %s"%(k
,v
) for k
,v
in played
.items()]))
196 return map(lambda x
:x
.decode("utf8"), [enigme
,answer
])
198 def pourmoi(self
, serv
, message
):
201 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
202 return (True,message
[size
+1:].strip(" "))
204 return (False,message
)
206 def on_privmsg(self
, serv
, ev
):
207 message
=ev
.arguments()[0]
208 auteur
= irclib
.nm_to_n(ev
.source())
210 test
=bot_unicode(message
)
211 except UnicodeBotError
:
213 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
215 message
=message
.split()
216 cmd
=message
[0].lower()
219 helpmsg_default
="""Liste des commandes :
220 HELP Affiche ce message d'aide
221 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
222 SCORES Affiche les scores"""
224 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
225 LEAVE Faire quitter un channel
226 PLAY Passe un channel en mode "jouer"
227 NOPLAY Passe un channel en mode "ne pas jouer"
228 QUIET Se taire sur un channel
229 NOQUIET Opposé de QUIET"""
231 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
232 SAY Fais envoyer un message sur un chan ou à une personne
233 STAY Ignorera les prochains LEAVE pour un chan
234 NOSTAY Opposé de STAY
235 STATUS Montre l'état courant
237 helpmsg
=helpmsg_default
238 if auteur
in self
.ops
:
240 if auteur
in self
.overops
:
241 helpmsg
+=helpmsg_overops
242 for ligne
in helpmsg
.split("\n"):
243 serv
.privmsg(auteur
,ligne
)
245 if auteur
in self
.ops
:
247 if message
[1] in self
.chanlist
:
248 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
250 serv
.join(message
[1])
251 self
.chanlist
.append(message
[1])
252 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
253 log(self
.serveur
,"priv",auteur
," ".join(message
))
255 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
259 if auteur
in self
.ops
and len(message
)>1:
260 if message
[1] in self
.chanlist
:
261 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
262 self
.quitter(message
[1]," ".join(message
[2:]))
263 self
.chanlist
.remove(message
[1])
264 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
266 serv
.privmsg(auteur
,"Non, je reste !")
267 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
269 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
273 if auteur
in self
.overops
:
275 if message
[1] in self
.stay_channels
:
276 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
277 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
279 self
.stay_channels
.append(message
[1])
280 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
281 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
283 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
287 if auteur
in self
.overops
:
289 if message
[1] in self
.stay_channels
:
290 self
.stay_channels
.remove(message
[1])
291 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
292 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
294 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
295 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
299 if auteur
in self
.ops
:
301 if message
[1] in self
.play_channels
:
302 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
303 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
305 self
.play_channels
.append(message
[1])
306 self
.play_status
[message
[1]]=[0,time
.time()-3600]
307 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
308 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
310 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
314 if auteur
in self
.ops
:
316 if message
[1] in self
.play_channels
:
317 self
.play_channels
.remove(message
[1])
318 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
319 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
321 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
322 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
326 if auteur
in self
.ops
:
328 if message
[1] in self
.quiet_channels
:
329 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
330 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
332 self
.quiet_channels
.append(message
[1])
333 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
334 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
336 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
340 if auteur
in self
.ops
:
342 if message
[1] in self
.quiet_channels
:
343 self
.quiet_channels
.remove(message
[1])
344 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
345 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
347 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
348 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
351 elif cmd
in ["states","status"]:
352 if auteur
in self
.overops
:
353 for k
in self
.play_status
.keys():
354 serv
.privmsg(auteur
,(u
"%s : %s"%(k
," | ".join([unicode(i
) for i
in self
.play_status
[k
]]))).encode("utf8") )
356 if auteur
in self
.overops
and len(message
)>2:
357 serv
.privmsg(message
[1]," ".join(message
[2:]))
358 log(self
.serveur
,"priv",auteur
," ".join(message
))
359 elif len(message
)<=2:
360 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
364 if auteur
in self
.overops
:
365 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
369 if len(message
) in [3,4] and message
[1].lower()=="transfert":
370 scores
=self
.get_scores()
371 de
,to
=auteur
,message
[2]
372 value
=scores
.get(de
,0)
375 asked
=int(message
[3])
377 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
382 serv
.privmsg(auteur
,"Vous n'avez pas de points")
385 serv
.privmsg(auteur
,"Bien tenté…")
388 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
391 self
.add_score(de
,-asked
)
392 self
.add_score(to
,asked
)
393 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
395 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
397 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
400 scores
=self
.get_scores().items()
402 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
404 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
406 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
407 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
408 elif auteur
in self
.overops
:
409 souscmd
=message
[1].lower()
413 scores
=self
.get_scores()
414 if scores
.has_key(todelete
):
416 self
.save_scores(scores
)
417 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
419 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
421 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
422 elif souscmd
in ["add","sub"]:
424 toadd
,val
=message
[2],message
[3]
428 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
432 self
.add_score(toadd
,val
)
433 serv
.privmsg(auteur
,"Done")
435 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
437 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
443 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
445 def on_pubmsg(self
, serv
, ev
):
446 auteur
= irclib
.nm_to_n(ev
.source())
448 message
= ev
.arguments()[0]
450 test
=bot_unicode(message
)
451 except UnicodeBotError
:
452 if not canal
in self
.quiet_channels
:
454 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
457 pour_moi
,message
=self
.pourmoi(serv
,message
)
458 if pour_moi
and message
.split()!=[]:
459 cmd
=message
.split()[0].lower()
461 args
=" ".join(message
.split()[1:])
464 if cmd
in ["meurs","die","crève"]:
465 if auteur
in self
.overops
:
467 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
469 serv
.privmsg(canal
,"%s: crève !"%(auteur))
470 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
471 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
472 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
473 if cmd
in ["part","leave","dégage"]:
474 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
475 or auteur
in self
.overops
):
477 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
478 self
.chanlist
.remove(canal
)
480 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
481 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
483 if cmd
in ["deviens","pseudo"]:
484 if auteur
in self
.ops
:
487 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
488 if cmd
in ["coucou"] and not canal
in self
.quiet_channels
:
489 serv
.privmsg(canal
,"%s: coucou"%(auteur))
490 if cmd
in ["ping"] and not canal
in self
.quiet_channels
:
491 serv
.privmsg(canal
,"%s: pong"%(auteur))
492 if cmd
in ["ville","capitale","pays","énigme","enigme","encore"]:
493 if canal
in self
.play_channels
:
494 if self
.play_status
.get(canal
,[-1])[0]==0:
496 self
.start_enigme(serv
,canal
)
498 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
500 serv
.privmsg(canal
,("%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1])).encode("utf8") )
502 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
503 if cmd
in ["score","!score"]:
504 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
505 if cmd
in ["scores","!scores"]:
506 scores
=self
.get_scores().items()
508 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
510 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
512 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
513 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
514 if is_tag(message
) and not canal
in self
.quiet_channels
:
515 if auteur
in self
.ops
:
516 action
=random
.choice(config_tag_actions
)
517 serv
.action(canal
,action
.encode("utf8"))
518 self
.quiet_channels
.append(canal
)
520 answer
=random
.choice(config_tag_answers
)
521 for ligne
in answer
.split("\n"):
522 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
528 if self
.play_status
.get(canal
,[-1])[0]==1:
529 answer
=self
.play_status
[canal
][2]
530 if reussi(message
.decode("utf8"),answer
,auteur
):
531 serv
.privmsg(canal
,(u
"%s: bravo ! (C'était %s)"%(auteur
,answer
)).encode("utf8"))
532 log(self
.serveur
,canal
,auteur
+"$win",message
)
533 self
.add_score(auteur
,1)
535 self
.play_status
[canal
]=[0,token
]
536 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
538 def on_kick(self
,serv
,ev
):
539 auteur
= irclib
.nm_to_n(ev
.source())
540 channel
= ev
.target()
541 victime
= ev
.arguments()[0]
542 raison
= ev
.arguments()[1]
543 if victime
==self
.nick
:
544 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
547 # on ne dit rien au rejoin
548 #l1,l2=config_kick_answers,config_kick_actions
549 #n1,n2=len(l1),len(l2)
550 #i=random.randrange(n1+n2)
552 # serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
554 # serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
556 def quitter(self
,chan
,leave_message
=None):
557 if leave_message
==None:
558 leave_message
=random
.choice(config_leave_messages
)
559 self
.serv
.part(chan
,message
=leave_message
.encode("utf8"))
562 quit_message
=random
.choice(config_quit_messages
)
563 self
.die(msg
=quit_message
.encode("utf8"))
565 def get_scores(self
):
566 f
=open(config_score_file
)
567 scores
=pickle
.load(f
)
571 def add_score(self
,pseudo
,value
):
572 scores
=self
.get_scores()
573 if scores
.has_key(pseudo
):
574 scores
[pseudo
]+=value
577 self
.save_scores(scores
)
579 def save_scores(self
,scores
):
580 f
=open(config_score_file
,"w")
581 pickle
.dump(scores
,f
)
585 return self
.serv
.get_nickname()
586 nick
= property(_getnick
)
588 if __name__
=="__main__":
591 print "Usage : Salesman.py <serveur> [--debug]"
594 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
598 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
599 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
601 serveur
=serveurs
[serveur
]
603 print "Server Unknown : %s"%(serveur)
605 salesman
=Salesman(serveur
,debug
)