Skip to content

Instantly share code, notes, and snippets.

@codl
Last active May 27, 2018 20:50
Show Gist options
  • Save codl/19b8e4cc1d390b232710d9da5ea74c7f to your computer and use it in GitHub Desktop.
Save codl/19b8e4cc1d390b232710d9da5ea74c7f to your computer and use it in GitHub Desktop.

this script converts glitchsoc-style profile metadata to mastodon v2.4.0 profile fields

it requires

  • python 3.x
  • psycopg2 (might be in your package manager as python-psycopg2, or python3-psycopg2)
  • a mastodon instance

how to use

python converter.py DBNAME [POSTGRES CONNECTION STRING]

your DBNAME is probably mastodon_production

if you followed the production guide then you don't need a connection string

more info on connection strings at https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING

a word of warning

this script will ask you to confirm every change before applying any of it

nonetheless, back up your database before doing this. im not responsible if your instance breaks

thanks

thanks. report issues to codl@codl.fr

from textwrap import dedent
import psycopg2
import sys
import json
def parse(note):
note = note.lstrip()
if not note.startswith('---\r\n'):
return
note = note[5:]
metadata = list()
while True:
line, _, note = note.partition('\r\n')
line = line.strip()
if line == '':
continue
if line in ('---', '...'):
# we're done here
break
key, sep, value = line.partition(':')
key = key.strip()
value = value.strip()
if not sep == ':' or key == '' or value == '':
# badly formatted metadata, bail
return
if key[0] == key[-1] and key[0] in ('"', "'"):
key = key[1:-1]
if value[0] == value[-1] and value[0] in ('"', "'"):
value = value[1:-1]
if len(key) > 255 or len(value) > 255:
# mastodon limits to 255 chars, bail
# better to let the user fix it themself than
# lose some of their data
return
metadata.append((key, value))
if len(metadata) > 4:
# mastodon only supports 4 fields, bail
return
return (tuple(metadata), note.strip('\r\n'))
def test_parse_empty():
assert parse("hello") is None
def test_parse():
assert parse(dedent("""---\r\ncomputer: 'over'\r\nvirus: very yes 👾\r\n...\r\n\r\ni'm fuckass and I walk like this. im the big boss here i run this town\r\n"""
)) == (
(
("computer","over"),
("virus", "very yes 👾")
),
"i'm fuckass and I walk like this. im the big boss here i run this town")
def test_parse_incorrect():
assert parse(dedent("""---\r\ncomputer:\r\n...\r\n\r\ni'm fuckass and I walk like this. im the big boss here i run this town\r\n"""
)) is None
def test_parse_dupe():
assert parse(dedent("""---\r\nvirus: no\r\nvirus: yes\r\n---\r\n"""
)) == (
(
("virus", "no"),
("virus", "yes")
),
""
)
def to_json(meta):
out = list()
for row in meta:
key, value = row
out.append({"name": key, "value": value})
return json.dumps(out)
def usage():
print(dedent("""
usage:
python converter.py DBNAME [POSTGRES CONNECTION STRING]
your DBNAME is probably mastodon_production
if you followed the production guide then you don't need a connection string
more info on connection strings at
<https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING>
this will ask you to confirm every change before applying any of it
nonetheless, back up your database before doing this. im not responsible if your instance breaks
thanks. report issues to codl@codl.fr
"""))
if __name__ == '__main__':
if len(sys.argv) < 2 or sys.argv[1] in ('-h', '--help'):
usage()
exit(128)
pg = psycopg2.connect(" ".join(sys.argv[3:]), dbname=sys.argv[1])
cur = pg.cursor()
cur.execute('SELECT accounts.id, accounts.username, accounts.note' +
' FROM accounts, users WHERE users.account_id = accounts.id;')
to_update = list()
for row in cur:
account_id, username, note = row
parsed = parse(note)
if not parsed or len(parsed[0]) == 0:
print('@{} did not have valid metadata'.format(username))
continue
to_update.append((account_id, username, note) + parsed)
for row in to_update:
account_id, username, oldnote, meta, note = row
print(dedent("""
We will update @{username} (#{account_id})
Original bio was:
{oldnote}
Found metadata:
{meta}
Stripped bio:
{note}
""").format(username=username, oldnote=oldnote, meta=meta,
note=note, account_id=account_id))
response = input("Is this okay? (y/N) ")
if not response or not response[0] in 'yY':
print("Okay. Aborting.")
exit(1)
cur.execute(
'UPDATE accounts SET note = %s, fields = %s WHERE id = %s',
(note, to_json(meta), account_id))
pg.commit()
print("Done!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment