Créez un badge personnalisé avec une image via l'API

Bien sûr, les badges inclus sont… sympas. Il n’y a rien de mal à cela. Mais, que faire si vous en voulez plus ? Que faire si vous voulez aller au-delà de l’ensemble de symboles prédéfini ? Bien sûr, il y a une page d’administration où vous pouvez les télécharger. Mais que faire si vous voulez créer beaucoup de badges ?

Eh bien, bonne nouvelle ! Vous pouvez le faire via l’API. Voici un code Python montrant comment cela fonctionne. (Et il décerne le badge à la fin, pour faire bonne mesure.)

Cela devrait être assez facile à lire, même si vous n’êtes pas un programmeur Python. Vous pouvez même le reproduire avec curl sur la ligne de commande, si vous le souhaitez.

Cela suppose que votre clé API est dans une variable d’environnement DISCOURSE_API_KEY.

#!/usr/bin/python3

import os         # Pour lire l'environnement
import sys        # Pour quitter. :)
import hashlib    # Pour le hachage de l'image

import requests   # Fait tout le travail réel


# Informations sur le site et l'authentification
DISCOURSE = "discussion.fedoraproject.org"
DISCOURSE_API_USER = "mattdm"
DISCOURSE_API_KEY = os.getenv("DISCOURSE_API_KEY")
if not DISCOURSE_API_KEY:
    print(f"Erreur : DISCOURSE_API_KEY doit être défini dans l'environnement", file=sys.stderr)
    sys.exit(2)

# Informations pour votre badge. Vraisemblablement, dans une utilisation réelle, vous ne coderiez pas cela en dur.
NAME = "Apex"
IMAGE = "apex.png"
DESCRIPTION = "Bénédiction du FPL"
MORE = "Vous êtes génial et tout le monde devrait le savoir !"
TARGET_USER = "mattdm"

# Nos en-têtes d'authentification, ci-dessus.
HEADERS = {'Api-Key': DISCOURSE_API_KEY, 'Api-Username': DISCOURSE_API_USER}

# À partir de là, vous pouvez obtenir les identifiants et les descriptions des groupes de badges, et
# les différents types de badges. Si vous le souhaitez. Pas utilisé dans cet exemple.
r = requests.get(f"https://{DISCOURSE}/admin/badges.json", headers=HEADERS)
if r.status_code != 200:
    print(f"Erreur lors de la récupération de la liste des badges : reçu {r.status_code}", file=sys.stderr)
    sys.exit(1)

# Vérifier que le nom n'existe pas déjà.
# Il y a probablement une meilleure façon de faire cela, mais cela suffira.
if NAME in list(map(lambda x: x['name'], r.json()['badges'])):
    print(f"Erreur : le badge '{NAME}' existe déjà.")
    sys.exit(3)

# Lire l'image. Vous voudriez une meilleure gestion des erreurs ici dans un code réel !
with open(IMAGE, "rb") as f:
    image_data = f.read()

# Il faudrait probablement faire quelques vérifications de base sur la
# taille/dimensions du fichier image juste à ce moment...

# Mais en tout cas, assembler un paquet d'informations à télécharger.
# La seule chose délicate ici est vraiment d'obtenir la somme de contrôle de l'image.
file_info = {'file_name': f'{IMAGE}',
             'file_size': f'{len(image_data)}',
             'type': 'badge_image',
             'metadata[sha1-checksum]': hashlib.sha1(image_data).hexdigest(),
             }


# Et demander à Discourse où l'envoyer.
r = requests.post(
    f"https://{DISCOURSE}/uploads/generate-presigned-put", json=file_info, headers=HEADERS)
if r.status_code != 200:
    print(
        f"Erreur lors de la demande de l'emplacement de téléchargement de l'image : reçu {r.status_code}", file=sys.stderr)
    sys.exit(1)

upload_url = r.json()['url']
upload_uid = r.json()['unique_identifier']

# Maintenant, mettez-le là où on nous a dit de le faire.
r = requests.put(upload_url, data=image_data)
if r.status_code != 200:
    print(
        f"Erreur lors du téléchargement de l'image vers le stockage externe : reçu {r.status_code}", file=sys.stderr)
    sys.exit(1)

# Et dire à Discourse que cela a fonctionné, et obtenir un identifiant que nous pourrons référencer plus tard.
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"Erreur lors de la finalisation du téléchargement : reçu {r.status_code}", file=sys.stderr)
    sys.exit(1)
image_id = r.json()['id']


# Remarque : si vous souhaitez utiliser Font Awesome, laissez `image_upload_id` vide et
# définissez plutôt `icon` sur le nom Font Awesome correspondant. Et bien sûr,
# vous pouvez ignorer tout le processus de téléchargement de l'image dans ce cas !
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"Erreur lors de la création du badge : reçu {r.status_code}", file=sys.stderr)
    sys.exit(1)

badge_id = r.json()['badge']['id']

print(f'Le badge "{NAME}: {DESCRIPTION}" a été créé sous l\'identifiant {badge_id} !')


# Et pour être complet, décerner le badge !

# Vous pouvez également ajouter une "raison", qui doit être liée à un message ou à un sujet.
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"Erreur lors de l'attribution du badge : reçu {r.status_code}", file=sys.stderr)
    sys.exit(1)

print(f'L\'utilisateur {TARGET_USER} s\'est vu décerner le nouveau badge !')

7 « J'aime »

Malheureusement, vous avez besoin d’une clé d’API d’administrateur global pour cela. J’espère que l’équipe mettra en œuvre un moyen d’effectuer des vérifications granulaires pour les droits de téléchargement, la création et la modification de badges, l’attribution et la suppression de badges.

Vous n’avez plus besoin d’une clé d’API d’administrateur global pour cela — lors de la création d’une clé d’API, vous pouvez spécifier différentes choses spécifiques liées à la création, l’octroi ou la suppression de badges. N’oubliez pas « téléchargement » si vous souhaitez faire ce que cet exemple montre et ajouter vos propres images. C’est dans une section distincte des autorisations de l’API de badges.

2 « J'aime »

Dans la publication originale, j’ai accidentellement mis la fonction de hachage SHA dans une chaîne de caractères, de sorte qu’elle a été définie littéralement comme le code Python, et non comme le hachage. Cela semblait fonctionner, mais il vaut probablement mieux que ce soit correct. Je ne sais pas ce qui vérifie cela, mais probablement si vous faites cela mal, l’image sera marquée comme corrompue à un moment donné dans le futur. Quoi qu’il en soit, j’ai modifié le code ci-dessus pour qu’il soit correct. Si vous repérez d’autres bugs, n’hésitez pas à me le faire savoir !

3 « J'aime »