Le bot irc du salon #z sur teepi (et autres).
Notez qu’il ne fait rien à part transmettre les messages de réseau en réseau (mais c’est son seul but aussi…)
par contre ça peut vous aider si vous êtes en manque d’inspiration de code python pour l’irc.
Je ne répond de rien si le bot plante chez vous, il a été programmé en 2 jours et ya pas de syncro sur les threads (plus particulièrement pas de section critique sur les opérations touchant à la liste des threads, seul point litigieux imho)
si vous n’aimez pas le frisson des threads hasardeux (hey, c’est un bot irc…) vous pouvez les rajouter.
plinklib.py
# -*- coding: UTF-8 -*-
import threading
import select
import socket
import sys
import time
import re
from time import gmtime, strftimeclass Logger(object):
def __init__(self, filename="default.txt"):
self.filename=filename
def start(self):
self.logfile = open(self.filename,'a')
self.logfile.write(strftime("Session Start: %a %b %d %H:%M:%S %Y", gmtime())+"\n")
def log(self, msg):
self.logfile.write(strftime("[%H:%M]“, gmtime()) + ” ” + msg + “\n”)
def flush(self):
self.logfile.flush()
class Auth(object):
accounts = {}
def __init__(self):
pass
def addaccount(self, nick, password, level):
self.accounts[nick] = {”password” : password,
“level” : level,
“mask” : “”}
def login(self, nick, password, mask):
for account in self.accounts:
if account == nick:
if self.accounts[account]["password"] == password:
self.accounts[account]["mask"] = mask
return True
return False
def hasright(self, mask, level):
for account in self.accounts:
if self.accounts[account]["mask"] == mask:
# we got our man !
if self.accounts[account]["level"] >= level:
return True
else:
print “l’utilisateur \”"+account+”\” a tenté d’effectuer une commande interdite”
return False
class Plink(object):
def __init__(self):
self.networks = {}
self.running = False
self.auth = Auth()
self.logger = Logger(”logs.txt”)
def addnetwork(self, config):
“”" creation d’un nouveau thread “”"
# thread de type ThreadClient (voir classe ci-bas)
self.networks[config[0]] = ThreadClient(self, self.networks, config)
def dynamicadd(self, config):
“”" creation d’un nouveau thread “”"
# thread de type ThreadClient (voir classe ci-bas)
self.networks[config[0]] = ThreadClient(self, self.networks, config)
self.networks[config[0]].start()
def getnetworks(self):
“”" retourne une liste de réseaux colorés “”"
netlist = “”
for net in self.networks:
if self.networks[net]:
# si il est pas supprimé (c’est ou cette cond, ou une section critique, alors…)
netlist += “%s%s ” % (self.networks[net].color, net)
return netlist
def start(self):
“”" Démarrage des threads “”"
self.running = True
self.logger.start()
self.logger.log(”*** Now talking in #z”) # fake évidemment ^^
for network in self.networks:
if self.networks[network]:
self.networks[network].start()
while self.running:
time.sleep(2)
sys.stdout.flush() # flush du log debug plink (console)
for network in self.networks:
if self.networks[network]:
self.networks[network].flush() # flush des logs des reseaux
self.logger.flush() # flush du log Eggdrop IRC
exit(0)
def quit(self, msg=”"):
print “fermeture…”
for network in self.networks:
self.networks[network].quit(msg)
self.networks[network] = None # pour que la taille de networks ne change pas
self.running = False
def networkclose(self, network):
self.networks[network].quit(”err”)
self.networks[network] = None
print “[PLINK] Réseau \”"+network+”\” supprimé.”
class ThreadClient(threading.Thread):
“”" Classe de thread “”"
networks = {}
username = “plink”
realname = “plink :)”
connected = False
running = False
onchan = False
def __init__(self, plink, networks, (network, color, server, port, chan, nick)):
threading.Thread.__init__(self)
self.plink = plink
self.networks = networks # dico des threads en cours
self.network = network
self.color = color
self.port = port
self.server = server
self.chan = chan
self.nick = nick
self.log = open(network+”.txt”, “wb”)
# credits for the regex belong to Python-IRC.py (see on google)
# minors corrections and ehancements by myself
# : optionnel (pour les notices ?)
self.svrmsg = re.compile(’^:([a-zA-Z0-9\.]+) ([0-9]+) ‘+ nick + ‘ :?(.*)$’)
self.chanmsg = re.compile(’^:(.+)![~]?(.+)@(.+) PRIVMSG (#?[azA-Z0-9_\\\-`\[\]{}()]+) :(.*)$’)
# notice, modes etc
self.genmsg = re.compile(’^:(.+)!~?(.+)@([a-zA-Z0-9\-\.]+) ([azA-Z0-9_\\\-`\[\]{}()]+) :?(.*)$’)
self.pingmsg = re.compile(’^PING :(.*)$’, re.IGNORECASE)
# regex des commandes “fun”
self.funkick = re.compile(’^’ + self.nick + ‘,? ?(?:tu peux)? ?(?:éjecter|ejecter|kick|kicker|quick|faire sortir)? ?(?:ce con de|ce con d\’) ?([a-z0-9_\-`]{1,32}) ?(?:sur ([a-z]{2,32}))? ?(?:stp|please|svp|s\’il te plait)’, re.IGNORECASE)
self.fungunther = re.compile(’gunther’,re.IGNORECASE)
def connect(self):
print “["+self.network+"] connection…”
self.sock = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
self.sock.settimeout(60)
self.sock.connect((self.server, self.port))
def logging(self):
# logging
print “["+self.network+"] identification…”
time.sleep(0.5)
self.send(’NICK %s’ % self.nick)
time.sleep(0.5)
self.send(’USER %s idk %s :%s’ % (self.username, self.server, self.realname))
time.sleep(1)
def join(self):
print “["+self.network+"] join…”
self.send (’JOIN %s’ % self.chan)
# verifier si on est bien dessus
self.onchan = True
def send(self, msg):
“”" envoi un message RAW sur le socket “”"
if self.sock:
self.sock.send(msg + “\n”)
# bon, pas besoin de debugger la sortie enfait…
#self.debug(”[OUT] ” + msg)
else:
print “erreur, impossible d’envoyer le msg, le socket est mort”
#self.log.write(”[ERR] erreur, impossible d’envoyer le msg, le socket est mort\n”)
def sendchan(self, msg):
“”" envoi un message au salon géré par le thread “”"
if self.onchan:
self.send(’PRIVMSG %s :%s’ % (self.chan, msg))
else:
pass
# on ne garde pas en mémoire
def sendallchan(self, msg):
“”" envoi un message à tout les salons du bot (utile pour les commandes “fun”) “”"
self.sendchan(msg)
self.sendothers(msg)
def sendothers(self, msg):
“”" envoi un message sur tous les autres réseaux “”"
for network in self.networks:
if network != self.network:
if self.networks[network]:
self.networks[network].sendchan(msg)
def quit(self,msg):
print “["+self.network+"] déconnection…”
self.send(”QUIT :%s” % msg)
time.sleep(1)
#self.sock.close()
self.running = False
print “["+self.network+"] arrêt du thread…”
def debug(self, debugmsg):
if self.log:
self.log.write(debugmsg + “\n”)
else:
print “Erreur, impossible d’écrire le log !”
def flush(self):
if self.log:
self.log.flush()
def processing(self, raw_msg):
for line in raw_msg.splitlines():
# GESTION DES MESSAGES SERVEUR
if self.svrmsg.match(line):
parse = self.svrmsg.match(line)
# lors de la reception du MOTD (ou si il n’existe pas)
if parse.group(2) == “376″ or parse.group(2) == “422″:
self.debug(”Le serveur est prêt -> join()”)
self.join()
else:
self.debug(”[IN:SERVER_MSG:"+parse.group(2)+"] “+line)
# GESTION DES MESSAGES PRIVEES/SALON
elif self.chanmsg.match(line):
parse = self.chanmsg.match(line)
mots = parse.group(5).split(” “)
login = parse.group(1)
mask = parse.group(1)+”!”+parse.group(2)+”@”+parse.group(3)
## messages privés
if parse.group(4) == self.nick:
self.debug(”[IN:PRIV_MSG:FROM "+parse.group(1)+"] “+ parse.group(5))
if mots[0].upper() == “QUIT” and self.plink.auth.hasright(mask, 1000):
self.plink.quit(”bye bye”)
# authentification des utilisateurs
if (len(mots) > 1) and (mots[0].upper() == “IDENTIFY”):
self.debug(”[AUTH REQUEST] “+parse.group(1))
if self.plink.auth.login(login, mots[1], mask):
self.debug(”[AUTH OK] “+parse.group(1))
self.send(”PRIVMSG ” + parse.group(1) + ” :” + “AUTHENTIFICATION OK”)
else:
# CHANNEL MSG
#self.debug(”[IN:CHAN_MSG:"+parse.group(4)+"] “+ parse.group(5))
if mots[0] == “ACTION”:
self.plink.logger.log(”*** %s@%s %s” % (parse.group(1), self.network, ” “.join(mots[1:])))
self.sendothers(”ACTION * %s %s”
% (parse.group(1), ” “.join(mots[1:])))
else:
self.plink.logger.log(”<%s&%s> %s” % (parse.group(1), self.network, parse.group(5)))
self.sendothers(’<%s%s> %s’ % ( self.color, parse.group(1), parse.group(5)))
if mots[0] == “!networks”:
self.send(”PRIVMSG ” + parse.group(1) + ” :”+ self.plink.getnetworks())
if mots[0] == “!boulet” and self.plink.auth.hasright(mask, 10):
self.sendallchan(mots[1] + ” est un boulet !”)
if mots[0] == “!python” and self.plink.auth.hasright(mask,9000):
print “["+self.network+"] execution de \”" + ” “.join(mots[1:]) + “\” en cours…”
exec(” “.join(mots[1:]))
if mots[0] == “!eval” and self.plink.auth.hasright(mask,8000):
print “["+self.network+"] evaluation de \”" + ” “.join(mots[1:]) + “\” en cours…”
ret = eval(” “.join(mots[1:]))
self.sendallchan(ret)
if self.fungunther.match(parse.group(5)):
self.sendallchan(”ooh, you touch my tralala…”)
if self.funkick.match(parse.group(5)) and self.plink.auth.hasright(mask, 50):
cmd = self.funkick.match(parse.group(5))
msg = “I am the alpha and the omega”
if cmd.group(2):
print “kick inter reseau”
#self.send(”KICK ” + self.chan + ” “+ cmd.group(2) + “I am the operator”)
else:
print “kick normal.”
time.sleep(3)
self.sendchan(”" + cmd.group(1) + “, I’m soooo confused :(”)
time.sleep(3)
self.sendchan(”But you must left.”)
time.sleep(1)
self.send(”KICK ” + self.chan + ” “+ cmd.group(1) + ” ” + msg)
time.sleep(3)
self.sendchan(”Vous savez, “+cmd.group(1) + ” le méritait.”)
# GESTION DES MESSAGES D’INFO
elif self.genmsg.match(line):
parse = self.genmsg.match(line)
if parse.group(4).upper() == “QUIT”:
self.plink.logger.log(”*** %s&%s has left %s (%s)”
% (parse.group(1), self.network, self.chan, parse.group(5)))
self.sendothers(”ACTION 3* %s%s3 est parti (%s)”
% (self.color, parse.group(1), parse.group(5)))
elif parse.group(4).upper() == “JOIN”:
self.plink.logger.log(”*** %s&%s (%s@%s) has joined %s”
% (parse.group(1), self.network,parse.group(2),parse.group(3),self.chan))
self.sendothers(”ACTION 3* %s%s3 vient d’entrer sur %s%s”
% (self.color, parse.group(1), self.color, self.network))
elif parse.group(4).upper() == “NICK”:
self.plink.logger.log(”*** %s&%s is now known as %s&%s”
% (parse.group(1), self.network,parse.group(5),self.network))
self.sendothers(”ACTION 3* %s%s3 s’apelle maintenant <%s%s3>”
% (self.color, parse.group(1),self.color, parse.group(5)))
else:
self.debug(”[IN:ERR:UNKNOW_GENERAL_MSG] “+ line)
# GESTION DES PINGS
elif self.pingmsg.match(line):
parse = self.pingmsg.match(line)
self.sock.send(’PONG :’ + parse.group(1)+’\n’)
else:
self.debug(”[IN:ERR:NO_MATCH!] “+ line)
def run(self):
self.running = True
self.connect()
self.logging()
while self.running:
is_readable = [self.sock]
is_writable = []
is_error = [self.sock]
r, w, e = select.select(is_readable, is_writable, is_error, 1.0)
if e:
print “erreur, connection coupée !”
self.plink.networkclose(self.network) # on demande à plink de nous arrêter/supprimer
elif r:
data = self.sock.recv(8912)
if data:
self.processing(data)
else:
print “["+self.network+"] buffer vide ! running -> false”
self.plink.networkclose(self.network) # on demande à plink de nous arrêter/supprimer
self.sock.close() # erreur!
et voilà le fichier “plink”, qui permet de lancer la bestiole :
plink
#!/usr/bin/python# -*- coding: UTF-8 -*-
__author__ = "Julian `uxmal` Bilcke"
__date__ = "28 Aug 2007"
__version__ = "$Revision: 3.1.0 $"
__appname__ = "Plink 3.1.0"
__credits__ = "mwa"
import time
from plinklib import Plink
PORT = 6667
CHANNEL = "#monjardin"
BOTNAME = "courgette"
networks = (
("teepi", 7, "irc.teepi.net", PORT, CHANNEL, BOTNAME),
#("langochat", 12, "irc.langochat.net", PORT, CHANNEL, BOTNAME),
#("ircube", 9, "irc.ircube.org", PORT, CHANNEL, BOTNAME),
#("p0w0w", 15, "irc.p0w0w.org", PORT, CHANNEL, BOTNAME),
)plink = Plink()
for network in networks:
plink.addnetwork(network)
time.sleep(1) # pour pas flooder de messages "xxx a rejoint yyy"
plink.auth.addaccount("tomate","fraise",9999)
plink.auth.addaccount("celeri","banane",9999)
plink.start()
# désolé ça s'affiche pas très bien le code python là dessus..
Il reste encore des trucs à régler, notamment un timing pour forcer le /join du bot si le serveur est la réincarnation d’une carpe,
et heu.. d’autres trucs sur la syncro (corriger le ACTION aussi)
Bon, je me rapelle plus exactement (2 mois ça fait longtemps) mais je crois que le bot créer un fichier de log compatible eggdrop, gère l’identification des gens et permet de kicker à distance (si on est identifié).
Il peut aussi executer du code python à la volée (mais attention les bugs sont létaux)
0 Responses to “Bot irc multi-réseaux en python”
Leave a Reply