# point/__init__.py
#
#
""" point package. """
__version__ = 12
## IMPORTS
from point.utils import *
from point.log import datefmt
## BASIC IMPORTS
import collections
import threading
import logging
import getpass
import _thread
import socket
import queue
import types
import errno
import select
import fcntl
import uuid
import json
import time
import imp
import sys
import os
import re
## ERRORS
[docs]class Error(BaseException): pass
[docs]class OverloadError(Error): pass
[docs]class MissingArgument(Error): pass
[docs]class MissingOutFunction(Error): pass
[docs]class NoText(Error): pass
[docs]class NoDate(Error): pass
[docs]class NoPath(Error): pass
[docs]class NoJSON(Error): pass
[docs]class NoFileName(Error): pass
[docs]class SignatureError(Error): pass
[docs]class NoSuchBotType(Error): pass
[docs]class NoEvent(Error): pass
[docs]class RemoteDisconnect(Error): pass
[docs]class NotOne(Error): pass
[docs]class TargetMissing(Error): pass
[docs]class NotSet(Error): pass
## BOOT
homedir = os.path.expanduser("~")
[docs]def boot(*args, **kwargs):
global kernel
cfg = args[0]
kernel.cfg.update(cfg)
if cfg.shell: hello("POINT")
if not kernel.cfg.workdir: kernel.cfg.workdir = j(homedir, "point.data", "")
make_dir(kernel.cfg.workdir)
if not kernel.cfg.loglevel: cfg.loglevel = "error"
from point.log import log_config
log_config(cfg.loglevel)
if kernel.cfg.loglevel:
logging.warn("E G G S")
logging.warn("")
show_eggs()
if kernel.cfg.loglevel:
logging.warn("")
logging.warn("C O N F I G")
logging.warn("")
for line in kernel.cfg.show(): logging.warn(line)
if kernel.cfg.loglevel:
logging.warn("")
logging.warn("B O O T")
logging.warn(" ")
kernel.plugs.load_package("point.plugs")
try: kernel.plugs.load_package("evidence.plugs")
except: logging.warn("no evidence")
kernel.plugs.init()
if kernel.cfg.shell: print(" ")
return kernel
## SHUTDOWN
[docs]def shutdown():
global kernel
if kernel.cfg.shell: print("")
logging.warn("shutdown is here !!")
for obj in kernel.run.obj_iter():
if obj == None: continue
try: obj.exit()
except: error()
kernel.cmnds.exit()
kernel.plugs.exit()
try: sys.stdout.flush() ; sys.stdout.close()
except: pass
os._exit(0)
## BASE
[docs]class Object(dict):
def __getattribute__(p, *args, **kwargs):
name = args[0]
result = ""
if name == "what": result = get_cls(p)
if name == "modname": result = get_modname(p)
if name == "plugname": result = get_plugname(3)
if name == "func": result = get_func(5)
if name == "kind": result = mj(p.modname, p.what, p.func)
if name == "stdin": return sys.stdin
if name == "stdout": return sys.stdout
if result: return result
return dict.__getattribute__(p, *args, **kwargs)
def __getattr__(p, name):
try: return p[name]
except KeyError:
if name == "timed":
val = ""
if "Date" in p: val = p.Date
elif "date" in p: val = p.date
elif "published" in p: val = p.published
if val: val = short_date(val)
elif "added" in p: val = to_time(p.added)
#else: result = time_time(time.time())
if not val: raise NoDate()
return val
if name == "_result": p["_result"] = Object()
if name == "_status": p["_status"] = Object()
if name == "_ready": p["_ready"] = threading.Event()
return p[name]
def __setattr__(p, name, value): return dict.__setitem__(p, name, value)
def __exists__(p, a):
try: return p[a]
except KeyError: False
def __lt__(p, a): return time_stamp(p.ddate) < time_stamp(a.ddate)
def __iter__(p): return p.clean_keys()
[docs] def obj_iter(p, *args, **kwargs):
for key in p.clean_keys(): yield p[key]
## TOUCH
[docs] def touch(p, *args, **kwargs):
try: res = getattr(p, args[0])
except (TypeError, AttributeError): res = None
return res
## LOCATORS
[docs] def get_root(p, *args, **kwargs): return j(os.path.expanduser("~"), *args)
[docs] def get_roots(p, *args, **kwargs):
result = []
return result
[docs] def get_target(p):
if "_target" in p: return p._target
return None
## OUTPUT
[docs] def to_json(p, *args, **kwargs): return json.dumps(p.clean(*args, **kwargs), default=smooth, *args, **kwargs)
[docs] def to_full(p, *args, **kwargs): return json.dumps(p, default=smooth, *args, **kwargs)
[docs] def make_signature(p, sig=None):
if "data" in p: return str(hashlib.sha1(bytes(str(p.data), "utf-8")).hexdigest())
raise NotSet
[docs] def make_path(p, *args, **kwargs): path = p.get_path(*args, **kwargs) ; make_dir(path) ; return path
## INPUT
[docs] def exit(p, *args, **kwargs): pass
[docs] def load(p, *args, **kwargs):
if args: path = p.make_path(*args, **kwargs)
else: path = p.make_path()
return p.load_file(path)
[docs] def load_file(p, *args, **kwargs):
path = args[0]
logging.debug("load_file %s" % " ".join(args))
ondisk = p.read(path)
fromdisk = json.loads(ondisk)
if "data" in fromdisk: p.update(fromdisk["data"])
return p
[docs] def load_json(p, *args, **kwargs): p.update(json.loads(args[0]))
[docs] def read(p, *args, **kwargs):
logging.debug("read %s" % " ".join(args))
path = args[0]
try: f = open(path, "r")
except IOError as ex:
if ex.errno == errno.ENOENT: return "{}"
raise
if "do_test" in p: f.line_buffering = False
res = ""
for line in f:
if not line.strip().startswith("#"): res += line
if not res.strip(): return "{}"
f.close()
return res
## PERSISTENCE
[docs] def dump(p, *args, **kwargs):
# object attributes settins before save
path = args[0]
p.path = path
todisk = Object()
todisk.create_type = p.what
todisk.stime = time.ctime(time.time())
todisk.modname = p.modname
todisk.version = __version__
todisk.data = p.clean()
todisk.signature = todisk.make_signature()
try: todisk.data.timed = p.timed
except NoDate: todisk.data.timed = to_time(time.ctime(time.time()))
try: result = todisk.to_json(indent=2, ensure_ascii=False)
except TypeError: raise NoJSON()
return result
[docs] def save(p, *args, **kwargs):
if not args: t = rtime()
else: t = args[0]
root = j(kernel.cfg.workdir, t)
make_dir(root)
p.sync(root)
[docs] def sync(p, *args, **kwargs):
# syn the object to disk
try: path = args[0]
except IndexError: path = p.path
logging.info("save %s" % " ".join(args))
fn = path.split(os.sep)[-1]
todisk = p.dump(path, **kwargs)
datafile = open(path + ".tmp", 'w')
fcntl.flock(datafile, fcntl.LOCK_EX | fcntl.LOCK_NB)
datafile.write(headertxt % (fn, __version__, "%s characters" % len(todisk)))
datafile.write(todisk)
datafile.write("\n")
fcntl.flock(datafile, fcntl.LOCK_UN)
datafile.close()
os.rename(path + ".tmp", path)
logging.info("saved %s" % path)
return p
## STATE
[docs] def register(p, *args, **kwargs):
name = args[0]
obj = args[1]
p[name] = obj
[docs] def clean_keys(p, *args, **kwargs):
for key in p.keys():
k = str(key)
if k.startswith("_"): continue
if k == "format": continue
if args and k in args[0]: continue
yield key
[docs] def clean(p, *args, **kwargs):
res = Object()
for key in p.clean_keys(): res[key] = p[key]
return res
[docs] def pretty(p): return json.dumps(p, indent=2)
## GETTIES
[docs] def get_rest(p, *args, **kwargs):
try: cmnd, rest = p.txt.split(" ", 1) ; return rest
except ValueError: return ""
## OBJECTORS
[docs] def objects(p, *args, **kwargs):
path = p.get_root(kernel.cfg.workdir, *args, **kwargs)
objs = []
for fnn in list_files(path, **kwargs):
obj = Object().load_file(fnn)
if "deleted" in obj: continue
objs.append(obj)
return objs
[docs] def has_obj(p, *args, **kwargs):
key = args[0]
value = args[1]
for obj in p.objects():
if key not in obj: continue
if value in obj[key]: return obj[key]
return None
[docs] def check_wanted(p, *args, **kwargs):
want = args[0]
for key, value in want.items():
if key == "format": continue
if key not in p: continue
if value.startswith("-"): continue
if value not in str(p[key]): return False
return True
[docs] def check_notwanted(p, *args, **kwargs):
not_want = args[0]
for key, value in not_want.items():
if key == "format": continue
if key not in p: continue
if value in p[key]: return True
return False
[docs] def selector(p, *args, **kwargs):
wanted = args[0]
keys = p.keys()
for w in wanted:
if w in keys: return True
return False
[docs] def parse(p, *args, **kwargs):
if "txt" not in p: logging.debug("txt not set: %s" % str(p)) ; return
result = Object()
result.update(p)
splitted = p.txt.split()
for word in splitted:
if word.startswith("."): result["ucmnd"] = word[1:]
if word.startswith("-"): result[word] = "knob" ; continue
if splitted and "ucmnd" not in result: result["ucmnd"] = splitted[0]
return result
[docs] def slice(p, *args, **kwargs):
""" args: list of keywords to slice the dict. """
o = Object()
arguments = args[0]
for arg in arguments:
try: o[arg] = p[arg]
except KeyError: continue
return o
## RESULT
[docs] def ready(p): p._ready.set()
[docs] def clear(p): p._ready.clear()
[docs] def wait(p, sec=180.0):
try: p._ready.wait(sec)
except: pass
[docs] def add(p, *args, **kwargs): p._result[time.time()] = args[0]
[docs] def reply(p, *args, **kwargs):
if "outer" in p: p.outer.write(str(args[0]) + "\n") ; p.outer.flush() ; return
if "format" in p and p.format == "timed":
tijd = to_time(time.ctime(time.time()))
txt = "%s " % tijd
else: txt = ""
txt += str(args[0])
if "channel" in p: p.say(p.channel, txt)
else: p.say("", txt)
# BHJTW: str() kan verkeert uitpakken ..
[docs] def say(p, *args, **kwargs): p._target.say(args[0], str(args[1]))
[docs] def show(p): return ["%s=%s" % (a, b) for a, b in p.items() if b]
[docs] def display(p, *args, **kwargs):
for res in sorted(p._result.keys()):
d = p._result[res]
if "channel" in p: p.say(p.channel, d)
else: p.say("", d)
p.ready()
## first and last
[docs] def first(p, *args, **kwargs):
key = args[0]
try: value = args[1]
except IndexError: value = None
result = None
for o in p.objects():
if key in o: continue
if value and o[key] != value: continue
if not result: result = o
if o.timed < result.timed: result = o
return result
[docs] def last(p, *args, **kwargs):
key = args[0]
try: value = args[1]
except IndexError: value = None
result = None
for o in p.objects():
if key not in o: continue
if value and o[key] != value: continue
if not result: result = o
if o.timed > result.timed: result = o
return result
## TASKS
[docs]class Runner(threading.Thread):
def __init__(p, *args, **kwargs):
threading.Thread.__init__(p, None, p._loop, "thread.%s" % str(time.time()), args, kwargs)
p.setDaemon(True)
p._queue = queue.Queue()
p._outqueue = queue.Queue()
p._state = "mainloop"
p.running = "waiting"
p.time_start = time.time()
def _loop(p):
while p._state:
p._state = "waiting"
args, kwargs = p._queue.get()
if not p._state: break
func = args[0]
try: obj = args[1]
except: obj = Object()
logging.info("run %s" % str(func))
p.running = get_funcname(str(func))
p._state = "running"
p.time_start = time.time()
try: func(*args[1:], **kwargs)
except: error()
time.sleep(0.01)
try: obj.ready()
except AttributeError: pass
kernel.ready()
_thread.interrupt_main()
[docs] def exit(p, *args, **kwargs): p._state = ""
[docs] def put(p, *args, **kwargs): p._queue.put((args, kwargs))
## DISPATCH
[docs]class Dispatcher(Object):
def __init__(p, *args, **kwargs):
Object.__init__(p, *args, **kwargs)
p._state = "running"
p.runners = collections.deque()
p.max = 50
[docs] def is_alive(p, *args, **kwargs):
for runner in p.runners:
if runner.is_alive(): return True
return False
[docs] def register(p, cbtype, cb, *args, **kwargs):
logging.debug("register %s" % cbtype)
if cbtype not in p: p[cbtype] = []
p[cbtype].append(cb)
[docs] def dispatch(p, *args, **kwargs):
event = args[0]
parsed = event.parse()
if not parsed: return
if "ucmnd" in parsed: p.execute(parsed.ucmnd, event)
[docs] def execute(p, *args, **kwargs):
cmnd = args[0]
event = args[1]
if cmnd not in p: event.ready() ; return
for func in p[cmnd]:
logging.info("exec %s" % str(func))
try: getattr(func, "threaded") ; kernel.workers.put(func, event)
except AttributeError: func(event)
[docs] def do_func(p, *args, **kwargs):
cmnd = args[0]
event = args[1]
if cmnd not in p: return
for func in p[cmnd]: func(event)
[docs] def exit(p, name=None):
for runner in p.runners:
if name and name not in runner.name: continue
runner.join(0.1)
p.ready()
[docs] def put(p, *args, **kwargs):
target = p.make_new()
target.put(*args, **kwargs)
return target
[docs] def make_new(p, *args, **kwargs):
for runner in p.runners:
if runner._state != "running": return runner
if len(p._runners) < p.max:
runner = Runner(*args, **kwargs)
p.runners.append(runner)
runner.start()
else: runner = random.choice(p._runners)
return runner
[docs] def cleanup(p, dojoin=False):
todo = []
for runner in p.runners:
if runner.stopped or not len(runner.queue): todo.append(runner)
for runner in todo: runner.stop() ; p.runners.remove(runner)
## PLUGINS
[docs]class Plugins(Object):
def __init__(p, *args, **kwargs):
Object.__init__(p, *args, **kwargs)
p.plugins = []
[docs] def get_names(p, plugsdir): return [x[:-3] for x in os.listdir(plugsdir) if x.endswith(".py")]
[docs] def load_plugs(p, path):
logging.warn("load %s" % path)
for plugname in p.get_names(path):
if "__" in plugname: continue
try:
p[plugname] = p.load_mod(plugname, path, force=True)
if plugname not in p.plugins: p.plugins.append(plugname)
except: error() ; continue
p.ready()
[docs] def load_package(p, modname):
mod = __import__(modname)
path, fn = os.path.split(mod.__file__)
path += os.sep + "plugs"
p.load_plugs(path)
[docs] def load_mod(p, plugname, pdir="", force=False):
logging.info("load %s" % plugname)
if plugname in p:
if not force: return p[plugname]
p[plugname] = imp.reload(p[plugname])
else:
if not pdir: pdir = j(p.root, "plugs")
search = imp.find_module(plugname, [pdir,])
p[plugname] = imp.load_module(plugname, *search)
return p[plugname]
[docs] def plug_exec(p, plugname, item):
logging.info("%s %s" % (item, plugname))
try:
todo = getattr(p[plugname], item)
todo()
except AttributeError: pass
[docs] def exit(p, *args, **kwargs):
for plugname in p.clean():
try: p.plug_exec(plugname, "shutdown") ; del p[plugname]
except KeyError: pass
p.ready()
[docs] def reload(p, plugname, force=False):
p.unload(plugname)
mod = p.load_mod(plugname, force)
return mod
[docs] def init(p, *args, **kwargs):
if args: target = args[0]
else: target = kernel.cfg.init
for plugname in p.plugins:
if target and target != "all" and plugname != target: continue
p.plug_exec(plugname, "init")
## BOTS
[docs]class Bot(Dispatcher):
def __init__(p, *args, **kwargs):
Dispatcher.__init__(p, *args, **kwargs)
p.time_in = 0
p.time_out = 0
p.time_start = time.time()
[docs] def exit(p, *args, **kwargs): p._state = "" ; p.ready()
[docs] def stop(p, *args, **kwargs): p._state = ""
[docs] def status(p, *args, **kwargs): return p._state
[docs] def join_channels(p, *args, **kwargs): pass
[docs] def begin(p, *args, **kwargs):
if p not in kernel.fleet: kernel.fleet.append(p)
p.connect()
while p._state:
time.sleep(0.01)
try: event = p.do_one()
except RemoteDisconnect: time.sleep(2) ; p.connect()
if p._state == "once":
break
kernel.ready()
_thread.interrupt_main()
[docs] def do_one(p, *args, **kwargs): pass
[docs] def get_prompt(p, *args, **kwargs): return ""
[docs] def connect(p, *args, **kwargs): pass
[docs] def announce(p, *args, **kwargs):
if "channels" in p:
for channel in p.channels: p.say(channel, args[0], **kwargs)
else: p.say(*args, **kwargs)
## CONSOLE
[docs]class ConsoleBot(Bot):
def _raw(p, *args, **kwargs):
txt = args[0]
try: sys.stdout.write(txt) ; sys.stdout.write("\n") ; sys.stdout.flush()
except Exception as ex: logging.warn(str(ex))
[docs] def say(p, *args, **kwargs):
try: txt = args[1]
except IndexError: txt = args[0]
p._raw(txt)
[docs] def get_prompt(p, *args, **kwargs): return "%s %s%s<%s " % (to_time(time.ctime(time.time())), BOLD, YELLOW, ENDC)
[docs] def do_one(p, *args, **kwargs):
o = Object()
if not kernel.cfg.shell: in_txt = " ".join(kernel.cfg.runargs) ; p._state = "once"
else: in_txt = input(p.get_prompt())
o.txt = in_txt
o._target = p
kernel.cmnds.dispatch(o)
p.time_in = time.time()
return o
## IRC
[docs]class IRCBot(Bot):
marker = "\r\n"
cc = "."
def __init__(p, *args, **kwargs):
Bot.__init__(p, *args, **kwargs)
p.connected = threading.Event()
p.register("004", p._onconnect)
p.register("513", p.handle_513)
p.register("433", p.handle_433)
p.register("366", p.handle_366)
p.register("PING", p.handle_ping)
p.register("INVITE", p.handle_invite)
p.register("PRIVMSG", p.handle_privmsg)
p.register("NOTICE", p.handle_notice)
p._lock = _thread.allocate_lock()
p._buffer = []
p._lastline = ""
p.encoding = "utf-8"
if "realname" not in p: p.realname = "point"
if "server" not in p: p.server = "localhost"
if "port" not in p: p.port = 6667
if "nick" not in p: p.nick = "point"
if "channels" not in p: p.channels = []
if "channel" in p and p["channel"] not in p.channels: p.channels.append(p.channel)
def _raw(p, txt):
logging.warn("> %s" % txt)
if not txt.endswith(p.marker): txt += p.marker
txt = bytes(txt, p.encoding)
if 'ssl' in p and p.ssl: p.sock.write(txt)
else: p.sock.send(txt[:512])
def _connect(p):
p.stopped = False
if "ipv6" in p: p.oldsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
else: p.oldsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
p.server = p.bind()
logging.warn('connect %s - %s' % (p.server, p.port))
p.oldsock.settimeout(60)
p.oldsock.connect((p.server, int(str(p.port or 6667))))
p.blocking = 1
p.oldsock.setblocking(p.blocking)
logging.warn('connection made to %s' % p.server)
p.fsock = p.oldsock.makefile("r")
if "blocking" in p: p.oldsock.settimeout(301.0)
if 'ssl' in p and p['ssl']: p.sock = socket.ssl(p.oldsock)
else: p.sock = p.oldsock
p.connecttime = time.time()
p.ready()
return True
def _onconnect(p, *args, **kwargs):
logging.warn("logged on !!")
if "onconnect" in p: time.sleep(0.5) ; p._raw(p.onconnect)
if "servermodes" in p: p._raw("MODE %s %s" % (p.nick, p.servermodes))
p.wait()
p.join_channels()
def _dodcc(p, event, s):
s.send(bytes('Welcome to POINT ' + event.nick + " !!\n", p.encoding))
_thread.start_new_thread(p._dccloop, (event, s))
def _dccloop(p, event, s):
sockfile = s.makefile('rw')
s.setblocking(True)
while 1:
try:
res = sockfile.readline()
if not res: break
res = res.rstrip()
logging.warn("< DCC %s" % res)
o = Object(_target=p, txt=res)
o.outer = sockfile
kernel.cmnds.dispatch(o)
except socket.timeout: time.sleep(0.01)
except socket.error as ex:
if ex.errno in [EAGAIN, ]: continue
else: raise
except Exception as ex: error()
sockfile.close()
def _dccconnect(p, event):
try:
rest = event.get_rest()
parsed = txt_parse(rest)
addr = parsed.args[2] ; port = parsed.args[3][:-1]
port = int(port)
if re.search(':', addr): s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
else: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((addr, port))
except Exception as ex: error() ; return
p._dodcc(event, s)
[docs] def do_one(p, *args, **kwargs):
if not p._buffer: p.read_some()
try: line = p._buffer.pop(0)
except IndexError: return Object()
e = Object()
e._target = p
o = irc_parse(e, line.rstrip())
try: p.do_func(o.etype, o)
except: error()
p.time_in = time.time()
return o
[docs] def read_some(p, *args, **kwargs):
try:
if "ssl" in p and p.ssl: inbytes = p.sock.read()
else: inbytes = p.sock.recv(512)
except socket.timeout: return
except Exception as ex: error() ; return
try: txt = str(inbytes, p.encoding)
except UnicodeDecodeError: txt = " "
if txt == "": raise RemoteDisconnect()
p._lastline += txt
splitted = p._lastline.split(p.marker)
for s in splitted[:-1]: logging.warn("< %s" % s) ; p._buffer.append(s)
p._lastline = splitted[-1]
[docs] def send(p, txt): p._raw(txt) ; time.sleep(3.0)
[docs] def bind(p):
server = p.server
try: p.oldsock.bind((server, 0))
except socket.error:
if not server:
try: socket.inet_pton(socket.AF_INET6, p.server)
except socket.error: pass
else: server = p.server
if not server:
try: socket.inet_pton(socket.AF_INET, p.server)
except socket.error: pass
else: server = p.server
if not server:
ips = []
try:
for item in socket.getaddrinfo(p.server, None):
if item[0] in [socket.AF_INET, socket.AF_INET6] and item[1] == socket.SOCK_STREAM:
ip = item[4][0]
if ip not in ips: ips.append(ip)
except socket.error: pass
else: server = random.choice(ips)
return server
[docs] def logon(p):
if "password" in p: p._raw("PASS %s" % p.password)
logging.warn('logging in on %s - this may take a while' % p.server)
p._raw("NICK %s" % p.nick or "p")
p._raw("USER %s localhost %s :%s" % (p.username or "p", p.server or "localhost", p.realname))
[docs] def say(p, *args, **kwargs):
try: channel, txt = args
except ValueError: channel = p.channel ; txt = args[0]
p.privmsg(channel, txt)
[docs] def join_channels(p, *args, **kwargs):
for channel in p.channels: p.join(channel)
[docs] def broadcast(p, txt):
for i in p.channels: p.say(i, txt)
[docs] def connect(p, reconnect=True):
try: res = p._connect()
except: error() ; return
if res: time.sleep(1) ; p.logon()
return res
[docs] def close(p):
if 'ssl' in p and p['ssl']: p.oldsock.shutdown(2) ; p.oldsock.close()
else: p.sock.shutdown(2) ; p.sock.close()
p.fsock.close()
[docs] def handle_notice(p, event): pass
[docs] def handle_ping(p, event): p.pongcheck = True ; p.pong()
[docs] def handle_433(p, event): p.donick(event.arguments[1] + "_")
[docs] def handle_invite(p, event): p.join(event.channel)
[docs] def handle_privmsg(p, event):
if event.txt.startswith("\001DCC"): p._dccconnect(event) ; return
if event.txt and event.txt[0] == p.cc: kernel.cmnds.dispatch(event)
[docs] def handle_513(p, event): p._raw("PONG %s" % event.arguments[6])
[docs] def handle_ctcp(p, event):
if event.txt and event.txt[0] == p.cc: resolve(event)
[docs] def donick(p, nick, setorig=False, save=False, whois=False): p.send('NICK %s\n' % nick[:16])
[docs] def join(p, channel, password=None):
if password: p._raw('JOIN %s %s' % (channel, password))
else: p._raw('JOIN %s' % channel)
[docs] def handle_366(p, *args, **kwargs): p.ready()
[docs] def part(p, channel):
p.send('PART %s' % channel)
if channel in p.channels: p.channels.remove(channel) ; p.save()
[docs] def who(p, who): p.send('WHO %s' % who.strip())
[docs] def names(p, channel): p.send('NAMES %s' % channel)
[docs] def whois(p, who): p.send('WHOIS %s' % who)
[docs] def privmsg(p, printto, what): p.send('PRIVMSG %s :%s' % (printto, what))
[docs] def voice(p, channel, who): p.send('MODE %s +v %s' % (channel, who))
[docs] def doop(p, channel, who): p.send('MODE %s +o %s' % (channel, who))
[docs] def delop(p, channel, who): p.send('MODE %s -o %s' % (channel, who))
[docs] def quit(p, reason='https://pikacode.com/bthate/point2'): p.send('QUIT :%s' % reason)
[docs] def notice(p, printto, what): p.send('NOTICE %s :%s' % (printto, what))
[docs] def ctcp(p, printto, what): p.send("PRIVMSG %s :\001%s\001" % (printto, what))
[docs] def ctcpreply(p, printto, what): p.send("NOTICE %s :\001%s\001" % (printto, what))
[docs] def action(p, printto, what, event=None, *args, **kwargs): p.send("PRIVMSG %s :\001ACTION %s\001" % (printto, what))
[docs] def getchannelmode(p, channel): p.send('MODE %s' % channel)
[docs] def settopic(p, channel, txt): p.send('TOPIC %s :%s' % (channel, txt))
[docs] def ping(p, *args, **kwargs): p.send('PING :%s' % p.server)
[docs] def pong(p, *args, **kwargs): p.send('PONG :%s' % p.server)
## irc_parse function
[docs]def irc_parse(obj, *args, **kwargs):
rawstr = str(args[0])
p = obj
p.servermsg = False
splitted = re.split('\s+', rawstr)
if not rawstr[0] == ':': p.servermsg = True
p.prefix = splitted[0]
if p.servermsg: p.etype = p.prefix
else: p.etype = splitted[1]
try:
nickuser = p.prefix.split('!')
p.origin = nickuser[1]
p.nick = nickuser[0][1:]
except IndexError: p.origin = p.prefix ; p.servermsg = True
if p.etype in pfc:
p.arguments = splitted[2:pfc[p.etype]+2]
txtsplit = re.split('\s+', rawstr, pfc[p.etype]+2)
p.txt = txtsplit[-1]
else: p.arguments = splitted[2:]
if p.arguments: p.target = p.arguments[0]
p.postfix = ' '.join(p.arguments)
if not "txt" in p: p.txt = rawstr.rsplit(":")[-1]
if p.txt.startswith(":"): p.txt = p.txt[1:]
if not "channel" in p:
for c in p.arguments + [p.txt, ]:
if c.startswith("#"): p.channel = c
if p.servermsg:
p.origin = p.origin[1:-1]
p.channel = p.origin
if not "origin" in p: p.origin = p.channel
if not "target" in p: p.target = p.channel or p.origin
return obj
## XMPP
[docs]class XMPPBot(Bot):
def __init__(p, *args, **kwargs):
import sleekxmpp
from sleekxmpp import clientxmpp
Bot.__init__(p, *args, **kwargs)
if "port" not in p: p.port = 5222
p.queue = queue.Queue()
p.xmpp = clientxmpp.ClientXMPP(p.user, getpass.getpass(stream=sys.stdout))
p.xmpp.add_event_handler("session_start", p.session_start)
p.xmpp.add_event_handler("message", p.handle_message)
p.xmpp.add_event_handler('disconnected', p.handle_disconnected)
p.xmpp.add_event_handler('connected', p.handle_connected)
p.xmpp.add_event_handler('presence_available', p.handle_presence)
p.xmpp.add_event_handler('presence_dnd', p.handle_presence)
p.xmpp.add_event_handler('presence_xa', p.handle_presence)
p.xmpp.add_event_handler('presence_chat', p.handle_presence)
p.xmpp.add_event_handler('presence_away', p.handle_presence)
p.xmpp.add_event_handler('presence_unavailable', p.handle_presence)
p.xmpp.add_event_handler('presence_subscribe', p.handle_presence)
p.xmpp.add_event_handler('presence_subscribed', p.handle_presence)
p.xmpp.add_event_handler('presence_unsubscribe', p.handle_presence)
p.xmpp.add_event_handler('presence_unsubscribed', p.handle_presence)
p.xmpp.add_event_handler('groupchat_message', p.handle_message)
p.xmpp.add_event_handler('groupchat_presence', p.handle_presence)
p.xmpp.add_event_handler('groupchat_subject', p.handle_presence)
p.xmpp.add_event_handler('failed_auth', p.handle_failedauth)
p.xmpp.exception = p.exception
p.xmpp.use_signals()
p.openfire = kernel.cfg.openfire
if p.openfire:
logging.warn("openfire used")
import ssl
p.xmpp.ssl_version = ssl.PROTOCOL_SSLv3
p._connected = threading.Event()
p.channels = []
p.time_in = time.time()
def _raw(p, txt): p.xmpp.send_raw(txt)
[docs] def connect(p):
try: p.xmpp.connect((p.server, p.port), use_ssl=p.openfire)
except: p.xmpp.connect((p.server, p.port))
[docs] def session_start(p, event): p.xmpp.send_presence()
[docs] def exception(p, ex):
p._state = ex
logging.error(str(ex))
p.stop()
[docs] def handle_failedauth(p, error, *args):
#p._state = error
logging.warn(error)
[docs] def handle_failure(p, ex, *args, **kwargs):
p._state = ex
logging.warn(ex)
[docs] def handle_disconnected(p, *args, **kwargs):
p._connected.clear()
p._state = "disconnected"
logging.error("disconnected")
[docs] def handle_connected(p, *args, **kwargs): p._state = "connected" ; p._connected.set() ; logging.warn("connected!")
[docs] def loop(p, *args, **kwargs): p.xmpp.process()
[docs] def say(p, *args, **kwargs):
try: channel, txt = args
except ValueError: channel = p.origin ; txt = args[0]
p.xmpp.send_message(channel, txt)
[docs] def do_one(p, *args, **kwargs):
p.time_in = time.time()
return p.queue.get()
[docs] def handle_message(p, data, *args, **kwargs):
if '<delay xmlns="urn:xmpp:delay"' in str(data): logging.debug("ignoring delayed message") ; return
logging.warn("< %s" % data)
m = Object(**data)
if m.type == "error": logging.warn("error %s" % m.to_json()) ; return
m.origin = stripped(str(m["from"]))
if not m.origin in p.channels: p.channels.append(m.origin)
m.channel = m.origin
m.to = m.origin
m.element = "message"
try: m.txt = m["body"]
except KeyError: pass
m._target = p
kernel.cmnds.dispatch(m)
[docs] def handle_presence(p, data, *args, **kwargs):
logging.info("< %s" % data)
o = Object(**data)
if "from" in p and p["from"]:
o.origin = stripped(str(p["from"]))
o.to = p.origin
else: o.origin = "" ; p.to = ""
o.element = "presence"
if o.type == 'subscribe':
pres = Object({'to': p["from"], 'type': 'subscribed'})
o.xmpp.send_presence(pres)
pres = Object({'to': p["from"], 'type': 'subscribe'})
o.xmpp.send_presence(pres)
else:
try: p.channels.remove(p.origin)
except (NotSet, ValueError): pass
o.no_dispatch = True
o._target = p
kernel.cmnds.dispatch(o)
## VARS
kernel = Object(format="keys")
kernel.cmnds = Dispatcher(format="keys")
kernel.workers = Dispatcher(format="keys")
kernel.direct = Dispatcher(format="keys")
kernel.plugs = Plugins(format="keys")
kernel.run = Object(format="keys")
kernel.cfg = Object(format="show")
kernel.fleet = []