Skip to content

Instantly share code, notes, and snippets.

@JerryFleming
Last active December 17, 2015 01:48
Show Gist options
  • Save JerryFleming/5530647 to your computer and use it in GitHub Desktop.
Save JerryFleming/5530647 to your computer and use it in GitHub Desktop.
Export SMS from you Android phones into HTML, friendly for webkit browsers (Chrome/Safari).
#!/usr/bin/python3
# Export SMS from you Android phones into HTML, friendly for webkit browsers (Chrome/Safari).
# This requires SL4A <https://code.google.com/p/android-scripting/>
# and Python3 <https://code.google.com/p/python-for-android/wiki/Python3>.
# by Jerry Fleming <jerryfleming2006@gmail.com> on 2013-03-25.
# No rights reserved. Use at your own risk.
import sqlite3
import datetime
import re
f = open('msg_%s.html' % str(datetime.date.today()), 'w')
def toTime(time, first=False):
time = str(datetime.datetime.fromtimestamp(int(time/1000)))
if first: return time.split()[0]
else: return time
rep = re.compile('\0|\x0c')
def txt(s):
s = rep.sub('', s.strip())
s = s.replace('&', '&amp;')
s = s.replace('<', '&lt;')
s = s.replace('>', '&gt;')
s = s.replace('"', '&quot;')
return s
TYPES = {
1: 'get',
2: 'send',
}
names = {}
path = '/dbdata/databases/com.android.providers.'
con = sqlite3.connect(path + 'contacts/contacts2.db')
cur = con.cursor()
cur.execute('SELECT number, name FROM view_v1_phones')
nump = re.compile(' |-|^\+?86|^12520')
for row in cur:
num = nump.sub('', row[0])
names[num] = row[1]
con = sqlite3.connect(path + 'telephony/mmssms.db')
cur = con.cursor()
cur1 = con.cursor()
f.write('''<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=0"/>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>SMS '''+str(datetime.datetime.now())+'''</title>
<style>
*
{
font-size: 14px;
}
.thread
{
display: block;
padding: 5px 0px;
margin: 0px 0px 30px 0px;
position: relative;
}
.thread.off
{
display: none;
}
.thread.on
{
display: block;
}
.thread:before
{
content: "";
position: absolute;
width: 100%;
border-top: 1px solid black;
top: 22px;
z-index: 15;
}
.thread.on:before
{
left: 0px;
top: 22px;
position: fixed;
}
.thread.on:after
{
left: 0px;
top: 0px;
width: 100%;
height: 40px;
opacity: .4;
z-index: 10;
background-color: white;
content: "";
position: fixed;
}
.thread.on .header
{
position: fixed;
left: 60px;
}
.thread.on .message
{
margin-top: 40px;
}
.header
{
position: absolute;
z-index: 20;
top: 10px;
left: 50px;
border: 1px solid black;
padding: 3px;
background-color: white;
display: block;
}
.control
{
position: absolute;
top: -4px;
left: -40px;
cursor: pointer;
width: 30px;
height: 30px;
border-radius: 15px;
background-color: green;
}
.control:before
{
content: "";
position: absolute;
top: 1px;
left: 1px;
cursor: pointer;
width: 28px;
height: 28px;
border-radius: 14px;
background-color: white;
}
.control:after
{
content: "";
position: absolute;
border: 9px solid transparent;
border-left-color: black;
border-right: 0;
border-left-width: 14px;
top: 6px;
left: 10px;
width: 0;
height: 0;
display: block;
}
.control.on:after
{
content: "";
position: absolute;
border: 9px solid transparent;
border-top-color: black;
border-bottom: 0;
border-top-width: 14px;
top: 10px;
left: 6px;
width: 0;
height: 0;
display: block;
}
.header .name
{
font-weight: bold;
}
.date
{
color: gray;
font-size: 12px;
}
.header .address
{
color: blue;
}
.count
{
font-size: 12px;
display: block;
position: absolute;
right: -80px;
background: white;
border: 1px solid black;
border-radius: 3px;
padding: 2px;
top: 2px;
}
.count:before
{
content: "共";
}
.count:after
{
content: "条";
}
.entries
{
display: none;
}
.message
{
display: block;
border-radius: 5px;
width: 80%;
margin: 15px;
padding: 10px;
padding-bottom: 25px;
-webkit-box-shadow: 3px 3px 8px #000;
position: relative;
}
.message .date
{
display: block;
position: absolute;
right: 10px;
bottom: 10px;
font-size: 12px;
}
.message.get
{
border: 1px solid #ccaf61;
background: -webkit-gradient(linear, left top, left bottom,
color-stop(0, #fbf394),
color-stop(100%, #f9dd59)
);
margin-right: auto;
}
.message.send
{
border: 1px solid #8dc754;
background: -webkit-gradient(linear, left top, left bottom,
color-stop(0, #e0fa9d),
color-stop(100%, #aaed86)
);
margin-left: auto;
}
</style>
</head>
<body>
''')
cur.execute('SELECT _id, date, message_count FROM threads ORDER BY date DESC')
for thread in cur:
cur1.execute('SELECT address, date, type, body FROM sms WHERE thread_id=? ORDER BY date DESC', (thread[0], ))
head = False
for msg in cur1:
if msg[2] not in (1, 2): continue
if not head:
address = nump.sub('', msg[0])
if address in names: name = names[address]
f.write('<div class="thread">\n')
f.write(' <div class="header">\n')
f.write(' <div class="control"></div>\n')
if address in names:
name = names[address]
f.write(' <span class="name">%s</span>\n' % name)
f.write(' <span class="address">%s</span>\n' % address)
f.write(' <span class="date">%s</span>\n' % toTime(thread[1], True))
f.write(' <span class="count">%s</span>\n' % thread[2])
f.write(' </div>\n')
f.write(' <div class="entries">\n')
head = True
f.write(' <div class="message %s">\n' % TYPES[msg[2]])
f.write(' <div class="content">%s</div>\n' % txt(msg[3]))
f.write(' <div class="date">%s</div>\n' % toTime(msg[1]))
f.write(' </div>\n')
if head:
f.write(' </div>\n')
f.write(' </div>\n')
f.write('''</body>
<script src="jquery.js"></script>
<script>
$('.control').click(function(){
$(this).parent().next().toggle();
$(this).toggleClass('on');
if($(this).hasClass('on'))
{
$('.thread').addClass('off');
$(this).parent().parent().removeClass('off').addClass('on');
}
else
{
$('.thread').removeClass('off').removeClass('on');
}
});
</script>
</html>''')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment