Skip to content

Instantly share code, notes, and snippets.

@daniele-athome
Last active September 2, 2022 07:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daniele-athome/ea10806fa3d37f4b2285fff235b5555b to your computer and use it in GitHub Desktop.
Save daniele-athome/ea10806fa3d37f4b2285fff235b5555b to your computer and use it in GitHub Desktop.
Convert a Kontalk database (v4) to HTML files
#!/usr/bin/env python3
# Convert a Kontalk conversation to HTML
# Usage: ./convert-threads.py messages.db
# HTML goes to files in current directory, one per thread
import sys
import hashlib
import sqlite3
from datetime import datetime
DISPLAYNAME_ME = 'Me'
COLOR_ME = 'black'
COLOR_PEER = '#a94138'
def sha1(text):
hashed = hashlib.sha1(text)
return hashed.hexdigest()
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
def get_group_name(conn, threadId):
g = conn.cursor()
g.execute('SELECT * FROM groups WHERE thread_id = ?', (threadId, ))
val = g.fetchone()
g.close()
return val['subject'] if val else None
dbfile = sys.argv[1]
conn = sqlite3.connect(dbfile)
conn.row_factory = dict_factory
t = conn.cursor()
t.execute('SELECT * from threads ORDER by timestamp')
thread = t.fetchone()
while thread:
c = conn.cursor()
c.execute('SELECT * FROM messages WHERE thread_id = ? ORDER BY timestamp', (thread['_id'], ))
groupName = get_group_name(conn, thread['_id'])
if groupName:
displayName = groupName
else:
displayName = thread['peer']
output_file = open(thread['peer'] + '.html', mode='w+')
print("""
<!DOCTYPE html>
<html lang="en">
<head>
<title>Kontalk messages with %s</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<style type="text/css">
* {
padding: 0;
margin: 0;
}
body {
font-family: arial, helvetica, serif;
}
h1 {
margin: 12px;
}
#conversation {
width: 100%%;
}
.stamp {
background-color: #f2f2f2;
color: #20435c;
font-weight: bold;
text-align: center;
padding: 8px;
}
.chat {
padding: 4px;
}
.chat .incoming {
color: %s;
}
.chat .outgoing {
color: %s;
}
.peer {
font-weight: bold;
}
</style>
</head>
<body>
<h1>Conversation with %s</h1>
<div id="conversation">
""" % (displayName, COLOR_PEER, COLOR_ME, displayName), file=output_file)
cur_date = None
row = c.fetchone()
while row:
date = datetime.fromtimestamp(row['timestamp']/1000)
date_fmt = date.strftime('%Y-%m-%d')
if not row['body_mime'] and not row['att_mime']:
content = "(encrypted)"
elif row['body_content']:
try:
content = row['body_content'].decode('utf-8')
except UnicodeDecodeError:
# damn Windows!!
content = row['body_content'].decode('iso-8859-1')
elif row['att_mime']:
content = "Media: %s" % (row['att_mime'], )
else:
raise ValueError("Unsupported message")
if not cur_date or date_fmt != cur_date.strftime('%Y-%m-%d'):
cur_date = date
print("""
<div class="stamp">%s</div>
""" % (date_fmt,), file=output_file)
if row['direction'] == 0:
name = row['peer']
css = 'incoming'
else:
name = DISPLAYNAME_ME
css = 'outgoing'
print("""
<div class="chat">
[%s] <span class="peer %s">%s</span>: %s
</div>
""" % (date.strftime('%H:%M:%S'), css, name, content), file=output_file)
row = c.fetchone()
print("""
</div>
</body>
</html>
""", file=output_file)
thread = t.fetchone()
conn.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment