Source code for rext

# rext/__init__.py
#
#

""" 
    Basic package for the Rext program. 

    Defines all the basic Objects and functionality.

"""

__version__ = 2

## IMPORTS

from rext.log import datefmt
from rext.utils import *

## BASIC IMPORTS

import collections
import threading
import readline
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): """ Basic Exception used in the Rext program. """ pass
[docs]class NoTarget(Error): """ Exception thrown incase of missing argument attribute. """ pass
[docs]class NoJSON(Error): """ Exception thrown incase no JSON could be decoded/encoded. """ pass
[docs]class RemoteDisconnect(Error): """ Exception thrown when the connection is disconnected. """ pass
[docs]class NotSet(Error): """ Exception thrown when a attribute is not set. """ pass
[docs]class FileNotFoundError(Error): """ Exception thrown when file is not found. """ pass ## BOOT
homedir = os.path.expanduser("~") histfile = j(homedir, ".rext_history") workdir = j(homedir, "rext.data", "") try: hostname = socket.getfqdn() except: hostname = "localhost" port = 10102
[docs]def boot(*args, **kwargs): """ The boot() function is needed to get the Rext program properly initialized. Use it at the beginning of your program. """ global kernel global workdir if args: cfg = args[0] else: cfg = Object() cfg.workdir = workdir kernel.cfg.update(cfg) if cfg.shell: hello("Rext") if kernel.cfg.workdir: workdir = kernel.cfg.workdir if not kernel.cfg.loglevel: cfg.loglevel = "error" from rext.log import cfg_log cfg_log(cfg.loglevel) make_dir(workdir) 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("rext.plugs") kernel.plugs.do_init(kernel.cfg.init) kernel.ready() return kernel ## SHUTDOWN
[docs]def shutdown(): """ The shutdown() function is used to close the Rext program in a apropiate manner. """ global kernel if kernel.cfg.shell: print("") logging.info("shutdown is here !!") readline.write_history_file(histfile) for obj in kernel.run: if obj == None: continue try: obj.exit() except AttributeError: pass 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): """ THE Basic Object on which the rest of the Rext program is based. """ def __getattribute__(zelf, *args, **kwargs): name = args[0] if name == "url_file": return zelf.get_url_file() if name == "url_show": return zelf.get_url_show() if name == "what": return get_name(zelf) if name == "plugname": return get_plugname(3) if name == "kind": return mj(zelf.plugname, zelf.what) if name == "stdin": return sys.stdin if name == "stdout": return sys.stdout return dict.__getattribute__(zelf, *args, **kwargs) def __getattr__(zelf, name): if name in zelf: return zelf[name] if name == "_ready": zelf["_ready"] = threading.Event() if name == "_state": zelf["_state"] = Object() if name == "_no_save": zelf["_no_save"] = Object() if name not in zelf: raise AttributeError(name) return zelf[name] def __setattr__(zelf, name, value): return dict.__setitem__(zelf, name, value) ## FLEET
[docs] def announce(zelf, *args, **kwargs): """ announce to all the running bots. """ for bot in kernel.fleet: bot.announce(args[0]) ## EXEC
[docs] def exec_str(zelf, *args, **kwargs): """ execute a string as a command. """ o = Object() o.txt = args[0] o._target = args[1] kernel.cmnds.dispatch(o) return o ## STATE
[docs] def define(zelf, *args, **kwargs): """ set a attribute on this object. """ name = args[0] value = args[1] zelf[name] = value
[docs] def register(zelf, *args, **kwargs): """ register a value in a list on this object. """ name = args[0] func = args[1] try: how = args[2] except IndexError: how = "normal" o = Object() o.func = func o.how = how if name not in zelf: zelf[name] = [] zelf[name].append(o) ## GETTERS
[docs] def get_root(zelf, *args, **kwargs): """ return the root dir of the Rext program. """ return j(os.path.expanduser("~"), *args)
[docs] def get_cmnd(zelf, *args, **kwargs): """ determine the command in the zelf.txt attribute, if present. """ if "txt" in zelf and zelf.txt: val = zelf.txt.split()[0] if "cc" in zelf: if val[0] != zelf.cc: return else: return val[1:] return val
[docs] def get_rest(zelf, *args, **kwargs): """ get the rest of the txt arguments (words) e.g. minus the first word (the command). """ try: cmnd, rest = zelf.txt.split(" ", 1) ; return rest except ValueError: return ""
[docs] def get_args(zelf, *args, **kwargs): """ get the arguments of the txt attribute. """ if "txt" in zelf: return zelf.txt.split()
[docs] def get_days(zelf, *args, **kwargs): """ get the number of days relative to the objects creation time. """ t1 = time.time() t2 = a_time(zelf.get_timed()) if t2: time_diff = float(t1 - t2) return str_day(time_diff)
[docs] def get_timed(zelf, *args, **kwargs): """ retrieve the creation time of an object. """ val = None if "added" in zelf: val = short_date(zelf.added) elif "Date" in zelf: val = short_date(zelf.Date) elif "date" in zelf: val = short_date(zelf.date) elif "published" in zelf: val = short_date(zelf.published) return val
[docs] def get_parsed(zelf, *args, **kwargs): """ parse the txt attribute, so command can be determined. """ rest = zelf.get_rest() if rest: return txt_parse(rest) return Object()
[docs] def get_keys(zelf, *args, **kwargs): """ return the keys of this object, skipping the unwanted keys e.g those that start with a "_". """ for key in zelf: k = str(key) if not k.startswith("_"): yield key
[docs] def get_clean(zelf, *args, **kwargs): """ return a cloned object, with only the proper keys used. """ res = Object() for key in zelf.get_keys(): res[key] = zelf[key] return res
[docs] def get_slice(zelf, *args, **kwargs): """ args: list of keywords to slice the dict. """ o = Object() arguments = args[0] for arg in arguments: try: o[arg] = zelf[arg] except KeyError: continue return o
[docs] def get_filetime(zelf, *args, **kwargs): """ get timestamp related filename. """ if "path" in zelf._no_save: return zelf._no_save.path.split(os.sep)[-1]
[docs] def get_url_file(zelf, *args, **kwargs): """ get the url of the object's file so that it can be retrieved when API server is running. """ import rext.plugs.api as api fn = zelf.get_filetime() if fn: return "http://%s:%s/get/%s" % (hostname, port, fn)
[docs] def get_url_show(zelf, *args, **kwargs): """ return url that gives a readable representation of the object's JSON file. """ import rext.plugs.api as api fn = zelf.get_filetime() if fn: return "http://%s:%s/show/%s" % (hostname, port, fn) ## CHECKERS
[docs] def check_wanted(zelf, *args, **kwargs): """ function to check whether an objects is desired, first argument is a dict that has the attributes to be matched. """ want = args[0] for key, value in want.items(): if key == "format": continue if key not in zelf: continue if value.startswith("-"): continue if value not in str(zelf[key]): return False return True
[docs] def check_notwanted(zelf, *args, **kwargs): """ function to check whether an objects is not desired, first argument is a dict that has the attributes to be matched. """ not_want = args[0] for key, value in not_want.items(): if key == "format": continue if key not in zelf: continue if value in zelf[key]: return True return False ## INPUT
[docs] def load(zelf, *args, **kwargs): """ load a JSON file into this object. """ if args: path = args[0] else: path = zelf._no_save["path"] logging.debug("load %s" % path) ondisk = zelf.read(path) fromdisk = json.loads(ondisk) zelf._no_save.path = path if "data" in fromdisk: zelf.update(fromdisk["data"]) return zelf
[docs] def read(zelf, *args, **kwargs): """ read the JSON file from disk. """ path = args[0] try: f = open(path, "r") except IOError as ex: if ex.errno == errno.ENOENT: return "{}" raise if "do_test" in zelf: 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(zelf, *args, **kwargs): """ create a JSON string ready to be saved to disk. """ path = args[0] todisk = Object() todisk.create_type = zelf.what todisk.version = __version__ todisk.data = zelf.get_clean() if "added" not in todisk.data: todisk.data.added = time.ctime(time.time()) todisk.signature = todisk.make_signature() try: result = todisk.to_json(indent=2, ensure_ascii=False) except TypeError: raise NoJSON() return result
[docs] def save(zelf, *args, **kwargs): """ save this object's JSON onto disk. """ if not args: t = rtime() else: t = args[0] root = j(workdir, t) make_dir(workdir) zelf.sync(root) return root
[docs] def sync(zelf, *args, **kwargs): """ sync this object's JSON to disk. """ try: path = args[0] ; zelf._no_save.path = path except IndexError: path = zelf._no_save.path logging.warn("sync %s" % path) todisk = zelf.dump(path, **kwargs) datafile = open(path + ".tmp", 'w') fcntl.flock(datafile, fcntl.LOCK_EX | fcntl.LOCK_NB) datafile.write(headertxt % (__version__, "%s characters" % len(todisk))) datafile.write(todisk) datafile.write("\n") fcntl.flock(datafile, fcntl.LOCK_UN) datafile.close() os.rename(path + ".tmp", path) return zelf ## OUTPUT
[docs] def reply(zelf, *args, **kwargs): """ send reply to object's origin. """ if "outer" in zelf: zelf.outer.write(str(args[0]) + "\n") ; zelf.outer.flush() ; return txt = args[0] if "channel" in zelf: zelf.say(zelf.channel, txt) else: zelf.say("", txt)
[docs] def say(zelf, *args, **kwargs): """ output text through the _target attribute (the bot that received the object). """ if "_target" in zelf: zelf._target.say(args[0], args[1]) else: raise NoTarget
[docs] def show(zelf): """ return a list of key,value pairs of this object's attributes. """ return ["%s=%s" % (a, b) for a, b in zelf.items() if b] ## ITERATORS
[docs] def objects(zelf, *args, **kwargs): """ return a list of all object's in the workdir. """ objs = [] path = zelf.get_root(workdir) for fnn in list_files(path, **kwargs): obj = Object().load(fnn) objs.append(obj) logging.debug("objects called from %s" % get_strace(2)) return objs
[docs] def selected(zelf, *args, **kwargs): """ return a list of desired objects, first argument is a dict giving the desired attributes. """ parsed = args[0] objs = [] for obj in zelf.objects(*args, **kwargs): if "deleted" in obj: continue if not obj.check_wanted(parsed.wanted): continue if not obj.selector(parsed.args or []): continue if obj.check_notwanted(parsed.not_wanted): continue objs.append(obj) return objs
[docs] def obj_iter(zelf, *args, **kwargs): """ iterate over the pure keys (not starting with "_"). """ for key in zelf.get_keys(): yield zelf[key] ## SELECTOR
[docs] def selector(zelf, *args, **kwargs): """ boolean function to see if this objects has the desired attributes. """ wanted = args[0] go = False for w in wanted: if zelf.get(w): go = True ; break return go ## WAITERS
[docs] def ready(zelf): """ signal the object into the ready state. """ zelf._ready.set()
[docs] def clear(zelf): """ clear the object's ready state. """ zelf._ready.clear()
[docs] def wait(zelf, sec=180.0): """ wait for the object to be put in a ready state. """ try: zelf._ready.wait(sec) except: pass ## TIME RELATED
[docs] def first(zelf, *args, **kwargs): """ return the first object where the key and/or value matches. """ key = args[0] try: value = args[1] except IndexError: value = None result = None for o in zelf.objects(): if key not in o: continue if value and o[key] != value: continue if not result: result = o if o.get("timed", "") < result.get("timed", ""): result = o return result
[docs] def last(zelf, *args, **kwargs): """ return the last object where the key and/or value matches. """ key = args[0] try: value = args[1] except IndexError: value = None result = None for o in zelf.objects(): if key not in o: continue if value and o[key] != value: continue if not result: result = o if o.get("timed", "") > result.get("timed", ""): result = o return result ## HELPERS
[docs] def to_json(zelf, *args, **kwargs): """ return the JSON string representation of this object. """ return json.dumps(zelf.get_clean(*args, **kwargs), default=smooth, *args, **kwargs)
[docs] def to_full(zelf, *args, **kwargs): """ return full JSON dump of this object. """ return json.dumps(zelf, default=smooth, *args, **kwargs)
[docs] def make_signature(zelf, sig=None): """ create a signature of the data contained in this object. """ if "data" in zelf: return str(hashlib.sha1(bytes(str(zelf.data), "utf-8")).hexdigest()) raise NotSet
[docs] def make_path(zelf, *args, **kwargs): """ return workdir path and create the workdir if necessary. """ path = zelf.get_path(*args, **kwargs) ; make_dir(path) ; return path
[docs] def pretty(zelf): """ return a nice formatted JSON string of this object. """ return json.dumps(zelf.get_clean(), indent=2, ensure_ascii=False) ## TASKS
[docs]class Runner(threading.Thread): """ The working unit of the Rext program, arguments are function, objects pairs pushed to the Runner. """ def __init__(zelf, *args, **kwargs): threading.Thread.__init__(zelf, None, zelf._loop, "thread.%s" % str(time.time()), args, kwargs) zelf.setDaemon(True) zelf._queue = queue.Queue() zelf._outqueue = queue.Queue() zelf._state = Object() zelf._state.status = "waiting" zelf._state.last_run = "none" zelf._state.time_in = time.time() zelf._state.time_start = time.time() def _loop(zelf): """ Loop that executes the function, object pairs. Arguments: function, object, args """ while zelf._state.status: zelf._state.status = "ready" args, kwargs = zelf._queue.get() if not zelf._state.status: break func = args[0] try: obj = args[1] except: obj = Object() logging.info("run %s" % get_name(func)) zelf._state.last_run = get_name(func) zelf._state.time_in = time.time() try: func(*args[1:], **kwargs) except: error() zelf._state.status = "done" try: obj.ready() except AttributeError: pass time.sleep(0.01) kernel.ready() _thread.interrupt_main()
[docs] def exit(zelf, *args, **kwargs): """ stop the Runner by setting the status to an empty string. """ zelf._state.status = ""
[docs] def put(zelf, *args, **kwargs): """ put arguments/kwargs to the Runner. """ zelf._queue.put_nowait((args, kwargs)) ## DISPATCH
[docs]class Dispatcher(Object): """ The Dispatcher is the object to delegate the workload to the Runners. Runners get instantiated when needed. """ def __init__(zelf, *args, **kwargs): Object.__init__(zelf, *args, **kwargs) zelf.runners = collections.deque() zelf._state = Object() zelf._state.time_in = time.time() zelf._state.time_out = time.time() zelf._state.time_start = time.time() zelf._state.last_run = "none" zelf.max = 50
[docs] def is_alive(zelf, *args, **kwargs): """ Check whether there are any Runners running. """ for runner in zelf.runners: if runner.is_alive(): return True return False
[docs] def register(zelf, *args, **kwargs): """ register a callback type with corresponding callback function. """ cbtype = args[0] cb = args[1] try: how = args[2] except IndexError: how = "normal" o = Object() o.func = cb o.how = how if cbtype not in zelf: zelf[cbtype] = [] zelf[cbtype].append(o)
[docs] def dispatch(zelf, *args, **kwargs): """ dispatch an event (object) onto a runner if matching command is found. """ event = args[0] zelf.execute(event.get_cmnd(), event)
[docs] def execute(zelf, *args, **kwargs): """ Execute a command, event pair. If the "threaded" attribute is set, dispatch to a Runner. """ cmnd = args[0] event = args[1] if cmnd not in zelf: event.ready() ; return for functor in zelf[cmnd]: logging.info(str(functor.func)) try: if functor.how == "threaded": kernel.workers.put(functor.func, event) else: functor.func(event) except: error()
[docs] def do_func(zelf, *args, **kwargs): """ Execute command/event pair if command is registered. """ cmnd = args[0] event = args[1] if cmnd not in zelf: return for functor in zelf[cmnd]: functor.func(event)
[docs] def exit(zelf, name=None): """ Stop the Runners running in this Dispatcher. """ for runner in zelf.runners: if name and name not in runner.name: continue runner.join(0.1) zelf.ready()
[docs] def put(zelf, *args, **kwargs): """ Put load to the Runner, create a new Runner if necessary. """ target = zelf.make_new() target.put(*args, **kwargs) return target
[docs] def make_new(zelf, *args, **kwargs): """ Create a Runner, try for idle Runners already available first. """ for runner in zelf.runners: if runner._state.status != "running": return runner if len(zelf.runners) < zelf.max: runner = Runner(*args, **kwargs) zelf.runners.append(runner) runner.start() else: runner = random.choice(zelf.runners) return runner
[docs] def cleanup(zelf, dojoin=False): """ Remove idle Runners. """ todo = [] for runner in zelf.runners: if runner.stopped or not len(runner.queue): todo.append(runner) for runner in todo: runner.stop() ; zelf.runners.remove(runner) ## PLUGINS
[docs]class Plugins(Object): """ Object to register Plugins with. """ def __init__(zelf, *args, **kwargs): Object.__init__(zelf, *args, **kwargs) zelf.plugins = []
[docs] def get_names(zelf, plugsdir): """ Return plugnames from the plugin directory. """ return [x[:-3] for x in os.listdir(plugsdir) if x.endswith(".py")]
[docs] def load_plugs(zelf, path): """ Load plugins from the plugin directory. """ logging.warn("plugs %s" % path) for plugname in zelf.get_names(path): if "__" in plugname: continue try: zelf[plugname] = zelf.load_mod(plugname, path, force=True) if plugname not in zelf.plugins: zelf.plugins.append(plugname) except: error() ; continue zelf.ready()
[docs] def load_package(zelf, modname): """ Load the plugins package. """ mod = __import__(modname) path, fn = os.path.split(mod.__file__) path += os.sep + "plugs" zelf.load_plugs(path)
[docs] def load_mod(zelf, plugname, pdir="", force=False): """ load a plugin;'s module. """ logging.info("mod %s" % plugname) if plugname in zelf: if not force: return zelf[plugname] zelf[plugname] = imp.reload(zelf[plugname]) else: if not pdir: pdir = j(zelf.root, "plugs") search = imp.find_module(plugname, [pdir,]) zelf[plugname] = imp.load_module(plugname, *search) return zelf[plugname]
[docs] def plug_init(zelf, *args, **kwargs): """ Initialize a plugin, use the init() function provided in the plugin, if available. """ if args: plugname = args[0] else: plugname = None if plugname not in zelf: return if "init" in dir(zelf[plugname]): logging.warn("init %s" % plugname) zelf[plugname].init(*args, **kwargs)
[docs] def plug_shutdown(zelf, plugname): """ Shutdown a plugin by calling the shutdown() function, if provided. """ if plugname not in zelf: return if "shutdown" in dir(zelf[plugname]): zelf[plugname].shutdown()
[docs] def exit(zelf, *args, **kwargs): """ close all plugins by calling there shutdown() function. """ for plugname in zelf: zelf.plug_shutdown(plugname) zelf.ready()
[docs] def reload(zelf, plugname, force=False): """ Reload (unload, load) a plugin. """ zelf.unload(plugname) mod = zelf.load_mod(plugname, force) return mod
[docs] def do_init(zelf, *args, **kwargs): """ Call the init function of all plugin or a specific one if argument is given. """ if args: target = args[0] else: target = None for plugname in zelf.plugins: if target and target != "all" and plugname != target: continue zelf.plug_init(plugname, *args, **kwargs) ## LOOPS
[docs]class Looper(Object): """ A Looper calls a function every x seconds. """ def __init__(zelf, *args, **kwargs): zelf._state.time_sleep = args[0] zelf._state.time_in = time.time() zelf.func = args[1] zelf.timer = threading.Timer(zelf._state.time_sleep, zelf.func_do) zelf.timer.start()
[docs] def start(zelf, *args, **kwargs): zelf.func_do()
[docs] def func_do(zelf, *args, **kwargs): zelf._state.time_in = time.time() try: zelf.func() except: error() logging.info("loop %s" % get_funcname(zelf.func)) zelf.timer = threading.Timer(zelf._state.time_sleep, zelf.func_do) zelf.timer.start() ## BOTS
[docs]class Bot(Dispatcher): """ Base Bot class. """ def __init__(zelf, *args, **kwargs): Dispatcher.__init__(zelf, *args, **kwargs) zelf.type = get_name(zelf)
[docs] def set_cfg(zelf, *args, **kwargs): zelf.cfg = cfg
[docs] def exit(zelf, *args, **kwargs): zelf._state.status = "" ; zelf.ready()
[docs] def join_channels(zelf, *args, **kwargs): pass
[docs] def begin(zelf, *args, **kwargs): zelf.connect() if kernel.cfg.shell: print("") zelf._state.status = "running" while zelf._state.status: time.sleep(0.01) try: event = zelf.do_one() except RemoteDisconnect: time.sleep(2) ; zelf.connect() zelf._state.time_in = time.time() if zelf._state.status == "once": break kernel.ready() _thread.interrupt_main()
[docs] def do_one(zelf, *args, **kwargs): pass
[docs] def get_prompt(zelf, *args, **kwargs): return ""
[docs] def connect(zelf, *args, **kwargs): pass
[docs] def announce(zelf, *args, **kwargs): if "channels" in zelf: for channel in zelf.channels: zelf.say(channel, args[0], **kwargs) else: zelf.say(*args, **kwargs) ## CONSOLE
[docs]class ConsoleBot(Bot):
[docs] def boot(zelf, *args, **kwargs): logging.warn("")
def _raw(zelf, *args, **kwargs): txt = args[0] logging.debug("> %s" % txt) try: sys.stdout.write(txt) ; sys.stdout.write("\n") ; sys.stdout.flush() except Exception as ex: logging.warn(str(ex))
[docs] def say(zelf, *args, **kwargs): try: txt = args[1] except IndexError: txt = args[0] zelf._state.time_out = time.time() zelf._raw(txt)
[docs] def get_prompt(zelf, *args, **kwargs): return "%s -=- %s%s<%s " % (short_date(time.ctime(time.time())).split()[1], BOLD, YELLOW, ENDC)
[docs] def do_one(zelf, *args, **kwargs): o = Object() if not kernel.cfg.shell: in_txt = " ".join(kernel.cfg.runargs) ; zelf._state.status = "once" else: in_txt = input(zelf.get_prompt()) zelf._state.time_in = time.time() o.txt = in_txt o._target = zelf kernel.cmnds.dispatch(o) return o ## IRC
[docs]class IRCBot(Bot): marker = "\r\n" cc = "." def __init__(zelf, *args, **kwargs): Bot.__init__(zelf, *args, **kwargs) zelf.connected = threading.Event() zelf.register("004", zelf._onconnect) zelf.register("513", zelf.handle_513) zelf.register("433", zelf.handle_433) zelf.register("366", zelf.handle_366) zelf.register("PING", zelf.handle_ping) zelf.register("INVITE", zelf.handle_invite) zelf.register("PRIVMSG", zelf.handle_privmsg) zelf.register("NOTICE", zelf.handle_notice) zelf._lock = _thread.allocate_lock() zelf._buffer = [] zelf._lastline = "" zelf.encoding = "utf-8" if "realname" not in zelf: zelf.realname = "z_" if "server" not in zelf: zelf.server = "localhost" if "port" not in zelf: zelf.port = 6667 if "nick" not in zelf: zelf.nick = "z_" if "channels" not in zelf: zelf.channels = [] if "channel" in zelf and zelf["channel"] not in zelf.channels: zelf.channels.append(zelf.channel) def _raw(zelf, txt): logging.debug("> %s" % txt) if not txt.endswith(zelf.marker): txt += zelf.marker txt = bytes(txt, zelf.encoding) if 'ssl' in zelf and zelf.ssl: zelf.sock.write(txt) else: zelf.sock.send(txt[:512]) zelf._state.time_out = time.time() def _connect(zelf): zelf.stopped = False if "ipv6" in zelf: zelf.oldsock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) else: zelf.oldsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) zelf.server = zelf.bind() logging.warn('connect %s - %s' % (zelf.server, zelf.port)) zelf.oldsock.settimeout(60) zelf.oldsock.connect((zelf.server, int(str(zelf.port or 6667)))) zelf.blocking = 1 zelf.oldsock.setblocking(zelf.blocking) logging.warn('connection made to %s' % zelf.server) zelf.fsock = zelf.oldsock.makefile("r") if "blocking" in zelf: zelf.oldsock.settimeout(301.0) if 'ssl' in zelf and zelf['ssl']: zelf.sock = socket.ssl(zelf.oldsock) else: zelf.sock = zelf.oldsock zelf.connecttime = time.time() return True def _onconnect(zelf, *args, **kwargs): logging.warn("logged on !!") if "onconnect" in zelf: time.sleep(0.5) ; zelf._raw(zelf.onconnect) if "servermodes" in zelf: zelf._raw("MODE %s %s" % (zelf.nick, zelf.servermodes)) zelf.ready() zelf.join_channels() def _dodcc(zelf, event, s): s.send(bytes('Welcome to Rext ' + event.nick + " !!\n", zelf.encoding)) _thread.start_new_thread(zelf._dccloop, (event, s)) def _dccloop(zelf, event, s): sockfile = s.makefile('rw') s.setblocking(True) while 1: try: res = sockfile.readline() if not res: break res = res.rstrip() logging.warn("< %s" % res.strip()) o = Object(_target=zelf, 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(zelf, event): try: rest = event.get_rest() parsed = txt_parse(rest) addr = parsed.args[1] ; port = parsed.args[2][:-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 zelf._dodcc(event, s)
[docs] def do_one(zelf, *args, **kwargs): if not zelf._buffer: zelf.read_some() try: line = zelf._buffer.pop(0) except IndexError: return Object() e = Object() e._target = zelf o = irc_parse(e, line.rstrip()) o.cc = zelf.cc try: zelf.do_func(o.etype, o) except: error() return o
[docs] def read_some(zelf, *args, **kwargs): try: if "ssl" in zelf and zelf.ssl: inbytes = zelf.sock.read() else: inbytes = zelf.sock.recv(512) except socket.timeout: return except Exception as ex: error() ; return try: txt = str(inbytes, zelf.encoding) except UnicodeDecodeError: txt = " " if txt == "": raise RemoteDisconnect() zelf._lastline += txt splitted = zelf._lastline.split(zelf.marker) for s in splitted[:-1]: logging.warn("< %s" % s.strip()) ; zelf._buffer.append(s) zelf._lastline = splitted[-1]
[docs] def send(zelf, txt): zelf._raw(txt) ; time.sleep(3.0)
[docs] def bind(zelf): server = zelf.server try: zelf.oldsock.bind((server, 0)) except socket.error: if not server: try: socket.inet_pton(socket.AF_INET6, zelf.server) except socket.error: pass else: server = zelf.server if not server: try: socket.inet_pton(socket.AF_INET, zelf.server) except socket.error: pass else: server = zelf.server if not server: ips = [] try: for item in socket.getaddrinfo(zelf.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(zelf): if "password" in zelf: zelf._raw("PASS %s" % zelf.password) logging.warn('logging in on %s - this may take a while' % zelf.server) zelf._raw("NICK %s" % zelf.nick or "z_") zelf._raw("USER %s localhost %s :%s" % (zelf.username or "z_", zelf.server or "localhost", zelf.realname))
[docs] def say(zelf, *args, **kwargs): try: channel, txt = args except ValueError: channel = zelf.channel ; txt = args[0] zelf.privmsg(channel, txt)
[docs] def join_channels(zelf, *args, **kwargs): for channel in zelf.channels: zelf.join(channel)
[docs] def broadcast(zelf, txt): for i in zelf.channels: zelf.say(i, txt)
[docs] def connect(zelf, reconnect=True): try: res = zelf._connect() except: error() ; return if res: time.sleep(1) ; zelf.logon() return res
[docs] def close(zelf): if 'ssl' in zelf and zelf['ssl']: zelf.oldsock.shutdown(2) ; zelf.oldsock.close() else: zelf.sock.shutdown(2) ; zelf.sock.close() zelf.fsock.close()
[docs] def handle_notice(zelf, event): pass
[docs] def handle_ping(zelf, event): zelf.pongcheck = True ; zelf.pong()
[docs] def handle_433(zelf, event): zelf.donick(event.arguments[1] + "_")
[docs] def handle_invite(zelf, event): zelf.join(event.channel)
[docs] def handle_privmsg(zelf, event): if event.txt.startswith("\001DCC"): zelf._dccconnect(event) ; return kernel.cmnds.dispatch(event)
[docs] def handle_513(zelf, event): zelf._raw("PONG %s" % event.arguments[6])
[docs] def handle_ctcp(zelf, event): if event.txt and event.txt[0] == zelf.cc: resolve(event)
[docs] def donick(zelf, nick, setorig=False, save=False, whois=False): zelf.send('NICK %s\n' % nick[:16])
[docs] def join(zelf, channel, password=None): if password: zelf._raw('JOIN %s %s' % (channel, password)) else: zelf._raw('JOIN %s' % channel)
[docs] def handle_366(zelf, *args, **kwargs): zelf.ready()
[docs] def part(zelf, channel): zelf.send('PART %s' % channel) if channel in zelf.channels: zelf.channels.remove(channel) ; zelf.save()
[docs] def who(zelf, who): zelf.send('WHO %s' % who.strip())
[docs] def names(zelf, channel): zelf.send('NAMES %s' % channel)
[docs] def whois(zelf, who): zelf.send('WHOIS %s' % who)
[docs] def privmsg(zelf, printto, what): zelf.send('PRIVMSG %s :%s' % (printto, what))
[docs] def voice(zelf, channel, who): zelf.send('MODE %s +v %s' % (channel, who))
[docs] def doop(zelf, channel, who): zelf.send('MODE %s +o %s' % (channel, who))
[docs] def delop(zelf, channel, who): zelf.send('MODE %s -o %s' % (channel, who))
[docs] def quit(zelf, reason='https://pikacode.com/bthate/z2'): zelf.send('QUIT :%s' % reason)
[docs] def notice(zelf, printto, what): zelf.send('NOTICE %s :%s' % (printto, what))
[docs] def ctcp(zelf, printto, what): zelf.send("PRIVMSG %s :\001%s\001" % (printto, what))
[docs] def ctcpreply(zelf, printto, what): zelf.send("NOTICE %s :\001%s\001" % (printto, what))
[docs] def action(zelf, printto, what, event=None, *args, **kwargs): zelf.send("PRIVMSG %s :\001ACTION %s\001" % (printto, what))
[docs] def getchannelmode(zelf, channel): zelf.send('MODE %s' % channel)
[docs] def settopic(zelf, channel, txt): zelf.send('TOPIC %s :%s' % (channel, txt))
[docs] def ping(zelf, *args, **kwargs): zelf.send('PING :%s' % zelf.server)
[docs] def pong(zelf, *args, **kwargs): zelf.send('PONG :%s' % zelf.server) ## irc_parse function
[docs]def irc_parse(obj, *args, **kwargs): rawstr = str(args[0]) zelf = obj zelf.servermsg = False splitted = re.split('\s+', rawstr) if not rawstr[0] == ':': zelf.servermsg = True zelf.prefix = splitted[0] if zelf.servermsg: zelf.etype = zelf.prefix else: zelf.etype = splitted[1] try: nickuser = zelf.prefix.split('!') zelf.origin = nickuser[1] zelf.nick = nickuser[0][1:] except IndexError: zelf.origin = zelf.prefix ; zelf.servermsg = True if zelf.etype in pfc: zelf.arguments = splitted[2:pfc[zelf.etype]+2] txtsplit = re.split('\s+', rawstr, pfc[zelf.etype]+2) zelf.txt = txtsplit[-1] else: zelf.arguments = splitted[2:] if zelf.arguments: zelf.target = zelf.arguments[0] zelf.postfix = ' '.join(zelf.arguments) if not "txt" in zelf: zelf.txt = rawstr.rsplit(":")[-1] if zelf.txt.startswith(":"): zelf.txt = zelf.txt[1:] if not "channel" in zelf: for c in zelf.arguments + [zelf.txt, ]: if c.startswith("#"): zelf.channel = c if zelf.servermsg: zelf.origin = zelf.origin[1:-1] zelf.channel = zelf.origin if not "origin" in zelf: zelf.origin = zelf.channel return zelf ## XMPP
[docs]class XMPPBot(Bot): def __init__(zelf, *args, **kwargs): import sleekxmpp from sleekxmpp import clientxmpp Bot.__init__(zelf, *args, **kwargs) if "port" not in zelf: zelf.port = 5222 zelf.queue = queue.Queue() zelf.xmpp = clientxmpp.ClientXMPP(zelf.user, getpass.getpass(stream=sys.stdout)) zelf.xmpp.add_event_handler("session_start", zelf.session_start) zelf.xmpp.add_event_handler("message", zelf.handle_message) zelf.xmpp.add_event_handler('disconnected', zelf.handle_disconnected) zelf.xmpp.add_event_handler('connected', zelf.handle_connected) zelf.xmpp.add_event_handler('presence_available', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_dnd', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_xa', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_chat', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_away', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_unavailable', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_subscribe', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_subscribed', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_unsubscribe', zelf.handle_presence) zelf.xmpp.add_event_handler('presence_unsubscribed', zelf.handle_presence) zelf.xmpp.add_event_handler('groupchat_message', zelf.handle_message) zelf.xmpp.add_event_handler('groupchat_presence', zelf.handle_presence) zelf.xmpp.add_event_handler('groupchat_subject', zelf.handle_presence) zelf.xmpp.add_event_handler('failed_auth', zelf.handle_failedauth) zelf.xmpp.exception = zelf.exception zelf.xmpp.use_signals() zelf.openfire = kernel.cfg.openfire if zelf.openfire: logging.warn("openfire used") import ssl zelf.xmpp.ssl_version = ssl.PROTOCOL_SSLv3 zelf._connected = threading.Event() zelf.channels = [] zelf._state.time_in = time.time() def _raw(zelf, txt): logging.debug("> %s" % txt) zelf.xmpp.send_raw(txt) zelf._state.time_out = time.time()
[docs] def announce(zelf, *args, **kwargs): for channel in zelf.channels: zelf.say(channel, args[0])
[docs] def connect(zelf): try: zelf.xmpp.connect((zelf.server, zelf.port), use_ssl=zelf.openfire) except: zelf.xmpp.connect((zelf.server, zelf.port))
[docs] def session_start(zelf, event): zelf.xmpp.send_presence() ; logging.warn("session started")
[docs] def exception(zelf, ex): zelf._state.error = ex logging.error(str(ex)) zelf.exit()
[docs] def handle_failedauth(zelf, error, *args): logging.error(error)
[docs] def handle_failure(zelf, ex, *args, **kwargs): zelf._state.error = ex logging.warn(ex)
[docs] def handle_disconnected(zelf, *args, **kwargs): zelf._connected.clear() zelf._state.status = "disconnected" logging.error("disconnected")
[docs] def handle_connected(zelf, *args, **kwargs): zelf._state.status = "connected" ; zelf._connected.set() ; logging.warn("connected!")
[docs] def loop(zelf, *args, **kwargs): zelf.xmpp.process()
[docs] def say(zelf, *args, **kwargs): try: channel, txt = args except ValueError: logging.warn("channel not set") ; return logging.debug("> %s" % txt) zelf.xmpp.send_message(channel, txt) zelf._state.time_out = time.time()
[docs] def do_one(zelf, *args, **kwargs): zelf.time_in = time.time() return zelf.queue.get()
[docs] def handle_message(zelf, 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.error(m.to_json()) ; return m.origin = stripped(str(m["from"])) if not m.origin in zelf.channels: zelf.channels.append(m.origin) m.channel = m.origin m.to = m.origin m.element = "message" m.txt = m["body"] m._target = zelf kernel.cmnds.dispatch(m)
[docs] def handle_presence(zelf, data, *args, **kwargs): logging.info("< %s" % data) o = Object(**data) if "from" in zelf and zelf["from"]: o.origin = stripped(str(zelf["from"])) o.to = zelf.origin else: o.origin = "" ; zelf.to = "" o.element = "presence" if o.type == 'subscribe': pres = Object({'to': zelf["from"], 'type': 'subscribed'}) o.xmpp.send_presence(pres) pres = Object({'to': zelf["from"], 'type': 'subscribe'}) o.xmpp.send_presence(pres) else: if "origin" in zelf: zelf.channels.remove(zelf.origin) o.no_dispatch = True o._target = zelf kernel.cmnds.dispatch(o) ## VARS
kernel = Object() kernel.cmnds = Dispatcher() kernel.workers = Dispatcher() kernel.plugs = Plugins() kernel.run = Object() kernel.cfg = Object() kernel.fleet = []