|
|
|
@ -14,22 +14,31 @@ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import argparse |
|
|
|
|
import cgi |
|
|
|
|
import datetime |
|
|
|
|
import html |
|
|
|
|
import json |
|
|
|
|
import sqlite3 |
|
|
|
|
|
|
|
|
|
import pydot |
|
|
|
|
|
|
|
|
|
from synapse.events import FrozenEvent |
|
|
|
|
from synapse.api.room_versions import KNOWN_ROOM_VERSIONS |
|
|
|
|
from synapse.events import make_event_from_dict |
|
|
|
|
from synapse.util.frozenutils import unfreeze |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_graph(db_name, room_id, file_prefix, limit): |
|
|
|
|
def make_graph(db_name: str, room_id: str, file_prefix: str, limit: int) -> None: |
|
|
|
|
""" |
|
|
|
|
Generate a dot and SVG file for a graph of events in the room based on the |
|
|
|
|
topological ordering by reading from a Synapse SQLite database. |
|
|
|
|
""" |
|
|
|
|
conn = sqlite3.connect(db_name) |
|
|
|
|
|
|
|
|
|
sql = "SELECT room_version FROM rooms WHERE room_id = ?" |
|
|
|
|
c = conn.execute(sql, (room_id,)) |
|
|
|
|
room_version = KNOWN_ROOM_VERSIONS[c.fetchone()[0]] |
|
|
|
|
|
|
|
|
|
sql = ( |
|
|
|
|
"SELECT json FROM event_json as j " |
|
|
|
|
"SELECT json, internal_metadata FROM event_json as j " |
|
|
|
|
"INNER JOIN events as e ON e.event_id = j.event_id " |
|
|
|
|
"WHERE j.room_id = ?" |
|
|
|
|
) |
|
|
|
@ -43,7 +52,10 @@ def make_graph(db_name, room_id, file_prefix, limit): |
|
|
|
|
|
|
|
|
|
c = conn.execute(sql, args) |
|
|
|
|
|
|
|
|
|
events = [FrozenEvent(json.loads(e[0])) for e in c.fetchall()] |
|
|
|
|
events = [ |
|
|
|
|
make_event_from_dict(json.loads(e[0]), room_version, json.loads(e[1])) |
|
|
|
|
for e in c.fetchall() |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
events.sort(key=lambda e: e.depth) |
|
|
|
|
|
|
|
|
@ -84,7 +96,7 @@ def make_graph(db_name, room_id, file_prefix, limit): |
|
|
|
|
"name": event.event_id, |
|
|
|
|
"type": event.type, |
|
|
|
|
"state_key": event.get("state_key", None), |
|
|
|
|
"content": cgi.escape(content, quote=True), |
|
|
|
|
"content": html.escape(content, quote=True), |
|
|
|
|
"time": t, |
|
|
|
|
"depth": event.depth, |
|
|
|
|
"state_group": state_group, |
|
|
|
@ -96,11 +108,11 @@ def make_graph(db_name, room_id, file_prefix, limit): |
|
|
|
|
graph.add_node(node) |
|
|
|
|
|
|
|
|
|
for event in events: |
|
|
|
|
for prev_id, _ in event.prev_events: |
|
|
|
|
for prev_id in event.prev_event_ids(): |
|
|
|
|
try: |
|
|
|
|
end_node = node_map[prev_id] |
|
|
|
|
except Exception: |
|
|
|
|
end_node = pydot.Node(name=prev_id, label="<<b>%s</b>>" % (prev_id,)) |
|
|
|
|
end_node = pydot.Node(name=prev_id, label=f"<<b>{prev_id}</b>>") |
|
|
|
|
|
|
|
|
|
node_map[prev_id] = end_node |
|
|
|
|
graph.add_node(end_node) |
|
|
|
@ -112,7 +124,7 @@ def make_graph(db_name, room_id, file_prefix, limit): |
|
|
|
|
if len(event_ids) <= 1: |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
cluster = pydot.Cluster(str(group), label="<State Group: %s>" % (str(group),)) |
|
|
|
|
cluster = pydot.Cluster(str(group), label=f"<State Group: {str(group)}>") |
|
|
|
|
|
|
|
|
|
for event_id in event_ids: |
|
|
|
|
cluster.add_node(node_map[event_id]) |
|
|
|
@ -126,7 +138,7 @@ def make_graph(db_name, room_id, file_prefix, limit): |
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
parser = argparse.ArgumentParser( |
|
|
|
|
description="Generate a PDU graph for a given room by talking " |
|
|
|
|
"to the given homeserver to get the list of PDUs. \n" |
|
|
|
|
"to the given Synapse SQLite file to get the list of PDUs. \n" |
|
|
|
|
"Requires pydot." |
|
|
|
|
) |
|
|
|
|
parser.add_argument( |
|
|
|
|