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 # Quand personne ne cause, on finit par se taire
59 # temps au bout duquel, si personne n'a parlé, on se tait
60 config_idle_time
=20*60
61 # liste des bots, qui ne sont pas considérés comme de l'activité
62 config_idle_bots
=["deconnaisseur","Basile","historien","hung","salesman","Shadobot","Wen","___"]
64 class UnicodeBotError(Exception):
66 def bot_unicode(chain
):
69 except UnicodeDecodeError:
72 def log(serveur
,channel
,auteur
=None,message
=None):
73 f
=open(get_config_logfile(serveur
),"a")
74 if auteur
==message
==None:
75 # alors c'est que c'est pas un channel mais juste une ligne de log
76 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
78 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
80 if config_debug_stdout
:
85 def reussi(message
,answer
,auteur
):
86 if auteur
in config_level3
:
87 return answer
in message
88 if auteur
in config_level2
:
89 return remplace_accents(answer
) in message
91 if re
.match(".*"+remplace_accents(answer
).lower(),remplace_accents(message
).lower()):
94 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
96 chain
=unicode(chain
,"utf8")
98 chain
=unicode(chain
,"utf8").lower()
99 allmatches
="("+"|".join(matches
)+")"
100 reg
=(avant
+allmatches
+apres
).lower()
101 o
=re
.match(reg
,chain
)
105 return is_something(chain
,config_tag_triggers
)
107 class RefuseError(Exception):
110 class Salesman(ircbot
.SingleServerIRCBot
):
111 def __init__(self
,serveur
,debug
=False):
112 temporary_pseudo
=config_pseudo
+str(random
.randrange(10000,100000))
113 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
114 temporary_pseudo
,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
117 self
.overops
=config_overops
118 self
.ops
=self
.overops
+config_ops
119 self
.chanlist
=config_chanlist
120 self
.stay_channels
=config_stay_channels
121 self
.play_channels
=config_play_channels
122 self
.play_status
={i
:[0] for i
in self
.play_channels
}
123 self
.last_activity
={}
124 self
.quiet_channels
=[]
126 def give_me_my_pseudo(self
,serv
):
127 serv
.privmsg("NickServ","RECOVER %s %s"%(config_pseudo
,config_password
))
128 serv
.privmsg("NickServ","RELEASE %s %s"%(config_pseudo
,config_password
))
130 serv
.nick(config_pseudo
)
132 def on_welcome(self
, serv
, ev
):
133 self
.serv
=serv
# ça serv ira :)
134 self
.give_me_my_pseudo(serv
)
135 serv
.privmsg("NickServ","identify %s"%(config_password))
136 log(self
.serveur
,"Connected")
138 self
.chanlist
=["#bot"]
139 self
.play_channels
=["#bot"]
140 for c
in self
.chanlist
:
141 log(self
.serveur
,"JOIN %s"%(c))
143 self
.update_activity(c
,"") # la chaîne vide ne sera jamais un nom de bot et donc marchera toujours
144 for c
in self
.play_channels
:
145 token
=time
.time()-3600
146 self
.play_status
[c
]=[0,token
]
147 serv
.execute_delayed(random
.randrange(ttrig
),self
.start_enigme
,(serv
,c
,token
))
149 def start_enigme(self
,serv
,channel
,token
=None):
150 # On reste silencieux si lechan n'est pas actif
151 if not self
.is_active(channel
):
152 serv
.execute_delayed(ttrig
*5,self
.start_enigme
,(serv
,channel
,token
))
154 if self
.play_status
[channel
][0]==0 and channel
in self
.play_channels
:
156 if token
==self
.play_status
[channel
][-1]:
159 if time
.time() > self
.play_status
[channel
][-1]+config_time_incompressible
:
164 enigme
,answer
=self
.get_enigme()
165 log(self
.serveur
,channel
,u
"$Énigme$".encode("utf8"),("%s | %s"%(enigme
, answer
)).encode("utf8"))
166 serv
.privmsg(channel
,enigme
.encode("utf8"))
168 self
.play_status
[channel
]=[1,enigme
,answer
,token
]
169 # ce bot n'a pas d'indices
170 serv
.execute_delayed(random
.randrange(ttrig
*7,ttrig
*10),self
.give_answer
,(serv
,channel
,token
))
173 def give_answer(self
,serv
,channel
,token
):
174 if self
.play_status
[channel
][0]==1 and self
.play_status
[channel
][-1]==token
:
175 answer
=self
.play_status
[channel
][2]
176 serv
.privmsg(channel
,"C'était : %s"%(answer).encode("utf8"))
178 self
.play_status
[channel
]=[0,token
]
179 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,channel
,token
))
181 def get_enigme(self
):
182 # on récupère les capitales
183 f
=open(config_source_file
)
184 l
=[i
.strip("\n") for i
in f
.readlines()]
186 l
=[i
.split(" | ") for i
in l
]
187 dec
={int(i
[0]):list(i
[1:]) for i
in l
}
188 # on va chercher combien de fois elles ont été jouées
189 played_file
=get_config_played_file(self
.serveur
)
193 l
=re
.findall("(.*):(.*)",t
)
194 played
={int(i
[0]):int(i
[1]) for i
in l
}
195 # on récupère le nombre d'occurrences le plus faible
196 mini
=min(played
.values())
197 # on choisit un id dans ceux qui ont ce nombre d'occurences
198 id_choisi
=random
.choice([k
for k
,v
in played
.items() if v
==mini
])
199 capitale
,pays
=dec
[id_choisi
]
200 # on peut jouer capitale -> pays ou pays -> capitale
201 enigme
,answer
=random
.choice([[capitale
,pays
],[pays
,capitale
]])
202 # on incrémente la choisie
204 # on enregistre le played_file
205 f
=open(played_file
,"w")
206 f
.write("\n".join(["%-3s : %s"%(k
,v
) for k
,v
in played
.items()]))
208 return map(lambda x
:x
.decode("utf8"), [enigme
,answer
])
210 def pourmoi(self
, serv
, message
):
213 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
214 return (True,message
[size
+1:].strip(" "))
216 return (False,message
)
218 def on_privmsg(self
, serv
, ev
):
219 message
=ev
.arguments()[0]
220 auteur
= irclib
.nm_to_n(ev
.source())
222 test
=bot_unicode(message
)
223 except UnicodeBotError
:
225 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
227 message
=message
.split()
228 cmd
=message
[0].lower()
231 helpmsg_default
="""Liste des commandes :
232 HELP Affiche ce message d'aide
233 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
234 SCORES Affiche les scores"""
236 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
237 LEAVE Faire quitter un channel
238 PLAY Passe un channel en mode "jouer"
239 NOPLAY Passe un channel en mode "ne pas jouer"
240 QUIET Se taire sur un channel
241 NOQUIET Opposé de QUIET"""
243 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
244 SAY Fais envoyer un message sur un chan ou à une personne
245 STAY Ignorera les prochains LEAVE pour un chan
246 NOSTAY Opposé de STAY
247 STATUS Montre l'état courant
249 helpmsg
=helpmsg_default
250 if auteur
in self
.ops
:
252 if auteur
in self
.overops
:
253 helpmsg
+=helpmsg_overops
254 for ligne
in helpmsg
.split("\n"):
255 serv
.privmsg(auteur
,ligne
)
257 if auteur
in self
.ops
:
259 if message
[1] in self
.chanlist
:
260 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
262 serv
.join(message
[1])
263 self
.chanlist
.append(message
[1])
264 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
265 log(self
.serveur
,"priv",auteur
," ".join(message
))
267 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
271 if auteur
in self
.ops
and len(message
)>1:
272 if message
[1] in self
.chanlist
:
273 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
274 self
.quitter(message
[1]," ".join(message
[2:]))
275 self
.chanlist
.remove(message
[1])
276 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
278 serv
.privmsg(auteur
,"Non, je reste !")
279 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
281 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
285 if auteur
in self
.overops
:
287 if message
[1] in self
.stay_channels
:
288 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
289 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
291 self
.stay_channels
.append(message
[1])
292 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
293 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
295 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
299 if auteur
in self
.overops
:
301 if message
[1] in self
.stay_channels
:
302 self
.stay_channels
.remove(message
[1])
303 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
304 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
306 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
307 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
311 if auteur
in self
.ops
:
313 if message
[1] in self
.play_channels
:
314 serv
.privmsg(auteur
,"Je play déjà sur %s."%(message
[1]))
315 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
317 self
.play_channels
.append(message
[1])
318 self
.play_status
[message
[1]]=[0,time
.time()-3600]
319 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
320 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
322 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
326 if auteur
in self
.ops
:
328 if message
[1] in self
.play_channels
:
329 self
.play_channels
.remove(message
[1])
330 serv
.privmsg(auteur
,"Play channels : "+" ".join(self
.play_channels
))
331 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
333 serv
.privmsg(auteur
,"Je ne play pas sur %s."%(message
[1]))
334 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
338 if auteur
in self
.ops
:
340 if message
[1] in self
.quiet_channels
:
341 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
342 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
344 self
.quiet_channels
.append(message
[1])
345 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
346 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
348 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
352 if auteur
in self
.ops
:
354 if message
[1] in self
.quiet_channels
:
355 self
.quiet_channels
.remove(message
[1])
356 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
357 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
359 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
360 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
363 elif cmd
in ["states","status"]:
364 if auteur
in self
.overops
:
365 for k
in self
.play_status
.keys():
366 serv
.privmsg(auteur
,(u
"%s : %s"%(k
," | ".join([unicode(i
) for i
in self
.play_status
[k
]]))).encode("utf8") )
368 if auteur
in self
.overops
and len(message
)>2:
369 serv
.privmsg(message
[1]," ".join(message
[2:]))
370 log(self
.serveur
,"priv",auteur
," ".join(message
))
371 elif len(message
)<=2:
372 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
376 if auteur
in self
.overops
:
377 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
381 if len(message
) in [3,4] and message
[1].lower()=="transfert":
382 scores
=self
.get_scores()
383 de
,to
=auteur
,message
[2]
384 value
=scores
.get(de
,0)
387 asked
=int(message
[3])
389 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
394 serv
.privmsg(auteur
,"Vous n'avez pas de points")
397 serv
.privmsg(auteur
,"Bien tenté…")
400 serv
.privmsg(auteur
,"Vous n'avez que %s points"%(value))
403 self
.add_score(de
,-asked
)
404 self
.add_score(to
,asked
)
405 serv
.privmsg(auteur
,"Transfert de %s points de %s à %s"%(asked
,de
,to
))
407 serv
.privmsg(auteur
,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
409 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
412 scores
=self
.get_scores().items()
414 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
416 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
418 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
419 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
420 elif auteur
in self
.overops
:
421 souscmd
=message
[1].lower()
425 scores
=self
.get_scores()
426 if scores
.has_key(todelete
):
428 self
.save_scores(scores
)
429 serv
.privmsg(auteur
,"Score de %s supprimé"%(todelete))
431 serv
.privmsg(auteur
,"Ce score n'existe pas : %s"%(todelete))
433 serv
.privmsg(auteur
,"Syntaxe : SCORES DEL <pseudo>")
434 elif souscmd
in ["add","sub"]:
436 toadd
,val
=message
[2],message
[3]
440 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
444 self
.add_score(toadd
,val
)
445 serv
.privmsg(auteur
,"Done")
447 serv
.privmsg(auteur
,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
449 serv
.privmsg(auteur
,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
455 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
457 def on_pubmsg(self
, serv
, ev
):
458 auteur
= irclib
.nm_to_n(ev
.source())
460 message
= ev
.arguments()[0]
461 self
.update_activity(canal
,auteur
)
463 test
=bot_unicode(message
)
464 except UnicodeBotError
:
465 if not canal
in self
.quiet_channels
:
467 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
470 pour_moi
,message
=self
.pourmoi(serv
,message
)
471 if pour_moi
and message
.split()!=[]:
472 cmd
=message
.split()[0].lower()
474 args
=" ".join(message
.split()[1:])
477 if cmd
in ["meurs","die","crève"]:
478 if auteur
in self
.overops
:
480 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
482 serv
.privmsg(canal
,"%s: crève !"%(auteur))
483 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
484 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
485 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
486 if cmd
in ["part","leave","dégage"]:
487 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
488 or auteur
in self
.overops
):
490 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
491 self
.chanlist
.remove(canal
)
493 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
494 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
496 if cmd
in ["deviens","pseudo"]:
497 if auteur
in self
.ops
:
500 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
501 if cmd
in ["coucou"] and not canal
in self
.quiet_channels
:
502 serv
.privmsg(canal
,"%s: coucou"%(auteur))
503 if cmd
in ["ping"] and not canal
in self
.quiet_channels
:
504 serv
.privmsg(canal
,"%s: pong"%(auteur))
505 if cmd
in ["ville","capitale","pays","énigme","enigme","encore"]:
506 if canal
in self
.play_channels
:
507 if self
.play_status
.get(canal
,[-1])[0]==0:
509 self
.start_enigme(serv
,canal
)
511 serv
.privmsg(canal
,"%s: Je peux souffler une minute ?"%(auteur))
513 serv
.privmsg(canal
,("%s: Rappel : %s"%(auteur
,self
.play_status
[canal
][1])).encode("utf8") )
515 serv
.privmsg(canal
,"%s: pas ici…"%(auteur))
516 if cmd
in ["score","!score"]:
517 serv
.privmsg(auteur
,"Votre score : %s"%(self
.get_scores().get(auteur
,0)) )
518 if cmd
in ["scores","!scores"]:
519 scores
=self
.get_scores().items()
521 scores
.sort(lambda x
,y
:cmp(x
[1],y
[1]))
523 serv
.privmsg(auteur
,"Scores by score : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
525 scores
.sort(lambda x
,y
:cmp(x
[0].lower(),y
[0].lower()))
526 serv
.privmsg(auteur
,"Scores by pseudo : "+" ; ".join(["%s %s"%(i
[0],i
[1]) for i
in scores
]))
527 if is_tag(message
) and not canal
in self
.quiet_channels
:
528 if auteur
in self
.ops
:
529 action
=random
.choice(config_tag_actions
)
530 serv
.action(canal
,action
.encode("utf8"))
531 self
.quiet_channels
.append(canal
)
533 answer
=random
.choice(config_tag_answers
)
534 for ligne
in answer
.split("\n"):
535 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
541 if self
.play_status
.get(canal
,[-1])[0]==1:
542 answer
=self
.play_status
[canal
][2]
543 if reussi(message
.decode("utf8"),answer
,auteur
):
544 serv
.privmsg(canal
,(u
"%s: bravo ! (C'était %s)"%(auteur
,answer
)).encode("utf8"))
545 log(self
.serveur
,canal
,auteur
+"$win",message
)
546 self
.add_score(auteur
,1)
548 self
.play_status
[canal
]=[0,token
]
549 serv
.execute_delayed(random
.randrange(Ttrig
*5,Ttrig
*10),self
.start_enigme
,(serv
,canal
,token
))
551 def on_kick(self
,serv
,ev
):
552 auteur
= irclib
.nm_to_n(ev
.source())
553 channel
= ev
.target()
554 victime
= ev
.arguments()[0]
555 raison
= ev
.arguments()[1]
556 if victime
==self
.nick
:
557 log(self
.serveur
,"%s kické de %s par %s (raison : %s)" %(victime
,channel
,auteur
,raison
))
560 self
.update_activity(message
[1],"")
561 # on ne dit rien au rejoin
562 #l1,l2=config_kick_answers,config_kick_actions
563 #n1,n2=len(l1),len(l2)
564 #i=random.randrange(n1+n2)
566 # serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
568 # serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
570 def quitter(self
,chan
,leave_message
=None):
571 if leave_message
==None:
572 leave_message
=random
.choice(config_leave_messages
)
573 self
.serv
.part(chan
,message
=leave_message
.encode("utf8"))
576 quit_message
=random
.choice(config_quit_messages
)
577 self
.die(msg
=quit_message
.encode("utf8"))
579 def get_scores(self
):
580 f
=open(config_score_file
)
581 scores
=pickle
.load(f
)
585 def add_score(self
,pseudo
,value
):
586 scores
=self
.get_scores()
587 if scores
.has_key(pseudo
):
588 scores
[pseudo
]+=value
591 self
.save_scores(scores
)
593 def save_scores(self
,scores
):
594 f
=open(config_score_file
,"w")
595 pickle
.dump(scores
,f
)
599 return self
.serv
.get_nickname()
600 nick
= property(_getnick
)
602 def update_activity(self
,canal
,pseudo
):
603 if not pseudo
in config_idle_bots
:
604 self
.last_activity
[canal
]=time
.time()
605 def is_active(self
,canal
):
606 return time
.time()-self
.last_activity
[canal
]<config_idle_time
608 if __name__
=="__main__":
611 print "Usage : Salesman.py <serveur> [--debug]"
614 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
618 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
619 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
621 serveur
=serveurs
[serveur
]
623 print "Server Unknown : %s"%(serveur)
625 salesman
=Salesman(serveur
,debug
)