aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/quarg/quassel/formatter.py
blob: 888d145e3d6cff9315d30d01f27cd6656d6335f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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]

    return f'{format_timestamp(message.time)}\t{message.buffer}\t{formatter(message)}'

def format_timestamp(time):
    # make sure to convert timestamps to local time
    return time.astimezone().strftime('%Y-%m-%d %H:%M:%S')

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):
    # 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)

    # This list can be unwieldily large. Mirror quassel's behaviour and cut off
    # after printing 15 users. If there were any more users than this, have
    # rest contain a value larger than 0
    users = [User.from_sender(e) for e in elements[0:15]]
    rest = max(0, len(elements) - 15)

    return users, servers, rest

def format_netsplit(has_ended, msg):
    users, (srv_left, srv_right), rest = parse_netsplit(msg.message)
    affected = ', '.join(user.nick for user in users) + (f' ({rest} more)' if rest else '')
    if has_ended:
        return f'=> Netsplit between {srv_left} and {srv_right} ended. Users joined: {affected}'
    return f'<= Netsplit between {srv_left} and {srv_right}. Users quit: {affected}'


def fmt(string):
    return partial(lambda string, msg: string.format(**msg._asdict()), string)

FORMATTERS = {
        MessageType.PRIVMSG:       fmt('<{user}> {message}'),
        MessageType.NOTICE:        fmt('[{user}] {message}'),
        MessageType.ACTION:        fmt('-*- {user} {message}'),
        MessageType.NICK:          fmt('<-> {user} is now known as {message}'),
        MessageType.MODE:          fmt('*** Mode {message} by {user}'),
        MessageType.JOIN:          fmt('--> {user} ({user.host}) has joined {buffer}'),
        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: partial(format_netsplit, True),
        MessageType.NETSPLIT_QUIT: partial(format_netsplit, False),
        MessageType.INVITE:        format_generic,
}