diff options
Diffstat (limited to 'gengraph.py')
-rw-r--r-- | gengraph.py | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/gengraph.py b/gengraph.py new file mode 100644 index 0000000..16a9c76 --- /dev/null +++ b/gengraph.py @@ -0,0 +1,133 @@ +# Copyright (C) 2021 Yuchen Pei. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +# Generates a graphviz dot file based on an irc log file. +# usage: +# python gengraph.py /path/to/input-file /path/to/output-file.dot +# after this you can generate a graph using graphviz: +# dot -Tsvg /path/to/output-file.dot -o out.svg + +# The log file is either in quassel (copied from quassel buffer) or +# znc format (collected with the log module). + +import sys +import textwrap + +class Message: + def __init__(self): + # message id + self.id = -1 + # sender + self.sender = "" + # the message / line + self.message = "" + # mssage ids this message is replying to + self.reply_to_msgs = [] + # previous message id of the same sender + self.previous_msg = -1 + + def to_dict(self): + return {'id': self.id, + 'sender': self.sender, + 'message': self.message, + 'reply_to_msgs': self.reply_to_msgs, + 'previous_msg': self.previous_msg} + + +def parse_log(log): + """Parses a quassel or znc log. + A line is in the form of + message: + [hh:mm:ss] <sender-nick> blahblah. + me (quassel): + [hh:mm:ss] -*- sender-nick does something. + me (znc): + [hh:mm:ss] * sender-nick does something. + other notices + [hh:mm:ss] *** Mode #abc +o def by ChanServ + args: + log: a string of quassel log. + return: + a list of Messages + """ + last_messages = dict() + index = 0 + messages = [] + for line in log.splitlines(): + message = Message() + line = line.lstrip() + [_, nick, payload] = line.split(' ', maxsplit=2) + # notices + if nick == '***': + continue + # me + if nick == '-*-' or nick == '*': + [_, _, nick, payload] = line.split(' ', maxsplit=3) + # message + else: + nick = nick[1:-1] # removes <> + message.message = line + message.id = index + # Remove special symbol for op + if nick[0] in ['@', '%']: + nick = nick[1:] + message.sender = nick + if nick in last_messages: + message.previous_msg = last_messages[nick] + last_messages[nick] = index + for participant, msg_id in last_messages.items(): + if payload.find(participant) != -1: + message.reply_to_msgs.append(msg_id) + messages.append(message) + index += 1 + return messages + + +def gen_graph(meeting): + dot = """ +digraph meeting { + graph [ + width = 10, + ranksep = 0.02, + concentrate = true + ]; + + node [ + shape = box, + margin = 0, + pad = 0 + ]; +""" + for msg in meeting: + quoted_message = textwrap.fill(msg.message.replace('"', '\\"')) + dot += f'\nm{str(msg.id)} [label="{quoted_message}"];' + if msg.id > 0: + dot += f'\nm{str(msg.id - 1)} -> m{str(msg.id)} [style="invis"];' + for msg in meeting: + if msg.previous_msg > -1: + dot += f'\nm{str(msg.previous_msg)} -> m{str(msg.id)} [style="dashed"];' + for id in msg.reply_to_msgs: + dot += f'\nm{str(id)} -> m{str(msg.id)};' + dot += '\n}' + return dot + +def main(): + log = open(sys.argv[1]).read() + meeting = parse_log(log) + graph = gen_graph(meeting) + open(sys.argv[2], 'w').write(graph) + +if __name__ == '__main__': + main() |