New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

rssbot

Package Overview
Dependencies
Maintainers
1
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rssbot - pypi Package Compare versions

Comparing version
653
to
655
+50
rssbot/loggers.py
# This file is placed in the Public Domain.
import logging
LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning':logging. WARNING,
'warn': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL
}
class Logging:
datefmt = "%H:%M:%S"
format = "%(module).3s %(message)s"
class Format(logging.Formatter):
def format(self, record):
record.module = record.module.upper()
return logging.Formatter.format(self, record)
def level(loglevel="debug"):
if loglevel != "none":
lvl = LEVELS.get(loglevel)
if not lvl:
return
logger = logging.getLogger()
for handler in logger.handlers:
logger.removeHandler(handler)
logger.setLevel(lvl)
formatter = Format(Logging.format, datefmt=Logging.datefmt)
ch = logging.StreamHandler()
ch.setFormatter(formatter)
logger.addHandler(ch)
def __dir__():
return (
'LEVELS',
'Logging',
'level'
)
# This file is placed in the Public Domain.
import json
import types
class Encoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, dict):
return o.items()
if isinstance(o, list):
return iter(o)
if isinstance(o, types.MappingProxyType):
return dict(o)
try:
return json.JSONEncoder.default(self, o)
except TypeError:
try:
return vars(o)
except TypeError:
return repr(o)
def dump(*args, **kw):
kw["cls"] = Encoder
return json.dump(*args, **kw)
def dumps(*args, **kw):
kw["cls"] = Encoder
return json.dumps(*args, **kw)
def load(s, *args, **kw):
return json.load(s, *args, **kw)
def loads(s, *args, **kw):
return json.loads(s, *args, **kw)
def __dir__():
return (
'dump',
'dumps',
'load',
'loads'
)
# This file is placed in the Public Domain.
import datetime
import json
import os
import pathlib
import threading
import time
from .marshal import dump, load
from .objects import Object, items, update
lock = threading.RLock()
class Cache:
objs = Object()
@staticmethod
def add(path, obj):
setattr(Cache.objs, path, obj)
@staticmethod
def get(path):
return getattr(Cache.objs, path, None)
@staticmethod
def update(path, obj):
setattr(Cache.objs, path, obj)
def deleted(obj):
return "__deleted__" in dir(obj) and obj.__deleted__
def find(type=None, selector=None, removed=False, matching=False):
if selector is None:
selector = {}
for pth in fns(type):
obj = Cache.get(pth)
if not obj:
obj = Object()
read(obj, pth)
Cache.add(pth, obj)
if not removed and deleted(obj):
continue
if selector and not search(obj, selector, matching):
continue
yield pth, obj
def fns(type=None):
if type is not None:
type = type.lower()
path = store()
for rootdir, dirs, _files in os.walk(path, topdown=True):
for dname in dirs:
if dname.count("-") != 2:
continue
ddd = os.path.join(rootdir, dname)
if type and type not in ddd.lower():
continue
for fll in os.listdir(ddd):
yield os.path.join(ddd, fll)
def fntime(daystr):
datestr = " ".join(daystr.split(os.sep)[-2:])
datestr = datestr.replace("_", " ")
if "." in datestr:
datestr, rest = datestr.rsplit(".", 1)
else:
rest = ""
timed = time.mktime(time.strptime(datestr, "%Y-%m-%d %H:%M:%S"))
if rest:
timed += float("." + rest)
return float(timed)
def last(obj, selector=None):
if selector is None:
selector = {}
result = sorted(
find(fqn(obj), selector),
key=lambda x: fntime(x[0])
)
res = ""
if result:
inp = result[-1]
update(obj, inp[-1])
res = inp[0]
return res
def read(obj, path):
with lock:
with open(path, "r", encoding="utf-8") as fpt:
try:
update(obj, load(fpt))
except json.decoder.JSONDecodeError as ex:
ex.add_note(path)
raise ex
def search(obj, selector, matching=False):
res = False
for key, value in items(selector):
val = getattr(obj, key, None)
if not val:
continue
if matching and value == val:
res = True
elif str(value).lower() in str(val).lower():
res = True
else:
res = False
break
return res
def write(obj, path=None):
with lock:
if path is None:
path = getpath(obj)
cdir(path)
with open(path, "w", encoding="utf-8") as fpt:
dump(obj, fpt, indent=4)
Cache.update(path, obj)
return path
class Workdir:
wdr = ""
@staticmethod
def init(name):
Workdir.wdr = os.path.expanduser(f"~/.{name}")
def cdir(path):
pth = pathlib.Path(path)
pth.parent.mkdir(parents=True, exist_ok=True)
def fqn(obj):
kin = str(type(obj)).split()[-1][1:-2]
if kin == "type":
kin = f"{obj.__module__}.{obj.__name__}"
return kin
def getpath(obj):
return store(ident(obj))
def ident(obj):
return os.path.join(fqn(obj), *str(datetime.datetime.now()).split())
def moddir(modname=None):
return os.path.join(Workdir.wdr, modname or "mods")
def pidname(name):
assert Workdir.wdr
return os.path.join(Workdir.wdr, f"{name}.pid")
def skel():
pth = pathlib.Path(store())
pth.mkdir(parents=True, exist_ok=True)
pth = pathlib.Path(moddir())
pth.mkdir(parents=True, exist_ok=True)
def store(fnm=""):
return os.path.join(Workdir.wdr, "store", fnm)
def types():
return os.listdir(store())
def __dir__():
return (
'Cache',
'Workdir',
'cdir',
'find',
'fntime',
'fqn',
'fqn',
'getpath',
'ident',
'read',
'skel',
'types',
'write'
)
# This file is placed in the Public Domain.
import threading
import time
from .threads import launch, name
class Timy(threading.Timer):
def __init__(self, sleep, func, *args, **kwargs):
super().__init__(sleep, func)
self.name = kwargs.get("name", name(func))
self.sleep = sleep
self.state = {}
self.state["latest"] = time.time()
self.state["starttime"] = time.time()
self.starttime = time.time()
class Timed:
def __init__(self, sleep, func, *args, thrname="", **kwargs):
self.args = args
self.func = func
self.kwargs = kwargs
self.sleep = sleep
self.name = thrname or kwargs.get("name", name(func))
self.target = time.time() + self.sleep
self.timer = None
def run(self):
self.timer.latest = time.time()
self.func(*self.args)
def start(self):
self.kwargs["name"] = self.name
timer = Timy(self.sleep, self.run, *self.args, **self.kwargs)
timer.start()
self.timer = timer
def stop(self):
if self.timer:
self.timer.cancel()
class Repeater(Timed):
def run(self):
launch(self.start)
super().run()
def __dir__():
return (
'Repeater',
'Timed'
)
+2
-2
Metadata-Version: 2.4
Name: rssbot
Version: 653
Summary: 24/7 Feed Fetcher
Version: 655
Summary: 24/7 IRC Feed Fetcher, a contribution back to society. Public Domain.
Author-email: Bart Thate <rssbotd@gmail.com>

@@ -6,0 +6,0 @@ License-Expression: Unlicense

@@ -11,4 +11,4 @@ [build-system]

name = "rssbot"
description = "24/7 Feed Fetcher"
version = "653"
description = "24/7 IRC Feed Fetcher, a contribution back to society. Public Domain."
version = "655"
authors = [

@@ -15,0 +15,0 @@ {name = "Bart Thate",email = "rssbotd@gmail.com"},

Metadata-Version: 2.4
Name: rssbot
Version: 653
Summary: 24/7 Feed Fetcher
Version: 655
Summary: 24/7 IRC Feed Fetcher, a contribution back to society. Public Domain.
Author-email: Bart Thate <rssbotd@gmail.com>

@@ -6,0 +6,0 @@ License-Expression: Unlicense

@@ -5,14 +5,14 @@ README.rst

bin/rssbot
rssbot/brokers.py
rssbot/caching.py
rssbot/clients.py
rssbot/command.py
rssbot/handler.py
rssbot/loggers.py
rssbot/marshal.py
rssbot/methods.py
rssbot/objects.py
rssbot/package.py
rssbot/serials.py
rssbot/persist.py
rssbot/repeats.py
rssbot/threads.py
rssbot/utility.py
rssbot/workdir.py
rssbot.egg-info/PKG-INFO

@@ -19,0 +19,0 @@ rssbot.egg-info/SOURCES.txt

# This file is placed in the Public Domain.
"client events"
import queue
import threading
import _thread
from .brokers import Fleet
from .handler import Handler

@@ -17,2 +12,10 @@ from .threads import launch

class Config:
name = "tob"
opts = ""
sets: dict[str,str] = {}
version = 141
class Client(Handler):

@@ -27,5 +30,5 @@

def announce(self, txt):
def announce(self, text):
if not self.silent:
self.raw(txt)
self.raw(text)

@@ -40,12 +43,15 @@ def display(self, event):

def dosay(self, channel, txt):
self.say(channel, txt)
def dosay(self, channel, text):
self.say(channel, text)
def raw(self, txt):
def raw(self, text):
raise NotImplementedError("raw")
def say(self, channel, txt):
self.raw(txt)
def say(self, channel, text):
self.raw(text)
def wait(self):
self.oqueue.join()
class Output(Client):

@@ -70,13 +76,55 @@

def wait(self):
try:
self.oqueue.join()
except Exception:
_thread.interrupt_main()
class Fleet:
clients: dict[str, Client] = {}
@staticmethod
def add(client):
Fleet.clients[repr(client)] = client
@staticmethod
def all():
return Fleet.clients.values()
@staticmethod
def announce(text):
for client in Fleet.all():
client.announce(text)
@staticmethod
def display(event):
client = Fleet.get(event.orig)
if client:
client.display(event)
@staticmethod
def get(origin):
return Fleet.clients.get(origin, None)
@staticmethod
def like(origin):
for orig in Fleet.clients:
if origin.split()[0] in orig.split()[0]:
yield orig
@staticmethod
def say(orig, channel, txt):
client = Fleet.get(orig)
if client:
client.say(channel, txt)
@staticmethod
def shutdown():
for client in Fleet.all():
client.wait()
client.stop()
def __dir__():
return (
'Client',
'Config',
'Fleet',
'Output'
)
)
# This file is placed in the Public Domain.
"write your own commands"
import inspect
import inspect
from typing import Callable
from .brokers import Fleet
from .clients import Fleet
from .methods import parse
from .package import getmod, modules

@@ -17,22 +16,14 @@

cmds = {}
names = {}
cmds: dict[str, Callable] = {}
names: dict[str, str] = {}
@staticmethod
def add(func) -> None:
name = func.__name__
modname = func.__module__.split(".")[-1]
Commands.cmds[name] = func
Commands.names[name] = modname
def add(*args):
for func in args:
name = func.__name__
Commands.cmds[name] = func
Commands.names[name] = func.__module__.split(".")[-1]
@staticmethod
def get(cmd):
func = Commands.cmds.get(cmd, None)
if func:
return func
name = Commands.names.get(cmd, None)
if name:
module = getmod(name)
if module:
scan(module)
return Commands.cmds.get(cmd, None)

@@ -42,3 +33,3 @@

def command(evt):
parse(evt)
parse(evt, evt.text)
func = Commands.get(evt.cmd)

@@ -59,30 +50,7 @@ if func:

def scanner(names=[]):
res = []
for nme in sorted(modules()):
if names and nme not in names:
continue
module = getmod(nme)
if not module:
continue
scan(module)
res.append(module)
return res
def table(checksum=""):
tbl = getmod("tbl")
if tbl and "NAMES" in dir(tbl):
Commands.names.update(tbl.NAMES)
else:
scanner()
def __dir__():
return (
'Commands',
'Comamnds',
'command',
'scan',
'scanner',
'table'
'scan'
)
# This file is placed in the Public Domain.
"handle events"
import queue
import threading
import time
import _thread
from typing import Callable
from .threads import launch
class Event:
def __init__(self):
self._ready = threading.Event()
self._thr = None
self.channel = ""
self.ctime = time.time()
self.orig = ""
self.result = {}
self.type = "event"
def ready(self):
self._ready.set()
def reply(self, text):
self.result[time.time()] = text
def wait(self, timeout=None):
self._ready.wait()
if self._thr:
self._thr.join(timeout)
class Handler:
def __init__(self):
self.cbs = {}
self.cbs: dict[str, Callable] = {}
self.queue = queue.Queue()

@@ -25,3 +47,3 @@

if func:
name = event.txt and event.txt.split()[0]
name = event.text and event.text.split()[0]
event._thr = launch(func, event, name=name)

@@ -33,10 +55,7 @@ else:

while True:
try:
event = self.poll()
if event is None:
break
event.orig = repr(self)
self.callback(event)
except (KeyboardInterrupt, EOFError):
_thread.interrupt_main()
event = self.poll()
if event is None:
break
event.orig = repr(self)
self.callback(event)

@@ -59,31 +78,2 @@ def poll(self):

class Event:
def __init__(self):
self._ready = threading.Event()
self._thr = None
self.args = []
self.channel = ""
self.ctime = time.time()
self.orig = ""
self.rest = ""
self.result = {}
self.txt = ""
self.type = "event"
def ready(self):
self._ready.set()
def reply(self, txt):
self.result[time.time()] = txt
def wait(self, timeout=None):
try:
self._ready.wait()
if self._thr:
self._thr.join(timeout)
except (KeyboardInterrupt, EOFError):
_thread.interrupt_main()
def __dir__():

@@ -90,0 +80,0 @@ return (

# This file is placed in the Public Domain.
"object as the first argument"
from .objects import fqn, items
from .objects import items, keys
def deleted(obj):
return "__deleted__" in dir(obj) and obj.__deleted__
def edit(obj, setter, skip=True):

@@ -38,3 +31,3 @@ for key, val in items(setter):

if not args:
args = keys(obj)
args = obj.__dict__.keys()
txt = ""

@@ -58,3 +51,3 @@ for key in args:

else:
txt += f"{key}={name(value, True)} "
txt += f"{key}={fqn(value)}((value))"
return txt.strip()

@@ -81,21 +74,21 @@

def parse(obj, txt=""):
if not txt:
if "txt" in dir(obj):
txt = obj.txt
def parse(obj, text):
data = {
"args": [],
"cmd": "",
"gets": {},
"index": None,
"init": "",
"opts": "",
"otxt": text,
"rest": "",
"silent": {},
"sets": {},
"text": text
}
for k, v in data.items():
setattr(obj, k, getattr(obj, k, v) or v)
args = []
obj.args = getattr(obj, "args", [])
obj.cmd = getattr(obj, "cmd", "")
obj.gets = getattr(obj, "gets", "")
obj.index = getattr(obj, "index", None)
obj.inits = getattr(obj, "inits", "")
obj.mod = getattr(obj, "mod", "")
obj.opts = getattr(obj, "opts", "")
obj.result = getattr(obj, "result", "")
obj.sets = getattr(obj, "sets", {})
obj.silent = getattr(obj, "silent", "")
obj.txt = txt or getattr(obj, "txt", "")
obj.otxt = obj.txt or getattr(obj, "otxt", "")
_nr = -1
for spli in obj.otxt.split():
nr = -1
for spli in text.split():
if spli.startswith("-"):

@@ -118,12 +111,6 @@ try:

key, value = spli.split("=", maxsplit=1)
if key == "mod":
if obj.mod:
obj.mod += f",{value}"
else:
obj.mod = value
continue
obj.sets[key] = value
continue
_nr += 1
if _nr == 0:
nr += 1
if nr == 0:
obj.cmd = spli

@@ -134,32 +121,15 @@ continue

obj.args = args
obj.txt = obj.cmd or ""
obj.text = obj.cmd or ""
obj.rest = " ".join(obj.args)
obj.txt = obj.cmd + " " + obj.rest
obj.text = obj.cmd + " " + obj.rest
else:
obj.txt = obj.cmd or ""
obj.text = obj.cmd or ""
def search(obj, selector, matching=False):
res = False
for key, value in items(selector):
val = getattr(obj, key, None)
if not val:
continue
if matching and value == val:
res = True
elif str(value).lower() in str(val).lower():
res = True
else:
res = False
break
return res
def __dir__():
return (
'deleted',
'edit',
'fmt',
'parse',
'search'
'name',
'parse'
)
# This file is placed in the Public Domain.
from ..clients import Fleet
from ..methods import fmt
from ..threads import name
from rssbot.clients import Fleet
from rssbot.methods import fmt
from rssbot.threads import name

@@ -18,2 +18,2 @@

return
event.reply(' | '.join([name(o) for o in Fleet.all()]))
event.reply(' | '.join([name(o) for o in Fleet.all()]))
# This file is placed in the Public Domain.
"find"
import time
from ..caching import find
from ..methods import fmt
from ..utility import elapsed, fntime
from ..workdir import long, skel, types
from rssbot.methods import fmt
from rssbot.persist import find, fntime, types
from rssbot.utility import elapsed
def fnd(event):
skel()
if not event.rest:

@@ -26,5 +21,4 @@ res = sorted([x.split('.')[-1].lower() for x in types()])

otype = event.args[0]
clz = long(otype)
nmr = 0
for fnm, obj in list(find(clz, event.gets)):
for fnm, obj in list(find(otype, event.gets)):
event.reply(f"{nmr} {fmt(obj)} {elapsed(time.time()-fntime(fnm))}")

@@ -31,0 +25,0 @@ nmr += 1

# This file is placed in the Public Domain.
"internet relay chat"
import base64

@@ -17,52 +14,41 @@ import logging

from ..brokers import Fleet
from ..caching import last, write
from ..clients import Output
from ..command import command
from ..handler import Event as IEvent
from ..methods import edit, fmt
from ..objects import Object, keys
from ..threads import launch
from ..utility import LEVELS
from ..workdir import Workdir, getpath
from rssbot.clients import Config as Main
from rssbot.clients import Output
from rssbot.command import Fleet, command
from rssbot.handler import Event as IEvent
from rssbot.loggers import LEVELS
from rssbot.methods import edit, fmt
from rssbot.objects import Object, keys
from rssbot.persist import getpath, last, write
from rssbot.threads import launch
from rssbot.utility import where
IGNORE = ["PING", "PONG", "PRIVMSG"]
NAME = Workdir.name
IGNORE = ["PING", "PONG", "PRIVMSG"]
initlock = threading.RLock()
saylock = threading.RLock()
lock = threading.RLock()
def init():
with initlock:
irc = IRC()
irc.start()
irc.events.joined.wait(30.0)
if irc.events.joined.is_set():
logging.warning(fmt(irc.cfg, skip=["password", "realname", "username"]))
else:
irc.stop()
return irc
def init(cfg):
irc = IRC()
irc.start()
irc.events.joined.wait(30.0)
if irc.events.joined.is_set():
logging.warning(fmt(irc.cfg, skip=["name", "password", "realname", "username"]))
else:
irc.stop()
return irc
def rlog(loglevel, txt, ignore=None):
if ignore is None:
ignore = []
for ign in ignore:
if ign in str(txt):
return
logging.log(LEVELS.get(loglevel), txt)
class Config:
channel = f"#{NAME}"
channel = f"#{Main.name}"
commands = False
control = "!"
nick = NAME
name = Main.name
nick = Main.name
password = ""
port = 6667
realname = NAME
realname = Main.name
sasl = False

@@ -72,4 +58,5 @@ server = "localhost"

sleep = 60
username = NAME
username = Main.name
users = False
version = 1

@@ -79,2 +66,3 @@ def __init__(self):

self.commands = Config.commands
self.name = Config.name
self.nick = Config.nick

@@ -95,2 +83,3 @@ self.port = Config.port

self.channel = ""
self.gets = {}
self.nick = ""

@@ -100,5 +89,10 @@ self.origin = ""

self.rest = ""
self.txt = ""
self.sets = {}
self.text = ""
def dosay(self, txt):
bot = Fleet.get(self.orig)
bot.dosay(self.channel, txt)
class TextWrap(textwrap.TextWrapper):

@@ -158,5 +152,5 @@

def announce(self, txt):
def announce(self, text):
for channel in self.channels:
self.say(channel, txt)
self.say(channel, text)

@@ -193,3 +187,3 @@ def connect(self, server, port=6667):

def direct(self, txt):
with saylock:
with lock:
time.sleep(2.0)

@@ -217,5 +211,5 @@ self.raw(txt)

_nr = -1
for txt in textlist:
for text in textlist:
_nr += 1
self.dosay(event.channel, txt)
self.dosay(event.channel, text)
if len(txtlist) > 3:

@@ -226,3 +220,3 @@ length = len(txtlist) - 3

def docommand(self, cmd, *args):
with saylock:
with lock:
if not args:

@@ -259,5 +253,5 @@ self.raw(cmd)

def dosay(self, channel, txt):
def dosay(self, channel, text):
self.events.joined.wait()
txt = str(txt).replace("\n", "")
txt = str(text).replace("\n", "")
txt = txt.replace(" ", " ")

@@ -271,3 +265,3 @@ self.docommand("PRIVMSG", channel, txt)

self.state.pongcheck = True
self.docommand("PONG", evt.txt or "")
self.docommand("PONG", evt.text or "")
elif cmd == "PONG":

@@ -342,3 +336,3 @@ self.state.pongcheck = False

rawstr = rawstr.replace("\001", "")
logging.debug(txt)
rlog("debug", txt, IGNORE)
obj = Event()

@@ -371,3 +365,3 @@ obj.args = []

obj.arguments.append(arg)
obj.txt = " ".join(txtlist)
obj.text = " ".join(txtlist)
else:

@@ -387,7 +381,7 @@ obj.command = obj.origin

obj.channel = obj.nick
if not obj.txt:
obj.txt = rawstr.split(":", 2)[-1]
if not obj.txt and len(arguments) == 1:
obj.txt = arguments[1]
splitted = obj.txt.split()
if not obj.text:
obj.text = rawstr.split(":", 2)[-1]
if not obj.text and len(arguments) == 1:
obj.text = arguments[1]
splitted = obj.text.split()
if len(splitted) > 1:

@@ -398,3 +392,3 @@ obj.args = splitted[1:]

obj.orig = object.__repr__(self)
obj.txt = obj.txt.strip()
obj.text = obj.text.strip()
obj.type = obj.command

@@ -431,11 +425,11 @@ return obj

def raw(self, txt):
txt = txt.rstrip()
rlog("info", txt, IGNORE)
txt = txt[:500]
txt += "\r\n"
txt = bytes(txt, "utf-8")
def raw(self, text):
text = text.rstrip()
rlog("debug", text, IGNORE)
text = text[:500]
text += "\r\n"
text = bytes(text, "utf-8")
if self.sock:
try:
self.sock.send(txt)
self.sock.send(text)
except (

@@ -479,7 +473,7 @@ OSError,

def say(self, channel, txt):
evt = Event()
evt.channel = channel
evt.reply(txt)
self.oput(evt)
def say(self, channel, text):
event = Event()
event.channel = channel
event.reply(text)
self.oput(event)

@@ -491,6 +485,6 @@ def some(self):

inbytes = self.sock.recv(512)
txt = str(inbytes, "utf-8")
if txt == "":
text = str(inbytes, "utf-8")
if text == "":
raise ConnectionResetError
self.state.lastline += txt
self.state.lastline += text
splitted = self.state.lastline.split("\r\n")

@@ -502,3 +496,2 @@ for line in splitted[:-1]:

def start(self):
last(self.cfg)
if self.cfg.channel not in self.channels:

@@ -546,3 +539,3 @@ self.channels.append(self.cfg.channel)

bot.state.nrerror += 1
bot.state.error = evt.txt
bot.state.error = evt.text
logging.debug(fmt(evt))

@@ -583,4 +576,4 @@

bot = Fleet.get(evt.orig)
if evt.txt.startswith("VERSION"):
txt = f"\001VERSION {NAME.upper()} 140 - {bot.cfg.username}\001"
if evt.text.startswith("VERSION"):
txt = f"\001VERSION {Config.name.upper()} {Config.version} - {bot.cfg.username}\001"
bot.docommand("NOTICE", evt.channel, txt)

@@ -593,14 +586,14 @@

return
if evt.txt:
if evt.txt[0] in [
if evt.text:
if evt.text[0] in [
"!",
]:
evt.txt = evt.txt[1:]
elif evt.txt.startswith(f"{bot.cfg.nick}:"):
evt.txt = evt.txt[len(bot.cfg.nick) + 1 :]
evt.text = evt.text[1:]
elif evt.text.startswith(f"{bot.cfg.nick}:"):
evt.text = evt.text[len(bot.cfg.nick) + 1 :]
else:
return
if evt.txt:
evt.txt = evt.txt[0].lower() + evt.txt[1:]
if evt.txt:
if evt.text:
evt.text = evt.text[0].lower() + evt.text[1:]
if evt.text:
launch(command, evt)

@@ -613,3 +606,3 @@

bot.state.nrerror += 1
bot.state.error = evt.txt
bot.state.error = evt.text
if evt.orig and evt.orig in bot.zelf:

@@ -630,3 +623,3 @@ bot.stop()

keys(config),
skip="control,password,realname,sleep,username".split(","),
skip="control,name,password,realname,sleep,username".split(","),
)

@@ -637,2 +630,3 @@ )

write(config, fnm or getpath(config))
event.reply("ok")

@@ -670,1 +664,13 @@

event.reply(dcd)
"utility"
def rlog(loglevel, txt, ignore=None):
if ignore is None:
ignore = []
for ign in ignore:
if ign in str(txt):
return
logging.log(LEVELS.get(loglevel), txt)
# This file is been placed in the Public Domain.
"available types"
from rssbot.persist import types
from ..workdir import types
def lst(event):

@@ -11,0 +8,0 @@ tps = types()

# This file is placed in the Public Domain.
"show modules"
from rssbot.package import modules
from ..package import modules
def mod(event):
event.reply(",".join(modules()))
# This file is placed in the Public Domain.
"rich site syndicate"
import html

@@ -25,12 +22,15 @@ import html.parser

from ..caching import find, last, write
from ..clients import Fleet
from ..methods import fmt
from ..objects import Object, update
from ..threads import Repeater, launch
from ..utility import elapsed, fntime, spl
from ..workdir import getpath
from rssbot.clients import Fleet
from rssbot.methods import fmt
from rssbot.objects import Object, update
from rssbot.persist import find, fntime, getpath, last, write
from rssbot.repeats import Repeater
from rssbot.threads import launch
from rssbot.utility import elapsed, spl
def init():
DEBUG = False
def init(cfg):
fetcher = Fetcher()

@@ -45,8 +45,5 @@ fetcher.start()

DEBUG = False
fetchlock = _thread.allocate_lock()
importlock = _thread.allocate_lock()
errors = {}
errors: dict[str, float] = {}
skipped = []

@@ -144,3 +141,3 @@

thrs = []
for _fn, feed in find("rss"):
for _fn, feed in find("rss.Rss"):
thrs.append(launch(self.fetch, feed, silent))

@@ -364,3 +361,3 @@ return thrs

setter = {"display_list": event.args[1]}
for fnm, feed in find("rss", {"rss": event.args[0]}):
for fnm, feed in find("rss.Rss", {"rss": event.args[0]}):
if feed:

@@ -376,3 +373,3 @@ update(feed, setter)

nrs = 0
for _fn, ooo in find("rss"):
for _fn, ooo in find("rss.Rss"):
nrs += 1

@@ -410,3 +407,3 @@ obj = Rss()

continue
has = list(find("rss", {"rss": url}, matching=True))
has = list(find("rss.Rss", {"rss": url}, matching=True))
if has:

@@ -433,3 +430,3 @@ skipped.append(url)

selector = {"rss": event.args[0]}
for fnm, fed in find("rss", selector):
for fnm, fed in find("rss.Rss", selector):
feed = Rss()

@@ -447,3 +444,3 @@ update(feed, fed)

return
for fnm, fed in find("rss"):
for fnm, fed in find("rss.Rss"):
feed = Rss()

@@ -464,3 +461,3 @@ update(feed, fed)

return
for fnm, fed in find("rss", removed=True):
for fnm, fed in find("rss.Rss", removed=True):
feed = Rss()

@@ -479,3 +476,3 @@ update(feed, fed)

nrs = 0
for fnm, fed in find("rss"):
for fnm, fed in find("rss.Rss"):
nrs += 1

@@ -492,3 +489,3 @@ elp = elapsed(time.time() - fntime(fnm))

return
for fnm, result in find("rss", {"rss": url}):
for fnm, result in find("rss.Rss", {"rss": url}):
if result:

@@ -495,0 +492,0 @@ event.reply(f"{url} is known")

# This file is placed in the Public Domain.
"enable silence mode"
from rssbot.command import Fleet
from ..brokers import Fleet
def sil(event):

@@ -11,0 +8,0 @@ bot = Fleet.get(event.orig)

# This file is placed in the Public Domain.
"show running threads"
import threading

@@ -11,3 +8,3 @@ import time

from ..utility import elapsed
from rssbot.utility import elapsed

@@ -14,0 +11,0 @@

# This file is placed in the Public Domain.
"uptime"
import time
from ..utility import elapsed
from rssbot.utility import elapsed

@@ -12,0 +9,0 @@

# This file is placed in the Public Domain.
"clean namespace"
import json
import types

@@ -35,5 +36,14 @@

def fqn(obj):
kin = str(type(obj)).split()[-1][1:-2]
if kin == "type":
kin = f"{obj.__module__}.{obj.__name__}"
return kin
def items(obj):
if isinstance(obj, dict):
return obj.items()
if isinstance(obj, types.MappingProxyType):
return obj.items()
return obj.__dict__.items()

@@ -48,7 +58,16 @@

def update(obj, data, empty=True):
for key, value in items(data):
if not empty and not value:
continue
setattr(obj, key, value)
def update(obj, data={}, empty=True):
if isinstance(obj, type):
for k, v in items(data):
if isinstance(getattr(obj, k, None), types.MethodType):
continue
setattr(obj, k, v)
elif isinstance(obj, dict):
for k, v in items(data):
setattr(obj, k, v)
else:
for key, value in items(data):
if not empty and not value:
continue
setattr(obj, key, value)

@@ -66,2 +85,3 @@

'construct',
'fqn',
'items',

@@ -68,0 +88,0 @@ 'keys',

# This file is placed in the Public Domain.
"module management"
import inspect
import logging
import importlib
import importlib.util
import os
import sys
import threading
import _thread
from .threads import launch
from .utility import importer, md5sum
from .workdir import Workdir, j, moddir
from .utility import spl, where
NAME = Workdir.name
PATH = os.path.dirname(inspect.getfile(Workdir))
lock = threading.RLock()
class Mods:
debug = False
dirs = {}
md5s = {}
dirs: dict[str, str] = {}
ignore: list[str] = []
@staticmethod
def dir(name, path=None):
if path is not None:
Mods.dirs[name] = path
else:
Mods.dirs[NAME + "." + name] = j(PATH, name)
def add(name=None, path=None):
Mods.dirs[name] = path
@staticmethod
def init(name, ignore="", local=False):
if name:
pkg = importer(name)
if pkg:
Mods.add(name, pkg.__path__[0])
if ignore:
Mods.ignore = spl(ignore)
if local:
Mods.add("mods", "mods")
def getmod(name):
for nme, path in Mods.dirs.items():
mname = nme + "." + name
module = sys.modules.get(mname, None)
if module:
return module
pth = j(path, f"{name}.py")
if Mods.md5s:
if os.path.exists(pth) and name != "tbl":
md5 = Mods.md5s.get(name, None)
if md5sum(pth) != md5:
file = pth.split(os.sep)[-1]
logging.info("md5 error %s", file)
mod = importer(mname, pth)
if mod:
return mod
mname = ""
pth = ""
if name in Mods.ignore:
return
for packname, path in Mods.dirs.items():
modpath = os.path.join(path, name + ".py")
if os.path.exists(modpath):
pth = modpath
mname = f"{packname}.{name}"
break
return sys.modules.get(mname, None) or importer(mname, pth)
def inits(names):
modz = []
for name in modules():
if name not in names:
continue
try:
module = getmod(name)
if module and "init" in dir(module):
thr = launch(module.init)
modz.append((module, thr))
except Exception as ex:
logging.exception(ex)
_thread.interrupt_main()
return modz
def importer(name, pth=None):
if pth and os.path.exists(pth):
spec = importlib.util.spec_from_file_location(name, pth)
else:
spec = importlib.util.find_spec(name)
if not spec:
return
mod = importlib.util.module_from_spec(spec)
if not mod:
return
sys.modules[name] = mod
spec.loader.exec_module(mod)
return mod

@@ -78,2 +66,4 @@

for name, path in Mods.dirs.items():
if name in Mods.ignore:
continue
if not os.path.exists(path):

@@ -83,25 +73,7 @@ continue

x[:-3] for x in os.listdir(path)
if x.endswith(".py") and not x.startswith("__")
])
if x.endswith(".py") and not x.startswith("__") and x not in Mods.ignore
])
return sorted(mods)
def setdirs(network=False, mods=False):
Mods.dir("modules")
Mods.dir("local", moddir())
if network:
Mods.dir("network")
if mods:
Mods.dir("mods", "mods")
def sums(checksum):
tbl = getmod("tbl")
if not tbl:
logging.info("no table")
return
if "MD5" in dir(tbl):
Mods.md5s.update(tbl.MD5)
def __dir__():

@@ -112,7 +84,3 @@ return (

'importer',
'inits',
'md5sum',
'modules',
'setdirs',
'sums'
)
# This file is placed in the Public Domain.
"non-blocking"
import logging

@@ -35,66 +32,10 @@ import queue

def join(self, timeout=None):
result = None
try:
super().join(timeout)
result = self.result
except (KeyboardInterrupt, EOFError):
_thread.interrupt_main()
return result
super().join(timeout)
return self.result
def run(self):
func, args = self.queue.get()
try:
self.result = func(*args)
except (KeyboardInterrupt, EOFError):
_thread.interrupt_main()
except Exception as ex:
logging.exception(ex)
_thread.interrupt_main()
self.result = func(*args)
class Timy(threading.Timer):
def __init__(self, sleep, func, *args, **kwargs):
super().__init__(sleep, func)
self.name = kwargs.get("name", name(func))
self.sleep = sleep
self.state = {}
self.state["latest"] = time.time()
self.state["starttime"] = time.time()
self.starttime = time.time()
class Timed:
def __init__(self, sleep, func, *args, thrname="", **kwargs):
self.args = args
self.func = func
self.kwargs = kwargs
self.sleep = sleep
self.name = thrname or kwargs.get("name", name(func))
self.target = time.time() + self.sleep
self.timer = None
def run(self):
self.timer.latest = time.time()
self.func(*self.args)
def start(self):
self.kwargs["name"] = self.name
timer = Timy(self.sleep, self.run, *self.args, **self.kwargs)
timer.start()
self.timer = timer
def stop(self):
if self.timer:
self.timer.cancel()
class Repeater(Timed):
def run(self):
launch(self.start)
super().run()
def launch(func, *args, **kwargs):

@@ -106,8 +47,15 @@ thread = Thread(func, *args, **kwargs)

def threadhook(args):
type, value, trace, thread = args
exc = value.with_traceback(trace)
if type not in (KeyboardInterrupt, EOFError):
logging.exception(exc)
_thread.interrupt_main()
def __dir__():
return (
'Repeater',
'Thread',
'launch',
'name'
'threadhook'
)
# This file is placed in the Public Domain.
"utilities"
import hashlib
import importlib.util
import logging
import os
import pathlib
import sys
import time
import _thread

@@ -22,23 +17,37 @@

"%d-%m",
"%m-%d",
"%m-%d"
]
LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'warn': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL,
}
def check(text):
args = sys.argv[1:]
for arg in args:
if not arg.startswith("-"):
continue
for char in text:
if char in arg:
return True
return False
class Formatter(logging.Formatter):
def daemon(verbose=False):
pid = os.fork()
if pid != 0:
os._exit(0)
os.setsid()
pid2 = os.fork()
if pid2 != 0:
os._exit(0)
if not verbose:
with open('/dev/null', 'r', encoding="utf-8") as sis:
os.dup2(sis.fileno(), sys.stdin.fileno())
with open('/dev/null', 'a+', encoding="utf-8") as sos:
os.dup2(sos.fileno(), sys.stdout.fileno())
with open('/dev/null', 'a+', encoding="utf-8") as ses:
os.dup2(ses.fileno(), sys.stderr.fileno())
os.umask(0)
os.chdir("/")
os.nice(10)
def format(self, record):
record.module = record.module.upper()
return logging.Formatter.format(self, record)
def elapsed(seconds, short=True):

@@ -95,47 +104,12 @@ txt = ""

def fntime(daystr):
datestr = " ".join(daystr.split(os.sep)[-2:])
datestr = datestr.replace("_", " ")
if "." in datestr:
datestr, rest = datestr.rsplit(".", 1)
else:
rest = ""
timed = time.mktime(time.strptime(datestr, "%Y-%m-%d %H:%M:%S"))
if rest:
timed += float("." + rest)
return float(timed)
def forever():
while True:
try:
time.sleep(0.1)
except (KeyboardInterrupt, EOFError):
break
def importer(name, pth):
if not os.path.exists(pth):
return
try:
spec = importlib.util.spec_from_file_location(name, pth)
if not spec or not spec.loader:
return
mod = importlib.util.module_from_spec(spec)
if not mod:
return
sys.modules[name] = mod
spec.loader.exec_module(mod)
logging.info("load %s", pth)
return mod
except Exception as ex:
logging.exception(ex)
_thread.interrupt_main()
def level(loglevel="debug"):
if loglevel != "none":
datefmt = "%H:%M:%S"
format_short = "%(module).3s %(message)-76s"
ch = logging.StreamHandler()
ch.setLevel(LEVELS.get(loglevel))
formatter = Formatter(fmt=format_short, datefmt=datefmt)
ch.setFormatter(formatter)
logger = logging.getLogger()
logger.addHandler(ch)
def md5sum(path):
import hashlib
with open(path, "r", encoding="utf-8") as file:

@@ -146,2 +120,20 @@ txt = file.read().encode("utf-8")

def pidfile(filename):
if os.path.exists(filename):
os.unlink(filename)
path2 = pathlib.Path(filename)
path2.parent.mkdir(parents=True, exist_ok=True)
with open(filename, "w", encoding="utf-8") as fds:
fds.write(str(os.getpid()))
def privileges():
import getpass
import pwd
pwnam2 = pwd.getpwnam(getpass.getuser())
os.setgid(pwnam2.pw_gid)
os.setuid(pwnam2.pw_uid)
def spl(txt):

@@ -151,18 +143,46 @@ try:

except (TypeError, ValueError):
result = [
txt,
]
result = []
return [x for x in result if x]
def where(obj):
import inspect
return os.path.dirname(inspect.getfile(obj))
def wrapped(func):
try:
func()
except (KeyboardInterrupt, EOFError):
pass
def wrap(func):
import termios
old = None
try:
old = termios.tcgetattr(sys.stdin.fileno())
except termios.error:
pass
try:
wrapped(func)
finally:
if old:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, old)
def __dir__():
return (
'cdir',
'check',
'daemon',
'elapsed',
'extract_date',
'fntime',
'importer',
'level',
'forever',
'md5sum',
'spl'
)
'pidfile',
'privileges',
'spl',
'where',
'wrap',
'wrapped'
)
# This file is placed in the Public Domain.
"client for a string"
class Fleet:
clients = {}
@staticmethod
def add(client):
Fleet.clients[repr(client)] = client
@staticmethod
def all():
return Fleet.clients.values()
@staticmethod
def announce(txt):
for client in Fleet.all():
client.announce(txt)
@staticmethod
def display(evt):
client = Fleet.get(evt.orig)
client.display(evt)
@staticmethod
def get(orig):
return Fleet.clients.get(orig, None)
@staticmethod
def say(orig, channel, txt):
client = Fleet.get(orig)
client.say(channel, txt)
@staticmethod
def shutdown():
for client in Fleet.all():
client.wait()
client.stop()
def __dir__():
return (
'Fleet',
)
# This file is placed in the Public Domain.
"object for a string"
import json.decoder
import os
import threading
from .methods import deleted, search
from .objects import Object, update
from .serials import dump, load
from .workdir import cdir, fqn, getpath, j, long, store
from .utility import fntime
lock = threading.RLock()
class Cache:
objs = {}
@staticmethod
def add(path, obj):
Cache.objs[path] = obj
@staticmethod
def get(path):
return Cache.objs.get(path, None)
@staticmethod
def update(path, obj):
if path in Cache.objs:
update(Cache.objs[path], obj)
else:
Cache.add(path, obj)
def find(clz, selector=None, removed=False, matching=False):
clz = long(clz)
if selector is None:
selector = {}
for pth in fns(clz):
obj = Cache.get(pth)
if not obj:
obj = Object()
read(obj, pth)
Cache.add(pth, obj)
if not removed and deleted(obj):
continue
if selector and not search(obj, selector, matching):
continue
yield pth, obj
def fns(clz):
pth = store(clz)
for rootdir, dirs, _files in os.walk(pth, topdown=False):
for dname in dirs:
ddd = j(rootdir, dname)
for fll in os.listdir(ddd):
yield j(ddd, fll)
def last(obj, selector=None):
if selector is None:
selector = {}
result = sorted(
find(fqn(obj), selector),
key=lambda x: fntime(x[0])
)
res = ""
if result:
inp = result[-1]
update(obj, inp[-1])
res = inp[0]
return res
def read(obj, path):
with lock:
with open(path, "r", encoding="utf-8") as fpt:
try:
update(obj, load(fpt))
except json.decoder.JSONDecodeError as ex:
ex.add_note(path)
raise ex
def write(obj, path=None):
with lock:
if path is None:
path = getpath(obj)
cdir(path)
with open(path, "w", encoding="utf-8") as fpt:
dump(obj, fpt, indent=4)
Cache.update(path, obj)
return path
def __dir__():
return (
'Cache',
'find',
'last',
'read',
'write'
)
# This file is placed in the Public Domain.
"json serializer"
from json import JSONEncoder
from json import dump as jdump
from json import dumps as jdumps
from json import load as load
from json import loads as loads
class Encoder(JSONEncoder):
def default(self, o):
if isinstance(o, dict):
return o.items()
if isinstance(o, list):
return iter(o)
try:
return JSONEncoder.default(self, o)
except TypeError:
try:
return vars(o)
except TypeError:
return repr(o)
def dump(obj, fp, *args, **kw):
kw["cls"] = Encoder
jdump(obj, fp, *args, **kw)
def dumps(obj, *args, **kw):
kw["cls"] = Encoder
return jdumps(obj, *args, **kw)
def __dir__():
return (
'dump',
'dumps',
'load',
'loads'
)
# This file is placed in the Public Domain.
"working directory"
import datetime
import os
import pathlib
j = os.path.join
class Workdir:
name = __file__.rsplit(os.sep, maxsplit=2)[-2]
wdr = ""
def cdir(path):
pth = pathlib.Path(path)
pth.parent.mkdir(parents=True, exist_ok=True)
def fqn(obj):
kin = str(type(obj)).split()[-1][1:-2]
if kin == "type":
kin = f"{obj.__module__}.{obj.__name__}"
return kin
def getpath(obj):
return store(ident(obj))
def ident(obj):
return j(fqn(obj), *str(datetime.datetime.now()).split())
def long(name):
split = name.split(".")[-1].lower()
res = name
for names in types():
if split == names.split(".")[-1].lower():
res = names
break
return res
def moddir():
assert Workdir.wdr
return j(Workdir.wdr, "mods")
def pidfile(filename):
if os.path.exists(filename):
os.unlink(filename)
path2 = pathlib.Path(filename)
path2.parent.mkdir(parents=True, exist_ok=True)
with open(filename, "w", encoding="utf-8") as fds:
fds.write(str(os.getpid()))
def pidname(name):
assert Workdir.wdr
return j(Workdir.wdr, f"{name}.pid")
def setwd(name, path=""):
path = path or os.path.expanduser(f"~/.{name}")
Workdir.wdr = Workdir.wdr or path
skel()
def skel():
result = ""
if not os.path.exists(store()):
pth = pathlib.Path(store())
pth.mkdir(parents=True, exist_ok=True)
pth = pathlib.Path(moddir())
pth.mkdir(parents=True, exist_ok=True)
result = str(pth)
return result
def store(pth=""):
assert Workdir.wdr
return j(Workdir.wdr, "store", pth)
def strip(pth, nmr=2):
return j(pth.split(os.sep)[-nmr:])
def types():
skel()
return os.listdir(store())
def __dir__():
return (
'Workdir',
'cdir',
'fqn',
'getpath',
'ident',
'j',
'long',
'moddir',
'pidfile',
'pidname',
'setwd',
'skel',
'store',
'strip',
'types'
)

Sorry, the diff of this file is not supported yet