Конечно, включённые значки — это… мило. С ними всё в порядке. Но что, если вы хотите большего? Что, если вы хотите выйти за пределы предопределённого набора символов? Конечно, есть административная страница, где можно их загружать. Но что, если вы хотите создать целый ворох значков?
Что ж, хорошие новости! Это можно сделать через API. Вот код на Python, показывающий, как это работает. (И в качестве бонуса он выдаёт значок в конце.)
Этот код должен быть довольно понятным, даже если вы не программист на Python. Вы даже можете воспроизвести его с помощью curl в командной строке, если хотите.
Ожидается, что ваш API-ключ будет храниться в переменной окружения DISCOURSE_API_KEY.
#!/usr/bin/python3
import os # Для чтения переменных окружения
import sys # Для выхода. :)
import hashlib # Для хэширования изображения
import requests # Выполняет основную работу
# Настройки сайта и аутентификации
DISCOURSE = "discussion.fedoraproject.org"
DISCOURSE_API_USER = "mattdm"
DISCOURSE_API_KEY = os.getenv("DISCOURSE_API_KEY")
if not DISCOURSE_API_KEY:
print(f"Ошибка: переменная окружения DISCOURSE_API_KEY должна быть установлена", file=sys.stderr)
sys.exit(2)
# Информация о вашем значке. В реальном сценарии, вероятно, вы не будете хардкодить эти данные.
NAME = "Apex"
IMAGE = "apex.png"
DESCRIPTION = "Благословение FPL"
MORE = "Вы потрясающий, и все должны это знать!"
TARGET_USER = "mattdm"
# Наши заголовки аутентификации, как указано выше.
HEADERS = {'Api-Key': DISCOURSE_API_KEY, 'Api-Username': DISCOURSE_API_USER}
# С помощью этого запроса можно получить ID и описания групп значков,
# а также различные типы значков. Если захотите. В этом примере мы это не используем.
r = requests.get(f"https://{DISCOURSE}/admin/badges.json", headers=HEADERS)
if r.status_code != 200:
print(f"Ошибка при получении списка значков: получен код {r.status_code}", file=sys.stderr)
sys.exit(1)
# Проверка, что имя ещё не существует.
# Вероятно, есть лучший способ сделать это, но для примера сойдёт.
if NAME in list(map(lambda x: x['name'], r.json()['badges'])):
print(f"Ошибка: значок '{NAME}' уже существует.")
sys.exit(3)
# Чтение изображения. В реальном коде здесь нужна более качественная обработка ошибок!
with open(IMAGE, "rb") as f:
image_data = f.read()
# Здесь, вероятно, стоит добавить проверку размера и размеров изображения....
# Но в любом случае, собираем пакет информации для загрузки.
# Единственная сложность здесь — получение контрольной суммы изображения.
file_info = {'file_name': f'{IMAGE}',
'file_size': f'{len(image_data)}',
'type': 'badge_image',
'metadata[sha1-checksum]': hashlib.sha1(image_data).hexdigest(),
}
# И спрашиваем у Discourse, куда его отправить.
r = requests.post(
f"https://{DISCOURSE}/uploads/generate-presigned-put", json=file_info, headers=HEADERS)
if r.status_code != 200:
print(
f"Ошибка при запросе места для загрузки изображения: получен код {r.status_code}", file=sys.stderr)
sys.exit(1)
upload_url = r.json()['url']
upload_uid = r.json()['unique_identifier']
# Теперь загружаем его туда, куда нам сказали.
r = requests.put(upload_url, data=image_data)
if r.status_code != 200:
print(
f"Ошибка при загрузке изображения во внешнее хранилище: получен код {r.status_code}", file=sys.stderr)
sys.exit(1)
# И сообщаем Discourse, что всё прошло успешно, получив ID, на который можно ссылаться позже.
r = requests.post(f"https://{DISCOURSE}/uploads/complete-external-upload",
data=f'unique_identifier={upload_uid}', headers=HEADERS)
if r.status_code != 200:
print(f"Ошибка при завершении загрузки: получен код {r.status_code}", file=sys.stderr)
sys.exit(1)
image_id = r.json()['id']
# Примечание: если вы хотите использовать Font Awesome, оставьте `image_upload_id` пустым
# и вместо этого установите `icon` в соответствующее имя иконки Font Awesome. И, конечно,
# в этом случае можно пропустить всю загрузку изображения!
badge = {'allow_title': 'true',
'multiple_grant': 'false',
'listable': 'true',
'show_posts': 'false',
'target_posts': 'false',
'name': f'{NAME}',
'description': f'{DESCRIPTION}',
'long_description': f'{DESCRIPTION} {MORE}',
'image_upload_id': f'{image_id}',
'badge_grouping_id': '5',
'badge_type_id': '1',
}
r = requests.post(
f"https://{DISCOURSE}/admin/badges.json", json=badge, headers=HEADERS)
if r.status_code != 200:
print(f"Ошибка при создании значка: получен код {r.status_code}", file=sys.stderr)
sys.exit(1)
badge_id = r.json()['badge']['id']
print(f'Значок "{NAME}: {DESCRIPTION}" создан с ID {badge_id}!')
# И для полноты картины выдадим значок!
# Вы также можете добавить "причину", которая должна ссылаться на пост или тему.
bestow = {'username': f"{TARGET_USER}", 'badge_id': f'{badge_id}'}
r = requests.post(f"https://{DISCOURSE}/user_badges",
json=bestow, headers=HEADERS)
if r.status_code != 200:
print(f"Ошибка при выдаче значка: получен код {r.status_code}", file=sys.stderr)
sys.exit(1)
print(f'Пользователю {TARGET_USER} выдан новый значок!')