react to keyword with message

This commit is contained in:
2026-05-27 15:50:17 +00:00
parent e1455e655f
commit 52da79db7e
3 changed files with 132 additions and 0 deletions

106
cogs/autoreply/autoreply.py Normal file
View File

@@ -0,0 +1,106 @@
import discord
from discord.ext import commands
import json
import logging
import os
import re
logger = logging.getLogger(__name__)
CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json")
DATA_PATH = os.path.join(os.path.dirname(__file__), "replies.json")
def load_json(path: str) -> dict:
with open(path, "r", encoding="utf-8") as f:
return json.load(f)
def save_json(path: str, data: dict) -> None:
with open(path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
class AutoReply(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self.config: dict = {}
self.rules: list[dict] = []
async def cog_load(self):
self._load_config()
self._load_rules()
def _load_config(self):
try:
self.config = load_json(CONFIG_PATH)
except (FileNotFoundError, json.JSONDecodeError) as e:
logger.error("[AutoReply] config error: %s", e)
def _load_rules(self):
try:
self.rules = load_json(DATA_PATH).get("rules", [])
logger.info("[AutoReply] %d rules loaded.", len(self.rules))
except FileNotFoundError:
self.rules = []
save_json(DATA_PATH, {"rules": []})
except json.JSONDecodeError as e:
logger.error("[AutoReply] replies.json parse error: %s", e)
def _save_rules(self):
save_json(DATA_PATH, {"rules": self.rules})
def _match(self, rule: dict, content: str) -> bool:
mode = rule.get("match_mode", "contains")
case_sensitive = rule.get("case_sensitive", False)
text = content if case_sensitive else content.lower()
for kw in rule.get("keywords", []):
kw_cmp = kw if case_sensitive else kw.lower()
if mode == "exact" and text == kw_cmp: return True
if mode == "contains" and kw_cmp in text: return True
if mode == "startswith" and text.startswith(kw_cmp): return True
if mode == "regex":
flags = 0 if case_sensitive else re.IGNORECASE
if re.search(kw, content, flags): return True
return False
@commands.Cog.listener()
async def on_message(self, message: discord.Message):
if message.author.bot or not message.guild:
return
for rule in self.rules:
if not rule.get("enabled", True):
continue
if self._match(rule, message.content):
await message.channel.send(rule["reply"])
if not self.config.get("match_all", False):
break
@commands.command(name="add_rule")
@commands.has_permissions(administrator=True)
async def add_rule(self, ctx: commands.Context, keywords: str, *, reply: str):
# add rules
rule = {
"keywords": [k.strip() for k in keywords.split(",")],
"match_mode": "contains",
"case_sensitive": False,
"reply": reply,
"enabled": True,
}
self.rules.append(rule)
self._save_rules()
kws = ", ".join(f"`{k}`" for k in rule["keywords"])
await ctx.send(f"Rule added")
@add_rule.error
async def add_rule_error(self, ctx: commands.Context, error):
if isinstance(error, commands.MissingPermissions):
await ctx.send("Шпацируй нахуй отсюда")
elif isinstance(error, commands.MissingRequiredArgument):
await ctx.send("Wrong usage")
async def setup(bot: commands.Bot):
await bot.add_cog(AutoReply(bot))

View File

@@ -0,0 +1,8 @@
{
"description": "Usage is admin-only; Reacts to messages containing keywords with a configured reply",
"usages": [
"!add_rule <keyword1,keyword2,...> <reply>"
],
"hidden": false,
"match_all": false
}

View File

@@ -0,0 +1,18 @@
{
"rules": [
{
"keywords": ["ping", "pong"],
"match_mode": "contains",
"case_sensitive": false,
"reply": "Pong",
"enabled": true
},
{
"keywords": ["kitty", "cat", "IMMEDIATE CAT REQUEST!!!!!!"],
"match_mode": "contains",
"case_sensitive": false,
"reply": "https://cataas.com/cat",
"enabled": true
}
]
}