Introduce networkbot and botcaps

This commit is contained in:
Némunaire 2012-08-28 00:27:02 +02:00
parent 52995e3f0e
commit ab6334127f
5 changed files with 196 additions and 6 deletions

25
bot.py
View File

@ -20,26 +20,32 @@ from datetime import datetime
from queue import Queue
import threading
from botcaps import BotCaps
from consumer import Consumer
import event
import hooks
from networkbot import NetworkBot
from server import Server
class Bot:
class Bot(BotCaps):
def __init__(self, servers=dict(), modules=dict(), mp=list()):
self.version = 3.2
self.version_txt = "3.2"
BotCaps.__init__(self, 3.2, "3.2-dev")
# Keep global context: servers and modules
self.servers = servers
self.modules = modules
# Context paths
self.modules_path = mp
self.datas_path = './datas/'
self.hooks = hooks.MessagesHook()
# Events
self.events = list()
self.event_timer = None
# Other known bots, making a bots network
self.network = dict()
# Messages to be treated
self.msg_queue = Queue()
self.msg_thrd = list()
self.msg_thrd_size = -1
@ -74,7 +80,6 @@ class Bot:
#else:
# print ("Update timer: no timer left")
def end_timer(self):
"""Function called at the end of the timer"""
#print ("end timer")
@ -149,6 +154,14 @@ class Bot:
self.msg_thrd_size += 2
def add_networkbot(self, srv, dest, dcc=None):
"""Append a new bot into the network"""
id = srv.id + "/" + dest
if id not in self.network:
self.network[id] = NetworkBot(self, srv, dest, dcc)
return self.network[id]
def quit(self, verb=False):
"""Save and unload modules and disconnect servers"""
if self.event_timer is not None:

28
botcaps.py Normal file
View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Nemubot is a modulable IRC bot, built around XML configuration files.
# Copyright (C) 2012 Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import hooks
class BotCaps:
def __init__(self, version=None, version_txt=None):
# Bot general informations
self.version = version
self.version_txt = version_txt
# Hooks
self.hooks = hooks.MessagesHook()

View File

@ -16,9 +16,20 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from networkbot import NetworkBot
nemubotversion = 3.2
NODATA = True
def getserver(toks, context, prompt):
"""Choose the server in toks or prompt"""
if len(toks) > 1 and toks[0] in context.servers:
return (context.servers[toks[0]], toks[1:])
elif prompt.selectedServer is not None:
return (prompt.selectedServer, toks)
else:
return (None, toks)
def close(data, toks, context, prompt):
"""Disconnect and forget (remove from the servers list) the server"""
if len(toks) > 1:
@ -64,6 +75,19 @@ def disconnect(data, toks, context, prompt):
else:
print (" Please SELECT a server or give its name in argument.")
def discover(data, toks, context, prompt):
"""Discover a new bot on a server"""
(srv, toks) = getserver(toks, context, prompt)
if srv is not None:
for name in toks[1:]:
if "!" in name:
bot = context.add_networkbot(srv, name)
bot.connect()
else:
print (" %s is not a valid fullname, for example: nemubot!nemubotV3@bot.nemunai.re")
else:
print (" Please SELECT a server or give its name in first argument.")
def hotswap(data, toks, context, prompt):
"""Reload a server class"""
if len(toks) > 1:

View File

@ -2,6 +2,7 @@
<nemubotmodule name="cmd_server">
<command name="close" call="close" />
<command name="connect" call="connect" />
<command name="discover" call="discover" />
<command name="disconnect" call="disconnect" />
<command name="hotswap" call="hotswap" />
<command name="join" call="join" />

124
networkbot.py Normal file
View File

@ -0,0 +1,124 @@
# -*- coding: utf-8 -*-
# Nemubot is a modulable IRC bot, built around XML configuration files.
# Copyright (C) 2012 Mercier Pierre-Olivier
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import random
import shlex
from botcaps import BotCaps
from DCC import DCC
class NetworkBot:
def __init__(self, context, srv, dest, dcc=None):
self.context = context
self.srv = srv
self.dcc = dcc
self.dest = dest
self.infos = None
self.my_tag = random.randint(0,255)
self.inc_tag = 0
self.tags = dict()
@property
def sender(self):
if self.dcc is not None:
return self.dcc.sender
return None
@property
def nick(self):
if self.dcc is not None:
return self.dcc.nick
return None
@property
def realname(self):
if self.dcc is not None:
return self.dcc.realname
return None
def send_cmd(self, cmd, data=None):
"""Create a tag and send the command"""
# First, define a tag
self.inc_tag = (self.inc_out + 1) % 256
while self.inc_tag in self.tags:
self.inc_tag = (self.inc_out + 1) % 256
tag = ("%c%c" % (self.my_tag, self.inc_tag)).encode()
if data is not None:
self.tags[tag] = data
else:
self.tags[tag] = cmd
# Send the command with the tag
self.send_response(tag, cmd)
def send_response(self, tag, msg):
"""Send a response with a tag"""
for line in msg.split("\n"):
self.dcc.send_dcc_raw(tag + b' ' + line.encode())
def send_ack(self, tag):
"""Acknowledge a command"""
if tag in self.tags:
del self.tags[tag]
self.send_response(tag, "ACK")
def connect(self):
"""Making the connexion with dest through srv"""
if self.dcc is None:
self.dcc = DCC(self.srv, self.dest)
self.dcc.treatement = self.hello
self.dcc.send_dcc("NEMUBOT###")
def disconnect(self, reason=""):
"""Close the connection and remove the bot from network list"""
del self.context.network[self.dcc.id]
self.dcc.send_dcc("DISCONNECT :%s" % reason)
self.dcc.disconnect()
def hello(self, line):
if line == b'NEMUBOT###':
self.dcc.treatement = self.treat_msg
self.send_cmd("MYTAG %c" % self.my_tag)
self.send_cmd("FETCH")
elif line != b'Hello ' + self.srv.nick.encode() + b'!':
self.disconnect("Sorry, I think you were a bot")
def treat_msg(self, line, cmd=None):
print (line)
words = line.split(b' ')
# Ignore invalid commands
if len(words) >= 2:
tag = words[0]
cmd = words[1]
if len(words) > 2:
args = shlex.split(line[len(tag) + len(cmd) + 2:].decode())
else:
args = list()
# Parse
if cmd == b'ACK':
if tag in self.tags:
del self.tags[tag]
elif cmd == b'MYTAG' and len(args) > 0:
while args[0] == self.my_tag:
self.my_tag = random.randint(0,255)
self.send_ack(tag)