Migrated components and plugins into other repos
This commit is contained in:
parent
25d3b9e95c
commit
2fff6c7e2d
|
@ -1,9 +1,9 @@
|
|||
# Application specific
|
||||
abots-git/
|
||||
abots/
|
||||
abots
|
||||
*.log
|
||||
*.vim
|
||||
*.swp
|
||||
*.swo
|
||||
test_*.py
|
||||
settings.json
|
||||
|
||||
|
|
134
client.py
134
client.py
|
@ -1,12 +1,10 @@
|
|||
#!/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.events import CoroEvent
|
||||
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.path import isfile
|
||||
|
||||
|
@ -20,131 +18,19 @@ settings["stream"]["formatter"] = "%(message)s"
|
|||
logger = Logger(logname, settings=settings)
|
||||
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_timeout = 3
|
||||
irc_args = irc_host, irc_port, irc_timeout, True
|
||||
irc_client, inbox, outbox, events = composer.start_socket(*irc_args)
|
||||
irc_client.ready.wait()
|
||||
composer.set_state("irc-host", irc_host)
|
||||
|
||||
with open("settings.json", "r") as config:
|
||||
settings = jsto(config.read())
|
||||
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()
|
||||
irc_client.send("USER babili - - -")
|
||||
irc_client.send("NICK babili")
|
||||
|
||||
def off(composer, sock):
|
||||
sock.send("QUIT bye-bye!")
|
||||
sleep(1)
|
||||
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)
|
||||
#composer.subscribe("startup/init")
|
||||
#composer.subscribe_many(["startup/init", "debug/log"])
|
||||
#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