Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,15 @@ def __init__(self):
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
super().__init__(command_prefix="$", intents=intents, help_command=None)
super().__init__(
command_prefix="$",
intents=intents,
help_command=None,
# глобальный дефолт: чужой текст (вебхуки, /say) не должен пинговать сервер
allowed_mentions=discord.AllowedMentions(
everyone=False, roles=False, users=True
),
)

self.channel_creators: dict[int, int] = {}
self.bot_created_channels: set[int] = set()
Expand Down Expand Up @@ -166,5 +174,8 @@ async def on_disconnect(self):
log.info("Бот отключен")


if not TOKEN:
raise SystemExit("TOKEN не задан: создай .env по образцу .env.example")

bot = CoolBot()
bot.run(TOKEN)
2 changes: 2 additions & 0 deletions cogs/anonymous.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ async def anonsay(self, interaction: discord.Interaction, message: str):
content=message[:2000],
username=self.bot.user.name,
avatar_url=self.bot.user.avatar.url if self.bot.user.avatar else None,
# аноним не должен никого пинговать
allowed_mentions=discord.AllowedMentions.none(),
)
await interaction.response.send_message(
embed=embeds.ok(description="отправлено", user=interaction.user),
Expand Down
6 changes: 4 additions & 2 deletions cogs/moderation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

log = logging.getLogger(__name__)

MAX_PURGE = 200


class ModerationCog(commands.Cog):
def __init__(self, bot: commands.Bot):
Expand All @@ -21,10 +23,10 @@ def __init__(self, bot: commands.Bot):
@app_commands.checks.has_permissions(manage_messages=True)
@app_commands.default_permissions(manage_messages=True)
async def clear(self, interaction: discord.Interaction, amount: int = 10):
if amount <= 0:
if amount <= 0 or amount > MAX_PURGE:
await interaction.response.send_message(
embed=embeds.err(
"количество должно быть больше нуля", user=interaction.user
f"количество · от 1 до {MAX_PURGE}", user=interaction.user
),
ephemeral=True,
)
Expand Down
5 changes: 5 additions & 0 deletions services/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

log = logging.getLogger(__name__)

# пересылаемый текст пишут пользователи: пинги людей оставляем,
# @everyone и роли через вебхук пробивать нельзя
SAFE_MENTIONS = discord.AllowedMentions(everyone=False, roles=False, users=True)


class WebhookService:
def __init__(self):
Expand Down Expand Up @@ -34,6 +38,7 @@ def invalidate(self, channel_id: int, name: str):
self.cache.pop(f"{channel_id}_{name}", None)

async def send(self, channel: discord.TextChannel, name: str, **kwargs):
kwargs.setdefault("allowed_mentions", SAFE_MENTIONS)
try:
webhook = await self.get_or_create_webhook(channel, name)
await webhook.send(**kwargs)
Expand Down
37 changes: 37 additions & 0 deletions tests/test_webhook_mentions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Вебхуки не должны пробивать @everyone и роли чужим текстом."""

import asyncio
from unittest.mock import AsyncMock

import discord

from services.webhook import SAFE_MENTIONS, WebhookService


def test_safe_mentions_block_everyone_and_roles():
assert SAFE_MENTIONS.everyone is False
assert SAFE_MENTIONS.roles is False
assert SAFE_MENTIONS.users is True


def test_send_injects_safe_mentions_by_default():
service = WebhookService()
webhook = AsyncMock()
service.get_or_create_webhook = AsyncMock(return_value=webhook)

asyncio.run(service.send(AsyncMock(), "Webhook", content="@everyone хех"))

assert webhook.send.await_args.kwargs["allowed_mentions"] is SAFE_MENTIONS


def test_send_keeps_explicit_mentions():
service = WebhookService()
webhook = AsyncMock()
service.get_or_create_webhook = AsyncMock(return_value=webhook)
none = discord.AllowedMentions.none()

asyncio.run(
service.send(AsyncMock(), "Webhook", content="x", allowed_mentions=none)
)

assert webhook.send.await_args.kwargs["allowed_mentions"] is none
Loading