Skip to content

Commit fd9b92f

Browse files
JacobCoffeeAlc-Alc
andauthored
feat: allow guild setting apis (partial) (#99)
Co-authored-by: Alc-Alc <alc@localhost>
1 parent 8765c51 commit fd9b92f

37 files changed

+1141
-452
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ repos:
2020
- repo: https://github.com/astral-sh/ruff-pre-commit
2121
rev: "v0.11.13"
2222
hooks:
23-
- id: ruff
23+
- id: ruff-check
2424
args: ["--fix"]
2525
- id: ruff-format
2626
- repo: https://github.com/sourcery-ai/sourcery

byte_bot/byte/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
from __future__ import annotations
44

5-
from byte_bot.byte import bot, lib
5+
from byte_bot.byte import bot, lib, plugins, views
66
from byte_bot.byte.lib.log import setup_logging
77

8-
__all__ = ["bot", "lib"]
8+
__all__ = ("bot", "lib", "plugins", "setup_logging", "views")
99

1010
setup_logging()

byte_bot/byte/bot.py

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from discord import Activity, Forbidden, Intents, Member, Message, NotFound
1111
from discord.ext.commands import Bot, CommandError, Context, ExtensionAlreadyLoaded
1212
from dotenv import load_dotenv
13+
from httpx import ConnectError
1314

1415
from byte_bot.byte.lib import settings
1516
from byte_bot.byte.lib.log import get_logger
@@ -23,18 +24,6 @@
2324
load_dotenv()
2425

2526

26-
async def on_member_join(member: Member) -> None:
27-
"""Handle member join event.
28-
29-
Args:
30-
member: Member object.
31-
"""
32-
await member.send(
33-
f"Welcome to {member.guild.name}! Please make sure to read the rules if you haven't already. "
34-
f"Feel free to ask any questions you have in the help channel."
35-
)
36-
37-
3827
class Byte(Bot):
3928
"""Byte Bot Base Class."""
4029

@@ -113,10 +102,11 @@ async def on_member_join(member: Member) -> None:
113102
Args:
114103
member: Member object.
115104
"""
116-
await member.send(
117-
f"Welcome to {member.guild.name}! Please make sure to read the rules if you haven't already. "
118-
f"Feel free to ask any questions you have in the help channel."
119-
)
105+
if not member.bot:
106+
await member.send(
107+
f"Welcome to {member.guild.name}! Please make sure to read the rules if you haven't already. "
108+
f"Feel free to ask any questions you have in the help channel."
109+
)
120110

121111
async def on_guild_join(self, guild: discord.Guild) -> None:
122112
"""Handle guild join event.
@@ -127,17 +117,34 @@ async def on_guild_join(self, guild: discord.Guild) -> None:
127117
await self.tree.sync(guild=guild)
128118
api_url = f"http://0.0.0.0:8000/api/guilds/create?guild_id={guild.id}&guild_name={guild.name}"
129119

130-
async with httpx.AsyncClient() as client:
131-
response = await client.post(api_url)
132-
133-
if response.status_code == httpx.codes.CREATED:
134-
logger.info("successfully added guild %s (ID: %s)", guild.name, guild.id)
135-
else:
136-
logger.error(
137-
"%s joined guild '%s' but was not added to database",
138-
self.user.name if self.user else "Bot",
139-
guild.name,
140-
)
120+
try:
121+
async with httpx.AsyncClient() as client:
122+
response = await client.post(api_url)
123+
124+
if response.status_code == httpx.codes.CREATED:
125+
logger.info("successfully added guild %s (id: %s)", guild.name, guild.id)
126+
embed = discord.Embed(
127+
title="Guild Joined",
128+
description=f"Joined guild {guild.name} (ID: {guild.id})",
129+
color=discord.Color.green(),
130+
)
131+
else:
132+
embed = discord.Embed(
133+
title="Guild Join Failed",
134+
description=f"Joined guild, but failed to add guild {guild.name} (ID: {guild.id}) to database",
135+
color=discord.Color.red(),
136+
)
137+
138+
if dev_guild := self.get_guild(settings.discord.DEV_GUILD_ID):
139+
if dev_channel := dev_guild.get_channel(settings.discord.DEV_GUILD_INTERNAL_ID):
140+
if hasattr(dev_channel, "send"):
141+
await dev_channel.send(embed=embed) # type: ignore[attr-defined]
142+
else:
143+
logger.error("dev channel not found.")
144+
else:
145+
logger.error("dev guild not found.")
146+
except ConnectError:
147+
logger.exception("failed to connect to api to add guild %s (id: %s)", guild.name, guild.id)
141148

142149

143150
def run_bot() -> None:

byte_bot/byte/lib/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
"""Byte library module."""
22

3-
from byte_bot.byte.lib import common, log, settings, utils
3+
from byte_bot.byte.lib import common, log, settings, types, utils
44

55
__all__ = [
66
"common",
7+
"common",
8+
"log",
79
"log",
810
"settings",
11+
"types",
912
"utils",
1013
]

byte_bot/byte/lib/checks.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
""":doc:`Checks <discord.py:Checks>` for Byte."""
2+
3+
from __future__ import annotations
4+
5+
from typing import TYPE_CHECKING
6+
7+
from discord.ext.commands import CheckFailure, Context, check
8+
9+
from byte_bot.byte.lib import settings
10+
11+
if TYPE_CHECKING:
12+
from discord.ext.commands._types import Check
13+
14+
__all__ = ("is_byte_dev", "is_guild_admin")
15+
16+
17+
def is_guild_admin() -> Check:
18+
"""Check if the user is a guild admin.
19+
20+
Returns:
21+
A check function.
22+
"""
23+
24+
async def predicate(ctx: Context) -> bool:
25+
"""Check if the user is a guild admin.
26+
27+
Args:
28+
ctx: Context object.
29+
30+
Returns:
31+
True if the user is a guild admin, False otherwise.
32+
"""
33+
if not ctx.guild:
34+
msg = "Command can only be used in a guild."
35+
raise CheckFailure(msg)
36+
if not (member := ctx.guild.get_member(ctx.author.id)):
37+
msg = "Member not found in the guild."
38+
raise CheckFailure(msg)
39+
return member.guild_permissions.administrator
40+
41+
return check(predicate)
42+
43+
44+
def is_byte_dev() -> Check:
45+
"""Determines if the user is a Byte developer or owner.
46+
47+
Returns:
48+
A check function.
49+
"""
50+
51+
async def predicate(ctx: Context) -> bool:
52+
"""Check if the user is a Byte developer or owner.
53+
54+
Args:
55+
ctx: Context object.
56+
57+
Returns:
58+
True if the user is a Byte developer or owner, False otherwise.
59+
"""
60+
if await ctx.bot.is_owner(ctx.author) or ctx.author.id == settings.discord.DEV_USER_ID:
61+
return True
62+
63+
if not ctx.guild:
64+
return False
65+
66+
member = ctx.guild.get_member(ctx.author.id)
67+
if not member:
68+
return False
69+
70+
return any(role.name == "byte-dev" for role in member.roles)
71+
72+
return check(predicate)

byte_bot/byte/lib/common/__init__.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,55 @@
33
.. todo:: temporary, these are not multi-guild friendly.
44
"""
55

6-
from byte_bot.byte.lib.common import assets, colors, guilds, links
6+
from typing import Any
7+
8+
from byte_bot.byte.lib.common import assets, colors, guilds, links, mention
79

810
__all__ = (
911
"assets",
1012
"colors",
13+
"config_options",
1114
"guilds",
1215
"links",
16+
"mention",
1317
)
18+
19+
config_options: list[dict[str, Any]] = [
20+
{
21+
"label": "Server Settings",
22+
"description": "Configure overall server settings",
23+
"sub_settings": [
24+
{"label": "Prefix", "field": "prefix", "data_type": "String"},
25+
{"label": "Help Channel ID", "field": "help_channel_id", "data_type": "Integer"},
26+
{"label": "Sync Label", "field": "sync_label", "data_type": "String"},
27+
{"label": "Issue Linking", "field": "issue_linking", "data_type": "True/False"},
28+
{"label": "Comment Linking", "field": "comment_linking", "data_type": "True/False"},
29+
{"label": "PEP Linking", "field": "pep_linking", "data_type": "True/False"},
30+
],
31+
},
32+
{
33+
"label": "GitHub Settings",
34+
"description": "Configure GitHub settings",
35+
"sub_settings": [
36+
{"label": "Discussion Sync", "field": "discussion_sync", "data_type": "True/False"},
37+
{"label": "GitHub Organization", "field": "github_organization", "data_type": "String"},
38+
{"label": "GitHub Repository", "field": "github_repository", "data_type": "String"},
39+
],
40+
},
41+
{
42+
"label": "StackOverflow Settings",
43+
"description": "Configure StackOverflow settings",
44+
"sub_settings": [
45+
{"label": "Tag Name", "field": "tag_name", "data_type": "Comma-Separated String"},
46+
],
47+
},
48+
{
49+
"label": "Allowed Users",
50+
"description": "Configure allowed users",
51+
"sub_settings": [
52+
{"label": "User ID", "field": "user_id", "data_type": "Integer"},
53+
],
54+
},
55+
# Forum Settings: Configure help and showcase forum settings
56+
# Byte Settings: Configure meta-level Byte features
57+
]

byte_bot/byte/lib/common/colors.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
from typing import Final
77

88
# --- Colors
9-
litestar_blue: Final = 0x7289DA
9+
litestar_blue: Final = 0x202235
10+
litestar_yellow: Final = 0xEDB641
11+
1012
python_blue: Final = 0x4B8BBE
1113
python_yellow: Final = 0xFFD43B
14+
1215
astral_yellow: Final = 0xD7FF64
1316
astral_purple: Final = 0x261230
17+
18+
byte_teal: Final = 0x42B1A8

byte_bot/byte/lib/common/links.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66
from typing import Final
77

88
# --- Links
9-
litestar_issues: Final = "https://github.com/litestar-org/litestar/issues"
10-
github_litestar_projects: Final = "https://github.com/orgs/litestar-org/litestar/projects"
11-
github_org_projects: Final = "https://github.com/orgs/litestar-org/projects"
12-
pypi_litestar: Final = "https://pypi.org/project/litestar/"
13-
pypi_polyfactory: Final = "https://pypi.org/project/polyfactory/"
149
mcve: Final = "https://stackoverflow.com/help/minimal-reproducible-example"
1510
pastebin: Final = "https://paste.pythondiscord.com"
1611
markdown_guide: Final = "https://support.discord.com/hc/en-us/articles/210298617-Markdown-Text-101-Chat-Formatting-Bold-Italic-Underline-#h_01GY0DAKGXDEHE263BCAYEGFJA"

0 commit comments

Comments
 (0)