Introduce networkbot and botcaps
This commit is contained in:
parent
52995e3f0e
commit
ab6334127f
25
bot.py
25
bot.py
@ -20,26 +20,32 @@ from datetime import datetime
|
|||||||
from queue import Queue
|
from queue import Queue
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from botcaps import BotCaps
|
||||||
from consumer import Consumer
|
from consumer import Consumer
|
||||||
import event
|
import event
|
||||||
import hooks
|
from networkbot import NetworkBot
|
||||||
from server import Server
|
from server import Server
|
||||||
|
|
||||||
class Bot:
|
class Bot(BotCaps):
|
||||||
def __init__(self, servers=dict(), modules=dict(), mp=list()):
|
def __init__(self, servers=dict(), modules=dict(), mp=list()):
|
||||||
self.version = 3.2
|
BotCaps.__init__(self, 3.2, "3.2-dev")
|
||||||
self.version_txt = "3.2"
|
|
||||||
|
|
||||||
|
# Keep global context: servers and modules
|
||||||
self.servers = servers
|
self.servers = servers
|
||||||
self.modules = modules
|
self.modules = modules
|
||||||
|
|
||||||
|
# Context paths
|
||||||
self.modules_path = mp
|
self.modules_path = mp
|
||||||
self.datas_path = './datas/'
|
self.datas_path = './datas/'
|
||||||
|
|
||||||
self.hooks = hooks.MessagesHook()
|
# Events
|
||||||
self.events = list()
|
self.events = list()
|
||||||
self.event_timer = None
|
self.event_timer = None
|
||||||
|
|
||||||
|
# Other known bots, making a bots network
|
||||||
|
self.network = dict()
|
||||||
|
|
||||||
|
# Messages to be treated
|
||||||
self.msg_queue = Queue()
|
self.msg_queue = Queue()
|
||||||
self.msg_thrd = list()
|
self.msg_thrd = list()
|
||||||
self.msg_thrd_size = -1
|
self.msg_thrd_size = -1
|
||||||
@ -74,7 +80,6 @@ class Bot:
|
|||||||
#else:
|
#else:
|
||||||
# print ("Update timer: no timer left")
|
# print ("Update timer: no timer left")
|
||||||
|
|
||||||
|
|
||||||
def end_timer(self):
|
def end_timer(self):
|
||||||
"""Function called at the end of the timer"""
|
"""Function called at the end of the timer"""
|
||||||
#print ("end timer")
|
#print ("end timer")
|
||||||
@ -149,6 +154,14 @@ class Bot:
|
|||||||
self.msg_thrd_size += 2
|
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):
|
def quit(self, verb=False):
|
||||||
"""Save and unload modules and disconnect servers"""
|
"""Save and unload modules and disconnect servers"""
|
||||||
if self.event_timer is not None:
|
if self.event_timer is not None:
|
||||||
|
28
botcaps.py
Normal file
28
botcaps.py
Normal 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()
|
@ -16,9 +16,20 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from networkbot import NetworkBot
|
||||||
|
|
||||||
nemubotversion = 3.2
|
nemubotversion = 3.2
|
||||||
NODATA = True
|
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):
|
def close(data, toks, context, prompt):
|
||||||
"""Disconnect and forget (remove from the servers list) the server"""
|
"""Disconnect and forget (remove from the servers list) the server"""
|
||||||
if len(toks) > 1:
|
if len(toks) > 1:
|
||||||
@ -64,6 +75,19 @@ def disconnect(data, toks, context, prompt):
|
|||||||
else:
|
else:
|
||||||
print (" Please SELECT a server or give its name in argument.")
|
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):
|
def hotswap(data, toks, context, prompt):
|
||||||
"""Reload a server class"""
|
"""Reload a server class"""
|
||||||
if len(toks) > 1:
|
if len(toks) > 1:
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
<nemubotmodule name="cmd_server">
|
<nemubotmodule name="cmd_server">
|
||||||
<command name="close" call="close" />
|
<command name="close" call="close" />
|
||||||
<command name="connect" call="connect" />
|
<command name="connect" call="connect" />
|
||||||
|
<command name="discover" call="discover" />
|
||||||
<command name="disconnect" call="disconnect" />
|
<command name="disconnect" call="disconnect" />
|
||||||
<command name="hotswap" call="hotswap" />
|
<command name="hotswap" call="hotswap" />
|
||||||
<command name="join" call="join" />
|
<command name="join" call="join" />
|
||||||
|
124
networkbot.py
Normal file
124
networkbot.py
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user