from math import inf import random import re import signal import sys import threading import time from mastodon import Mastodon import redis from systemd import journal kInvalidUserRe = re.compile('Invalid user (.*) from') r = redis.Redis(decode_responses=True) mastodon = Mastodon( access_token = 'pytooter_usercred.secret', api_base_url = 'https://botsin.space') class Reader(threading.Thread): def handle(self, entry): message = entry['MESSAGE'] m = kInvalidUserRe.match(message) if not m: return (username,) = m.groups() if not username: return ts = entry['__REALTIME_TIMESTAMP'] self.emit(ts.timestamp(), username) def emit(self, ts, username): r.zadd('user:first_seen', {username: ts}, nx=True) print(ts, username) def run(self): latest = r.get('journal:latest') if latest: print('seek to', latest) latest = float(latest) else: print('no bookmark') latest = time.time() with journal.Reader() as j: j.add_match(_SYSTEMD_UNIT="ssh.service") j.seek_realtime(latest) while True: for entry in j: self.handle(entry) latest = entry['__REALTIME_TIMESTAMP'].timestamp() print('bookmark to', latest) r.set('journal:latest', latest) j.wait() class Publisher(threading.Thread): def run(self): latest = r.get('publisher:latest') if latest: print('seek to', latest) latest = '(' + latest else: print('no bookmark') latest = -inf while True: result = r.zrangebyscore('user:first_seen', latest, inf, withscores=True, num=1, start=0) if result: [(username, ts)] = result if username: print('publish', username) mastodon.toot(username) print('bookmark to', ts) r.set('publisher:latest', ts) latest = f'({ts}' # 60 minute mean, 5 minute stddev delay = random.gauss(3600, 300) print('sleep', delay) time.sleep(delay) def main(): reader = Reader() reader.start() publisher = Publisher() publisher.start() if __name__ == '__main__': main()