diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..0977c21 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,9 @@ +# Copyright (c) NiceBots +# SPDX-License-Identifier: MIT + +services: + redis: + image: redis:alpine + ports: + - "6379:6379" + command: --loglevel debug \ No newline at end of file diff --git a/config.example.yaml b/config.example.yaml index 115e952..97db981 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -36,10 +36,16 @@ extensions: every: 300 # 300 seconds = 5 minutes bot: token: "bot token here" # Your bot token + cache: + type: "memory" # Cache type. Possible values: "memory" or "redis" + redis: # Redis configuration (only used if type is "redis") + host: "localhost" # Redis server host + port: 6379 # Redis server port + db: 0 # Redis database number + password: null # Redis password (optional) + ssl: false # Whether to use SSL for Redis connection logging: level: "INFO" # The logging level. Possible values: DEBUG, INFO, WARNING, ERROR, CRITICAL use: bot: true # Whether to run the bot backend: false # Whether to run the backend - - diff --git a/readme.md b/readme.md index 2952ec4..2512ee7 100644 --- a/readme.md +++ b/readme.md @@ -114,8 +114,35 @@ BOTKIT__extensions__listings__enabled=false BOTKIT__extensions__listings__topgg_token=your_top.gg_token BOTKIT__extensions__ping__enabled=true BOTKIT__logging__level=INFO +BOTKIT__cache__type=redis +BOTKIT__cache__redis__host=redis.example.com +BOTKIT__cache__redis__port=6379 ``` +### Cache Configuration + +Botkit supports two types of caching: + +- **Memory Cache**: Simple in-memory cache (default) +- **Redis Cache**: Distributed cache using Redis + +To configure the cache, use the `cache` section under the `bot` in your config: + +```yaml +bot: + cache: + type: "redis" # Use "memory" for in-memory cache + redis: # Redis configuration (only needed when type is "redis") + host: "localhost" + port: 6379 + db: 0 + password: "optional" # Optional Redis password + ssl: false # Whether to use SSL +``` + +If Redis cache is requested but no configuration is provided, Botkit will fall back to +memory cache with a warning. + ## Creating Extensions Extensions are in truth just python located in the `src/extensions` directory. When diff --git a/src/custom/__init__.py b/src/custom/__init__.py index dbf8023..ca750d4 100644 --- a/src/custom/__init__.py +++ b/src/custom/__init__.py @@ -51,9 +51,33 @@ def load_translations(self) -> None: class Bot(bridge.Bot): - def __init__(self, *args: Any, **options: Any) -> None: + def __init__( + self, *args: Any, cache_type: str = "memory", cache_config: dict[str, Any] | None = None, **options: Any + ) -> None: self.translations: list[ExtensionTranslation] = options.pop("translations", []) - self.cache: aiocache.SimpleMemoryCache | aiocache.RedisCache = aiocache.SimpleMemoryCache() + + self.botkit_cache: aiocache.BaseCache + # Initialize cache based on type and config + if cache_type == "redis": + if cache_config: + logger.info("Using Redis cache") + self.botkit_cache = aiocache.RedisCache( + endpoint=cache_config.get("host", "localhost"), + port=cache_config.get("port", 6379), + db=cache_config.get("db", 0), + password=cache_config.get("password"), + ssl=cache_config.get("ssl", False), + namespace="botkit", + ) + else: + logger.warning( + "Redis cache type specified but no configuration provided. Falling back to memory cache." + ) + self.botkit_cache = aiocache.SimpleMemoryCache(namespace="botkit") + else: + logger.info("Using memory cache") + self.botkit_cache = aiocache.SimpleMemoryCache(namespace="botkit") + super().__init__(*args, **options) @self.listen(name="on_ready", once=True) diff --git a/src/start.py b/src/start.py index e592696..b519d31 100644 --- a/src/start.py +++ b/src/start.py @@ -145,10 +145,14 @@ async def setup_and_start_bot( intents = discord.Intents.default() if config.get("prefix"): intents.message_content = True + # Get cache configuration + cache_config = config.get("cache", {}) bot = custom.Bot( intents=intents, help_command=None, command_prefix=(config.get("prefix", {}).get("prefix") or commands.when_mentioned), + cache_type=cache_config.get("type", "memory"), + cache_config=cache_config.get("redis"), ) for function, its_config in bot_functions: setup_func(function, bot=bot, config=its_config) diff --git a/src/utils/cooldown.py b/src/utils/cooldown.py index 474c4dc..93f841d 100644 --- a/src/utils/cooldown.py +++ b/src/utils/cooldown.py @@ -92,7 +92,7 @@ def cooldown[C: commands.Cog, **P]( # noqa: PLR0913 def inner(func: CogCommandFunction[C, P]) -> CogCommandFunction[C, P]: @wraps(func) async def wrapper(self: C, ctx: custom.ApplicationContext, *args: P.args, **kwargs: P.kwargs) -> None: - cache = ctx.bot.cache + cache = ctx.bot.botkit_cache key_value: str = await parse_reactive_setting(key, ctx.bot, ctx) limit_value: int = await parse_reactive_setting(limit, ctx.bot, ctx) per_value: int = await parse_reactive_setting(per, ctx.bot, ctx)