Claro, las insignias incluidas son… agradables. No hay nada de malo en ellas. Pero, ¿y si quieres más? ¿Qué pasa si quieres ir más allá del conjunto de símbolos predefinido? Claro, hay una página de administración donde puedes subirlas. Pero, ¿y si quieres crear un montón de insignias?
¡Buenas noticias! Puedes hacerlo a través de la API. Aquí tienes código Python que muestra cómo funciona. (Y otorga la insignia al final, para mayor seguridad).
Esto debería ser bastante fácil de leer, incluso si no eres un programador de Python. Incluso puedes replicarlo con curl en la línea de comandos, si lo deseas.
Esto espera que tu clave API esté en una variable de entorno DISCOURSE_API_KEY.
#!/usr/bin/python3
import os # Para leer el entorno
import sys # Para salir. :)
import hashlib # Para el hash de la imagen
import requests # Hace todo el trabajo real
# Sitio y datos de autenticación
DISCOURSE = "discussion.fedoraproject.org"
DISCOURSE_API_USER = "mattdm"
DISCOURSE_API_KEY = os.getenv("DISCOURSE_API_KEY")
if not DISCOURSE_API_KEY:
print(f"Error: DISCOURSE_API_KEY debe estar configurado en el entorno", file=sys.stderr)
sys.exit(2)
# Información para tu insignia. Presumiblemente, en uso real no codificarías esto.
NAME = "Apex"
IMAGE = "apex.png"
DESCRIPTION = "Bendición de la FPL"
MORE = "¡Eres increíble y todo el mundo debería saberlo!"
TARGET_USER = "mattdm"
# Nuestras cabeceras de autenticación, de arriba.
HEADERS = {'Api-Key': DISCOURSE_API_KEY, 'Api-Username': DISCOURSE_API_USER}
# A partir de esto, puedes obtener los IDs y descripciones de los grupos de insignias, y
# los diversos tipos de insignias. Si quieres. No se usa en este ejemplo.
r = requests.get(f"https://{DISCOURSE}/admin/badges.json", headers=HEADERS)
if r.status_code != 200:
print(f"Error al obtener la lista de insignias: se obtuvo {r.status_code}", file=sys.stderr)
sys.exit(1)
# Comprueba que el nombre no exista ya.
# Probablemente haya una mejor manera de hacer esto, pero será suficiente.
if NAME in list(map(lambda x: x['name'], r.json()['badges'])):
print(f"Error: la insignia '{NAME}' ya existe.")
sys.exit(3)
# Lee la imagen. ¡Querrías un mejor manejo de errores aquí en código real!
with open(IMAGE, "rb") as f:
image_data = f.read()
# Probablemente deberías hacer algunas comprobaciones de cordura sobre el
# tamaño/dimensiones del archivo de imagen justo aquí....
# Pero de todos modos, ensambla un paquete de información sobre él para subirlo.
# Lo único complicado aquí es realmente obtener la suma de verificación de la imagen.
file_info = {'file_name': f'{IMAGE}',
'file_size': f'{len(image_data)}',
'type': 'badge_image',
'metadata[sha1-checksum]': hashlib.sha1(image_data).hexdigest(),
}
# Y pregunta a Discourse a dónde enviarlo.
r = requests.post(
f"https://{DISCOURSE}/uploads/generate-presigned-put", json=file_info, headers=HEADERS)
if r.status_code != 200:
print(
f"Error al preguntar dónde subir la imagen: se obtuvo {r.status_code}", file=sys.stderr)
sys.exit(1)
upload_url = r.json()['url']
upload_uid = r.json()['unique_identifier']
# Ahora ponlo donde nos dijeron que lo hiciéramos.
r = requests.put(upload_url, data=image_data)
if r.status_code != 200:
print(
f"Error al subir la imagen al almacenamiento externo: se obtuvo {r.status_code}", file=sys.stderr)
sys.exit(1)
# Y dile a Discourse que funcionó, y obtén un ID al que podamos referenciar más tarde.
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"Error al completar la carga: se obtuvo {r.status_code}", file=sys.stderr)
sys.exit(1)
image_id = r.json()['id']
# Nota: si quieres usar Font Awesome, deja `image_upload_id` en blanco y
# en su lugar establece `icon` al nombre correspondiente de Font Awesome. Y por supuesto
# ¡puedes omitir toda la parte de carga de imágenes en ese caso!
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"Error al crear la insignia: se obtuvo {r.status_code}", file=sys.stderr)
sys.exit(1)
badge_id = r.json()['badge']['id']
print(f'¡La insignia "{NAME}: {DESCRIPTION}" ha sido creada como {badge_id}!')
# Y para completar, ¡otorga la insignia!
# También puedes añadir una "razón", que debe enlazar a una publicación o tema.
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"Error al otorgar la insignia: se obtuvo {r.status_code}", file=sys.stderr)
sys.exit(1)
print(f'¡El usuario {TARGET_USER} ha recibido la nueva insignia!')