Skip to content

Instantly share code, notes, and snippets.

@FlyMyPG
Last active May 19, 2017 21:07
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FlyMyPG/41b273afe525541c2bc7334d9384b134 to your computer and use it in GitHub Desktop.
Save FlyMyPG/41b273afe525541c2bc7334d9384b134 to your computer and use it in GitHub Desktop.
Read all toots you've written (excluding Boosts).
# -*- coding: utf-8 -*-
"""
ReadMyToots.py - Download the toots you wrote, in chronological order.
Author: BobC - https://mastodon.hasameli.com/@BobC
License: "CC BY-NC-SA 4.0" https://creativecommons.org/licenses/by-nc-sa/4.0/
Usage: python ReadMyToots.py [UserName InstanceName LoginEmail Password]+
Parameters: UserName - Your handle on this instance (no '@')
InstanceName - The instance address (no 'http://')
LoginEmail - Yup
Password - Yup
Parameters must form complete sets. Multiple sets are supported.
WARNING: No parameter validation is done. Blood & guts fly everywhere.
Output: Status and progress messages go to stdout.
Toots go to file: "[User_Name]@[InstanceName].txt"
Dependencies: Be sure you have Mastodon.py installed:
pip install mastodon.py
This ***SHOULD*** work on ALL major Python 3.x platforms (Win, Lin, Mac).
"""
DEBUG = False # Set to True for so much fun!
import sys
if __name__ != "__main__":
print("This program is NOT a library and must NOT be imported!", file=sys.stderr)
sys.exit()
# And now we will continue with our regularly scheduled program...
import os
import re
import textwrap
import pathlib as pl
import html
from mastodon import Mastodon
if DEBUG is True:
import code ####DEBUG
import pprint ####DEBUG
## Local functions:
if DEBUG is True:
def pdict(dct, indent=1, spaces=3):
""" Print a dict that may contain other dicts """
tabs = ' '*spaces*indent
for key, val in dct.items():
if not isinstance(val, dict):
try:
print("%s%s = "%(tabs, key), val)
except:
print("%s%s = [unprintable]"%(tabs, key))
else:
print("%s%s = {dict}"%(tabs, key))
pdict(val, indent=indent+1)
return
def witext(string, indent=1, width=80, spaces=3):
""" Return a wrapped and indented string """
tabs = ' '*spaces*indent
wide = width - len(tabs)
lines = string.split("\n") # Paragraphs and line breaks
lists = (textwrap.wrap(line, wide, initial_indent=tabs, subsequent_indent=tabs)
for line in lines)
body = "\n".join("\n".join(lst) for lst in lists)
return body
def striphtml(data):
""" Remove or replace HTML tags and replace symbols """
data = html.unescape(data) # Replace symbols
data = re.sub(r'<[/ ]*br[/ ]*>', '\n', data) # Preserve line breaks
data = re.sub(r'</*p>', '\n', data) # Preserve paragraphs
data = re.sub(r'<[^<]+?>', '', data) # Strip all other tags
return data
## Main code:
if DEBUG is True:
p = pprint.PrettyPrinter().pprint ####DEBUG
appName = os.path.splitext(os.path.basename(__file__))[0]
appArgNames = "UserName InstanceName LoginEmail Password"
appArgs = len(appArgNames.split())
args = sys.argv[1:] # Parameters follow program name
numArgs = len(args)
numAcct = 0
if numArgs > 0 and numArgs % appArgs == 0: # Allow only complete parameter sets
numAcct = int(numArgs / appArgs)
# Read parameters as sets
parameterSets = [args[(x*appArgs):(x+1)*appArgs] for x in range(numAcct)]
numAcct = len(parameterSets)
print("Read %d set(s) of user parameters.\n"%(numAcct))
else:
print("Error: Argument[s] missing.")
print("Usage: %s %s ..."%(appName, appArgNames))
sys.exit()
# Repeat for each account
for pset in parameterSets:
# Simplify parameter access
me, instance, email, password = pset
#TODO: How about some parameter validation?
outFile = "%s@%s.txt"%(me, instance)
# Prep filenames for persistent credentials
cliCred = "%s_%s_clientcred.txt"%(appName, instance)
usrCred = "%s_%s_usercred.txt"%(appName, me)
# Register app - only once!
url = "https://%s"%(instance)
if pl.Path(cliCred).is_file():
print("Using persistent client credentials.")
else:
print("Obtaining client credentials (persistent not present).")
Mastodon.create_app(appName, api_base_url=url, to_file=cliCred)
# Create mastodon client
mastodon = Mastodon(api_base_url=url, client_id=cliCred)
# User login - either every time, or use persisted
if pl.Path(usrCred).is_file(): # Use persistent
print("Using persistent user credentials.")
numAcct = 1
else: # Create fresh
print("Obtaining user credentials (persistent not present).")
# Login
mastodon.log_in(username=email, password=password, to_file=usrCred)
numAcct = 1
# Create actual instance
mastodon = Mastodon(api_base_url=url, client_id=cliCred, access_token=usrCred)
# Get integer user ID
user_id = mastodon.account_search(me)[0]['id']
print("user_id for %s = %s"%(me, user_id))
# Collect all toots for user
print("\nGathering toots for @%s@%s "%(me, instance), end='')
sys.stdout.flush()
toots = []
t = mastodon.account_statuses(user_id)
while len(t) > 0:
print('.', end='')
sys.stdout.flush()
toots += t
t = mastodon.account_statuses(user_id, max_id=t[-1]['id'])
print()
print("Processing %d toots, id %d to %d\n"%(
len(toots), toots[-1]['id'], toots[0]['id']))
with open(outFile, 'w') as f:
for toot in reversed(toots): # Process from oldest to newest
# Gather toots by me that aren't boosts
if toot['reblog'] is None:
print("\n%s\n"%(toot['created_at']),
witext(striphtml(toot['content'])), file=f)
print("Toots written to: %s\n"%(outFile))
if DEBUG is True:
# Go interactive only when stdout not redirected ####DEBUG
if os.fstat(0) == os.fstat(1): ####DEBUG
print("\nEntering Interactive Mode: ^Z or ^D when done.") ####DEBUG
code.interact(local=dict(globals(), **locals())) ####DEBUG
print("Exiting!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment