summaryrefslogtreecommitdiff
path: root/gengraph.py
diff options
context:
space:
mode:
Diffstat (limited to 'gengraph.py')
-rw-r--r--gengraph.py133
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()