]> gitweb.pimeys.fr Git - bots/skeleton.git/blob - skeleton.py
Skeleton.py un bot qui ne fait rien de spécial, mais qui n'attend que ça.
[bots/skeleton.git] / skeleton.py
1 #!/usr/bin/python
2 # -*- encoding: utf-8 -*-
3
4 # Codé par 20-100
5
6 # Un bot IRC qui ne fait rien. Base pour en coder un autre.
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 # on récupère la config
20 import config
21
22
23
24 def get_config_logfile(serveur):
25 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
26 return config.logfile_template%(serveurs[serveur])
27
28 def get_filesize():
29 return ex("ls -s %s"%(config.thisfile))[1].split()[0]
30
31 def log(serveur,channel,auteur=None,message=None):
32 f=open(get_config_logfile(serveur),"a")
33 if auteur==message==None:
34 # alors c'est que c'est pas un channel mais juste une ligne de log
35 chain="%s %s"%(time.strftime("%F %T"),channel)
36 else:
37 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
38 f.write(chain+"\n")
39 if config.debug_stdout:
40 print chain
41 f.close()
42
43 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
44 if case_sensitive:
45 chain=unicode(chain,"utf8")
46 else:
47 chain=unicode(chain,"utf8").lower()
48 allmatches="("+"|".join(matches)+")"
49 reg=(avant+allmatches+apres).lower()
50 o=re.match(reg,chain)
51 return o
52
53 def is_insult(chain,debug=True):
54 return is_something(chain,config.insultes,avant=".*(?:^| |')")
55 def is_not_insult(chain):
56 chain=unicode(chain,"utf8")
57 insult_regexp=u"("+u"|".join(config.insultes)+u")"
58 middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
59 reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
60 if re.match(reg,chain):
61 return True
62 else:
63 return False
64 def is_compliment(chain,debug=True):
65 return is_something(chain,config.compliment_triggers,avant=".*(?:^| |')")
66 def is_tag(chain):
67 return is_something(chain,config.tag_triggers)
68 def is_tesla(chain):
69 return is_something(chain,config.tesla_triggers,avant=u"^",apres=u"$",debug=True)
70 def is_merci(chain):
71 return is_something(chain,config.merci_triggers)
72 def is_tamere(chain):
73 return is_something(chain,config.tamere_triggers)
74 def is_bad_action_trigger(chain,pseudo):
75 return is_something(chain,config.bad_action_triggers,avant=u"^",
76 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
77 def is_good_action_trigger(chain,pseudo):
78 return is_something(chain,config.good_action_triggers,avant=u"^",
79 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
80 def is_bonjour(chain):
81 return is_something(chain,config.bonjour_triggers,avant=u"^")
82 def is_bonne_nuit(chain):
83 return is_something(chain,config.bonne_nuit_triggers,avant=u"^")
84 def is_pan(chain):
85 return re.match(u"^(pan|bim|bang)( .*)?$",unicode(chain,"utf8").lower().strip())
86
87 def is_time(conf):
88 _,_,_,h,m,s,_,_,_=time.localtime()
89 return (conf[0],0,0)<(h,m,s)<(conf[1],0,0)
90 def is_day():
91 return is_time(config.daytime)
92 def is_night():
93 return is_time(config.nighttime)
94
95
96 class UnicodeBotError(Exception):
97 pass
98 def bot_unicode(chain):
99 try:
100 unicode(chain,"utf8")
101 except UnicodeDecodeError as exc:
102 raise UnicodeBotError
103
104 class Skeleton(ircbot.SingleServerIRCBot):
105 def __init__(self,serveur,debug=False):
106 temporary_pseudo=config.irc_pseudo+str(random.randrange(10000,100000))
107 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
108 temporary_pseudo,"Ceci est l'ircname du bot", 10)
109 self.debug=debug
110 self.serveur=serveur
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.quiet_channels=config.quiet_channels
116 self.last_perdu=0
117
118 def give_me_my_pseudo(self,serv):
119 serv.privmsg("NickServ","RECOVER %s %s"%(config.irc_pseudo,config.irc_password))
120 serv.privmsg("NickServ","RELEASE %s %s"%(config.irc_pseudo,config.irc_password))
121 time.sleep(0.3)
122 serv.nick(config.irc_pseudo)
123
124 def on_welcome(self, serv, ev):
125 self.serv=serv # ça serv ira :)
126 self.give_me_my_pseudo(serv)
127 serv.privmsg("NickServ","identify %s"%(config.irc_password))
128 log(self.serveur,"Connected")
129 if self.debug:
130 self.chanlist=["#bot"]
131 for c in self.chanlist:
132 log(self.serveur,"JOIN %s"%(c))
133 serv.join(c)
134
135 def pourmoi(self, serv, message):
136 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
137 pseudo=self.nick
138 size=len(pseudo)
139 if message[:size]==pseudo and len(message)>size and message[size]==":":
140 return (True,message[size+1:].lstrip(" "))
141 else:
142 return (False,message)
143
144 def on_privmsg(self, serv, ev):
145 message=ev.arguments()[0]
146 auteur = irclib.nm_to_n(ev.source())
147 try:
148 test=bot_unicode(message)
149 except UnicodeBotError:
150 if config.utf8_trigger:
151 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
152 return
153 message=message.split()
154 cmd=message[0].lower()
155 notunderstood=False
156 if cmd=="help":
157 helpdico={"help":["""HELP <commande>
158 Affiche de l'aide sur la commande""",None,None],
159 "join": [None, """JOIN <channel>
160 Me fait rejoindre le channel""",None],
161 "leave": [None,"""LEAVE <channel>
162 Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
163 "quiet": [None,"""QUIET <channel>
164 Me rend silencieux sur le channel.""",None],
165 "noquiet": [None,"""NOQUIET <channel>
166 Me rend la parole sur le channel.""",None],
167 "lost": [None,"""LOST <channel>
168 Me fait perdre sur le channel.""",None],
169 "say": [None,None,"""SAY <channel> <message>
170 Me fait parler sur le channel."""],
171 "do": [None,None,"""DO <channel> <action>
172 Me fait faitre une action (/me) sur le channel."""],
173 "stay": [None,None,"""STAY <channel>
174 Ajoute le channel à ma stay_list."""],
175 "nostay": [None,None,"""NOSTAY <channel>
176 Retire le channel de ma stay_list."""],
177 "ops": [None,None,"""OPS
178 Affiche la liste des ops."""],
179 "overops": [None,None,"""OVEROPS
180 Affiche la liste des overops."""],
181 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
182 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
183 "die": [None,None,"""DIE
184 Me déconnecte du serveur IRC."""]
185 }
186 helpmsg_default="Liste des commandes disponibles :\nHELP"
187 helpmsg_ops=" JOIN LEAVE QUIET NOQUIET LOST RECONNECT"
188 helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
189 op,overop=auteur in self.ops, auteur in self.overops
190 if len(message)==1:
191 helpmsg=helpmsg_default
192 if op:
193 helpmsg+=helpmsg_ops
194 if overop:
195 helpmsg+=helpmsg_overops
196 else:
197 helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
198 helpmsg=helpmsgs[0]
199 if op and helpmsgs[1]:
200 if helpmsg:
201 helpmsg+="\n"+helpmsgs[1]
202 else:
203 helpmsg=helpmsgs[1]
204 if overop and helpmsgs[2]:
205 if helpmsg:
206 helpmsg+="\n"+helpmsgs[2]
207 else:
208 helpmsg=helpmsgs[2]
209 for ligne in helpmsg.split("\n"):
210 serv.privmsg(auteur,ligne)
211 elif cmd=="join":
212 if auteur in self.ops:
213 if len(message)>1:
214 if message[1] in self.chanlist:
215 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
216 else:
217 serv.join(message[1])
218 self.chanlist.append(message[1])
219 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
220 log(self.serveur,"priv",auteur," ".join(message))
221 else:
222 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
223 else:
224 notunderstood=True
225 elif cmd=="leave":
226 if auteur in self.ops and len(message)>1:
227 if message[1] in self.chanlist:
228 if not (message[1] in self.stay_channels) or auteur in self.overops:
229 self.quitter(message[1]," ".join(message[2:]))
230 self.chanlist.remove(message[1])
231 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
232 else:
233 serv.privmsg(auteur,"Non, je reste !")
234 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
235 else:
236 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
237 else:
238 notunderstood=True
239 elif cmd=="stay":
240 if auteur in self.overops:
241 if len(message)>1:
242 if message[1] in self.stay_channels:
243 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
244 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
245 else:
246 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
247 self.stay_channels.append(message[1])
248 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
249 else:
250 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
251 else:
252 notunderstood=True
253 elif cmd=="nostay":
254 if auteur in self.overops:
255 if len(message)>1:
256 if message[1] in self.stay_channels:
257 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
258 self.stay_channels.remove(message[1])
259 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
260 else:
261 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
262 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
263
264 else:
265 notunderstood=True
266 elif cmd=="die":
267 if auteur in self.overops:
268 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
269 self.mourir()
270 else:
271 notunderstood=True
272 elif cmd=="quiet":
273 if auteur in self.ops:
274 if len(message)>1:
275 if message[1] in self.quiet_channels:
276 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
277 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
278 else:
279 self.quiet_channels.append(message[1])
280 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
281 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
282 else:
283 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
284 else:
285 notunderstood=True
286 elif cmd=="noquiet":
287 if auteur in self.ops:
288 if len(message)>1:
289 if message[1] in self.quiet_channels:
290 self.quiet_channels.remove(message[1])
291 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
292 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
293 else:
294 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
295 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
296 else:
297 notunderstood=True
298 elif cmd=="say":
299 if auteur in self.overops and len(message)>2:
300 serv.privmsg(message[1]," ".join(message[2:]))
301 log(self.serveur,"priv",auteur," ".join(message))
302 elif len(message)<=2:
303 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
304 else:
305 notunderstood=True
306 elif cmd=="do":
307 if auteur in self.overops and len(message)>2:
308 serv.action(message[1]," ".join(message[2:]))
309 log(self.serveur,"priv",auteur," ".join(message))
310 elif len(message)<=2:
311 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
312 else:
313 notunderstood=True
314 elif cmd=="kick":
315 if auteur in self.overops and len(message)>2:
316 serv.kick(message[1],message[2]," ".join(message[3:]))
317 log(self.serveur,"priv",auteur," ".join(message))
318 elif len(message)<=2:
319 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
320 else:
321 notunderstood=True
322 elif cmd=="ops":
323 if auteur in self.overops:
324 serv.privmsg(auteur," ".join(self.ops))
325 else:
326 notunderstood=True
327 elif cmd=="overops":
328 if auteur in self.overops:
329 serv.privmsg(auteur," ".join(self.overops))
330 else:
331 notunderstood=True
332 else:
333 notunderstood=True
334 if notunderstood:
335 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
336
337 def on_pubmsg(self, serv, ev):
338 auteur = irclib.nm_to_n(ev.source())
339 canal = ev.target()
340 message = ev.arguments()[0]
341 try:
342 test=bot_unicode(message)
343 except UnicodeBotError:
344 if config.utf8_trigger and not canal in self.quiet_channels:
345 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
346 return
347 pour_moi,message=self.pourmoi(serv,message)
348 if pour_moi and message.split()!=[]:
349 cmd=message.split()[0].lower()
350 try:
351 args=" ".join(message.split()[1:])
352 except:
353 args=""
354 if cmd in ["meurs","die","crève"]:
355 if auteur in self.overops:
356 log(self.serveur,canal,auteur,message+"[successful]")
357 self.mourir()
358 else:
359 serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
360 log(self.serveur,canal,auteur,message+"[failed]")
361
362 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
363 if auteur in self.ops and (not (canal in self.stay_channels)
364 or auteur in self.overops):
365 self.quitter(canal)
366 log(self.serveur,canal,auteur,message+"[successful]")
367 if canal in self.chanlist:
368 self.chanlist.remove(canal)
369 else:
370 serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
371 log(self.serveur,canal,auteur,message+"[failed]")
372
373 elif cmd in ["deviens","pseudo"]:
374 if auteur in self.ops:
375 become=args
376 serv.nick(become)
377 log(self.serveur,canal,auteur,message+"[successful]")
378
379 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
380 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
381 elif cmd in ["ping"] and not canal in self.quiet_channels:
382 serv.privmsg(canal,"%s: pong"%(auteur))
383 if is_insult(message) and not canal in self.quiet_channels:
384 if is_not_insult(message):
385 answer=random.choice(config.compliment_answers)
386 for ligne in answer.split("\n"):
387 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
388 else:
389 answer=random.choice(config.insultes_answers)
390 for ligne in answer.split("\n"):
391 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
392 elif is_compliment(message) and not canal in self.quiet_channels:
393 answer=random.choice(config.compliment_answers)
394 for ligne in answer.split("\n"):
395 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
396 if is_tesla(message) and not canal in self.quiet_channels:
397 l1,l2=config.tesla_answers,config.tesla_actions
398 n1,n2=len(l1),len(l2)
399 i=random.randrange(n1+n2)
400 if i>=n1:
401 serv.action(canal,l2[i-n1].encode("utf8"))
402 else:
403 serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
404 if is_tamere(message) and not canal in self.quiet_channels:
405 answer=random.choice(config.tamere_answers)
406 for ligne in answer.split("\n"):
407 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
408 if is_tag(message) and not canal in self.quiet_channels:
409 if auteur in self.ops:
410 action=random.choice(config.tag_actions)
411 serv.action(canal,action.encode("utf8"))
412 self.quiet_channels.append(canal)
413 else:
414 answer=random.choice(config.tag_answers)
415 for ligne in answer.split("\n"):
416 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
417 if is_merci(message):
418 answer=random.choice(config.merci_answers)
419 for ligne in answer.split("\n"):
420 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
421 if is_bonjour(message) and not canal in self.quiet_channels:
422 if is_night():
423 answer=random.choice(config.night_answers)
424 elif is_day():
425 answer=random.choice(config.bonjour_answers)
426 else:
427 answer=random.choice(config.bonsoir_answers)
428 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
429 if is_bonne_nuit(message) and not canal in self.quiet_channels:
430 answer=random.choice(config.bonne_nuit_answers)
431 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
432 else:
433 if not canal in self.quiet_channels:
434 mypseudo=self.nick
435 if re.match((u"^("+u"|".join(config.bonjour_triggers)
436 +ur")( {}| all| tout le monde| (à )?tous)(\.| ?!)?$"
437 ).format(mypseudo).lower(), message.decode("utf8").strip().lower()):
438 answer = random.choice(config.bonjour_answers)
439 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
440
441 def on_action(self, serv, ev):
442 action = ev.arguments()[0]
443 auteur = irclib.nm_to_n(ev.source())
444 channel = ev.target()
445 try:
446 test=bot_unicode(action)
447 except UnicodeBotError:
448 if config.utf8_trigger and not channel in self.quiet_channels:
449 serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
450 return
451 mypseudo=self.nick
452
453 if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
454 l1,l2=config.bad_action_answers,config.bad_action_actions
455 n1,n2=len(l1),len(l2)
456 i=random.randrange(n1+n2)
457 if i>=n1:
458 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
459 else:
460 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
461 if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
462 l1,l2=config.good_action_answers,config.good_action_actions
463 n1,n2=len(l1),len(l2)
464 i=random.randrange(n1+n2)
465 if i>=n1:
466 serv.action(channel,l2[i-n1].format(auteur).format(auteur).encode("utf8"))
467 else:
468 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
469
470 def on_kick(self,serv,ev):
471 auteur = irclib.nm_to_n(ev.source())
472 channel = ev.target()
473 victime = ev.arguments()[0]
474 raison = ev.arguments()[1]
475 if victime==self.nick:
476 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
477 time.sleep(2)
478 serv.join(channel)
479 l1,l2=config.kick_answers,config.kick_actions
480 n1,n2=len(l1),len(l2)
481 i=random.randrange(n1+n2)
482 if i>=n1:
483 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
484 else:
485 serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
486
487 def quitter(self,chan,leave_message=None):
488 if leave_message==None:
489 leave_message=random.choice(config.leave_messages)
490 self.serv.part(chan,message=leave_message.encode("utf8"))
491
492 def mourir(self):
493 quit_message=random.choice(config.quit_messages)
494 self.die(msg=quit_message.encode("utf8"))
495
496 def _getnick(self):
497 return self.serv.get_nickname()
498 nick=property(_getnick)
499
500
501 if __name__=="__main__":
502 import sys
503 if len(sys.argv)==1:
504 print "Usage : skeleton.py <serveur> [--debug]"
505 exit(1)
506 serveur=sys.argv[1]
507 if "debug" in sys.argv or "--debug" in sys.argv:
508 debug=True
509 else:
510 debug=False
511 if "--quiet" in sys.argv:
512 config.debug_stdout=False
513 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
514 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
515 try:
516 serveur=serveurs[serveur]
517 except KeyError:
518 print "Server Unknown : %s"%(serveur)
519 exit(404)
520 bot=Skeleton(serveur,debug)
521 bot.start()