Migrated components and plugins into other repos

This commit is contained in:
aewens 2019-05-29 23:51:31 -04:00
parent 25d3b9e95c
commit 2fff6c7e2d
6 changed files with 12 additions and 305 deletions

4
.gitignore vendored
View File

@ -1,9 +1,9 @@
# Application specific
abots-git/
abots/
abots
*.log
*.vim
*.swp
*.swo
test_*.py
settings.json

134
client.py
View File

@ -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)

View File

@ -1,3 +0,0 @@
from components.socket import IRCSocketClient
from components.composer import Composer
from components.utils import hooks

View File

@ -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]

View File

@ -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

View File

@ -1,4 +0,0 @@
#from abots.helpers import infinitedict
from collections import defaultdict
hooks = defaultdict(lambda: None)