aboutsummaryrefslogblamecommitdiffstatshomepage
path: root/quarg/quassel/formatter.py
blob: 888d145e3d6cff9315d30d01f27cd6656d6335f2 (plain) (tree)
1
2
3
4
5
6
7
               
                             
                             
 

                                           
 




























                                                                                                    
 
                                                                                      
 


                                                          
 




















                                                                                     















                                                                                                                                




                                                                               
 
                               
 
                                    

                                                                                           


                                                                                                
 
 

                                                                              

              





                                                                                       




                                                  
                                                    
                                                  
                                                    
                                                  

                                                                   

                                                  
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,
}