Skip to content

Commit ea5c265

Browse files
provide a helpful error message when BOT_TOKEN is not set, use pydantic for env management
1 parent 084b45f commit ea5c265

File tree

1 file changed

+37
-27
lines changed

1 file changed

+37
-27
lines changed

botstrap.py

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,40 @@
77
from types import TracebackType
88
from typing import Any, Final, cast
99

10-
from dotenv import load_dotenv
10+
import dotenv
1111
from httpx import Client, HTTPStatusError, Response
1212

13+
log = logging.getLogger("botstrap") # note this instance will not have the .trace level
14+
15+
# TODO: Remove once better error handling for constants.py is in place.
16+
if (dotenv.dotenv_values().get("BOT_TOKEN") or os.getenv("BOT_TOKEN")) is None:
17+
msg = (
18+
"Couldn't find the `BOT_TOKEN` environment variable. "
19+
"Make sure to add it to your `.env` file like this: `BOT_TOKEN=value_of_your_bot_token`"
20+
)
21+
log.fatal(msg)
22+
sys.exit(1)
23+
1324
# Filter out the send typing monkeypatch logs from bot core when we import to get constants
1425
logging.getLogger("pydis_core").setLevel(logging.WARNING)
1526

27+
# As a side effect, this also configures our logging styles
1628
from bot.constants import ( # noqa: E402
29+
Bot as BotConstants,
30+
Guild as GuildConstants,
1731
Webhooks,
1832
_Categories, # pyright: ignore[reportPrivateUsage]
1933
_Channels, # pyright: ignore[reportPrivateUsage]
2034
_Emojis, # pyright: ignore[reportPrivateUsage]
2135
_Roles, # pyright: ignore[reportPrivateUsage]
2236
)
23-
from bot.log import get_logger # noqa: E402
2437

25-
load_dotenv()
26-
log = get_logger("botstrap")
27-
# Silence noisy httpcore logger
28-
get_logger("httpcore").setLevel("INFO")
38+
# Silence noisy loggers
39+
logging.getLogger("httpx").setLevel(logging.WARNING)
40+
logging.getLogger("httpcore").setLevel(logging.WARNING)
41+
2942

3043
ENV_FILE = Path(".env.server")
31-
BOT_TOKEN = os.getenv("BOT_TOKEN", None)
32-
GUILD_ID = os.getenv("GUILD_ID", None)
3344

3445
COMMUNITY_FEATURE = "COMMUNITY"
3546
PYTHON_HELP_CHANNEL_NAME = "python_help"
@@ -40,21 +51,13 @@
4051
GUILD_FORUM_TYPE = 15
4152
EMOJI_REGEX = re.compile(r"<:(\w+):(\d+)>")
4253

43-
if not BOT_TOKEN:
44-
message = (
45-
"Couldn't find the `BOT_TOKEN` environment variable. "
46-
"Make sure to add it to your `.env` file like this: `BOT_TOKEN=value_of_your_bot_token`"
47-
)
48-
log.warning(message)
49-
raise ValueError(message)
50-
51-
if not GUILD_ID:
52-
message = (
54+
if GuildConstants.id == type(GuildConstants).model_fields["id"].default:
55+
msg = (
5356
"Couldn't find the `GUILD_ID` environment variable. "
5457
"Make sure to add it to your `.env` file like this: `GUILD_ID=value_of_your_discord_server_id`"
5558
)
56-
log.warning(message)
57-
raise ValueError(message)
59+
log.error(msg)
60+
sys.exit(1)
5861

5962

6063
class SilencedDict(dict[str, Any]):
@@ -86,10 +89,10 @@ class DiscordClient(Client):
8689

8790
CDN_BASE_URL: Final[str] = "https://cdn.discordapp.com"
8891

89-
def __init__(self, guild_id: int | str):
92+
def __init__(self, *, guild_id: int | str, bot_token: str):
9093
super().__init__(
9194
base_url="https://discord.com/api/v10",
92-
headers={"Authorization": f"Bot {BOT_TOKEN}"},
95+
headers={"Authorization": f"Bot {bot_token}"},
9396
event_hooks={"response": [self._raise_for_status]},
9497
)
9598
self.guild_id = guild_id
@@ -284,8 +287,15 @@ def clone_emoji(self, *, new_name: str, original_emoji_id: str | int) -> str:
284287
class BotStrapper:
285288
"""Bootstrap the bot configuration for a given guild."""
286289

287-
def __init__(self, guild_id: int | str, env_file: Path):
288-
self.client = DiscordClient(guild_id=guild_id)
290+
def __init__(
291+
self,
292+
*,
293+
guild_id: int | str,
294+
env_file: Path,
295+
bot_token: str,
296+
):
297+
self.guild_id = guild_id
298+
self.client = DiscordClient(guild_id=guild_id, bot_token=bot_token)
289299
self.env_file = env_file
290300

291301
def __enter__(self):
@@ -310,12 +320,12 @@ def check_guild_membership(self) -> None:
310320
"""Check the bot is in the required guild."""
311321
if not self.client.check_if_in_guild():
312322
client_id = self.client.app_info["id"]
313-
log.error("The bot is not a member of the configured guild with ID %s.", GUILD_ID)
323+
log.error("The bot is not a member of the configured guild with ID %s.", self.guild_id)
314324
log.warning(
315325
"Please invite with the following URL and rerun this script: "
316326
"https://discord.com/oauth2/authorize?client_id=%s&guild_id=%s&scope=bot+applications.commands&permissions=8",
317327
client_id,
318-
GUILD_ID,
328+
self.guild_id,
319329
)
320330
raise BotstrapError("Bot is not a member of the configured guild.")
321331

@@ -463,7 +473,7 @@ def run(self) -> None:
463473

464474

465475
if __name__ == "__main__":
466-
botstrap = BotStrapper(guild_id=GUILD_ID, env_file=ENV_FILE)
476+
botstrap = BotStrapper(guild_id=GuildConstants.id, env_file=ENV_FILE, bot_token=BotConstants.token)
467477
with botstrap:
468478
botstrap.run()
469479
log.info("Botstrap completed successfully. Configuration has been written to %s", ENV_FILE)

0 commit comments

Comments
 (0)