通过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 = "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} 已获得新徽章!')

7 个赞

很遗憾,您需要一个全局管理员 API 密钥才能执行此操作。我希望团队能够实现一种方法,对上传权限、徽章创建和编辑、徽章分配以及徽章移除进行细粒度检查。

您现在不再需要全局管理员 API 密钥了——在创建 API 密钥时,您可以指定与创建、授予或销毁徽章相关的不同具体事项。如果您想执行此示例所示的操作并添加自己的图片,请不要忘记“上传”。这与徽章 API 权限位于单独的部分。

2 个赞

在原始帖子中,我不小心将 sha 哈希摘要函数放在了字符串中,因此它被设置为字面上的 python 代码,而不是摘要。这似乎仍然有效,但_可能实际上应该是正确的_。我不确定是什么在检查这一点,但如果你这样做错了,图像很可能会在将来某个时候被标记为损坏。无论如何,我已经编辑了上面的代码,它应该是正确的。如果你发现任何其他错误,请告诉我!

3 个赞