[alpha] Migrated irc socket client from abots, added irc parser
This commit is contained in:
parent
e2541d644f
commit
e47d8d0441
153
client.py
153
client.py
|
@ -1,7 +1,8 @@
|
|||
#!/opt/abots/env/bin/python3
|
||||
|
||||
from abots.net import IRCSocketClient
|
||||
from components import IRCSocketClient
|
||||
from abots.helpers import Logger, infinitedict, isnumeric
|
||||
|
||||
from time import sleep
|
||||
from os import remove as delete_file
|
||||
from os.path import isfile
|
||||
|
@ -20,7 +21,7 @@ irc_host = "localhost"
|
|||
irc_port = 6667
|
||||
irc_timeout = 3
|
||||
|
||||
irc_client = IRCSocketClient(irc_host, irc_port, daemon=True)#, timeout=irc_timeout)
|
||||
irc_client = IRCSocketClient(irc_host, irc_port, timeout=irc_timeout)
|
||||
irc_client.start()
|
||||
inbox = irc_client.queues.get("inbox")
|
||||
outbox = irc_client.queues.get("outbox")
|
||||
|
@ -29,44 +30,128 @@ events = irc_client.queues.get("events")
|
|||
#sleep(2)
|
||||
irc_client.ready.wait()
|
||||
|
||||
"""
|
||||
|
||||
:center.tilde.chat NOTICE * :*** Looking up your hostname...
|
||||
:center.tilde.chat NOTICE * :*** Could not resolve your hostname: Domain not found; using your IP address (127.0.0.1) instead.
|
||||
:center.tilde.chat CAP * LS :account-notify account-tag away-notify batch cap-notify chghost echo-message extended-join invite-notify message-tags sasl server-time
|
||||
:center.tilde.chat 001 babili|test :Welcome to the tilde.chat IRC Network babili|test!babili|test@127.0.0.1
|
||||
:center.tilde.chat 002 babili|test :Your host is center.tilde.chat, running version InspIRCd-3
|
||||
:center.tilde.chat 003 babili|test :This server was created 17:19:44 May 06 2019
|
||||
|
||||
"""
|
||||
|
||||
def parse(message):
|
||||
prefix, command, params = message.lstrip(":").rstrip("\r\n").split(" ", 2)
|
||||
# from section 2.3.1 of rfc1459
|
||||
def parse_message(message):
|
||||
meta = dict()
|
||||
meta["source"] = "nick" if "!" in prefix else "server"
|
||||
meta["type"] = "numeric" if isnumeric(command) else "alpha"
|
||||
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(parameters, dirt):
|
||||
params = parameters.partition(dirt)
|
||||
return params[0] + params[2]
|
||||
|
||||
# NOTE - collect *might* not be needed, most testing is needed
|
||||
def request(collect=None):
|
||||
response = irc_client.recv()
|
||||
if collect is not None:
|
||||
response = collect + response
|
||||
complete = response[-2:] == "\r\n"
|
||||
lines = response.split("\r\n")
|
||||
collect = None if complete else lines[-1]
|
||||
if not complete:
|
||||
lines = lines[:-1]
|
||||
return lines, collect
|
||||
|
||||
def need(collect, values, matches=dict()):
|
||||
assert isinstance(collect, str) or collect is None, "Expected str|None"
|
||||
assert isinstance(values, str), "Expected str"
|
||||
assert isinstance(matches, dict), "Expected dict"
|
||||
if "," in values:
|
||||
values = values.split(",")
|
||||
elif values != "*":
|
||||
values = [values]
|
||||
lines, collect = request(collect)
|
||||
results = list()
|
||||
for line in lines:
|
||||
meta = parse_message(line)
|
||||
matched = True
|
||||
for key, match in matches.items():
|
||||
if meta.get(key, None) != match:
|
||||
matched = False
|
||||
break
|
||||
if matched:
|
||||
if values == "*":
|
||||
results.append(meta)
|
||||
continue
|
||||
results.append({value: meta.get(value, None) for value in values})
|
||||
return results, collect
|
||||
|
||||
def need_one(collect, values, matches=dict()):
|
||||
results, collect = need(collect, values, matches)
|
||||
if len(results) == 1:
|
||||
return results[0], collect
|
||||
return dict(), collect
|
||||
|
||||
def need_param(collect, matches=dict(), default=""):
|
||||
result, collect = need_one(collect, "params", matches)
|
||||
return result.get("params", default), collect
|
||||
|
||||
def extract(response, key, after, dirt=":"):
|
||||
value = response.get(key, None)
|
||||
if value is None:
|
||||
return None
|
||||
bisect = cleanup(value, dirt).split(after, 1)
|
||||
if len(bisect) != 2:
|
||||
return None
|
||||
before, result = bisect
|
||||
return result.strip()
|
||||
|
||||
def extract_params(collect, after, matches, dirt=":"):
|
||||
result, collect = need_one(collect, "params", matches)
|
||||
return extract(result, "params", after), collect
|
||||
|
||||
name = "babili"
|
||||
first = False
|
||||
server = None
|
||||
collect = None
|
||||
irc_client.send("CAP LS")
|
||||
caps, collect = extract_params(collect, "* LS",
|
||||
{"source": "server", "command": "CAP"})
|
||||
if caps is not None:
|
||||
logger.debug(caps)
|
||||
irc_client.send(f"CAP REQ :{caps}")
|
||||
status, collect = need_param(collect, {"source": "server", "command": "CAP"})
|
||||
ack = "ACK" in status
|
||||
logger.debug("ACK!")
|
||||
irc_client.send("CAP END")
|
||||
irc_client.send(f"USER {name} - - -")
|
||||
irc_client.send(f"NICK {name}")
|
||||
while True:
|
||||
for letter in irc_client.recv():
|
||||
if letter is not None:# or len(letter) > 0:
|
||||
#meta = parse(letter)
|
||||
#print(meta)
|
||||
#source = meta["source"]
|
||||
#if not first and source == "server":
|
||||
# server = source
|
||||
# first = True
|
||||
#elif context == server and command == "CAP":
|
||||
logger.debug(letter)
|
||||
sleep(1)
|
||||
#for letter in irc_client.recv():
|
||||
# if letter is not None:
|
||||
# logger.debug(letter)
|
||||
responses, collect = need(collect, "params",
|
||||
{"source": "nick", "nick": "NickServ"})
|
||||
nickserv_complain = "please choose a different nick"
|
||||
register = False
|
||||
for response in responses:
|
||||
if nickserv_complain in response.get("params", ""):
|
||||
register = True
|
||||
break
|
||||
if register:
|
||||
logger.debug("Needs to register bot now")
|
||||
irc_client.stop()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from components.socket import IRCSocketClient
|
|
@ -0,0 +1,50 @@
|
|||
"""
|
||||
|
||||
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):
|
||||
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
|
Loading…
Reference in New Issue