はい、付属のバッジは…良いですね。何も問題はありません。しかし、もっと欲しいと思いませんか?定義済みのシンボルセット を超えたい と思いませんか?確かに、アップロードできる管理ページはありますが、たくさんのバッジ を作成したい場合はどうでしょうか?
さて、朗報です!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} に新しいバッジが授与されました!')