Add the ability to talk with other nemubot over DCC
This commit is contained in:
parent
30da270557
commit
a2d9757d06
186
bot.py
186
bot.py
@ -20,17 +20,19 @@ 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
|
||||
|
||||
ID_letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
class Bot(BotCaps):
|
||||
class Bot:
|
||||
def __init__(self, servers=dict(), modules=dict(), mp=list()):
|
||||
BotCaps.__init__(self, 3.2, "3.2-dev")
|
||||
# Bot general informations
|
||||
self.version = 3.2
|
||||
self.version_txt = "3.2-dev"
|
||||
|
||||
# Keep global context: servers and modules
|
||||
self.servers = servers
|
||||
@ -44,8 +46,12 @@ class Bot(BotCaps):
|
||||
self.events = list()
|
||||
self.event_timer = None
|
||||
|
||||
# Own hooks
|
||||
self.hooks = hooks.MessagesHook(self)
|
||||
|
||||
# Other known bots, making a bots network
|
||||
self.network = dict()
|
||||
self.hooks_cache = dict()
|
||||
|
||||
# Messages to be treated
|
||||
self.msg_queue = Queue()
|
||||
@ -194,6 +200,180 @@ class Bot(BotCaps):
|
||||
for srv in k:
|
||||
self.servers[srv].disconnect()
|
||||
|
||||
# Hooks cache
|
||||
|
||||
def create_cache(self, name):
|
||||
if name not in self.hooks_cache:
|
||||
if isinstance(self.hooks.__dict__[name], list):
|
||||
self.hooks_cache[name] = list()
|
||||
|
||||
# Start by adding locals hooks
|
||||
for h in self.hooks.__dict__[name]:
|
||||
tpl = (h, 0, self.hooks.__dict__[name])
|
||||
self.hooks_cache[name].append(tpl)
|
||||
|
||||
# Now, add extermal hooks
|
||||
level = 0
|
||||
while level == 0 or lvl_exist:
|
||||
lvl_exist = False
|
||||
for ext in self.network:
|
||||
if len(self.network[ext].hooks) > level:
|
||||
lvl_exist = True
|
||||
for h in self.network[ext].hooks[level].__dict__[name]:
|
||||
if h not in self.hooks_cache[name]:
|
||||
self.hooks_cache[name].append((h, level + 1,
|
||||
self.network[ext].hooks[level].__dict__[name]))
|
||||
level += 1
|
||||
|
||||
elif isinstance(self.hooks.__dict__[name], dict):
|
||||
self.hooks_cache[name] = dict()
|
||||
|
||||
# Start by adding locals hooks
|
||||
for h in self.hooks.__dict__[name]:
|
||||
self.hooks_cache[name][h] = (self.hooks.__dict__[name][h], 0,
|
||||
self.hooks.__dict__[name])
|
||||
|
||||
# Now, add extermal hooks
|
||||
level = 0
|
||||
while level == 0 or lvl_exist:
|
||||
lvl_exist = False
|
||||
for ext in self.network:
|
||||
if len(self.network[ext].hooks) > level:
|
||||
lvl_exist = True
|
||||
for h in self.network[ext].hooks[level].__dict__[name]:
|
||||
if h not in self.hooks_cache[name]:
|
||||
self.hooks_cache[name][h] = (self.network[ext].hooks[level].__dict__[name][h], level + 1, self.network[ext].hooks[level].__dict__[name])
|
||||
level += 1
|
||||
|
||||
else:
|
||||
raise Exception(name + " hook type unrecognized")
|
||||
|
||||
return self.hooks_cache[name]
|
||||
|
||||
# Treatment
|
||||
|
||||
def check_rest_times(self, store, hook):
|
||||
"""Remove from store the hook if it has been executed given time"""
|
||||
if hook.times == 0:
|
||||
if isinstance(store, dict):
|
||||
store[hook.name].remove(hook)
|
||||
if len(store) == 0:
|
||||
del store[hook.name]
|
||||
elif isinstance(store, list):
|
||||
store.remove(hook)
|
||||
|
||||
def treat_pre(self, msg):
|
||||
"""Treat a message before all other treatment"""
|
||||
for h, lvl, store in self.create_cache("all_pre"):
|
||||
h.run(msg)
|
||||
self.check_rest_times(store, h)
|
||||
|
||||
|
||||
def treat_cmd(self, msg):
|
||||
"""Treat a command message"""
|
||||
treated = list()
|
||||
|
||||
# First, treat simple hook
|
||||
cmd_hook = self.create_cache("cmd_hook")
|
||||
if msg.cmd[0] in cmd_hook:
|
||||
(hks, lvl, store) = cmd_hook[msg.cmd[0]]
|
||||
for h in hks:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, h)
|
||||
|
||||
# Then, treat regexp based hook
|
||||
cmd_rgxp = self.create_cache("cmd_rgxp")
|
||||
for hook, lvl, store in cmd_rgxp:
|
||||
if hook.is_matching(msg.cmd[0], msg.channel):
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
|
||||
# Finally, treat default hooks if not catched before
|
||||
cmd_default = self.create_cache("cmd_default")
|
||||
for hook, lvl, store in cmd_default:
|
||||
if treated:
|
||||
break
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
|
||||
return treated
|
||||
|
||||
def treat_ask(self, msg):
|
||||
"""Treat an ask message"""
|
||||
treated = list()
|
||||
|
||||
# First, treat simple hook
|
||||
ask_hook = self.create_cache("ask_hook")
|
||||
if msg.content in ask_hook:
|
||||
hks, lvl, store = ask_hook[msg.content]
|
||||
for h in hks:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, h)
|
||||
|
||||
# Then, treat regexp based hook
|
||||
ask_rgxp = self.create_cache("ask_rgxp")
|
||||
for hook, lvl, store in ask_rgxp:
|
||||
if hook.is_matching(msg.content, msg.channel):
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
|
||||
# Finally, treat default hooks if not catched before
|
||||
ask_default = self.create_cache("ask_default")
|
||||
for hook, lvl, store in ask_default:
|
||||
if treated:
|
||||
break
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
|
||||
return treated
|
||||
|
||||
def treat_answer(self, msg):
|
||||
"""Treat a normal message"""
|
||||
treated = list()
|
||||
|
||||
# First, treat simple hook
|
||||
msg_hook = self.create_cache("msg_hook")
|
||||
if msg.content in msg_hook:
|
||||
hks, lvl, store = msg_hook[msg.content]
|
||||
for h in hks:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, h)
|
||||
|
||||
# Then, treat regexp based hook
|
||||
msg_rgxp = self.create_cache("msg_rgxp")
|
||||
for hook, lvl, store in msg_rgxp:
|
||||
if hook.is_matching(msg.content, msg.channel):
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
|
||||
# Finally, treat default hooks if not catched before
|
||||
msg_default = self.create_cache("msg_default")
|
||||
for hook, lvl, store in msg_default:
|
||||
if len(treated) > 0:
|
||||
break
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(store, hook)
|
||||
|
||||
return treated
|
||||
|
||||
|
||||
def hotswap(bak):
|
||||
return Bot(bak.servers, bak.modules, bak.modules_path)
|
||||
|
28
botcaps.py
28
botcaps.py
@ -1,28 +0,0 @@
|
||||
# -*- 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()
|
@ -38,7 +38,7 @@ class Consumer(threading.Thread):
|
||||
# Create, parse and treat the message
|
||||
try:
|
||||
msg = Message(srv, raw, time, prvt)
|
||||
res = msg.treat(self.context.hooks)
|
||||
res = msg.treat()
|
||||
except:
|
||||
print ("\033[1;31mERROR:\033[0m occurred during the "
|
||||
"processing of the message: %s" % raw)
|
||||
@ -56,6 +56,9 @@ class Consumer(threading.Thread):
|
||||
elif isinstance(res, Response):
|
||||
srv.send_response(res, data)
|
||||
|
||||
# Inform that the message has been treated
|
||||
srv.msg_treated(data)
|
||||
|
||||
except queue.Empty:
|
||||
pass
|
||||
finally:
|
||||
|
145
hooks.py
145
hooks.py
@ -19,7 +19,9 @@
|
||||
from response import Response
|
||||
|
||||
class MessagesHook:
|
||||
def __init__(self):
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
# Store specials hook
|
||||
self.all_pre = list() # Treated before any parse
|
||||
#self.all_post = list() # Treated before send message to user
|
||||
@ -42,25 +44,30 @@ class MessagesHook:
|
||||
|
||||
def add_hook(self, store, hook):
|
||||
"""Insert in the right place a hook into the given store"""
|
||||
if isinstance(store, dict) and hook.name is not None:
|
||||
if hook.name not in store:
|
||||
store[hook.name] = list()
|
||||
store[hook.name].append(hook)
|
||||
elif isinstance(store, list):
|
||||
store.append(hook)
|
||||
if store in self.context.hooks_cache:
|
||||
del self.context.hooks_cache[store]
|
||||
|
||||
attr = getattr(self, store)
|
||||
if attr is None:
|
||||
print ("Warning: unrecognized hook store type")
|
||||
return
|
||||
|
||||
if isinstance(attr, dict) and hook.name is not None:
|
||||
if hook.name not in attr:
|
||||
attr[hook.name] = list()
|
||||
attr[hook.name].append(hook)
|
||||
elif isinstance(attr, list):
|
||||
attr.append(hook)
|
||||
else:
|
||||
print ("Warning: unrecognized hook store type")
|
||||
|
||||
def register_hook_attributes(self, store, module, node):
|
||||
if node.hasAttribute("name"):
|
||||
self.add_hook(getattr(self, store + "_hook"), Hook(getattr(module,
|
||||
node["call"]),
|
||||
node["name"]))
|
||||
self.add_hook(store + "_hook", Hook(getattr(module, node["call"]),
|
||||
node["name"]))
|
||||
elif node.hasAttribute("regexp"):
|
||||
self.add_hook(getattr(self, store + "_rgxp"), Hook(getattr(module,
|
||||
node["call"]),
|
||||
None, None,
|
||||
node["regexp"]))
|
||||
self.add_hook(store + "_rgxp", Hook(getattr(module, node["call"]),
|
||||
None, None, node["regexp"]))
|
||||
|
||||
def register_hook(self, module, node):
|
||||
"""Create a hook from configuration node"""
|
||||
@ -75,116 +82,6 @@ class MessagesHook:
|
||||
node["type"] == "all"):
|
||||
self.register_hook_attributes("answer", module, node)
|
||||
|
||||
def check_rest_times(self, store, hook):
|
||||
"""Remove from store the hook if it has been executed given time"""
|
||||
if hook.times == 0:
|
||||
if isinstance(store, dict):
|
||||
store[hook.name].remove(hook)
|
||||
if len(store) == 0:
|
||||
del store[hook.name]
|
||||
elif isinstance(store, list):
|
||||
store.remove(hook)
|
||||
|
||||
def treat_pre(self, msg):
|
||||
"""Treat a message before all other treatment"""
|
||||
for h in self.all_pre:
|
||||
h.run(msg)
|
||||
self.check_rest_times(self.all_pre, h)
|
||||
|
||||
|
||||
def treat_cmd(self, msg):
|
||||
"""Treat a command message"""
|
||||
treated = list()
|
||||
|
||||
# First, treat simple hook
|
||||
if msg.cmd[0] in self.cmd_hook:
|
||||
for h in self.cmd_hook[msg.cmd[0]]:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.cmd_hook, h)
|
||||
|
||||
# Then, treat regexp based hook
|
||||
for hook in self.cmd_rgxp:
|
||||
if hook.is_matching(msg.cmd[0], msg.channel):
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.cmd_rgxp, hook)
|
||||
|
||||
# Finally, treat default hooks if not catched before
|
||||
for hook in self.cmd_default:
|
||||
if treated:
|
||||
break
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.cmd_default, hook)
|
||||
|
||||
return treated
|
||||
|
||||
def treat_ask(self, msg):
|
||||
"""Treat an ask message"""
|
||||
treated = list()
|
||||
|
||||
# First, treat simple hook
|
||||
if msg.content in self.ask_hook:
|
||||
for h in self.ask_hook[msg.content]:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.ask_hook, h)
|
||||
|
||||
# Then, treat regexp based hook
|
||||
for hook in self.ask_rgxp:
|
||||
if hook.is_matching(msg.content, msg.channel):
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.ask_rgxp, hook)
|
||||
|
||||
# Finally, treat default hooks if not catched before
|
||||
for hook in self.ask_default:
|
||||
if treated:
|
||||
break
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.ask_default, hook)
|
||||
|
||||
return treated
|
||||
|
||||
def treat_answer(self, msg):
|
||||
"""Treat a normal message"""
|
||||
treated = list()
|
||||
|
||||
# First, treat simple hook
|
||||
if msg.content in self.msg_hook:
|
||||
for h in self.msg_hook[msg.content]:
|
||||
res = h.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.msg_hook, h)
|
||||
|
||||
# Then, treat regexp based hook
|
||||
for hook in self.msg_rgxp:
|
||||
if hook.is_matching(msg.content, msg.channel):
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.msg_rgxp, hook)
|
||||
|
||||
# Finally, treat default hooks if not catched before
|
||||
for hook in self.msg_default:
|
||||
if len(treated) > 0:
|
||||
break
|
||||
res = hook.run(msg)
|
||||
if res is not None and res != False:
|
||||
treated.append(res)
|
||||
self.check_rest_times(self.msg_default, hook)
|
||||
|
||||
return treated
|
||||
|
||||
|
||||
class Hook:
|
||||
"""Class storing hook informations"""
|
||||
|
@ -214,11 +214,11 @@ def register_hooks(module, context, prompt):
|
||||
|
||||
# Register legacy hooks
|
||||
if hasattr(module, "parseanswer"):
|
||||
context.hooks.add_hook(context.hooks.cmd_default, Hook(module.parseanswer))
|
||||
context.hooks.add_hook("cmd_default", Hook(module.parseanswer))
|
||||
if hasattr(module, "parseask"):
|
||||
context.hooks.add_hook(context.hooks.ask_default, Hook(module.parseask))
|
||||
context.hooks.add_hook("ask_default", Hook(module.parseask))
|
||||
if hasattr(module, "parselisten"):
|
||||
context.hooks.add_hook(context.hooks.msg_default, Hook(module.parselisten))
|
||||
context.hooks.add_hook("msg_default", Hook(module.parselisten))
|
||||
|
||||
##########################
|
||||
# #
|
||||
|
23
message.py
23
message.py
@ -43,6 +43,7 @@ def save():
|
||||
|
||||
class Message:
|
||||
def __init__ (self, srv, line, timestamp, private = False):
|
||||
self.raw = line
|
||||
self.srv = srv
|
||||
self.time = timestamp
|
||||
self.channel = None
|
||||
@ -136,14 +137,14 @@ class Message:
|
||||
return False
|
||||
return self.srv.accepted_channel(self.channel)
|
||||
|
||||
def treat(self, hooks):
|
||||
def treat(self):
|
||||
"""Parse and treat the message"""
|
||||
if self.cmd == "PING":
|
||||
self.srv.send_pong(self.content)
|
||||
elif self.cmd == "PRIVMSG" and self.ctcp:
|
||||
self.parsectcp()
|
||||
elif self.cmd == "PRIVMSG" and self.authorize():
|
||||
return self.parsemsg (hooks)
|
||||
return self.parsemsg()
|
||||
elif self.channel in self.srv.channels:
|
||||
if self.cmd == "353":
|
||||
self.srv.channels[self.channel].parse353(self)
|
||||
@ -191,13 +192,10 @@ class Message:
|
||||
self.srv.send_ctcp(self.sender, "ERRMSG Unknown or unimplemented CTCP request")
|
||||
|
||||
def reparsemsg(self):
|
||||
if self.hooks is not None:
|
||||
self.parsemsg(self.hooks)
|
||||
else:
|
||||
print ("Can't reparse message")
|
||||
self.parsemsg()
|
||||
|
||||
def parsemsg (self, hooks):
|
||||
hooks.treat_pre(self)
|
||||
def parsemsg (self):
|
||||
self.srv.context.treat_pre(self)
|
||||
#Treat all messages starting with 'nemubot:' as distinct commands
|
||||
if self.content.find("%s:"%self.srv.nick) == 0:
|
||||
#Remove the bot name
|
||||
@ -211,7 +209,7 @@ class Message:
|
||||
|
||||
# Ask hooks
|
||||
else:
|
||||
return hooks.treat_ask(self)
|
||||
return self.srv.context.treat_ask(self)
|
||||
|
||||
#Owner commands
|
||||
elif self.content[0] == '`' and self.sender == self.srv.owner:
|
||||
@ -236,7 +234,6 @@ class Message:
|
||||
|
||||
#Messages stating with !
|
||||
elif self.content[0] == '!' and len(self.content) > 1:
|
||||
self.hooks = hooks
|
||||
try:
|
||||
self.cmd = shlex.split(self.content[1:])
|
||||
except ValueError:
|
||||
@ -277,13 +274,13 @@ class Message:
|
||||
conn = DCC(self.srv, self.sender)
|
||||
conn.send_file("bot_sample.xml")
|
||||
else:
|
||||
return hooks.treat_cmd(self)
|
||||
return self.srv.context.treat_cmd(self)
|
||||
|
||||
else:
|
||||
res = hooks.treat_answer(self)
|
||||
res = self.srv.context.treat_answer(self)
|
||||
# Assume the message starts with nemubot:
|
||||
if res is None and self.private:
|
||||
return hooks.treat_ask(self)
|
||||
return self.srv.context.treat_ask(self)
|
||||
return res
|
||||
|
||||
# def parseOwnerCmd(self, cmd):
|
||||
|
153
networkbot.py
153
networkbot.py
@ -16,20 +16,27 @@
|
||||
# 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 json
|
||||
import random
|
||||
import shlex
|
||||
import urllib.parse
|
||||
import zlib
|
||||
|
||||
from botcaps import BotCaps
|
||||
from DCC import DCC
|
||||
import hooks
|
||||
from response import Response
|
||||
|
||||
class NetworkBot:
|
||||
def __init__(self, context, srv, dest, dcc=None):
|
||||
# General informations
|
||||
self.context = context
|
||||
self.srv = srv
|
||||
self.dcc = dcc
|
||||
self.dest = dest
|
||||
self.infos = None
|
||||
|
||||
self.dcc = dcc # DCC connection to the other bot
|
||||
self.hooks = list()
|
||||
|
||||
# Tags monitor
|
||||
self.my_tag = random.randint(0,255)
|
||||
self.inc_tag = 0
|
||||
self.tags = dict()
|
||||
@ -52,39 +59,64 @@ class NetworkBot:
|
||||
return self.dcc.realname
|
||||
return None
|
||||
|
||||
def isDCC(self, someone):
|
||||
"""Abstract implementation"""
|
||||
return True
|
||||
|
||||
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
|
||||
self.inc_tag = (self.inc_tag + 1) % 256
|
||||
while self.inc_tag in self.tags:
|
||||
self.inc_tag = (self.inc_out + 1) % 256
|
||||
self.inc_tag = (self.inc_tag + 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
|
||||
self.tags[tag] = (cmd, data)
|
||||
|
||||
# Send the command with the tag
|
||||
self.send_response(tag, cmd)
|
||||
self.send_response_final(tag, cmd)
|
||||
|
||||
def send_response(self, tag, msg):
|
||||
def send_response(self, res, tag):
|
||||
self.send_response_final(tag, [res.sender, res.channel, res.nick, res.nomore, res.title, res.more, res.count, json.dumps(res.messages)])
|
||||
|
||||
def msg_treated(self, tag):
|
||||
self.send_ack(tag)
|
||||
|
||||
def send_response_final(self, tag, msg):
|
||||
"""Send a response with a tag"""
|
||||
for line in msg.split("\n"):
|
||||
self.dcc.send_dcc_raw(tag + b' ' + line.encode())
|
||||
if isinstance(msg, list):
|
||||
cnt = b''
|
||||
for i in msg:
|
||||
if i is None:
|
||||
cnt += b' ""'
|
||||
elif isinstance(i, int):
|
||||
cnt += (' %d' % i).encode()
|
||||
elif isinstance(i, float):
|
||||
cnt += (' %f' % i).encode()
|
||||
else:
|
||||
cnt += b' "' + urllib.parse.quote(i).encode() + b'"'
|
||||
if False and len(cnt) > 10:
|
||||
cnt = b' Z ' + zlib.compress(cnt)
|
||||
print (cnt)
|
||||
self.dcc.send_dcc_raw(tag + cnt)
|
||||
else:
|
||||
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")
|
||||
self.send_response_final(tag, "ACK")
|
||||
|
||||
def connect(self):
|
||||
"""Making the connexion with dest through srv"""
|
||||
if self.dcc is None:
|
||||
if self.dcc is None or not self.dcc.connected:
|
||||
self.dcc = DCC(self.srv, self.dest)
|
||||
self.dcc.treatement = self.hello
|
||||
self.dcc.send_dcc("NEMUBOT###")
|
||||
else:
|
||||
self.send_cmd("FETCH")
|
||||
|
||||
def disconnect(self, reason=""):
|
||||
"""Close the connection and remove the bot from network list"""
|
||||
@ -101,24 +133,89 @@ class NetworkBot:
|
||||
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())
|
||||
|
||||
# Is it a response?
|
||||
if tag in self.tags:
|
||||
# Is it compressed content?
|
||||
if words[1] == b'Z':
|
||||
#print (line)
|
||||
line = zlib.decompress(line[len(tag) + 3:])
|
||||
self.response(line, tag, [urllib.parse.unquote(arg) for arg in shlex.split(line[len(tag) + 1:].decode())], self.tags[tag])
|
||||
else:
|
||||
args = list()
|
||||
cmd = words[1]
|
||||
if len(words) > 2:
|
||||
args = shlex.split(line[len(tag) + len(cmd) + 2:].decode())
|
||||
args = [urllib.parse.unquote(arg) for arg in args]
|
||||
else:
|
||||
args = list()
|
||||
#print ("request:", line)
|
||||
self.request(tag, cmd, args)
|
||||
|
||||
# Parse
|
||||
if cmd == b'ACK':
|
||||
if tag in self.tags:
|
||||
del self.tags[tag]
|
||||
def response(self, line, tag, args, t):
|
||||
(cmds, data) = t
|
||||
#print ("response for", cmds, ":", args)
|
||||
|
||||
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)
|
||||
if isinstance(cmds, list):
|
||||
cmd = cmds[0]
|
||||
else:
|
||||
cmd = cmds
|
||||
cmds = list(cmd)
|
||||
|
||||
if args[0] == 'ACK': # Acknowledge a command
|
||||
del self.tags[tag]
|
||||
|
||||
elif cmd == "FETCH" and len(args) >= 5:
|
||||
level = int(args[1])
|
||||
while len(self.hooks) <= level:
|
||||
self.hooks.append(hooks.MessagesHook(self.context))
|
||||
|
||||
if args[2] == "": args[2] = None
|
||||
if args[3] == "": args[3] = None
|
||||
if args[4] == "": args[4] = list()
|
||||
else: args[4] = args[4].split(',')
|
||||
|
||||
self.hooks[level].add_hook(args[0], hooks.Hook(self.exec_hook, args[2], None, args[3], args[4]))
|
||||
|
||||
elif cmd == "HOOK" and len(args) >= 8:
|
||||
# Rebuild the response
|
||||
if args[1] == '': args[1] = None
|
||||
if args[2] == '': args[2] = None
|
||||
if args[3] == '': args[3] = None
|
||||
if args[4] == '': args[4] = None
|
||||
if args[5] == '': args[5] = None
|
||||
if args[6] == '': args[6] = None
|
||||
res = Response(args[0], channel=args[1], nick=args[2], nomore=args[3], title=args[4], more=args[5], count=args[6])
|
||||
for msg in json.loads(args[7]):
|
||||
res.append_message(msg)
|
||||
if len(res.messages) <= 1:
|
||||
res.alone = True
|
||||
self.srv.send_response(res, None)
|
||||
|
||||
|
||||
def request(self, tag, cmd, args):
|
||||
# Parse
|
||||
if cmd == b'MYTAG' and len(args) > 0: # Inform about choosen tag
|
||||
while args[0] == self.my_tag:
|
||||
self.my_tag = random.randint(0,255)
|
||||
self.send_ack(tag)
|
||||
|
||||
elif cmd == b'FETCH': # Get known commands
|
||||
for name in ["cmd_hook", "ask_hook", "msg_hook"]:
|
||||
elts = self.context.create_cache(name)
|
||||
for elt in elts:
|
||||
(hooks, lvl, store) = elts[elt]
|
||||
for h in hooks:
|
||||
self.send_response_final(tag, [name, lvl, elt, h.regexp, ','.join(h.channels)])
|
||||
self.send_ack(tag)
|
||||
|
||||
elif (cmd == b'HOOK' or cmd == b'"HOOK"') and len(args) > 0: # Action requested
|
||||
self.context.receive_message(self, args[0].encode(), True, tag)
|
||||
|
||||
|
||||
def exec_hook(self, msg):
|
||||
self.send_cmd(["HOOK", msg.raw])
|
||||
|
@ -110,6 +110,10 @@ class Server(threading.Thread):
|
||||
def send_pong(self, cnt):
|
||||
self.s.send(("PONG %s\r\n" % cnt).encode ())
|
||||
|
||||
def msg_treated(self, origin):
|
||||
"""Do nothing, here for implement abstract class"""
|
||||
pass
|
||||
|
||||
def send_response(self, res, origin):
|
||||
if res.channel is not None and res.channel != self.nick:
|
||||
self.send_msg(res.channel, res.get_message())
|
||||
|
Loading…
x
Reference in New Issue
Block a user