Skip to content

Commit 82fa0b1

Browse files
refactor: sync emojis to the test guild
1 parent 3d44321 commit 82fa0b1

File tree

1 file changed

+55
-2
lines changed

1 file changed

+55
-2
lines changed

botstrap.py

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
import base64
12
import logging
23
import os
34
import re
45
import sys
56
from pathlib import Path
6-
from typing import Any, cast
7+
from typing import Any, Final, cast
78

89
from dotenv import load_dotenv
910
from httpx import Client, HTTPStatusError, Response
@@ -15,6 +16,7 @@
1516
Webhooks,
1617
_Categories, # pyright: ignore[reportPrivateUsage]
1718
_Channels, # pyright: ignore[reportPrivateUsage]
19+
_Emojis, # pyright: ignore[reportPrivateUsage]
1820
_Roles, # pyright: ignore[reportPrivateUsage]
1921
)
2022
from bot.log import get_logger # noqa: E402
@@ -35,6 +37,7 @@
3537
RULES_CHANNEL_NAME = "rules"
3638
GUILD_CATEGORY_TYPE = 4
3739
GUILD_FORUM_TYPE = 15
40+
EMOJI_REGEX = re.compile(r"<:(\w+):(\d+)>")
3841

3942
if not BOT_TOKEN:
4043
message = (
@@ -76,6 +79,8 @@ def __getitem__(self, item: str):
7679
class DiscordClient(Client):
7780
"""An HTTP client to communicate with Discord's APIs."""
7881

82+
CDN_BASE_URL: Final[str] = "https://cdn.discordapp.com"
83+
7984
def __init__(self, guild_id: int | str):
8085
super().__init__(
8186
base_url="https://discord.com/api/v10",
@@ -239,6 +244,37 @@ def create_webhook(self, name: str, channel_id_: int) -> str:
239244
new_webhook = response.json()
240245
return new_webhook["id"]
241246

247+
def list_emojis(self) -> list[dict[str, Any]]:
248+
"""Lists all the emojis for the guild."""
249+
response = self.get(f"/guilds/{self.guild_id}/emojis")
250+
return response.json()
251+
252+
def get_emoji_contents(self, id_: str | int) -> bytes | None:
253+
"""Fetches the image data for an emoji by ID."""
254+
# emojis are located at https://cdn.discordapp.com/emojis/{emoji_id}.{ext}
255+
response = self.get(f"{self.CDN_BASE_URL}/emojis/{emoji_id!s}.webp")
256+
return response.content
257+
258+
def clone_emoji(self, *, new_name: str, original_emoji_id: str | int) -> str:
259+
"""Creates a new emoji in the guild, cloned from another emoji by ID."""
260+
emoji_data = self.get_emoji_contents(original_emoji_id)
261+
if not emoji_data:
262+
log.warning("Couldn't find emoji with ID %s.", original_emoji_id)
263+
return ""
264+
265+
payload = {
266+
"name": new_name,
267+
"image": f"data:image/png;base64,{base64.b64encode(emoji_data).decode('utf-8')}",
268+
}
269+
270+
response = self.post(
271+
f"/guilds/{self.guild_id}/emojis",
272+
json=payload,
273+
headers={"X-Audit-Log-Reason": f"Creating {new_name} emoji as part of PyDis botstrap"},
274+
)
275+
new_emoji = response.json()
276+
return new_emoji["id"]
277+
242278

243279
with DiscordClient(guild_id=GUILD_ID) as discord_client:
244280
if discord_client.upgrade_application_flags_if_necessary():
@@ -332,7 +368,24 @@ def create_webhook(self, name: str, channel_id_: int) -> str:
332368
config_str += f"webhooks_{webhook_name}__id={webhook_id}\n"
333369

334370
config_str += "\n#Emojis\n"
335-
config_str += "emojis_trashcan=🗑️"
371+
372+
existing_emojis = discord_client.list_emojis()
373+
log.debug("Syncing emojis with bot configuration.")
374+
for emoji_config_name, emoji_config in _Emojis.model_fields.items():
375+
if not (match := EMOJI_REGEX.match(emoji_config.default)):
376+
continue
377+
emoji_name = match.group(1)
378+
emoji_id = match.group(2)
379+
380+
for emoji in existing_emojis:
381+
if emoji["name"] == emoji_name:
382+
emoji_id = emoji["id"]
383+
break
384+
else:
385+
log.info("Creating emoji %s", emoji_name)
386+
emoji_id = discord_client.clone_emoji(new_name=emoji_name, original_emoji_id=emoji_id)
387+
388+
config_str += f"emojis_{emoji_config_name}=<:{emoji_name}:{emoji_id}>\n"
336389

337390
with env_file_path.open("wb") as file:
338391
file.write(config_str.encode("utf-8"))

0 commit comments

Comments
 (0)