mirror of
https://github.com/jlu5/PyLink.git
synced 2024-12-24 11:42:51 +01:00
Today's code dump, featuring:
- PLUGIN SUPPORT and COMMAND HANDLING, wow!!!!!!! - Restructuring of files so that there's only one protocol module (anything else is too much to maintain for now) - Split protocol things into utils.py - Bugfixes: don't go into an endless loop of text spamming when the remote host closes the connection!
This commit is contained in:
parent
80a2ce1d0a
commit
9b4fb50f25
@ -3,6 +3,7 @@ bot:
|
||||
nick: pylink
|
||||
user: pylink
|
||||
realname: PyLink Service Client
|
||||
prefix: "@"
|
||||
|
||||
login:
|
||||
# PyLink administrative login - Change this, or the service will not start!
|
||||
@ -21,6 +22,8 @@ server:
|
||||
# SID - required for InspIRCd and TS6 based servers. This must be three characters long.
|
||||
# The first char must be a digit [0-9], and the remaining two chars may be letters [A-Z] or digits.
|
||||
sid: "0AL"
|
||||
# Set protocol module
|
||||
protocol: inspircd
|
||||
channel: "#pylink"
|
||||
|
||||
# Plugins to load (omit the .py extension)
|
||||
plugins:
|
||||
- hello
|
||||
|
57
main.py
57
main.py
@ -7,6 +7,9 @@ import threading
|
||||
import socket
|
||||
import multiprocessing
|
||||
import time
|
||||
import sys
|
||||
|
||||
import proto
|
||||
print('PyLink starting...')
|
||||
|
||||
with open("config.yml", 'r') as f:
|
||||
@ -15,19 +18,6 @@ with open("config.yml", 'r') as f:
|
||||
# if conf['login']['password'] == 'changeme':
|
||||
# print("You have not set the login details correctly! Exiting...")
|
||||
|
||||
class IrcUser():
|
||||
def __init__(self, nick, ts, uid, ident='null', host='null',
|
||||
realname='PyLink dummy client', realhost='null',
|
||||
ip='0.0.0.0'):
|
||||
self.nick = nick
|
||||
self.ts = ts
|
||||
self.uid = uid
|
||||
self.ident = ident
|
||||
self.host = host
|
||||
self.realhost = realhost
|
||||
self.ip = ip
|
||||
self.realname = realname
|
||||
|
||||
class Irc():
|
||||
def __init__(self):
|
||||
# Initialize some variables
|
||||
@ -36,6 +26,7 @@ class Irc():
|
||||
self.users = {}
|
||||
self.channels = {}
|
||||
self.name = conf['server']['netname']
|
||||
self.conf = conf
|
||||
|
||||
self.serverdata = conf['server']
|
||||
ip = self.serverdata["ip"]
|
||||
@ -43,23 +34,12 @@ class Irc():
|
||||
self.sid = self.serverdata["sid"]
|
||||
print("Connecting to network %r on %s:%s" % (self.name, ip, port))
|
||||
|
||||
protoname = self.serverdata['protocol']
|
||||
# With the introduction of Python 3, relative imports are no longer
|
||||
# allowed from normal applications ran from the command line. Instead,
|
||||
# these imported libraries must be installed as a package using distutils
|
||||
# or something similar.
|
||||
#
|
||||
# But I don't want that! Where PyLink is at right now (a total WIP), it is
|
||||
# a lot more convenient to run the program directly from the source folder.
|
||||
protocols_folder = [os.path.join(os.getcwd(), 'protocols')]
|
||||
# Here, we override the module lookup and import the protocol module
|
||||
# dynamically depending on which module was configured.
|
||||
moduleinfo = imp.find_module(protoname, protocols_folder)
|
||||
self.proto = imp.load_source(protoname, moduleinfo[1])
|
||||
self.socket = socket.socket()
|
||||
self.socket.connect((ip, port))
|
||||
self.proto.connect(self)
|
||||
proto.connect(self)
|
||||
self.connected = True
|
||||
self.loaded = []
|
||||
self.load_plugins()
|
||||
self.run()
|
||||
|
||||
def run(self):
|
||||
@ -67,22 +47,33 @@ class Irc():
|
||||
data = ""
|
||||
while self.connected:
|
||||
try:
|
||||
data += self.socket.recv(1024).decode("utf-8")
|
||||
data = self.socket.recv(4096).decode("utf-8")
|
||||
buf += data
|
||||
if not data:
|
||||
break
|
||||
buf += data
|
||||
while '\n' in buf:
|
||||
line, buf = buf.split('\n', 1)
|
||||
print("<- {}".format(line))
|
||||
self.proto.handle_events(self, line)
|
||||
except socket.error:
|
||||
proto.handle_events(self, line)
|
||||
except socket.error as e:
|
||||
print('Received socket.error: %s, exiting.' % str(e))
|
||||
self.connected = False
|
||||
sys.exit(1)
|
||||
break
|
||||
sys.exit(1)
|
||||
|
||||
def send(self, data):
|
||||
data = data.encode("utf-8") + b"\n"
|
||||
print("-> {}".format(data.decode("utf-8").strip("\n")))
|
||||
self.socket.send(data)
|
||||
|
||||
def load_plugins(self):
|
||||
to_load = conf['plugins']
|
||||
plugins_folder = [os.path.join(os.getcwd(), 'plugins')]
|
||||
# Here, we override the module lookup and import the plugins
|
||||
# dynamically depending on which were configured.
|
||||
for plugin in to_load:
|
||||
moduleinfo = imp.find_module(plugin, plugins_folder)
|
||||
self.loaded.append(imp.load_source(plugin, moduleinfo[1]))
|
||||
print("loaded plugins: %s" % self.loaded)
|
||||
|
||||
|
||||
irc_obj = Irc()
|
||||
|
7
plugins/hello.py
Normal file
7
plugins/hello.py
Normal file
@ -0,0 +1,7 @@
|
||||
import sys, os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
import proto
|
||||
|
||||
@proto.add_cmd
|
||||
def hello(irc, source, command, args):
|
||||
proto._sendFromUser(irc, 'PRIVMSG %s :hello!' % source)
|
@ -2,26 +2,29 @@ import threading
|
||||
import socket
|
||||
import time
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
from utils import *
|
||||
|
||||
# TODO: make PyLink a package so I don't have to hack the import system
|
||||
# like this.
|
||||
from os import sys, path
|
||||
sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
|
||||
from main import IrcUser
|
||||
global bot_commands
|
||||
# This should be a mapping of command names to functions
|
||||
bot_commands = {}
|
||||
|
||||
# From http://www.inspircd.org/wiki/Modules/spanningtree/UUIDs.html
|
||||
chars = string.digits + string.ascii_uppercase
|
||||
iters = [iter(chars) for _ in range(6)]
|
||||
a = [next(i) for i in iters]
|
||||
class IrcUser():
|
||||
def __init__(self, nick, ts, uid, ident='null', host='null',
|
||||
realname='PyLink dummy client', realhost='null',
|
||||
ip='0.0.0.0'):
|
||||
self.nick = nick
|
||||
self.ts = ts
|
||||
self.uid = uid
|
||||
self.ident = ident
|
||||
self.host = host
|
||||
self.realhost = realhost
|
||||
self.ip = ip
|
||||
self.realname = realname
|
||||
|
||||
def next_uid(sid, level=-1):
|
||||
try:
|
||||
a[level] = next(iters[level])
|
||||
return sid + ''.join(a)
|
||||
except StopIteration:
|
||||
return UID(level-1)
|
||||
def __repr__(self):
|
||||
keys = [k for k in dir(self) if not k.startswith("__")]
|
||||
return ','.join(["%s=%s" % (k, getattr(self, k)) for k in keys])
|
||||
|
||||
def _sendFromServer(irc, msg):
|
||||
irc.send(':%s %s' % (irc.sid, msg))
|
||||
@ -32,7 +35,7 @@ def _sendFromUser(irc, msg, user=None):
|
||||
irc.send(':%s %s' % (user, msg))
|
||||
|
||||
def _join(irc, channel):
|
||||
_sendFromUser(irc, "FJOIN {channel} {ts} +nt :,{uid}".format(sid=irc.sid,
|
||||
_sendFromUser(irc, "JOIN {channel} {ts} +nt :,{uid}".format(sid=irc.sid,
|
||||
ts=int(time.time()), uid=irc.pseudoclient.uid, channel=channel))
|
||||
|
||||
def _uidToNick(irc, uid):
|
||||
@ -47,7 +50,7 @@ def connect(irc):
|
||||
irc.pseudoclient = IrcUser('PyLink', ts, uid, 'pylink', host,
|
||||
'PyLink Client')
|
||||
irc.users['PyLink'] = irc.pseudoclient
|
||||
|
||||
|
||||
f = irc.send
|
||||
f('CAPAB START 1203')
|
||||
# This is hard coded atm... We should fix it eventually...
|
||||
@ -71,13 +74,24 @@ def connect(irc):
|
||||
|
||||
# :7NU PING 7NU 0AL
|
||||
def handle_ping(irc, servernumeric, command, args):
|
||||
if args[3] == irc.sid:
|
||||
_sendFromServer(irc, 'PONG %s' % args[2])
|
||||
if args[1] == irc.sid:
|
||||
_sendFromServer(irc, 'PONG %s' % args[1])
|
||||
|
||||
def handle_privmsg(irc, numeric, command, args):
|
||||
def handle_privmsg(irc, source, command, args):
|
||||
# _sendFromUser(irc, 'PRIVMSG %s :hello!' % numeric)
|
||||
print(irc.users)
|
||||
print(irc.channels)
|
||||
prefix = irc.conf['bot']['prefix']
|
||||
if args[0] == irc.pseudoclient.uid:
|
||||
cmd_args = args[1].split(' ', 1)
|
||||
cmd = cmd_args[0]
|
||||
try:
|
||||
cmd_args = cmd_args[1]
|
||||
except IndexError:
|
||||
cmd_args = []
|
||||
try:
|
||||
bot_commands[cmd](irc, source, command, args)
|
||||
except KeyError:
|
||||
_sendFromUser(irc, 'PRIVMSG %s :unknown command %r' % (source, cmd))
|
||||
|
||||
def handle_error(irc, numeric, command, args):
|
||||
print('Received an ERROR, killing!')
|
||||
@ -89,11 +103,9 @@ def handle_fjoin(irc, servernumeric, command, args):
|
||||
channel = args[0]
|
||||
# tl;dr InspIRCd sends each user's channel data in the form of 'modeprefix(es),UID'
|
||||
# We'll save each user in this format too, at least for now.
|
||||
print(args)
|
||||
users = args[-1].split()
|
||||
users = [x.split(',') for x in users]
|
||||
print(users)
|
||||
|
||||
|
||||
'''
|
||||
if channel not in irc.channels.keys():
|
||||
irc.channels[channel]['users'] = users
|
||||
@ -103,7 +115,7 @@ def handle_fjoin(irc, servernumeric, command, args):
|
||||
'''
|
||||
|
||||
def handle_uid(irc, numeric, command, args):
|
||||
# :1SR UID 1SRAAAAAU 1428974823 synnero ow.my.eye.rs ow.my.eye.rs GLolol 2604:180:1::d34d:d87b 1425951245 +Wiosw +ACGKNOQXacfgklnoqvx :move along, nothing to see here!
|
||||
# :70M UID 70MAAAAAB 1429934638 GL 0::1 hidden-7j810p.9mdf.lrek.0000.0000.IP gl 0::1 1429934638 +Wioswx +ACGKNOQXacfgklnoqvx :realname
|
||||
uid, ts, nick, realhost, host, ident, ip = args[0:7]
|
||||
realname = args[-1]
|
||||
irc.users[nick] = IrcUser(nick, ts, uid, ident, host, realname, realhost, ip)
|
||||
@ -112,11 +124,13 @@ def handle_quit(irc, numeric, command, args):
|
||||
# :1SRAAGB4T QUIT :Quit: quit message goes here
|
||||
nick = _uidToNick(irc, numeric)
|
||||
del irc.users[nick]
|
||||
'''
|
||||
for k, v in irc.channels.items():
|
||||
try:
|
||||
del irc.channels[k][users][v]
|
||||
except KeyError:
|
||||
pass
|
||||
'''
|
||||
|
||||
def handle_events(irc, data):
|
||||
# Each server message looks something like this:
|
||||
@ -146,6 +160,8 @@ def handle_events(irc, data):
|
||||
|
||||
numeric = args[0]
|
||||
command = args[1]
|
||||
args = args[2:]
|
||||
print(args)
|
||||
except IndexError:
|
||||
return
|
||||
|
||||
@ -155,3 +171,6 @@ def handle_events(irc, data):
|
||||
func(irc, numeric, command, args)
|
||||
except KeyError: # unhandled event
|
||||
pass
|
||||
|
||||
def add_cmd(func):
|
||||
bot_commands[func.__name__.lower()] = func
|
@ -1,8 +0,0 @@
|
||||
def connect(irc):
|
||||
print('%s: Using PyLink stub/testing protocol.' % irc.name)
|
||||
print('Send password: %s' % irc.serverdata['sendpass'])
|
||||
print('Receive password: %s' % irc.serverdata['recvpass'])
|
||||
print('Server: %s:%s' % (irc.serverdata["ip"], irc.serverdata["port"]))
|
||||
|
||||
def handle_events(irc, data):
|
||||
print('%s: Received event: %s' % (irc.name, data))
|
13
utils.py
Normal file
13
utils.py
Normal file
@ -0,0 +1,13 @@
|
||||
import string
|
||||
|
||||
# From http://www.inspircd.org/wiki/Modules/spanningtree/UUIDs.html
|
||||
chars = string.digits + string.ascii_uppercase
|
||||
iters = [iter(chars) for _ in range(6)]
|
||||
a = [next(i) for i in iters]
|
||||
|
||||
def next_uid(sid, level=-1):
|
||||
try:
|
||||
a[level] = next(iters[level])
|
||||
return sid + ''.join(a)
|
||||
except StopIteration:
|
||||
return UID(level-1)
|
Loading…
Reference in New Issue
Block a user