]>
gitweb.pimeys.fr Git - bots/bbc.git/blob - skeleton.py
2 # -*- encoding: utf-8 -*-
6 Un bot IRC qui ne fait rien. Base pour en coder un vrai.
14 from commands
import getstatusoutput
as ex
16 # on récupère la config
20 def get_config_logfile(serveur
):
21 """Renvoie le nom du fichier de log en fonction du serveur."""
22 serveurs
= {"acoeur.crans.org" : "acoeur", "irc.crans.org" : "crans"}
23 return config
.logfile_template
% (serveurs
[serveur
])
26 """Récupère la taille de ce fichier."""
27 return ex("ls -s %s" % (config
.thisfile
))[1].split()[0]
29 def log(serveur
, channel
, auteur
=None, message
=None):
30 f
= open(get_config_logfile(serveur
), "a")
31 if auteur
== message
== None:
32 # alors c'est que c'est pas un channel mais juste une ligne de log
33 chain
= "%s %s" % (time
.strftime("%F %T"), channel
)
35 chain
= "%s [%s:%s] %s" % (time
.strftime("%F %T"), channel
, auteur
, message
)
37 if config
.debug_stdout
:
41 def is_something(chain
, matches
, avant
=u
".*(?:^| )", apres
=u
"(?:$|\.| |,|;).*", case_sensitive
=False, debug
=False):
43 chain
= unicode(chain
, "utf8")
45 chain
= unicode(chain
, "utf8").lower()
46 allmatches
= "("+"|".join(matches
)+")"
47 reg
= (avant
+allmatches
+apres
).lower()
48 o
= re
.match(reg
, chain
)
51 def is_insult(chain
, debug
=True):
52 return is_something(chain
, config
.insultes
, avant
=".*(?:^| |')")
53 def is_not_insult(chain
):
54 chain
= unicode(chain
, "utf8")
55 insult_regexp
= u
"("+u
"|".join(config
.insultes
)+u
")"
56 middle_regexp
= u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
57 reg
= ".*pas %s%s.*" % (middle_regexp
, insult_regexp
)
58 if re
.match(reg
, chain
):
62 def is_compliment(chain
, debug
=True):
63 return is_something(chain
, config
.compliment_triggers
, avant
=".*(?:^| |')")
65 return is_something(chain
, config
.tag_triggers
)
67 return is_something(chain
, config
.tesla_triggers
, avant
=u
"^", apres
=u
"$", debug
=True)
69 return is_something(chain
, config
.merci_triggers
)
71 return is_something(chain
, config
.tamere_triggers
)
72 def is_bad_action_trigger(chain
, pseudo
):
73 return is_something(chain
, config
.bad_action_triggers
, avant
=u
"^",
74 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*" % (pseudo
))
75 def is_good_action_trigger(chain
, pseudo
):
76 return is_something(chain
, config
.good_action_triggers
, avant
=u
"^",
77 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*" % (pseudo
))
78 def is_bonjour(chain
):
79 return is_something(chain
, config
.bonjour_triggers
, avant
=u
"^")
80 def is_bonne_nuit(chain
):
81 return is_something(chain
, config
.bonne_nuit_triggers
, avant
=u
"^")
83 return re
.match(u
"^(pan|bim|bang)( .*)?$", unicode(chain
, "utf8").lower().strip())
86 _
, _
, _
, h
, m
, s
, _
, _
, _
= time
.localtime()
87 return (conf
[0], 0, 0)<(h
, m
, s
)<(conf
[1], 0, 0)
89 return is_time(config
.daytime
)
91 return is_time(config
.nighttime
)
94 class UnicodeBotError(Exception):
96 def bot_unicode(chain
):
98 unicode(chain
, "utf8")
99 except UnicodeDecodeError as exc
:
100 raise UnicodeBotError
102 class Skeleton(ircbot
.SingleServerIRCBot
):
103 def __init__(self
, serveur
, debug
=False):
104 temporary_pseudo
= config
.irc_pseudo
+str(random
.randrange(10000, 100000))
105 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
106 temporary_pseudo
, "Ceci est l'ircname du bot", 10)
108 self
.serveur
= serveur
109 self
.overops
= config
.overops
110 self
.ops
= self
.overops
+config
.ops
111 self
.chanlist
= config
.chanlist
112 self
.stay_channels
= config
.stay_channels
113 self
.quiet_channels
= config
.quiet_channels
116 def give_me_my_pseudo(self
, serv
):
117 serv
.privmsg("NickServ", "RECOVER %s %s" % (config
.irc_pseudo
, config
.irc_password
))
118 serv
.privmsg("NickServ", "RELEASE %s %s" % (config
.irc_pseudo
, config
.irc_password
))
120 serv
.nick(config
.irc_pseudo
)
122 def on_welcome(self
, serv
, ev
):
123 self
.serv
= serv
# ça serv ira :)
124 self
.give_me_my_pseudo(serv
)
125 serv
.privmsg("NickServ", "identify %s" % (config
.irc_password
))
126 log(self
.serveur
, "Connected")
128 self
.chanlist
= ["#bot"]
129 for c
in self
.chanlist
:
130 log(self
.serveur
, "JOIN %s" % (c
))
133 def pourmoi(self
, serv
, message
):
134 """renvoie (False, lemessage) ou (True, le message amputé de "pseudo: ")"""
137 if message
[:size
] == pseudo
and len(message
)>size
and message
[size
] == ":":
138 return (True, message
[size
+1:].lstrip(" "))
140 return (False, message
)
142 def on_privmsg(self
, serv
, ev
):
143 message
= ev
.arguments()[0]
144 auteur
= irclib
.nm_to_n(ev
.source())
147 except UnicodeBotError
:
148 if config
.utf8_trigger
:
149 serv
.privmsg(auteur
, random
.choice(config
.utf8_fail_answers
).encode("utf8"))
151 message
= message
.split()
152 cmd
= message
[0].lower()
153 notunderstood
= False
155 helpdico
= {"help":["""HELP <commande>
156 Affiche de l'aide sur la commande""", None, None],
157 "join": [None, """JOIN <channel>
158 Me fait rejoindre le channel""", None],
159 "leave": [None, """LEAVE <channel>
160 Me fait quitter le channel (sauf s'il est dans ma stay_list).""", None],
161 "quiet": [None, """QUIET <channel>
162 Me rend silencieux sur le channel.""", None],
163 "noquiet": [None, """NOQUIET <channel>
164 Me rend la parole sur le channel.""", None],
165 "lost": [None, """LOST <channel>
166 Me fait perdre sur le channel.""", None],
167 "say": [None, None, """SAY <channel> <message>
168 Me fait parler sur le channel."""],
169 "do": [None, None, """DO <channel> <action>
170 Me fait faitre une action (/me) sur le channel."""],
171 "stay": [None, None, """STAY <channel>
172 Ajoute le channel à ma stay_list."""],
173 "nostay": [None, None, """NOSTAY <channel>
174 Retire le channel de ma stay_list."""],
175 "ops": [None, None, """OPS
176 Affiche la liste des ops."""],
177 "overops": [None, None, """OVEROPS
178 Affiche la liste des overops."""],
179 "kick": [None, None, """KICK <channel> <pseudo> [<raison>]
180 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
181 "die": [None, None, """DIE
182 Me déconnecte du serveur IRC."""]
184 helpmsg_default
= "Liste des commandes disponibles :\nHELP"
185 helpmsg_ops
= " JOIN LEAVE QUIET NOQUIET LOST RECONNECT"
186 helpmsg_overops
= " SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
187 op
, overop
= auteur
in self
.ops
, auteur
in self
.overops
188 if len(message
) == 1:
189 helpmsg
= helpmsg_default
191 helpmsg
+= helpmsg_ops
193 helpmsg
+= helpmsg_overops
195 helpmsgs
= helpdico
.get(message
[1].lower(), ["Commande inconnue.", None, None])
196 helpmsg
= helpmsgs
[0]
197 if op
and helpmsgs
[1]:
199 helpmsg
+= "\n"+helpmsgs
[1]
201 helpmsg
= helpmsgs
[1]
202 if overop
and helpmsgs
[2]:
204 helpmsg
+= "\n"+helpmsgs
[2]
206 helpmsg
= helpmsgs
[2]
207 for ligne
in helpmsg
.split("\n"):
208 serv
.privmsg(auteur
, ligne
)
210 if auteur
in self
.ops
:
212 if message
[1] in self
.chanlist
:
213 serv
.privmsg(auteur
, "Je suis déjà sur %s" % (message
[1]))
215 serv
.join(message
[1])
216 self
.chanlist
.append(message
[1])
217 serv
.privmsg(auteur
, "Channels : "+" ".join(self
.chanlist
))
218 log(self
.serveur
, "priv", auteur
, " ".join(message
))
220 serv
.privmsg(auteur
, "Channels : "+" ".join(self
.chanlist
))
224 if auteur
in self
.ops
and len(message
)>1:
225 if message
[1] in self
.chanlist
:
226 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
227 self
.quitter(message
[1], " ".join(message
[2:]))
228 self
.chanlist
.remove(message
[1])
229 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[successful]")
231 serv
.privmsg(auteur
, "Non, je reste !")
232 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[failed]")
234 serv
.privmsg(auteur
, "Je ne suis pas sur %s" % (message
[1]))
238 if auteur
in self
.overops
:
240 if message
[1] in self
.stay_channels
:
241 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[failed]")
242 serv
.privmsg(auteur
, "Je stay déjà sur %s." % (message
[1]))
244 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[successful]")
245 self
.stay_channels
.append(message
[1])
246 serv
.privmsg(auteur
, "Stay channels : "+" ".join(self
.stay_channels
))
248 serv
.privmsg(auteur
, "Stay channels : "+" ".join(self
.stay_channels
))
251 elif cmd
== "nostay":
252 if auteur
in self
.overops
:
254 if message
[1] in self
.stay_channels
:
255 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[successful]")
256 self
.stay_channels
.remove(message
[1])
257 serv
.privmsg(auteur
, "Stay channels : "+" ".join(self
.stay_channels
))
259 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[failed]")
260 serv
.privmsg(auteur
, "Je ne stay pas sur %s." % (message
[1]))
265 if auteur
in self
.overops
:
266 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[successful]")
271 if auteur
in self
.ops
:
273 if message
[1] in self
.quiet_channels
:
274 serv
.privmsg(auteur
, "Je me la ferme déjà sur %s" % (message
[1]))
275 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[failed]")
277 self
.quiet_channels
.append(message
[1])
278 serv
.privmsg(auteur
, "Quiet channels : "+" ".join(self
.quiet_channels
))
279 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[successful]")
281 serv
.privmsg(auteur
, "Quiet channels : "+" ".join(self
.quiet_channels
))
284 elif cmd
== "noquiet":
285 if auteur
in self
.ops
:
287 if message
[1] in self
.quiet_channels
:
288 self
.quiet_channels
.remove(message
[1])
289 serv
.privmsg(auteur
, "Quiet channels : "+" ".join(self
.quiet_channels
))
290 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[successful]")
292 serv
.privmsg(auteur
, "Je ne me la ferme pas sur %s." % (message
[1]))
293 log(self
.serveur
, "priv", auteur
, " ".join(message
)+"[failed]")
297 if auteur
in self
.overops
and len(message
)>2:
298 serv
.privmsg(message
[1], " ".join(message
[2:]))
299 log(self
.serveur
, "priv", auteur
, " ".join(message
))
300 elif len(message
) <= 2:
301 serv
.privmsg(auteur
, "Syntaxe : SAY <channel> <message>")
305 if auteur
in self
.overops
and len(message
)>2:
306 serv
.action(message
[1], " ".join(message
[2:]))
307 log(self
.serveur
, "priv", auteur
, " ".join(message
))
308 elif len(message
) <= 2:
309 serv
.privmsg(auteur
, "Syntaxe : DO <channel> <action>")
313 if auteur
in self
.overops
and len(message
)>2:
314 serv
.kick(message
[1], message
[2], " ".join(message
[3:]))
315 log(self
.serveur
, "priv", auteur
, " ".join(message
))
316 elif len(message
) <= 2:
317 serv
.privmsg(auteur
, "Syntaxe : KICK <channel> <pseudo> [<raison>]")
321 if auteur
in self
.overops
:
322 serv
.privmsg(auteur
, " ".join(self
.ops
))
325 elif cmd
== "overops":
326 if auteur
in self
.overops
:
327 serv
.privmsg(auteur
, " ".join(self
.overops
))
333 serv
.privmsg(auteur
, "Je n'ai pas compris. Essayez HELP…")
335 def on_pubmsg(self
, serv
, ev
):
336 auteur
= irclib
.nm_to_n(ev
.source())
338 message
= ev
.arguments()[0]
341 except UnicodeBotError
:
342 if config
.utf8_trigger
and not canal
in self
.quiet_channels
:
343 serv
.privmsg(canal
, (u
"%s: %s" % (auteur
, random
.choice(config
.utf8_fail_answers
))).encode("utf8"))
345 pour_moi
, message
= self
.pourmoi(serv
, message
)
346 if pour_moi
and message
.split() != []:
347 cmd
= message
.split()[0].lower()
349 args
= " ".join(message
.split()[1:])
352 if cmd
in ["meurs", "die", "crève"]:
353 if auteur
in self
.overops
:
354 log(self
.serveur
, canal
, auteur
, message
+"[successful]")
357 serv
.privmsg(canal
, ("%s: %s" % (auteur
, random
.choice(config
.quit_fail_messages
))).encode("utf8"))
358 log(self
.serveur
, canal
, auteur
, message
+"[failed]")
360 elif cmd
in ["part", "leave", "dégage", "va-t-en", "tut'tiresailleurs, c'estmesgalets"]:
361 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
362 or auteur
in self
.overops
):
364 log(self
.serveur
, canal
, auteur
, message
+"[successful]")
365 if canal
in self
.chanlist
:
366 self
.chanlist
.remove(canal
)
368 serv
.privmsg(canal
, ("%s: %s" % (auteur
, random
.choice(config
.leave_fail_messages
))).encode("utf8"))
369 log(self
.serveur
, canal
, auteur
, message
+"[failed]")
371 elif cmd
in ["deviens", "pseudo"]:
372 if auteur
in self
.ops
:
375 log(self
.serveur
, canal
, auteur
, message
+"[successful]")
377 if cmd
in ["meur", "meurt", "meurre", "meurres"] and not canal
in self
.quiet_channels
:
378 serv
.privmsg(canal
, '%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)' % (auteur
))
379 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
380 serv
.privmsg(canal
, "%s: pong" % (auteur
))
381 if is_insult(message
) and not canal
in self
.quiet_channels
:
382 if is_not_insult(message
):
383 answer
= random
.choice(config
.compliment_answers
)
384 for ligne
in answer
.split("\n"):
385 serv
.privmsg(canal
, "%s: %s" % (auteur
, ligne
.encode("utf8")))
387 answer
= random
.choice(config
.insultes_answers
)
388 for ligne
in answer
.split("\n"):
389 serv
.privmsg(canal
, "%s: %s" % (auteur
, ligne
.encode("utf8")))
390 elif is_compliment(message
) and not canal
in self
.quiet_channels
:
391 answer
= random
.choice(config
.compliment_answers
)
392 for ligne
in answer
.split("\n"):
393 serv
.privmsg(canal
, "%s: %s" % (auteur
, ligne
.encode("utf8")))
394 if is_tesla(message
) and not canal
in self
.quiet_channels
:
395 l1
, l2
= config
.tesla_answers
, config
.tesla_actions
396 n1
, n2
= len(l1
), len(l2
)
397 i
= random
.randrange(n1
+n2
)
399 serv
.action(canal
, l2
[i
-n1
].encode("utf8"))
401 serv
.privmsg(canal
, "%s: %s" % (auteur
, l1
[i
].encode("utf8")))
402 if is_tamere(message
) and not canal
in self
.quiet_channels
:
403 answer
= random
.choice(config
.tamere_answers
)
404 for ligne
in answer
.split("\n"):
405 serv
.privmsg(canal
, "%s: %s" % (auteur
, ligne
.encode("utf8")))
406 if is_tag(message
) and not canal
in self
.quiet_channels
:
407 if auteur
in self
.ops
:
408 action
= random
.choice(config
.tag_actions
)
409 serv
.action(canal
, action
.encode("utf8"))
410 self
.quiet_channels
.append(canal
)
412 answer
= random
.choice(config
.tag_answers
)
413 for ligne
in answer
.split("\n"):
414 serv
.privmsg(canal
, "%s: %s" % (auteur
, ligne
.encode("utf8")))
415 if is_merci(message
):
416 answer
= random
.choice(config
.merci_answers
)
417 for ligne
in answer
.split("\n"):
418 serv
.privmsg(canal
, "%s: %s" % (auteur
, ligne
.encode("utf8")))
419 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
421 answer
= random
.choice(config
.night_answers
)
423 answer
= random
.choice(config
.bonjour_answers
)
425 answer
= random
.choice(config
.bonsoir_answers
)
426 serv
.privmsg(canal
, answer
.format(auteur
).encode("utf8"))
427 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
428 answer
= random
.choice(config
.bonne_nuit_answers
)
429 serv
.privmsg(canal
, answer
.format(auteur
).encode("utf8"))
431 if not canal
in self
.quiet_channels
:
433 if re
.match((u
"^("+u
"|".join(config
.bonjour_triggers
)
434 +ur
")( {}| all| tout le monde| (à )?tous)(\.| ?!)?$"
435 ).format(mypseudo
).lower(), message
.decode("utf8").strip().lower()):
436 answer
= random
.choice(config
.bonjour_answers
)
437 serv
.privmsg(canal
, answer
.format(auteur
).encode("utf8"))
439 def on_action(self
, serv
, ev
):
440 action
= ev
.arguments()[0]
441 auteur
= irclib
.nm_to_n(ev
.source())
442 channel
= ev
.target()
445 except UnicodeBotError
:
446 if config
.utf8_trigger
and not channel
in self
.quiet_channels
:
447 serv
.privmsg(channel
, (u
"%s: %s" % (auteur
, random
.choice(config
.utf8_fail_answers
))).encode("utf8"))
451 if is_bad_action_trigger(action
, mypseudo
) and not channel
in self
.quiet_channels
:
452 l1
, l2
= config
.bad_action_answers
, config
.bad_action_actions
453 n1
, n2
= len(l1
), len(l2
)
454 i
= random
.randrange(n1
+n2
)
456 serv
.action(channel
, l2
[i
-n1
].format(auteur
).encode("utf8"))
458 serv
.privmsg(channel
, l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
459 if is_good_action_trigger(action
, mypseudo
) and not channel
in self
.quiet_channels
:
460 l1
, l2
= config
.good_action_answers
, config
.good_action_actions
461 n1
, n2
= len(l1
), len(l2
)
462 i
= random
.randrange(n1
+n2
)
464 serv
.action(channel
, l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
466 serv
.privmsg(channel
, l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
468 def on_kick(self
, serv
, ev
):
469 auteur
= irclib
.nm_to_n(ev
.source())
470 channel
= ev
.target()
471 victime
= ev
.arguments()[0]
472 raison
= ev
.arguments()[1]
473 if victime
== self
.nick
:
474 log(self
.serveur
, "%s kické de %s par %s (raison : %s)" % (victime
, channel
, auteur
, raison
))
477 l1
, l2
= config
.kick_answers
, config
.kick_actions
478 n1
, n2
= len(l1
), len(l2
)
479 i
= random
.randrange(n1
+n2
)
481 serv
.action(channel
, l2
[i
-n1
].format(auteur
).encode("utf8"))
483 serv
.privmsg(channel
, l1
[i
].format(auteur
).encode("utf8"))
485 def quitter(self
, chan
, leave_message
=None):
486 if leave_message
== None:
487 leave_message
= random
.choice(config
.leave_messages
)
488 self
.serv
.part(chan
, message
=leave_message
.encode("utf8"))
491 quit_message
= random
.choice(config
.quit_messages
)
492 self
.die(msg
=quit_message
.encode("utf8"))
495 return self
.serv
.get_nickname()
496 nick
= property(_getnick
)
499 if __name__
== "__main__":
501 if len(sys
.argv
) == 1:
502 print "Usage : skeleton.py <serveur> [--debug]"
504 serveur
= sys
.argv
[1]
505 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
509 if "--quiet" in sys
.argv
:
510 config
.debug_stdout
= False
511 serveurs
= {"a♡":"acoeur.crans.org", "acoeur":"acoeur.crans.org", "acoeur.crans.org":"acoeur.crans.org",
512 "irc":"irc.crans.org", "crans":"irc.crans.org", "irc.crans.org":"irc.crans.org"}
514 serveur
= serveurs
[serveur
]
516 print "Server Unknown : %s" % (serveur
)
518 bot
= Skeleton(serveur
, debug
)