dotfiles/scripts/discord-whois

138 lines
3.9 KiB
Python
Executable file

#!/usr/bin/env python3
# <https://discord.com/developers/docs/resources/user#user-object>
# <https://discord.com/developers/docs/resources/user#user-object#get-user>
# <https://discord.com/developers/docs/reference>
import sys
import os
import urllib.request
import urllib.error
import colorama
import time
import argparse
import json
import typing
DISCORD_EPOCH = 1420070400000 # milliseconds
# https://discord.com/developers/docs/resources/user#user-object-user-flags
DISCORD_FLAGS = {
"Discord Employee": 1 << 0,
"Discord Partner": 1 << 1,
"HypeSquad Events": 1 << 2,
"Bug Hunter Level 1": 1 << 3,
"House of Bravery": 1 << 6,
"House of Brilliance": 1 << 7,
"House of Balance": 1 << 8,
"Early Supporter": 1 << 9,
"Team User": 1 << 10,
"System": 1 << 12,
"Bug Hunter Level 2": 1 << 14,
"Verified Bot": 1 << 16,
"Verified Bot Developer": 1 << 17,
}
parser = argparse.ArgumentParser()
parser.add_argument("user_snowflake", type=int)
parser.add_argument("--bot-token", type=str)
parser.add_argument("--image-size", type=int)
parser.add_argument("--get-prop", type=str)
cli_args = parser.parse_args()
user_snowflake = cli_args.user_snowflake
bot_token = cli_args.bot_token
if bot_token is None:
with open(
os.path.expanduser("~/.config/dotfiles/discord-tools-bot-token.txt")
) as f:
bot_token = f.read().strip()
image_size = cli_args.image_size
if not (image_size is None or (image_size > 0 and image_size & (image_size - 1)) == 0):
parser.error("image_size must be greater than zero and a power of two")
try:
opener = urllib.request.build_opener()
# Don't send the User-Agent header, Discord blocks the default one
opener.addheaders = []
with opener.open(
urllib.request.Request(
"http://discord.com/api/users/{}".format(user_snowflake),
headers={"Authorization": "Bot {}".format(bot_token)},
),
timeout=10,
) as response:
raw_data = json.load(response)
except urllib.error.HTTPError as err:
print(err, file=sys.stderr)
print(err.read(), file=sys.stderr)
raise err
data = {}
data["ID"] = raw_data["id"]
data["Name"] = "{}#{}".format(raw_data["username"], raw_data["discriminator"])
default_avatar_url = "https://cdn.discordapp.com/embed/avatars/{}.png".format(
int(raw_data["discriminator"], 10) % 5
)
avatar_url = (
"https://cdn.discordapp.com/avatars/{}/{}.{}".format(
raw_data["id"],
raw_data["avatar"],
"gif" if raw_data["avatar"].startswith("a_") else "png",
)
if raw_data["avatar"] is not None
else default_avatar_url
)
if image_size is not None:
avatar_url += "?size={}".format(image_size)
data["Avatar"] = avatar_url
data["Default avatar"] = default_avatar_url
data["Bot"] = raw_data.get("bot", False)
data["System user"] = raw_data.get("system", False)
# https://discord.com/developers/docs/reference#convert-snowflake-to-datetime
snowflake_creation_time = (user_snowflake >> 22) + DISCORD_EPOCH
data["Created at"] = "{}.{} UTC".format(
time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(snowflake_creation_time // 1000)),
snowflake_creation_time % 1000,
)
user_flags = raw_data["public_flags"]
if user_flags == 0:
data["Flags"] = "<none>"
else:
user_flag_names = []
for flag_name, bitmask in DISCORD_FLAGS.items():
if user_flags & bitmask:
user_flag_names.append(flag_name)
data["Flags"] = ", ".join(user_flag_names)
if cli_args.get_prop is None:
max_name_length = max(map(len, data.keys()))
for name, value in data.items():
if value is True:
value = "yes"
elif value is False:
value = "no"
print(
"{}{:>{}}:{} {}".format(
colorama.Style.BRIGHT,
name,
max_name_length + 1,
colorama.Style.RESET_ALL,
value,
)
)
else:
print(data[cli_args.get_prop])