Skip to content

Commit

Permalink
feat: add ping roles
Browse files Browse the repository at this point in the history
Closes #17
  • Loading branch information
Skelmis committed Feb 8, 2025
1 parent 02d8810 commit e1c6c5e
Show file tree
Hide file tree
Showing 11 changed files with 323 additions and 97 deletions.
252 changes: 164 additions & 88 deletions poetry.lock

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
[tool.poetry]
name = "suggestions bot"
[project]
name = "suggestions-bot"
version = "3.29"
description = "A Discord bot designed to build better communities by encouraging a positive and constructive relationship between community and staff."
authors = ["Suggestions Bot"]
authors = []
license = "AGPL-3.0"
readme = "readme.md"

[tool.poetry]
package-mode = false

[tool.poetry.dependencies]
python = "^3.10"
disnake = {extras = ["speed"], version = "2.10.1"}
alaric = "^1.5.0"
function-cooldowns = "^2.0.1"
function-cooldowns = "^2.1.0"
httpx = "^0.28.1"
orjson = "^3.10.11"
skelmis-commons = "^1.4.0"
Expand Down
7 changes: 6 additions & 1 deletion suggestions/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import traceback
from pathlib import Path
from string import Template
from typing import Type, Optional, Union, Any
from typing import Type, Optional, Union, Any, Final

import aiohttp
import alaric
Expand Down Expand Up @@ -68,6 +68,11 @@ def __init__(self, *args, **kwargs):
tz=datetime.timezone.utc
)

# TODO Set this
self.guild_subscription_sku_id: Final[int] = int(
os.environ.get("guild_subscription_sku_id")
)

self.is_prod: bool = True if os.environ.get("PROD", None) else False

db = None
Expand Down
15 changes: 15 additions & 0 deletions suggestions/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,18 @@ async def check(interaction: disnake.Interaction):
return True

return commands.check(check) # type: ignore


def ensure_guild_has_subscription():
async def check(interaction: disnake.Interaction):
entitlements: list[disnake.Entitlement] = [
e
for e in interaction.entitlements
if e.is_active() and e.sku_id == interaction.bot.guild_subscription_sku_id
]
if entitlements is None:
return await interaction.response.require_premium()

return True

return commands.check(check) # type: ignore
11 changes: 10 additions & 1 deletion suggestions/cogs/suggestion_cog.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
SuggestionNotFound,
)
from suggestions.interaction_handler import InteractionHandler
from suggestions.objects import Suggestion, GuildConfig, QueuedSuggestion
from suggestions.objects import (
Suggestion,
GuildConfig,
QueuedSuggestion,
PremiumGuildConfig,
)
from suggestions.objects.suggestion import SuggestionState
from suggestions.utility import r2, wrap_with_error_handler

Expand Down Expand Up @@ -121,8 +126,12 @@ async def suggest(
except disnake.Forbidden as e:
raise MissingPermissionsToAccessQueueChannel from e

premium_guild_config: PremiumGuildConfig = (
await PremiumGuildConfig.from_id(qs.guild_id, interaction.bot.state)
)
qs_embed: disnake.Embed = await qs.as_embed(self.bot)
msg = await queue_channel.send(
content=premium_guild_config.queued_suggestions_prefix or None,
embed=qs_embed,
components=[
await buttons.SuggestionsQueueApprove(
Expand Down
4 changes: 4 additions & 0 deletions suggestions/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
UserConfig,
Error,
QueuedSuggestion,
PremiumGuildConfig,
)
from suggestions.objects.stats import MemberStats

Expand All @@ -24,6 +25,9 @@ def __init__(self, connection_url):
self.guild_configs: Document = Document(
self.db, "guild_configs", converter=GuildConfig
)
self.premium_guild_configs: Document = Document(
self.db, "premium_guild_configs", converter=PremiumGuildConfig
)
self.user_configs: Document = Document(
self.db, "user_configs", converter=UserConfig
)
Expand Down
3 changes: 3 additions & 0 deletions suggestions/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,6 @@ def __init__(
self.suggestion_id: str = sid
self.user_facing_message = user_facing_message
self.message = message if message is not None else self.__doc__

class PremiumRequired(disnake.DiscordException):
"""This command requires a premium subscription to run."""
10 changes: 9 additions & 1 deletion suggestions/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,13 @@
from .suggestion import Suggestion
from .error import Error
from .queued_suggestion import QueuedSuggestion
from .premium_guild_config import PremiumGuildConfig

__all__ = ("Suggestion", "GuildConfig", "UserConfig", "Error", "QueuedSuggestion")
__all__ = (
"Suggestion",
"GuildConfig",
"UserConfig",
"Error",
"QueuedSuggestion",
"PremiumGuildConfig",
)
86 changes: 86 additions & 0 deletions suggestions/objects/premium_guild_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from __future__ import annotations

import typing
from typing import Optional

import logoo
from alaric import AQ
from alaric.comparison import EQ
from commons.caching import NonExistentEntry

if typing.TYPE_CHECKING:
from suggestions import State

logger = logoo.Logger(__name__)


class PremiumGuildConfig:
__slots__ = ["_id", "suggestions_prefix", "queued_suggestions_prefix"]

def __init__(
self,
_id: int,
suggestions_prefix: str = "",
queued_suggestions_prefix: str = "",
):
self._id = _id
self.suggestions_prefix = suggestions_prefix
self.queued_suggestions_prefix = queued_suggestions_prefix

def as_dict(self):
return {
"_id": self._id,
"suggestions_prefix": self.suggestions_prefix,
"queued_suggestions_prefix": self.queued_suggestions_prefix,
}

def as_filter(self):
return {"_id": self._id}

def __repr__(self):
return f"PremiumGuildConfig({self.as_dict()})"

@property
def guild_id(self) -> int:
return self._id

@classmethod
async def from_id(cls, guild_id: int, state: State) -> PremiumGuildConfig:
"""Returns a valid PremiumGuildConfig instance from an id.
Parameters
----------
guild_id: int
The guild we want
state: State
Internal state to marshall data
Returns
-------
PremiumGuildConfig
The valid guilds config
"""
try:
gc = state.premium_guild_configs.get_entry(guild_id)
logger.debug(
"Found cached PremiumGuildConfig for guild %s",
guild_id,
extra_metadata={"guild_id": guild_id},
)
return gc
except NonExistentEntry:
pass

guild_config: Optional[PremiumGuildConfig] = (
await state.premium_guild_config_db.find(AQ(EQ("_id", guild_id)))
)
if not guild_config:
logger.info(
"Created new PremiumGuildConfig for %s",
guild_id,
extra_metadata={"guild_id": guild_id},
)
guild_config = cls(_id=guild_id)

state.refresh_premium_guild_config(guild_config)
return guild_config
6 changes: 5 additions & 1 deletion suggestions/objects/suggestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)
from suggestions.interaction_handler import InteractionHandler
from suggestions.low_level import MessageEditing
from suggestions.objects import UserConfig, GuildConfig
from suggestions.objects import UserConfig, GuildConfig, PremiumGuildConfig

if TYPE_CHECKING:
from suggestions import SuggestionsBot, State, Colors
Expand Down Expand Up @@ -1064,11 +1064,15 @@ async def setup_initial_messages(
]

try:
premium_guild_config: PremiumGuildConfig = await PremiumGuildConfig.from_id(
self.guild_id, bot.state
)
channel = await bot.get_or_fetch_channel(
guild_config.suggestions_channel_id
)
channel: disnake.TextChannel = cast(disnake.TextChannel, channel)
message: disnake.Message = await channel.send(
content=premium_guild_config.suggestions_prefix or None,
embed=await self.as_embed(bot),
components=[components_to_send],
)
Expand Down
16 changes: 15 additions & 1 deletion suggestions/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from commons.caching import NonExistentEntry, TimedCache
from logoo import Logger

from suggestions.objects import GuildConfig, UserConfig
from suggestions.objects import GuildConfig, UserConfig, PremiumGuildConfig

if TYPE_CHECKING:
from suggestions import SuggestionsBot
Expand Down Expand Up @@ -62,6 +62,11 @@ def __init__(self, database: SuggestionsMongoManager, bot: SuggestionsBot):
lazy_eviction=False,
ttl_from_last_access=True,
)
self.premium_guild_configs: TimedCache = TimedCache(
global_ttl=timedelta(minutes=30),
lazy_eviction=False,
ttl_from_last_access=True,
)
self.user_configs: TimedCache = TimedCache(
global_ttl=timedelta(minutes=30),
lazy_eviction=False,
Expand Down Expand Up @@ -140,6 +145,10 @@ def queued_suggestions_db(self) -> Document:
def guild_config_db(self) -> Document:
return self.database.guild_configs

@property
def premium_guild_config_db(self) -> Document:
return self.database.premium_guild_configs

@property
def user_config_db(self) -> Document:
return self.database.user_configs
Expand All @@ -162,6 +171,11 @@ def notify_shutdown(self):
def refresh_guild_config(self, guild_config: GuildConfig) -> None:
self.guild_configs.add_entry(guild_config.guild_id, guild_config, override=True)

def refresh_premium_guild_config(self, guild_config: PremiumGuildConfig) -> None:
self.premium_guild_configs.add_entry(
guild_config.guild_id, guild_config, override=True
)

def refresh_user_config(self, user_config: UserConfig) -> None:
self.user_configs.add_entry(user_config.user_id, user_config, override=True)

Expand Down

0 comments on commit e1c6c5e

Please sign in to comment.