Migrated components and plugins into other repos
This commit is contained in:
parent
25d3b9e95c
commit
2fff6c7e2d
|
@ -1,9 +1,9 @@
|
||||||
# Application specific
|
# Application specific
|
||||||
abots-git/
|
|
||||||
abots/
|
|
||||||
abots
|
abots
|
||||||
*.log
|
*.log
|
||||||
|
*.vim
|
||||||
*.swp
|
*.swp
|
||||||
|
*.swo
|
||||||
test_*.py
|
test_*.py
|
||||||
settings.json
|
settings.json
|
||||||
|
|
||||||
|
|
134
client.py
134
client.py
|
@ -1,12 +1,10 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from components import IRCSocketClient, Composer, hooks
|
from components import IRCSocketClient, Composer
|
||||||
|
from plugins import *
|
||||||
from abots.net import PrefixSocketClient
|
from abots.net import PrefixSocketClient
|
||||||
from abots.events import CoroEvent
|
|
||||||
from abots.helpers import Logger, infinitedict, isnumeric, coroutine, generator
|
from abots.helpers import Logger, infinitedict, isnumeric, coroutine, generator
|
||||||
from abots.helpers import jsto, jots
|
|
||||||
|
|
||||||
from time import sleep
|
|
||||||
from os import remove as delete_file
|
from os import remove as delete_file
|
||||||
from os.path import isfile
|
from os.path import isfile
|
||||||
|
|
||||||
|
@ -20,131 +18,19 @@ settings["stream"]["formatter"] = "%(message)s"
|
||||||
logger = Logger(logname, settings=settings)
|
logger = Logger(logname, settings=settings)
|
||||||
logger.start()
|
logger.start()
|
||||||
|
|
||||||
composer = Composer(hooks, logger)
|
composer = Composer(logger)
|
||||||
|
|
||||||
irc_host = "irc.tilde.chat"#"localhost"
|
irc_host = "irc.freenode.net"#"irc.tilde.chat"#"localhost"
|
||||||
irc_port = 6697
|
irc_port = 6697
|
||||||
irc_timeout = 3
|
irc_timeout = 3
|
||||||
irc_args = irc_host, irc_port, irc_timeout, True
|
irc_args = irc_host, irc_port, irc_timeout, True
|
||||||
irc_client, inbox, outbox, events = composer.start_socket(*irc_args)
|
irc_client, inbox, outbox, events = composer.start_socket(*irc_args)
|
||||||
irc_client.ready.wait()
|
irc_client.ready.wait()
|
||||||
|
composer.set_state("irc-host", irc_host)
|
||||||
|
|
||||||
with open("settings.json", "r") as config:
|
irc_client.send("USER babili - - -")
|
||||||
settings = jsto(config.read())
|
irc_client.send("NICK babili")
|
||||||
assert isinstance(settings, dict), "Expected dict"
|
|
||||||
irc_settings = settings.get("irc", dict())
|
|
||||||
name = irc_settings.get("name", "")
|
|
||||||
password = irc_settings.get("password", "")
|
|
||||||
author = irc_settings.get("author", "")
|
|
||||||
auto_join = irc_settings.get("auto-join", list())
|
|
||||||
requirements = irc_settings.get("requirements", list())
|
|
||||||
require_met = CoroEvent()
|
|
||||||
|
|
||||||
def off(composer, sock):
|
#composer.subscribe("startup/init")
|
||||||
sock.send("QUIT bye-bye!")
|
#composer.subscribe_many(["startup/init", "debug/log"])
|
||||||
sleep(1)
|
#composer.get_messages(irc_client)
|
||||||
sock.stop()
|
|
||||||
composer.kill_switch.set()
|
|
||||||
|
|
||||||
@composer.register_hook
|
|
||||||
def log(cancel, event):
|
|
||||||
sock, line = (yield)
|
|
||||||
meta = composer.parse_message(line)
|
|
||||||
if meta.get("command", None) == "ERROR":
|
|
||||||
logger.error(meta.get("raw"))
|
|
||||||
event.set()
|
|
||||||
logger.debug(meta.get("raw"))
|
|
||||||
|
|
||||||
@composer.register_hook
|
|
||||||
def pivot(cancel, event):
|
|
||||||
sock, line = (yield)
|
|
||||||
meta = composer.parse_message(line)
|
|
||||||
tags = meta.get("tags", dict())
|
|
||||||
if tags.get("account", None) != author:
|
|
||||||
return
|
|
||||||
if meta.get("command", None) != "PRIVMSG":
|
|
||||||
return
|
|
||||||
params = composer.cleanup(meta.get("params", ""))
|
|
||||||
source, message = params.split(" ", 1)
|
|
||||||
if message == "!quit":
|
|
||||||
cancel()
|
|
||||||
event.set()
|
|
||||||
off(composer, sock)
|
|
||||||
elif message == "!debug":
|
|
||||||
logger.debug(meta)
|
|
||||||
|
|
||||||
|
|
||||||
@composer.register_hook
|
|
||||||
def identify(cancel, event):
|
|
||||||
sock, line = (yield)
|
|
||||||
meta = composer.parse_message(line)
|
|
||||||
if meta.get("nick", None) != "NickServ":
|
|
||||||
return
|
|
||||||
if meta.get("command", None) != "NOTICE":
|
|
||||||
return
|
|
||||||
params = composer.cleanup(meta.get("params", ""))
|
|
||||||
nickserv_complain = "please choose a different nick"
|
|
||||||
if nickserv_complain not in params:
|
|
||||||
return
|
|
||||||
cancel()
|
|
||||||
sleep(0.01)
|
|
||||||
sock.send(f"PRIVMSG NickServ IDENTIFY {password}")
|
|
||||||
sleep(1)
|
|
||||||
composer.subscribe("log")
|
|
||||||
for channel in auto_join:
|
|
||||||
sock.send(f"JOIN {channel}")
|
|
||||||
composer.subscribe("pivot")
|
|
||||||
#off(composer, sock)
|
|
||||||
event.set()
|
|
||||||
|
|
||||||
@composer.register_hook
|
|
||||||
def init(cancel, event):
|
|
||||||
sock, line = (yield)
|
|
||||||
meta = composer.parse_message(line)
|
|
||||||
if meta.get("source", None) != "server":
|
|
||||||
return
|
|
||||||
if meta.get("command", None) != "NOTICE":
|
|
||||||
return
|
|
||||||
if "hostname" not in meta.get("params", ""):
|
|
||||||
return
|
|
||||||
cancel()
|
|
||||||
logger.debug(meta["raw"])
|
|
||||||
sleep(0.01)
|
|
||||||
sock.send("CAP LS")
|
|
||||||
event.set()
|
|
||||||
|
|
||||||
@composer.register_hook
|
|
||||||
def caps(cancel, event):
|
|
||||||
sock, line = (yield)
|
|
||||||
meta = composer.parse_message(line)
|
|
||||||
if meta.get("source", None) != "server":
|
|
||||||
return
|
|
||||||
if meta.get("command", None) != "CAP":
|
|
||||||
return
|
|
||||||
params = composer.cleanup(meta.get("params", ""))
|
|
||||||
if "* LS" in params:
|
|
||||||
capabilities = params.split("* LS", 1)[1].strip()
|
|
||||||
logger.debug(f"Capabilities: {capabilities}")
|
|
||||||
sock.send(f"CAP REQ :{capabilities}")
|
|
||||||
for cap in capabilities.split(" "):
|
|
||||||
if cap in requirements:
|
|
||||||
requirements.remove(cap)
|
|
||||||
if len(requirements) == 0 and not require_met.is_set():
|
|
||||||
require_met.set()
|
|
||||||
if not require_met.is_set():
|
|
||||||
cancel()
|
|
||||||
event.set()
|
|
||||||
off(composer, sock)
|
|
||||||
elif "* ACK" in params:
|
|
||||||
cancel()
|
|
||||||
logger.debug(f"ACK!")
|
|
||||||
event.set()
|
|
||||||
if require_met.is_set():
|
|
||||||
composer.subscribe("identify")
|
|
||||||
sleep(0.01)
|
|
||||||
sock.send("CAP END")
|
|
||||||
sock.send(f"USER {name} - - -")
|
|
||||||
sock.send(f"NICK {name}")
|
|
||||||
|
|
||||||
composer.subscribe_many(["init", "caps"])
|
|
||||||
composer.get_messages(irc_client)
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
from components.socket import IRCSocketClient
|
|
||||||
from components.composer import Composer
|
|
||||||
from components.utils import hooks
|
|
|
@ -1,121 +0,0 @@
|
||||||
from components import IRCSocketClient
|
|
||||||
from abots.events import CoroEvent
|
|
||||||
from abots.helpers import Logger, infinitedict, isnumeric, sha256
|
|
||||||
from abots.helpers import coroutine, generator
|
|
||||||
|
|
||||||
from time import sleep
|
|
||||||
from os import remove as delete_file
|
|
||||||
from os.path import isfile
|
|
||||||
from functools import wraps
|
|
||||||
|
|
||||||
class Composer:
|
|
||||||
def __init__(self, hooks, logger=None):
|
|
||||||
self.hooks = hooks
|
|
||||||
self.logger = logger
|
|
||||||
self._listeners = dict()
|
|
||||||
self.kill_switch = CoroEvent()
|
|
||||||
|
|
||||||
def start_socket(self, host, port, timeout=None, secure=False):
|
|
||||||
sock = IRCSocketClient(host, port, timeout=timeout, secure=secure)
|
|
||||||
sock.start()
|
|
||||||
inbox = sock.queues.get("inbox")
|
|
||||||
outbox = sock.queues.get("outbox")
|
|
||||||
events = sock.queues.get("events")
|
|
||||||
return sock, inbox, outbox, events
|
|
||||||
|
|
||||||
def register_hook(self, func):
|
|
||||||
hook_name = func.__name__
|
|
||||||
if hook_name in list(self.hooks):
|
|
||||||
return
|
|
||||||
self.hooks[hook_name] = func
|
|
||||||
return generator(func)
|
|
||||||
|
|
||||||
def subscribe(self, hook_name):
|
|
||||||
key = sha256()
|
|
||||||
hook = self.hooks[hook_name]
|
|
||||||
if hook is not None:
|
|
||||||
event = CoroEvent()
|
|
||||||
gen_hook = generator(hook)
|
|
||||||
self._listeners[key] = gen_hook(self.unsubscribe(key), event)
|
|
||||||
|
|
||||||
def subscribe_many(self, hook_names):
|
|
||||||
for hook_name in hook_names:
|
|
||||||
self.subscribe(hook_name)
|
|
||||||
|
|
||||||
def unsubscribe(self, key):
|
|
||||||
def cancel():
|
|
||||||
try:
|
|
||||||
del self._listeners[key]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
return cancel
|
|
||||||
|
|
||||||
@generator
|
|
||||||
def send_to_listeners(self, sock):
|
|
||||||
line = (yield)
|
|
||||||
for key in list(self._listeners):
|
|
||||||
listener = self._listeners.get(key, None)
|
|
||||||
if listener is None:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
listener.send((sock, line))
|
|
||||||
except StopIteration:
|
|
||||||
cancel = self.unsubscribe(key)
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
def get_messages(self, sock):
|
|
||||||
cache = ""
|
|
||||||
listeners = self.send_to_listeners(sock)
|
|
||||||
while not self.kill_switch.is_set():
|
|
||||||
for recv in sock.recv():
|
|
||||||
if recv is None or len(recv) == 0:
|
|
||||||
continue
|
|
||||||
if "\n" in recv:
|
|
||||||
lines = recv.split("\n")
|
|
||||||
listeners.send(cache + lines[0].strip("\r"))
|
|
||||||
for line in lines[1:-1]:
|
|
||||||
listeners.send(line.strip("\r"))
|
|
||||||
cache = lines[-1]
|
|
||||||
else:
|
|
||||||
cache = cache + recv
|
|
||||||
return cache
|
|
||||||
|
|
||||||
# from section 2.3.1 of rfc1459
|
|
||||||
def parse_message(self, message):
|
|
||||||
meta = dict()
|
|
||||||
meta["raw"] = message
|
|
||||||
if message is None or len(message) == 0:
|
|
||||||
return meta
|
|
||||||
has_tags = message[0] == "@"
|
|
||||||
meta["has_tags"] = has_tags
|
|
||||||
if has_tags:
|
|
||||||
tags, message = message.lstrip("@").split(" ", 1)
|
|
||||||
meta["tags"] = dict()
|
|
||||||
for tag in tags.split(";"):
|
|
||||||
key, value = tag.split("=", 1)
|
|
||||||
meta["tags"][key] = value
|
|
||||||
has_prefix = message[0] == ":"
|
|
||||||
message = message.lstrip(":")
|
|
||||||
meta["has_prefix"] = has_prefix
|
|
||||||
if not has_prefix:
|
|
||||||
command, params = message.lstrip(":").split(" ", 1)
|
|
||||||
meta["type"] = "alpha"
|
|
||||||
else:
|
|
||||||
prefix, command, params = message.split(" ", 2)
|
|
||||||
source = "nick" if "!" in prefix else "server"
|
|
||||||
meta["prefix"] = prefix
|
|
||||||
meta["source"] = source
|
|
||||||
meta["type"] = "numeric" if isnumeric(command) else "alpha"
|
|
||||||
if source == "nick":
|
|
||||||
nick, user_info = prefix.split("!", 1)
|
|
||||||
user, host = user_info.split("@", 1)
|
|
||||||
meta["nick"] = nick
|
|
||||||
meta["user"] = user
|
|
||||||
meta["host"] = host
|
|
||||||
meta["command"] = command
|
|
||||||
meta["params"] = params
|
|
||||||
return meta
|
|
||||||
|
|
||||||
def cleanup(self, parameters, dirt=":"):
|
|
||||||
params = parameters.partition(dirt)
|
|
||||||
return params[0] + params[2]
|
|
|
@ -1,51 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
IRC Socket Client
|
|
||||||
=================
|
|
||||||
|
|
||||||
Formatted to read and write IRC socket data
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from abots.net import SocketClient
|
|
||||||
|
|
||||||
from socket import timeout as sock_timeout
|
|
||||||
|
|
||||||
class IRCSocketClient(SocketClient):
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
kwargs["buffer_size"] = 512
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
def _format_message(self, message, *args):
|
|
||||||
if len(args) > 0:
|
|
||||||
formatted = message.format(*args)
|
|
||||||
else:
|
|
||||||
formatted = message
|
|
||||||
packaged = f"{formatted}\r\n"
|
|
||||||
return packaged.encode()
|
|
||||||
|
|
||||||
#def recv(self):
|
|
||||||
# data = ""
|
|
||||||
# for buf in self._obtain(self._outbox):
|
|
||||||
# data = data + buf
|
|
||||||
# return data
|
|
||||||
|
|
||||||
# See rfc2812
|
|
||||||
#max_message_length = 512
|
|
||||||
#while True:#len(data) <= max_message_length:
|
|
||||||
# # Automatically break loop to prevent infinite loop
|
|
||||||
# # Allow at least twice the needed iterations to occur exiting loop
|
|
||||||
# # Force bufsize to cap out at buffer_size
|
|
||||||
# try:
|
|
||||||
# packet = self.sock.recv()#self.buffer_size)
|
|
||||||
# # The socket can either be broken or no longer open at all
|
|
||||||
# except (BrokenPipeError, OSError) as e:
|
|
||||||
# #if not isinstance(e, sock_timeout):
|
|
||||||
# # self._attempt_reconnect()
|
|
||||||
# continue
|
|
||||||
# #print(packet.decode())
|
|
||||||
# if len(packet) == 0:
|
|
||||||
# break
|
|
||||||
# data = data + packet
|
|
||||||
##print(data.decode())
|
|
||||||
#return data.decode() if decode else data
|
|
|
@ -1,4 +0,0 @@
|
||||||
#from abots.helpers import infinitedict
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
hooks = defaultdict(lambda: None)
|
|
Loading…
Reference in New Issue