aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/quarg/quassel/formatter.py
blob: 12972884189edd3d3ee9839063e23b62225ee18a (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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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 <string>.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,
}