From 83b091486668aac9fdf80eff1bd15ce0ac4273c4 Mon Sep 17 00:00:00 2001 From: Wolfgang Müller Date: Thu, 22 Apr 2021 18:54:05 +0200 Subject: Initial prototype --- quarg/quassel/__init__.py | 0 quarg/quassel/formatter.py | 139 +++++++++++++++++++++++++++++++++++++++++++++ quarg/quassel/types.py | 51 +++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 quarg/quassel/__init__.py create mode 100644 quarg/quassel/formatter.py create mode 100644 quarg/quassel/types.py (limited to 'quarg/quassel') diff --git a/quarg/quassel/__init__.py b/quarg/quassel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/quarg/quassel/formatter.py b/quarg/quassel/formatter.py new file mode 100644 index 0000000..1297288 --- /dev/null +++ b/quarg/quassel/formatter.py @@ -0,0 +1,139 @@ +import datetime + +# from functools import partial +from typing import NamedTuple +from quarg.quassel.types import MessageType + +class User(NamedTuple): + nick: str + host: str + prefix: str + + @classmethod + def from_sender(cls, sender, prefixes=''): + nick, *host = sender.split('!', 1) + prefix = prefixes[0] if prefixes else '' + return cls(nick, host[0] if host else '', prefix) + + def __repr__(self): + return f'{self.prefix}{self.nick}' + +class Message(NamedTuple): + type: MessageType + time: datetime.datetime + buffer: str + user: User + message: str + + @classmethod + def from_backlog(cls, backlog): + return cls(MessageType(backlog.type), backlog.time, backlog.buffer.buffername, + User.from_sender(backlog.sender.sender, backlog.senderprefixes), backlog.message) + +def format_from(backlog_row): + message = Message.from_backlog(backlog_row) + formatter = FORMATTERS[message.type] + timestamp = message.time.isoformat(sep=' ', timespec='seconds') + + return f'{timestamp}\t{message.buffer}\t{formatter(message)}' + +def format_privmsg(msg): + return f'<{msg.user}> {msg.message}' + +def format_notice(msg): + return f'[{msg.user}] {msg.message}' + +def format_action(msg): + return f'-*- {msg.user} {msg.message}' + +def format_nick(msg): + return f'<-> {msg.user} is now known as {msg.message}' + +def format_mode(msg): + return f'*** Mode {msg.message} by {msg.user}' + +def format_join(msg): + return f'--> {msg.user} ({msg.user.host}) has joined {msg.buffer}' + +def format_part(msg): + if msg.message: + return f'<-- {msg.user} has left {msg.buffer} ({msg.message})' + + return f'<-- {msg.user} has left {msg.buffer}' + +def format_quit(msg): + if msg.message: + return f'<-- {msg.user} has quit ({msg.message})' + + return f'<-- {msg.user} has quit' + +def format_kick(msg): + target, *kickmsg = msg.message.split(' ', 1) + + if kickmsg: + return f'<-* {msg.user} has kicked {target} from {msg.buffer} ({kickmsg[0]})' + + return f'<-* {msg.user} has kicked {target} from {msg.buffer}' + +def format_kill(msg): + # pylint: disable=line-too-long + + # As of 2021-04-24 not even Quassel implements printing this message [1]. + # They do have a symbol [2] for it, however, so use that along with the message + # [1] https://github.com/quassel/quassel/blob/285215315e6f2420724532323a4b1bccae156cb1/src/uisupport/uistyle.cpp#L950 + # [2] https://github.com/quassel/quassel/blob/285215315e6f2420724532323a4b1bccae156cb1/src/uisupport/uistyle.cpp#L1079-L1080 + return f'<-x {msg.message}' + +def format_generic(msg): + return f'* {msg.message}' + +def parse_netsplit(splitmsg): + # splitmsg contains user!host separated by #:# ... + elements = splitmsg.split('#:#') + + # ... however, the last element contains the split servers instead + servers = elements.pop().split(' ', 1) + + # TODO: This takes ages if the netsplit was large + users = [User.from_sender(e) for e in elements] + + return users, servers + +def format_netsplit_join(msg): + users, (srv_left, srv_right) = parse_netsplit(msg.message) + have_joined = ', '.join(user.nick for user in users) + return f'=> Netsplit between {srv_left} and {srv_right} ended. Users joined: {have_joined}' + +def format_netsplit_quit(msg): + users, (srv_left, srv_right) = parse_netsplit(msg.message) + have_quit = ', '.join(user.nick for user in users) + return f'<= Netsplit between {srv_left} and {srv_right}. Users quit: {have_quit}' + +# TODO inline the format strings here and have a wrapper function that gives msg? +# TODO also .format(**msg) + +def format_from_string(string, msg): + return string.format(**msg._asdict()) + +# MessageType.PART: partial(format_from_string, '<-- {user} has left {buffer}'), + +FORMATTERS = { + MessageType.PRIVMSG: format_privmsg, + MessageType.NOTICE: format_notice, + MessageType.ACTION: format_action, + MessageType.NICK: format_nick, + MessageType.MODE: format_mode, + MessageType.JOIN: format_join, + MessageType.PART: format_part, + MessageType.QUIT: format_quit, + MessageType.KICK: format_kick, + MessageType.KILL: format_kill, + MessageType.SERVER: format_generic, + MessageType.INFO: format_generic, + MessageType.ERROR: format_generic, + MessageType.DAYCHANGE: format_generic, + MessageType.TOPIC: format_generic, + MessageType.NETSPLIT_JOIN: format_netsplit_join, + MessageType.NETSPLIT_QUIT: format_netsplit_quit, + MessageType.INVITE: format_generic, +} diff --git a/quarg/quassel/types.py b/quarg/quassel/types.py new file mode 100644 index 0000000..36f385a --- /dev/null +++ b/quarg/quassel/types.py @@ -0,0 +1,51 @@ +from enum import Enum + +class BufferType(Enum): + INVALID = 0x0 + STATUS = 0x1 + CHANNEL = 0x2 + QUERY = 0x4 + GROUP = 0x8 + + @classmethod + def describe(cls): + return 'buffer type' + +class MessageFlag(Enum): + NONE = 0x0 + SELF = 0x1 + HIGHLIGHT = 0x2 + REDIRECTED = 0x4 + SERVERMSG = 0x8 + STATUSMSG = 0x10 + IGNORED = 0x20 + BACKLOG = 0x80 + + @classmethod + def describe(cls): + return 'message flag' + +class MessageType(Enum): + PRIVMSG = 0x1 + NOTICE = 0x2 + ACTION = 0x4 + NICK = 0x8 + MODE = 0x10 + JOIN = 0x20 + PART = 0x40 + QUIT = 0x80 + KICK = 0x100 + KILL = 0x200 + SERVER = 0x400 + INFO = 0x800 + ERROR = 0x1000 + # as far as I can see DAYCHANGE does not show up in the database + DAYCHANGE = 0x2000 + TOPIC = 0x4000 + NETSPLIT_JOIN = 0x8000 + NETSPLIT_QUIT = 0x10000 + INVITE = 0x20000 + + @classmethod + def describe(cls): + return 'message type' -- cgit v1.2.3-2-gb3c3