Skip to content

Instantly share code, notes, and snippets.

@meatyite
Last active August 28, 2019 08:50
Show Gist options
  • Save meatyite/300f40a04301975672418807b631ec74 to your computer and use it in GitHub Desktop.
Save meatyite/300f40a04301975672418807b631ec74 to your computer and use it in GitHub Desktop.
Make twitter DMs from requested twitter data into an actual readable HTML file
"""
Turns your twitter DMs from the "request your data" feature into readable HTML files
You'll need to remove the "window.YTD.direct_message.part0 = " thing from the JS file before running this script
You'll also need to extract the archive inside of the "direct_message_media" folder if you want images to show up
Please execuse me for this horrible piece of code
LICENSED UNDER THE WTFPL, NO COPYRIGHT WHATSOEVER
"""
import tweeterid
import json
import sys
from urllib.parse import urlparse
from os.path import basename
def isMediaUrlsNone(mediaUrls, message_id):
if '<br/>'.join(mediaUrls) == '':
return '[None]<br/>'
else:
urls_html = ""
for media_url in mediaUrls:
if media_url.endswith('.mp4') or media_url.endswith('.mp4?tag=1'):
urls_html += "<video src={} controls/><br/>".format(media_url)
else:
urls_html += "<img src={}/><br/>".format("direct_message_media/" + message_id + '-' + basename(urlparse(media_url).path).replace('/', ''))
return urls_html
import sys
def query_yes_no(question, default="no"):
"""
(yes i stole this from stackoverflow)
Ask a yes/no question via input() and return their answer.
"question" is a string that is presented to the user.
"default" is the presumed answer if the user just hits <Enter>.
It must be "yes" (the default), "no" or None (meaning
an answer is required of the user).
The "answer" return value is True for "yes" or False for "no".
"""
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
sys.stdout.write(question + prompt)
choice = input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' "
"(or 'y' or 'n').\n")
def main():
non_bmp_map = dict.fromkeys(range(0x10000, sys.maxunicode + 1), 0xfffd)
original_raw_json = open('direct-message.js', 'r', encoding='utf-8').read()
original_json = json.loads(original_raw_json)
for dm_conversations_json in original_json:
dm_convo_html = "<body>"
dm_conversations_json = dm_conversations_json['dmConversation']['messages'][::-1]
twitter_ids = {}
for _message in dm_conversations_json:
message = _message['messageCreate']
sender_id = message['senderId']
recipient_id = message['recipientId']
if not(sender_id in twitter_ids.keys()):
twitter_ids[sender_id] = tweeterid.id_to_handle(sender_id)
if not(recipient_id in twitter_ids.keys()):
twitter_ids[recipient_id] = tweeterid.id_to_handle(recipient_id)
sender = twitter_ids[sender_id]
dm_convo_html += "<p>({}) [{}]: {}<br/>{}</p>".format(message['createdAt'], sender, message["text"], isMediaUrlsNone(message['mediaUrls'], str(message['id'])))
rtl = query_yes_no("Would you like to make this conversation with {} RTL (right to left)?".format(' - '.join(twitter_ids.values())))
if rtl:
dm_convo_html += """\
<style>
p {
direction:RTL
}
</style>
"""
else:
dm_convo_html += """\
<style>
p {
direction:LTR
}
</style>
"""
dm_convo_html += "</body>"
open(' - '.join(twitter_ids.values()) + '.html', 'wb').write(dm_convo_html.encode())
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment