Bot démocratique

Contenu du snippet

Bot IRC destiné à la gestion et la modération du salon par les utilisateurs.

Il permet à n'importe-qui de lancer un vote demandant une action (kick, ban, kickban, unban, topic), auquel les utilisateurs peuvent répondre.

S'il y a une majorité de 'oui', le bot effectue l'action.

Son point fort est qu'il permet donc une gestion du salon sans nécessiter d'opérateur ;

son point faible est qu'il ne possède pas d'anti-flood.

Il est donc conseillé de le coupler à un bot de modération qui lui, gérera le flood et les attaques de clones éventuelles.

Source / Exemple :


#!/usr/bin/python
# -*- coding: latin-1 -*-

import os
import socket
import ConfigParser
import string
import re
import time
import threading

class config:

	def __init__(self):
		"""Generate the bot config"""
		conf = '''[Server]
# IRC Server
serv = irc.??
# port
port = 6667

[Bot]
nick = DBot
ident = dbot
realname = Democratic Bot

[Salon]
chan = #chan
		
[Owner]
# Nick(s) of the owner(s) (use comma to separate nicks)
owner = YaCoU, bot
		
[Misc]
# Voting time (seconds)
tmp = 10
# Trigger character (e.g. : !kick)
trigger = !
# Ban type (nick or ident or host)
bantype = host
		
[Colors]
# And for the lame, colors ! (^K : , ^B : )
color_1 =
color_2 =
'''

		f = open('dbot.cfg','a')
		f.write(conf)
		raw_input('Error : Config file missing ! It have been automaticly generated, now, fill it !\nPress enter to continue...')
		sys.exit()

class general:
	
	
	def __init__(self):
		"""initialize some required vars"""
		self.conf()
		self.readbuffer = ''
		self.sock = socket.socket()
		self.regOp = re.compile(':?[~@&]+' + self.NICK)
		regStrip = re.compile("\x03(?:\d{1,2}(?:,\d{1,2})?)?", re.UNICODE) # For catching mIRC's colors code like ^B^K12,3[text]
		self._strip = lambda msg: string.lower(regStrip.sub('', msg).strip(chr(15)).strip(chr(22)).strip(chr(31)))
		
	def conf(self):
		"""check if the config file is complete"""
		if os.path.isfile('dbot.cfg'): 
			self.conf = ConfigParser.ConfigParser()
			self.conf.read('dbot.cfg')
			try:
				self.SERV = self.conf.get('Server', 'serv')
				if self.SERV == '' or self.SERV == 'irc.??':
					raise ValueError('server')
				self.PORT = int(self.conf.get('Server', 'port'))
				if self.PORT == '':
					raise ValueError('port')
				self.NICK = self.conf.get('Bot', 'nick')
				if self.NICK == '':
					raise ValueError('pseudo')
				self.IDENT = self.conf.get('Bot', 'ident')
				if self.IDENT == '':
					raise ValueError('ident')
				self.REALNAME = self.conf.get('Bot', 'realname')
				if self.REALNAME == '':
					raise ValueError('real name')
				self.CHAN = self.conf.get('Salon', 'chan')
				if self.CHAN == '':
					raise ValueError('salon')				
				self.owner = self.conf.get('Owner', 'owner')
				if self.owner == '':
					raise ValueError('owner')
				self.BANTYPE = self.conf.get('Misc', 'bantype')
				if self.BANTYPE == '':
					raise ValueError('bantype')
				self.tmpVote = self.conf.get('Misc', 'tmp')
				if self.tmpVote == '':
					raise ValueError('temps de vote')
				self.TRIGGER = self.conf.get('Misc', 'trigger')
				if self.TRIGGER == '':
					raise ValueError('trigger')
				self.COLORS = [self.conf.get('Colors', 'color_1'), self.conf.get('Colors','color_2')]
			
			except ConfigParser.NoSectionError, err: # If a section miss
				raw_input('Error : No section named ' + str(err).split()[2] + '.\nPress enter to continue...')
				sys.exit()
			except ValueError, err: # If a value miss
				raw_input('Error : No data in section \'' + str(err) + '\'.\nPress enter to continue...')
				sys.exit()
		else:
			config()
	
	def connection(self):
		""" connect bot to the server"""
		try:
			self.sock.connect((self.SERV, self.PORT))
			self.sock.send('NICK %s\r\n' % self.NICK)
			self.sock.send('USER %s %s dbot :%s\r\n' % (self.IDENT, self.SERV, self.REALNAME))
		except:
			raw_input('Error : can\'t connect to IRC server, check configuation and internet connection.\nPress enter to continue...')
			sys.exit()
			
	def tab2str(self, tab):
		"""return a str from a list"""
		result = ''
		for word in tab:
			result += word + chr(32)
		return result[:-1]

class Timer(threading.Thread):

	def __init__(self, parent, name=''):
		threading.Thread.__init__(self)
		self.irc = parent
		self.gen = parent.gen
		self.name = name
		self.statut = 0
		
	def run(self):
		if self.name <> '': # If no name has be sent, it's the method instanciation
			self.irc.msg(self.gen.CHAN, '{0}Vote now ! "{0}{1}vote yes{1}{0}" or "{0}{1}vote no{1}{0}" >{0}{1} {3}s{1}{0} <.'.format(self.gen.COLORS[0], self.gen.COLORS[1], self.gen.TRIGGER, self.gen.tmpVote))
			self.statut = 1
			if self.gen.tmpVote > 5:
				time.sleep(int(self.gen.tmpVote) - 5)
				self.irc.msg(self.gen.CHAN, '{1}{0} 5{1}{0} seconds left to vote !'.format(self.gen.COLORS[0], self.gen.COLORS[1]))
				time.sleep(5)
			else :
				time.sleep(int(self.gen.tmpVote))
			self.statut = 0
			self.irc.vote()

		
class IRC:

	def __init__(self):
		self.gen = general()
		print 'Configuration loaded'
		self.gen.connection()
		print 'Connection...'
		self.threadCount = 0
		self.avert = None
		self.sock = self.gen.sock
		self.cmd()
		self.timer = Timer(self)
		self.nickList = []
		self.kickedNick = None
		self.bannedNick = None
		self.readbuffer = self.gen.readbuffer
		self.join(self.gen.CHAN)
		self.tVote = ['kick', 'ban', 'kickban', 'unban', 'topic']
		self.VCount = 0
		self.votedAction = None
		self.paused = None
		self.whoVote = []
		self.waitingNicks = []

	def cmd(self):
		"""IRC commands"""
		self.join = lambda chan: self.sock.send('JOIN %s\r\n' % chan)
		self.msg = lambda target, msg: self.sock.send('PRIVMSG %s :%s\r\n' % (target, msg))
		self.notice = lambda target, msg: self.sock.send('NOTICE %s :%s\r\n' % (target, msg))
		self.names = lambda chan: self.sock.send('NAMES %s\r\n' % chan)
		self.whois = lambda nick: self.sock.send('WHOIS %s %s\r\n' % (nick, nick))
		self.kick = lambda chan, nick, reason='': self.sock.send('KICK %s %s :%s\r\n' % (chan, nick, reason))
		self.ban = lambda chan, mask: self.sock.send('MODE %s +b %s\r\n' % (chan, mask))
		self.unban = lambda chan, mask: self.sock.send('MODE %s -b %s\r\n' % (chan, mask))
		self.topic = lambda chan, topic: self.sock.send('TOPIC %s :%s\r\n' % (chan, topic))
		
	def vote(self):
		if self.VCount > 0:
			action = self.votedAction[0]
			firstArg = self.votedAction[1][0]
			strippedFirstArg = self.gen._strip(self.votedAction[1][0])
			secondArg = self.gen.tab2str(self.votedAction[1][1:])
			self.msg(self.gen.CHAN, '{0}{1}Yes{1}{0} wins !'.format(self.gen.COLORS[0], self.gen.COLORS[1]))
			if action == 'kick':
				if strippedFirstArg <> string.lower(self.gen.NICK):
					self.kick(self.gen.CHAN, strippedFirstArg, secondArg)
				else:
					self.msg(self.gen.CHAN, 'Nice try !')
			
			elif action == 'ban':
				self.bannedNick = strippedFirstArg
				self.whois(self.bannedNick)
				
			elif action == 'kickban':
				if strippedFirstArg <> string.lower(self.gen.NICK):
					self.kick(self.gen.CHAN, strippedFirstArg, secondArg)
				else:
					self.msg(self.gen.CHAN, 'Nice try !')
				self.bannedNick = strippedFirstArg
				self.whois(self.bannedNick)
				
			elif action == 'unban':
				self.unban(self.gen.CHAN, strippedFirstArg)
				
			elif action == 'topic':
				self.topic(self.gen.CHAN, firstArg + ' ' + secondArg)
		else:
			self.msg(self.gen.CHAN, '{0}{1}No{1}{0} wins !'.format(self.gen.COLORS[0], self.gen.COLORS[1]))

		self.votedAction = None
		self.VCount = 0
		self.whoVote = []
		self.waitingNicks = []

	def debug(self, text):
		"""display all the lines except whois and names"""
		rawsToIgnore = ['379', '378', '319', '312', '313', '310', '317', '311', '307', '318', '366', '353']
		if text[1] not in rawsToIgnore:
			print self.gen._strip(self.gen.tab2str(text[1:])) 
		else:
			pass
			
	def run(self):
		"""main loop"""
		while 1:
			self.readbuffer = self.readbuffer + self.sock.recv(1024)
			if not self.readbuffer:
				break

			else:
				tmp = string.split(self.readbuffer,'\n')
				self.readbuffer = tmp.pop()

				for line in tmp:
					line = string.rstrip(line)
					line = string.split(line)
					# Complete debug : print line
					if line[0]=='PING':
						self.sock.send('PONG %s\r\n' % line[1])						
					elif len(line) > 2:
						self.debug(line)
						if line[1] == '311': # Whois
							if self.bannedNick:
								if line[3] <> self.gen.NICK:
									if self.gen.BANTYPE == 'nick':
										mask = line[3] + '*!*@*'
									elif self.gen.BANTYPE == 'ident':
										mask = '*!' + line[4]
									else:
										mask = '*!*@' + line[5]
									self.ban(self.gen.CHAN, mask)
									self.bannedNick = None
								
							elif self.timer.statut <> 0:
								if line[5] not in self.whoVote:
									if line[3] == self.waitingNicks[0][0]:
										self.whoVote.append(line[5])
										self.VCount += 1 if self.waitingNicks[0][1] == 'yes' else -1
										self.notice(self.waitingNicks[0][0], 'Vote cast !')
										self.waitingNicks.pop(0)
								else :
									self.notice(self.waitingNicks[0][0], 'You have already voted !')
									
						elif len(line) >= 3:
							print self.gen._strip(self.gen.tab2str(line[2:])) # D
							if len(line) >= 4 and line[1] == 'PRIVMSG':
								nick = line[0].split('!')[0].split(':')[1]
								if line[2] == self.gen.CHAN:
									if len(line[3]) >= 3:
										if line[3][:2] == ':' + self.gen.TRIGGER:
											command = self.gen._strip(line[3][2:])
											# Commands to execute only is bot isn't paused
											if command == 'vote' and len(line) >= 5 and self.paused is None:
												vote = self.gen._strip(line[4])
												if (self.timer.statut <> 0 and (vote == 'yes' or vote == 'no')):
													self.waitingNicks.append([nick, vote])
													self.whois(nick)
												else :
													if self.timer.statut == 0:
														if self.gen._strip(line[4]) in self.tVote:
															self.votedAction = (self.gen._strip(line[4]), line[5:])
															self.timer = Timer(self, self.threadCount)
															self.timer.start()
															self.threadCount += 1
													else :
														self.notice(nick, 'Vote already in progress !')
											# Only owners commands working even the bot is paused
											if command == 'quit' and nick in self.gen.owner:
												self.sock.send('QUIT :Ddisconnected\r\n')
												sys.exit('success')
										
											elif command == 'pause' and nick in self.gen.owner:
												if self.paused is not None:
													if self.timer.statut == 0:
														self.paused = None
														self.sock.send('AWAY\r\n')
														self.msg(self.gen.CHAN, 'Bot reactivated.')
													else:
														self.notice(nick, 'Please wait while the vote is in progress to pause the bot.')
												else:
													self.paused = 1
													self.sock.send('AWAY :Bot paused.\r\n')
													self.msg(self.gen.CHAN, 'Bot paused.')
											# Commands working event the bot is paused
											elif command == 'help' and len(line) >= 4:
												self.notice(nick, 'Bot commands : !vote <kick, ban, kickban, unban, topic> <params> (nick or reason or topic) to call a vote.')
												self.notice(nick, '!vote [yes, no] pour to vote.')

										
go = IRC()
go.run()

Conclusion :


Je suis un éternel débutant, je sais que l'imbrication de conditions est bof,
soyez indulgents sur ce point, sinon, je suis ouvert à toute critique.

Je voulais mettre un zip avec le programme convertit en .exe par py2exe, mais ça rentre pas.

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.

Du même auteur (cs_YaCoUbA)