APIを使って画像付きのカスタムバッジを作成する

はい、付属のバッジは…良いですね。何も問題はありません。しかし、もっと欲しいと思いませんか?定義済みのシンボルセット を超えたい と思いませんか?確かに、アップロードできる管理ページはありますが、たくさんのバッジ を作成したい場合はどうでしょうか?

さて、朗報です!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 = "Blessing of the FPL"
MORE = "You are awesome and everyone should know!"
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}" が {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} に新しいバッジが授与されました!')

「いいね!」 7

残念ながら、これにはグローバル管理者 API キーが必要です。チームがアップロード権限、バッジの作成と編集、バッジの付与、バッジの削除を細かくチェックする方法を実装してくれることを願っています。

もうグローバル管理者 API キーは必要ありません。API キーを作成する際に、バッジの作成、付与、または削除に関連するさまざまな具体的な項目を指定できます。この例のように独自の画像を追加したい場合は、「アップロード」も忘れないでください。これはバッジ API の権限とは別のセクションにあります。

「いいね!」 2

元の投稿で、誤ってshaハッシュダイジェスト関数を文字列内に配置してしまいました。そのため、ダイジェストではなく、文字通りPythonコードとして設定されていました。それは機能したようですが、おそらく実際には正しくあるべきです。何がそれをチェックしているのかわかりませんが、おそらくこれを間違えると、将来的に画像が破損しているとマークされるでしょう。とにかく、上記のコードを編集したので、正しくなっているはずです。他にバグを見つけたら、お知らせください!

「いいね!」 3