├── .gitignore ├── README.md └── iExport.py /.gitignore: -------------------------------------------------------------------------------- 1 | LibraryMessagesBackup 2 | node_modules 3 | package-lock.json 4 | LibraryMessagesBackup 5 | *.html 6 | *.txt 7 | backup 8 | backup/* 9 | .tags 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iExport 2 | 3 | If you have iMessage, then the files are backed up in `~/Library/Messages/chat.db` 4 | 5 | This script will process them from a wild collection of `.plist`s to a single html file 6 | 7 | It relies on sqlite3 & python (should be factory installs) 8 | 9 | 1. `git clone https://www.github.com/saylestyler/iExport && cd iExport` 10 | 2. run `python iExport.py > your-backup.html` 11 | 3. njoy `open your-backup.html -a Firefox` 12 | -------------------------------------------------------------------------------- /iExport.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # this is from pip install, you might gotta do this too but who knows with python? ugh... 4 | # If you wish to install a non-brew-packaged Python package, 5 | # create a virtual environment using python3 -m venv path/to/venv. 6 | # Then use path/to/venv/bin/python and path/to/venv/bin/pip. 7 | 8 | import base64 9 | import mimetypes 10 | import html 11 | from os import path 12 | import sqlite3 13 | 14 | CHAT_DB = path.expanduser("~/iExport/LibraryMessagesBackup/chat.db") 15 | 16 | EPOCH = 978307200 17 | 18 | print(""" 19 | 20 | 21 |
22 | 23 | 61 | 62 | 63 | """) 64 | 65 | 66 | def export_all(): 67 | db = sqlite3.connect(CHAT_DB) 68 | cursor = db.cursor() 69 | 70 | rows = cursor.execute(""" 71 | SELECT chat_identifier 72 | FROM chat; 73 | """) 74 | for row in rows: 75 | export(row[0]) 76 | 77 | 78 | def export(chat_id): 79 | db = sqlite3.connect(CHAT_DB) 80 | cursor = db.cursor() 81 | 82 | rows = cursor.execute(""" 83 | SELECT datetime(m.date + ?, 'unixepoch', 'localtime') as fmtdate, 84 | m.is_from_me, 85 | m.text, 86 | a.filename 87 | FROM chat as c 88 | INNER JOIN chat_message_join AS cm 89 | ON cm.chat_id = c.ROWID 90 | INNER JOIN message AS m 91 | ON m.ROWID = cm.message_id 92 | LEFT JOIN message_attachment_join AS ma 93 | ON ma.message_id = m.ROWID 94 | LEFT JOIN attachment as a 95 | ON a.ROWID = ma.attachment_id 96 | WHERE c.chat_identifier = ? 97 | ORDER BY m.date 98 | LIMIT 10; 99 | """, (EPOCH, chat_id)) # NOTE: limit here 100 | 101 | for row in rows: 102 | date = row[0] 103 | who = "me" if row[1] is 1 else "contact" 104 | if row[3]: 105 | attachment = path.expanduser(row[3]) 106 | text = 'right click, copy '.format(path.abspath(attachment)) 107 | else: 108 | text = html.escape(row[3] or '') 109 | if not text: 110 | continue # not text = falsy in python (?) 111 | line = " ".format(who, date, text) 112 | print(line) # this will print to stdouot if not >'d to output/file/watevr.html 113 | 114 | print(""" 115 | 116 | 117 | """) 118 | 119 | export_all() 120 | --------------------------------------------------------------------------------