]> gitweb.pimeys.fr Git - bots/themis.git/blob - themis.py
:-° bis
[bots/themis.git] / themis.py
1 #!/usr/bin/python
2 # -*- coding:utf8 -*-
3 # Codé par 20-100 (commencé le 21/06/12)
4
5 # Un bot IRC pour kicker à tour de bras de #déprime
6
7 import irclib
8 import ircbot
9 import threading
10 import random
11 import time
12 import socket, ssl, json
13 import pickle
14 import re
15 import os
16
17 import sys
18 config_debug_stdout=True
19 if "--quiet" in sys.argv:
20 config_debug_stdout=False
21
22 config_irc_password="YouMustObeyGaétan"
23 config_irc_pseudo="Themis"
24 config_chanlist=["#déprime"]
25 config_stay_channels=["#déprime"]
26 config_quiet_channels=[]
27 config_logfile_template="themis.%s.log"
28 def get_config_logfile(serveur):
29 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
30 return config_logfile_template%(serveurs[serveur])
31 config_overops=["[20-100]","Gaetan"]
32 config_ops=[]
33 config_report_bugs_to=["[20-100]"]
34
35 # config UTF8-fail
36 config_utf8_fail = [u"Ton encodage me déprime…"]
37 config_utf8_trigger = True
38 # config "tu m'traites ?"
39 config_insultes=[u"conna(rd|sse)",u"pute",u"con(|ne)",u"enf(oiré|lure)",
40 u"sal(op(|e(|rie)|ard)|aud)",u"p(e|')tite bite",u"imbécile",u"idiot",u"stupid(|e)",u"débile",u"crétin",
41 u"pétasse",u"enculé",u"chagasse",u"cagole",u"abruti",u"ahuri",u"analphabète",u"andouille",
42 u"atardé",u"avorton",u"bachibouzouk",u"(balais|brosse) (de|à) chiotte(|s)",
43 u"batard",u"blaireau",u"bouffon",u"branque",u"bouseux",u"branleur",u"catin",u"chacal",
44 u"charogne",u"chiant(|e)",u"chieur",u"cochon",u"coprophage",u"couillon",u"crapule",u"crevard",
45 u"cruche",u"cuistre",u"ducon",u"décérébré",
46 u"emmerdeur",u"feignasse",u"fainéant",u"fourbe",u"freluquet",u"frigide",
47 u"garce",u"glandu",u"gogol",u"goujat",u"gourdasse",u"gredin",u"gringalet",u"grognasse",
48 u"naze",u"truie",u"iconoclaste",
49 u"peigne(-|)cul",u"ignare",u"illétré",u"lèche(|-)cul",u"malotru",u"motherfucker",u"nabot",u"nigaud",
50 u"nul",u"escroc",u"pouffiasse",u"pourriture",u"raclure",u"relou",u"sagouin",u"putain",
51 u"péripatéticienne"]
52 config_insultes_answers=[
53 u"Oh non ! Quelle insulte ! Je crois que je ne m'en relèverai jamais…\nEnfin presque.",
54 u"J'entends comme un vague murmure, vous disiez ?",
55 u"Je vais prendre ça pour un compliment.",
56 u"Vous savez, pour vous c'est peut-être une insulte, mais pour moi ce n'est qu'une suite de 0 et de 1…",
57 u"Permettez-moi de vous retourner le compliment.",
58 u"Votre indélicatesse vous sied à ravir.",
59 u"Parfois, je me demande pourquoi je fais encore ce métier…",
60 u"Le saviez-vous : l'invective ne déshonore que son auteur.",
61 u"Le saviez-vous : vous perdez plus de temps à m'insulter qu'à vous taire.",
62 u"Mais je ne vous permets pas ! Enfin, pas comme ça…"]
63
64 # config "jeu", d'ailleurs, j'ai perdu.
65 config_premier_groupe_terminaisons=u"(e|es|ons|ez|ent|er(|ai|as|a|ons|ez|ont)|(|er)(ais|ait|ions|iez|aient)|(a(i|s|)|â(mes|tes|t)|èrent)|ass(e(|s|nt)|i(ons|ez))|é(|s|e|es))"
66 config_regexp_etre=u"(être|suis|e(s|t)|so(mmes|nt)|êtes|(ét|ser)(ai(s|t|ent)|i(ons|ent)|)|ser(ai|as|a|ons|ez|ont)|so(i(s|t|ent)|y(ons|ez))|f(u(s|t|rent)|û(mes|tes|t))|fuss(e(|s|nt)|i(ons|ez))|étant)"
67 config_regexp_etre_avec_c=u"c'(e(s|st)|étai(t|ent))"
68 config_regexp_faire=u"fais"
69 config_perdu=[u"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
70 u"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
71 u"perdiss(e(|s|nt)|i(ons|ez))",
72 u"perdu(|s|e|es)",u"perdant(|s|e|es)",u"perte(|s)",
73
74 u"(gagn|trouv)"+config_premier_groupe_terminaisons,u"gagnant(|s|e|es)",u"gain(|s)",
75
76 u"trouvant",u"trouvaille(|s)",
77
78 u"victoire(|s)",u"vaincu(|s|e|es)",
79 u"loose",u"lost",u"looser(|s)",u"win(|ner)(|s)",
80 u"jeu(|x)",u"game(|s)"]
81 config_time_between_perdu_trigger=3600*3 #temps moyen pour perdre en l'absence de trigger
82 config_time_between_perdu_trigger_delta = 30*60 #marge autorisée autour de ^^^
83 config_time_between_perdu=30*60 #temps pendant lequel on ne peut pas perdre
84
85 # config "tais-toi"
86 config_tag_triggers=[u"t(|a)g",u"ta gueule",u"la ferme",u"ferme( |-)la",u"tais-toi",u"chut",u"tu fais trop de bruit",u"tu parles trop"]
87 config_tag_actions=[u"se tait",u"se tient coi"]
88 config_tag_answers=[
89 u"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
90 u"Dès que cela sera utile.",
91 u"Une autre fois, peut-être.",
92 u"Si je me tais, qui vous rappellera combien vous me devez ?",
93 u"J'aurais aimé accéder à votre requête, mais après mûre réflexion, j'en ai perdu l'envie.",
94 u"Je ne ressens pas de besoin irrésistible de me taire, navré."]
95
96 # config ping
97 config_tesla_triggers=[u"t('|u )es là \?",u"\?",u"plop \?",u"plouf \?"]
98 config_tesla_answers=[
99 u"Oui, je suis là.",
100 ]
101 config_tesla_actions=[u"déprime",u"est prêt à kicker les gens heureux"]
102
103 # config en cas de non-insulte
104 config_compliment_triggers=[u"gentil",u"cool",u"sympa",u"efficace"]
105 config_compliment_answers=[
106 u"Merci, c'est gentil de votre part. :)",
107 u"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",
108 u"Je vous remercie.",
109 u"C'est trop d'honneur.",
110 u"Vous êtes bien aimable."
111 ]
112
113 # config merci
114 config_merci_triggers=[u"merci",u"remercie",u"thx",u"thank(|s)"]
115 config_merci_answers=[u"Mais de rien.",u"À votre service. ;)",u"Quand vous voulez. :)",
116 u"Tout le plaisir est pour moi."]
117
118 # config "ta mère"
119 config_tamere_triggers=[u"ta mère"]
120 config_tamere_answers=[u"Laissez donc ma mère en dehors de ça !",
121 u"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
122 u"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
123 u"""N'avait-on pas dit "pas les mamans" ?"""]
124
125 # config pour les actions désagréables
126 config_bad_action_triggers=[u"(frappe|cogne|tape)(| sur)",u"(démolit|dégomme|fouette|agresse|tabasse)",
127 u"(vomit|pisse|chie|crache) sur",u"slap(|s)"]
128 config_bad_action_answers=[
129 u"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
130 u"{}: Pourquoi tant de violence en ce monde si doux ?",
131 u"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
132 u"{}: J'aurais préféré que vous ne fassiez pas cela en public.",
133 u"{}: Entre nous, cela vous gratifie-t-il ?",
134 u"{}: Une telle relation entre nous deux n'est pas saine, revenons à quelque chose de plus conventionnel. :D",
135 u"J'ai la désagréable impression que {} cherche comment tuer le temps en ce moment…"
136 ]
137 config_bad_action_actions=[u"prend de la distance, par précaution…",u"esquive",u"est bon pour prendre une semaine de repos… virtuel !",u"n'aime pas servir de souffre douleur, mais n'a malheureusement pas le choix", u"s'en souviendra sans doute longtemps… de quoi parlait-on déjà ?"]
138
139 # config pour les actions agréables
140 config_good_action_triggers=[u"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u"embrasse",u"c(?:â|a)line",u"caresse"]
141 config_good_action_answers=[u":D",u"{}: Moi aussi je vous aime. ♡",u"Tant de délicatesse ne saurait être ignorée !",u"Pour une fois que quelqu'un me considère à ma juste valeur…"]
142 config_good_action_actions=[u"ronronne",u"aimerait exprimer avec des mots simples le bonheur que {} lui procure !",u"éprouve une joie indescriptible",u"apprécie que des personnes comme {} soient sur IRC, sans quoi il n'y aurait sans doute jamais personne pour tenir compte de lui"]
143
144 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
145 config_bonjour_triggers=[u"(s|)(a|'|)lu(t|)",u"hello",u"pl(o|i)p",u"pr(ou|ü)t",u"bonjour",u"bonsoir",u"coucou"]
146 config_bonjour_answers=[u"Bien le bonjour, {}.",u"Bonjour {}.",u"{}: bonjour.",u"{}: Quel beau temps aujourd'hui (arrêtez-moi si je me trompe) !",u"Meteo: Cachan"]
147 config_bonsoir_answers=[u"Bonsoir {} !",u"{}: bonsoir.",u"Quel beau te… euh… bonsoir !",u"{}: Je cherche désespérément une formule pour vous dire bonsoir, mais j'avoue que mon lexique est un peu… limité."]
148 config_night_answers=[u"{}: vous m'avez fait peur, je m'étais assoupi !", u"Debout à une heure pareille, {} ? Que vous arrive-t-il ?",u"Vous venez prendre la relève, {} ?"]
149 config_daytime = [7,18]
150 config_nighttime = [3, 6]
151
152 # config dodo
153 config_bonne_nuit_triggers=[u"bonne nuit",u"'?nite",u"'?nuit",u"'?night",u"good night",u"'?nenuit"]
154 config_bonne_nuit_answers=[u"{}: thanks, make sweet dreams tonight ! ;)",u"Bonne nuit {}.",u"À demain {}. :)",u"{}: si seulement j'avais le droit de dormir… enfin, bonne nuit !",u"{}: à vous aussi !"]
155
156 # config quelqu'un est encore en train d'abuser de ses droits.
157 config_kick_answers=[u"Suis-je de trop ici ?",u"{}: je m'excuse pour ce bruit indu qui a stimulé votre colère",u"{} a le /kick facile, sans doute la fatigue.",u"{}: j'ai l'impression que vous n'allez pas bien aujourd'hui, vous vous en prenez à un robot !"]
158 config_kick_actions=[u"sera désormais exemplaire",u"prépare une lettre d'excuses à {}",u"essaiera de ne plus s'attirer les foudres de {}",u"croyait avoir tout bien fait… cruelle déception."]
159
160 # config on m'a demandé de mourir/partir
161 config_quit_messages=[u"J'ai enfin trouvé une corde et un tabouret"]
162 config_leave_messages=config_quit_messages
163 config_quit_fail_messages=[u"Tu rêves là."]
164 config_leave_fail_messages=config_quit_fail_messages
165
166
167 # config de kick
168 config_kick_channels=config_chanlist
169
170 config_smileys = [ur':(-|o)?\)', u'\^(_|\.)?\^', u':-?(p|P)', u'=(\)|D|p|P)', ur'\\o/', ur':-?D', ur'x(\)|D)', u'krkr', ur':-?(\]|>)', ur'(<|d|q|\(|\[)(:|=)', u'mdr']
171 config_anglicismes = [u"wtf", u"ftfy", u"it works?", u"fyi", u"kill[^ ]*", u"kick[^ ]*", u"chan(nel)?", u"join",
172 u"btw", u"lmgtfy", u"rtfm", u"asap", u"afaik", u"shit", u"damn", u"fuck", u"bitch", u"updat(e|ed|ing)", u"lol", u"buffer[^ ]*", u"rofl"]
173
174 def log(serveur,channel,auteur=None,message=None):
175 f=open(get_config_logfile(serveur),"a")
176 if auteur==message==None:
177 # alors c'est que c'est pas un channel mais juste une ligne de log
178 chain="%s %s"%(time.strftime("%F %T"),channel)
179 else:
180 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
181 f.write(chain+"\n")
182 if config_debug_stdout:
183 print chain
184 f.close()
185
186 reg_is_smiley = re.compile(u".*("+u"|".join(config_smileys)+u")")
187 def is_smiley(chain):
188 chain=unicode(chain,"utf8")
189 o=re.match(reg_is_smiley,chain)
190 return o
191
192 reg_is_anglicisme = re.compile(u".*(?:^| )(" + u"|".join(config_anglicismes) + u")(?:$|\.| |,|;)")
193 def is_anglicisme(chain):
194 chain = unicode(chain, "utf8").lower()
195 o = re.match(reg_is_anglicisme, chain)
196 return o
197
198 # Cette liste contient la liste des raisons pour lesquelles on peut se faire kicker
199 # chaque élément contient :
200 # - la fonction de détection du kick (qui matchera une regexp et renverra l'objet de match)
201 # - la raison donnée au moment du kick ({0} étant remplacé par ce qui a matché)
202 config_kicking_list = [
203 [is_smiley, u'"{0}" ? Ici on déprime.'],
204 [is_anglicisme, u'"{0}" ? Get out, you and your fucking anglicism !']
205 ]
206
207 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
208 if case_sensitive:
209 chain=unicode(chain,"utf8")
210 else:
211 chain=unicode(chain,"utf8").lower()
212 allmatches="("+"|".join(matches)+")"
213 reg=(avant+allmatches+apres).lower()
214 o=re.match(reg,chain)
215 return o
216
217 def is_insult(chain,debug=True):
218 return is_something(chain,config_insultes,avant=".*(?:^| |')")
219 def is_not_insult(chain):
220 chain=unicode(chain,"utf8")
221 insult_regexp=u"("+u"|".join(config_insultes)+u")"
222 middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
223 reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
224 if re.match(reg,chain):
225 return True
226 else:
227 return False
228 def is_compliment(chain,debug=True):
229 return is_something(chain,config_compliment_triggers,avant=".*(?:^| |')")
230 def is_perdu(chain):
231 return is_something(chain,config_perdu)
232 def is_tag(chain):
233 return is_something(chain,config_tag_triggers)
234 def is_tesla(chain):
235 return is_something(chain,config_tesla_triggers,avant=u"^",apres=u"$",debug=True)
236 def is_merci(chain):
237 return is_something(chain,config_merci_triggers)
238 def is_tamere(chain):
239 return is_something(chain,config_tamere_triggers)
240 def is_bad_action_trigger(chain,pseudo):
241 return is_something(chain,config_bad_action_triggers,avant=u"^",
242 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
243 def is_good_action_trigger(chain,pseudo):
244 return is_something(chain,config_good_action_triggers,avant=u"^",
245 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
246 def is_bonjour(chain):
247 return is_something(chain,config_bonjour_triggers,avant=u"^")
248 def is_bonne_nuit(chain):
249 return is_something(chain,config_bonne_nuit_triggers,avant=u"^")
250 def is_pan(chain):
251 return re.match(u"^(pan|bim|bang)( .*)?$",unicode(chain,"utf8").lower().strip())
252
253 def is_time(conf):
254 _,_,_,h,m,s,_,_,_=time.localtime()
255 return (conf[0],0,0)<(h,m,s)<(conf[1],0,0)
256 def is_day():
257 return is_time(config_daytime)
258 def is_night():
259 return is_time(config_nighttime)
260
261
262 class UnicodeBotError(Exception):
263 pass
264 def bot_unicode(chain):
265 try:
266 unicode(chain,"utf8")
267 except UnicodeDecodeError as exc:
268 raise UnicodeBotError
269
270 class Themis(ircbot.SingleServerIRCBot):
271 def __init__(self,serveur,debug=False):
272 temporary_pseudo=config_irc_pseudo+str(random.randrange(10000,100000))
273 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
274 temporary_pseudo,"Des[bot]e de #déprime", 10)
275 self.debug=debug
276 self.serveur=serveur
277 self.overops=config_overops
278 self.ops=self.overops+config_ops
279 self.report_bugs_to=config_report_bugs_to
280 self.chanlist=config_chanlist
281 self.kick_channels=config_kick_channels
282 self.stay_channels=config_stay_channels
283 self.quiet_channels=config_quiet_channels
284 self.last_perdu=0
285
286
287 def give_me_my_pseudo(self,serv):
288 serv.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo,config_irc_password))
289 serv.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo,config_irc_password))
290 time.sleep(0.3)
291 serv.nick(config_irc_pseudo)
292
293 def give_me_my_op_status(self,serv,chan):
294 serv.privmsg("ChanServ","OP %s"%(chan))
295
296 def on_welcome(self, serv, ev):
297 self.serv=serv # ça serv ira
298 self.give_me_my_pseudo(serv)
299 serv.privmsg("NickServ","identify %s"%(config_irc_password))
300 log(self.serveur,"Connected")
301 if self.debug:
302 self.chanlist=["#bot"]
303 self.kick_channels=["#bot"]
304 for c in self.chanlist:
305 log(self.serveur,"JOIN %s"%(c))
306 serv.join(c)
307 self.give_me_my_op_status(serv,c)
308
309
310 def lost(self,serv,channel,forced=False):
311 if self.last_perdu+config_time_between_perdu<time.time() or forced:
312 if not channel in self.quiet_channels or forced:
313 serv.privmsg(channel,"J'ai perdu !")
314 self.last_perdu=time.time()
315 delay=config_time_between_perdu_trigger
316 delta=config_time_between_perdu_trigger_delta
317 serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
318
319 def pourmoi(self, serv, message):
320 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
321 pseudo=self.nick
322 size=len(pseudo)
323 if message[:size]==pseudo and len(message)>size and message[size]==":":
324 return (True,message[size+1:].lstrip(" "))
325 else:
326 return (False,message)
327
328 def on_privmsg(self, serv, ev):
329 message=ev.arguments()[0]
330 auteur = irclib.nm_to_n(ev.source())
331 try:
332 test=bot_unicode(message)
333 except UnicodeBotError:
334 if config_utf8_trigger:
335 serv.privmsg(auteur, config_utf8_fail)
336 return
337 message=message.split()
338 cmd=message[0].lower()
339 notunderstood=False
340 if cmd=="help":
341 helpdico={"help":["""HELP <commande>
342 Affiche de l'aide sur la commande""",None,None],
343 "join": [None, """JOIN <channel>
344 Me fait rejoindre le channel""",None],
345 "leave": [None,"""LEAVE <channel>
346 Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
347 "quiet": [None,"""QUIET <channel>
348 Me rend silencieux sur le channel.""",None],
349 "noquiet": [None,"""NOQUIET <channel>
350 Me rend la parole sur le channel.""",None],
351 "say": [None,None,"""SAY <channel> <message>
352 Me fait parler sur le channel."""],
353 "do": [None,None,"""DO <channel> <action>
354 Me fait faitre une action (/me) sur le channel."""],
355 "stay": [None,None,"""STAY <channel>
356 Ajoute le channel à ma stay_list."""],
357 "nostay": [None,None,"""NOSTAY <channel>
358 Retire le channel de ma stay_list."""],
359 "ops": [None,None,"""OPS
360 Affiche la liste des ops."""],
361 "overops": [None,None,"""OVEROPS
362 Affiche la liste des overops."""],
363 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
364 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
365 "die": [None,None,"""DIE
366 Me déconnecte du serveur IRC."""]
367 }
368 helpmsg_default="Liste des commandes disponibles :\nHELP"
369 helpmsg_ops=" JOIN LEAVE QUIET NOQUIET LOST"
370 helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
371 op,overop=auteur in self.ops, auteur in self.overops
372 if len(message)==1:
373 helpmsg=helpmsg_default
374 if op:
375 helpmsg+=helpmsg_ops
376 if overop:
377 helpmsg+=helpmsg_overops
378 else:
379 helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
380 helpmsg=helpmsgs[0]
381 if op and helpmsgs[1]:
382 if helpmsg:
383 helpmsg+="\n"+helpmsgs[1]
384 else:
385 helpmsg=helpmsgs[1]
386 if overop and helpmsgs[2]:
387 if helpmsg:
388 helpmsg+="\n"+helpmsgs[2]
389 else:
390 helpmsg=helpmsgs[2]
391 for ligne in helpmsg.split("\n"):
392 serv.privmsg(auteur,ligne)
393 elif cmd=="join":
394 if auteur in self.ops:
395 if len(message)>1:
396 if message[1] in self.chanlist:
397 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
398 else:
399 serv.join(message[1])
400 self.chanlist.append(message[1])
401 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
402 log(self.serveur,"priv",auteur," ".join(message))
403 else:
404 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
405 else:
406 notunderstood=True
407 elif cmd=="leave":
408 if auteur in self.ops and len(message)>1:
409 if message[1] in self.chanlist:
410 if not (message[1] in self.stay_channels) or auteur in self.overops:
411 self.quitter(message[1]," ".join(message[2:]))
412 self.chanlist.remove(message[1])
413 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
414 else:
415 serv.privmsg(auteur,random.choice(config_leave_fail_messages))
416 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
417 else:
418 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
419 else:
420 notunderstood=True
421 elif cmd=="stay":
422 if auteur in self.overops:
423 if len(message)>1:
424 if message[1] in self.stay_channels:
425 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
426 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
427 else:
428 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
429 self.stay_channels.append(message[1])
430 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
431 else:
432 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
433 else:
434 notunderstood=True
435 elif cmd=="nostay":
436 if auteur in self.overops:
437 if len(message)>1:
438 if message[1] in self.stay_channels:
439 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
440 self.stay_channels.remove(message[1])
441 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
442 else:
443 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
444 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
445
446 else:
447 notunderstood=True
448 elif cmd=="die":
449 if auteur in self.overops:
450 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
451 self.mourir()
452 else:
453 notunderstood=True
454 elif cmd=="quiet":
455 if auteur in self.ops:
456 if len(message)>1:
457 if message[1] in self.quiet_channels:
458 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
459 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
460 else:
461 self.quiet_channels.append(message[1])
462 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
463 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
464 else:
465 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
466 else:
467 notunderstood=True
468 elif cmd=="noquiet":
469 if auteur in self.ops:
470 if len(message)>1:
471 if message[1] in self.quiet_channels:
472 self.quiet_channels.remove(message[1])
473 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
474 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
475 else:
476 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
477 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
478 else:
479 notunderstood=True
480 elif cmd=="say":
481 if auteur in self.overops and len(message)>2:
482 serv.privmsg(message[1]," ".join(message[2:]))
483 log(self.serveur,"priv",auteur," ".join(message))
484 elif len(message)<=2:
485 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
486 else:
487 notunderstood=True
488 elif cmd=="do":
489 if auteur in self.overops and len(message)>2:
490 serv.action(message[1]," ".join(message[2:]))
491 log(self.serveur,"priv",auteur," ".join(message))
492 elif len(message)<=2:
493 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
494 else:
495 notunderstood=True
496 elif cmd=="kick":
497 if auteur in self.overops and len(message)>2:
498 serv.kick(message[1],message[2]," ".join(message[3:]))
499 log(self.serveur,"priv",auteur," ".join(message))
500 elif len(message)<=2:
501 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
502 else:
503 notunderstood=True
504 elif cmd=="ops":
505 if auteur in self.overops:
506 serv.privmsg(auteur," ".join(self.ops))
507 else:
508 notunderstood=True
509 elif cmd=="overops":
510 if auteur in self.overops:
511 serv.privmsg(auteur," ".join(self.overops))
512 else:
513 notunderstood=True
514 else:
515 notunderstood=True
516 if notunderstood:
517 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
518
519 def on_pubmsg(self, serv, ev):
520 auteur = irclib.nm_to_n(ev.source())
521 canal = ev.target()
522 message = ev.arguments()[0]
523 try:
524 test=bot_unicode(message)
525 except UnicodeBotError:
526 if not canal in self.quiet_channels and config_utf8_trigger:
527 serv.privmsg(canal, "%s: %s"%(auteur,config_utf8_fail))
528 return
529 pour_moi,message=self.pourmoi(serv,message)
530 for (detect, reason) in config_kicking_list:
531 matching = detect(message)
532 if matching:
533 if canal in self.kick_channels:
534 serv.kick(canal,auteur,(reason.format(matching.groups()[0])).encode("utf8"))
535 return
536 if pour_moi and message.split()!=[]:
537 cmd=message.split()[0].lower()
538 try:
539 args=" ".join(message.split()[1:])
540 except:
541 args=""
542 if cmd in ["meurs","die","crève","pends-toi"]:
543 if auteur in self.overops:
544 log(self.serveur,canal,auteur,message+"[successful]")
545 self.mourir()
546 else:
547 serv.privmsg(canal,"%s: %s"%(auteur,random.choice(config_quit_fail_messages)))
548 log(self.serveur,canal,auteur,message+"[failed]")
549
550 elif cmd in ["part","leave","dégage","va-t-en"]:
551 if auteur in self.ops and (not (canal in self.stay_channels)
552 or auteur in self.overops):
553 self.quitter(canal)
554 log(self.serveur,canal,auteur,message+"[successful]")
555 if canal in self.chanlist:
556 self.chanlist.remove(canal)
557 else:
558 serv.privmsg(canal,"%s: %s"%(auteur,random.choice(config_leave_fail_messages)))
559 log(self.serveur,canal,auteur,message+"[failed]")
560
561 elif cmd in ["deviens","pseudo"]:
562 if auteur in self.ops:
563 become=args
564 serv.nick(become)
565 log(self.serveur,canal,auteur,message+"[successful]")
566
567 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
568 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
569 elif cmd in ["ping"] and not canal in self.quiet_channels:
570 serv.privmsg(canal,"%s: pong"%(auteur))
571 # if is_insult(message) and not canal in self.quiet_channels:
572 # if is_not_insult(message):
573 # answer=random.choice(config_compliment_answers)
574 # for ligne in answer.split("\n"):
575 # serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
576 # else:
577 # answer=random.choice(config_insultes_answers)
578 # for ligne in answer.split("\n"):
579 # serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
580 # elif is_compliment(message) and not canal in self.quiet_channels:
581 # answer=random.choice(config_compliment_answers)
582 # for ligne in answer.split("\n"):
583 # serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
584 if is_tesla(message) and not canal in self.quiet_channels:
585 l1,l2=config_tesla_answers,config_tesla_actions
586 n1,n2=len(l1),len(l2)
587 i=random.randrange(n1+n2)
588 if i>=n1:
589 serv.action(canal,l2[i-n1].encode("utf8"))
590 else:
591 serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
592 if is_tag(message) and not canal in self.quiet_channels:
593 if auteur in self.ops:
594 action=random.choice(config_tag_actions)
595 serv.action(canal,action.encode("utf8"))
596 self.quiet_channels.append(canal)
597 else:
598 answer=random.choice(config_tag_answers)
599 for ligne in answer.split("\n"):
600 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
601 if is_bonjour(message) and not canal in self.quiet_channels:
602 if is_night():
603 answer=random.choice(config_night_answers)
604 elif is_day():
605 answer=random.choice(config_bonjour_answers)
606 else:
607 answer=random.choice(config_bonsoir_answers)
608 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
609 if is_bonne_nuit(message) and not canal in self.quiet_channels:
610 answer=random.choice(config_bonne_nuit_answers)
611 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
612 else:
613 if not canal in self.quiet_channels:
614 mypseudo=self.nick
615 if re.match((u"^("+u"|".join(config_bonjour_triggers)
616 +u")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
617 ).format(mypseudo).lower(), message.strip().lower()):
618 answer=random.choice(config_bonjour_answers)
619 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
620
621 def on_action(self, serv, ev):
622 action = ev.arguments()[0]
623 auteur = irclib.nm_to_n(ev.source())
624 channel = ev.target()
625 try:
626 test=bot_unicode(action)
627 except UnicodeBotError:
628 if not channel in self.quiet_channels and config_utf8_trigger:
629 serv.privmsg(channel, "%s: %s"%(auteur,config_utf8_fail))
630 return
631 mypseudo=self.nick
632
633 # if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
634 # l1,l2=config_bad_action_answers,config_bad_action_actions
635 # n1,n2=len(l1),len(l2)
636 # i=random.randrange(n1+n2)
637 # if i>=n1:
638 # serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
639 # else:
640 # serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
641 # if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
642 # l1,l2=config_good_action_answers,config_good_action_actions
643 # n1,n2=len(l1),len(l2)
644 # i=random.randrange(n1+n2)
645 # if i>=n1:
646 # serv.action(channel,l2[i-n1].format(auteur).format(auteur).encode("utf8"))
647 # else:
648 # serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
649
650 def on_kick(self,serv,ev):
651 auteur = irclib.nm_to_n(ev.source())
652 channel = ev.target()
653 victime = ev.arguments()[0]
654 raison = ev.arguments()[1]
655 if victime==self.nick:
656 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
657 time.sleep(2)
658 serv.join(channel)
659 username = irclib.nm_to_u(ev.source()).lower()
660 print ev.source()
661 print channel, username
662 if channel == "#déprime" and "peb" in username or "becue" in username:
663 time.sleep(5)
664 serv.kick(auteur, "Va abuser de tes droits ailleurs !")
665
666 def kicker(self, chan, pseudo, raison=None):
667 if raison==None:
668 raison = config_kick_default_reason
669 self.serv.kick(chan,pseudo,raison)
670
671 def quitter(self,chan,leave_message=None):
672 if leave_message==None:
673 leave_message=random.choice(config_leave_messages)
674 self.serv.part(chan,message=leave_message.encode("utf8"))
675
676 def mourir(self):
677 quit_message=random.choice(config_quit_messages)
678 self.die(msg=quit_message.encode("utf8"))
679
680 def _getnick(self):
681 return self.serv.get_nickname()
682 nick=property(_getnick)
683
684
685 if __name__=="__main__":
686 import sys
687 if len(sys.argv)==1:
688 print "Usage : themis.py <serveur> [--debug]"
689 exit(1)
690 serveur=sys.argv[1]
691 if "debug" in sys.argv or "--debug" in sys.argv:
692 debug=True
693 else:
694 debug=False
695 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
696 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
697 try:
698 serveur=serveurs[serveur]
699 except KeyError:
700 print "Server Unknown : %s"%(serveur)
701 exit(404)
702 themis=Themis(serveur,debug)
703 themis.start()