Skip to content

Instantly share code, notes, and snippets.

@joachimesque
Last active August 23, 2023 15:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joachimesque/237bda8712f8fd29d0aaccf1ac30d8a5 to your computer and use it in GitHub Desktop.
Save joachimesque/237bda8712f8fd29d0aaccf1ac30d8a5 to your computer and use it in GitHub Desktop.
A simple command line utility that returns a JSON-formatted definitions list of the handle and url of a Fediverse address/handle/url
"""Returns a JSON-formatted definitions list of the handle and url of a Fediverse address."""
import json
import re
import click
import requests
API_ASK = ".well-known/webfinger?resource=acct:"
API_REL_PROFILE = "http://webfinger.net/rel/profile-page"
API_REL_SELF = "self"
# RegEx from https://regex101.com/library/ac4fG5
FEDI_HANDLE_REGEX = r"@?\b([A-Z0-9._%+-]+)@([A-Z0-9.-]+\.[A-Z]{2,})\b"
FEDI_URL_REGEX = (
r"(?:https:\/\/)?([-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,}\b)\/(?:@|user\/)?([\w]*)"
)
TYPE_HANDLE = "handle"
TYPE_URL = "url"
def kill(reason):
"""Exits the program in run from command line, or returns a boolean status + reason"""
if __name__ == "__main__":
click.echo(reason)
exit(1)
return (False, reason)
def get_url(name, domain):
"""Uses the name and domain to ask the mastodon server the valid url"""
ask_url = f"https://{domain}/{API_ASK}{name}@{domain}"
try:
r = requests.get(ask_url)
result = r.json()
except requests.exceptions.ConnectionError:
kill("Unexpected Error: Bad domain name. Please verify your informations.")
except requests.exceptions.JSONDecodeError:
kill("Unexpected Error: Bad username. Please verify your informations.")
except Exception as err:
kill(f"Unexpected {err=}, {type(err)=}")
if "aliases" in result:
return result["aliases"][0]
if "links" in result:
for link in result["links"]:
if link["rel"] == API_REL_PROFILE:
return link["href"]
for link in result["links"]:
if link["rel"] == API_REL_SELF:
return link["href"]
def get_handle(name, domain):
"""Uses the name and domain to create a simple Fediverse handle"""
return f"@{name}@{domain}"
def get_handle_parts(str_input):
"""Tests the input string against a handle and and URL regex,
and returns a dict containing the input type, the name and domain
"""
handle_test = re.match(FEDI_HANDLE_REGEX, str_input, re.IGNORECASE)
if handle_test:
input_type = TYPE_HANDLE
name = handle_test.group(1)
domain = handle_test.group(2)
return {"type": input_type, "name": name, "domain": domain}
url_test = re.match(FEDI_URL_REGEX, str_input, re.IGNORECASE)
if url_test:
input_type = TYPE_URL
name = url_test.group(2)
domain = url_test.group(1)
return {"type": input_type, "name": name, "domain": domain}
return {"type": None, "name": "", "domain": ""}
def get_handle_and_url(handle_or_url):
"""Returns a JSON-formatted definitions list of the handle and url of a Fediverse address.
HANDLE_OR_URL should be a valid Fediverse handle or profile URL in the form
- @username@domain for a handle
- [https://]domain/[@|user/]username (parts between brackets are optional)
"""
handle_parts = get_handle_parts(handle_or_url)
if not handle_parts["type"]:
kill(
"Unexpected Error: the string you supplied isn’t a recognizable handle or url"
)
url = get_url(handle_parts["name"], handle_parts["domain"])
handle = get_handle(handle_parts["name"], handle_parts["domain"])
json_export = json.dumps({"url": url, "handle": handle})
if __name__ == "__main__":
click.echo(json_export)
return (True, str(json_export))
@click.command()
@click.argument("handle_or_url", type=str)
def main(handle_or_url):
get_handle_and_url(handle_or_url)
if __name__ == "__main__":
main()
@joachimesque
Copy link
Author

joachimesque commented Aug 23, 2023

Usage

$ python3 script.py @joachim@boitam.eu
{"url": "https://boitam.eu/@joachim", "handle": "@joachim@boitam.eu"}
$ python3 script.py https://boitam.eu/@joachim
{"url": "https://boitam.eu/@joachim", "handle": "@joachim@boitam.eu"}

Or

$ python3
Python 3.9.17 (main, Jun 20 2023, 17:03:39)
Type "help", "copyright", "credits" or "license" for more information.
>>> import script
>>> script.get_handle_and_url("https://boitam.eu/@joachim")
(True, '{"url": "https://boitam.eu/@joachim", "handle": "@joachim@boitam.eu"}')
>>> script.get_handle_and_url("@joachim@boitam.eu")
(True, '{"url": "https://boitam.eu/@joachim", "handle": "@joachim@boitam.eu"}')

Requirements

  • Python 3.9+
  • Click=8.1.7
  • Requests=2.31.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment