Skip to content

Commit

Permalink
Merge
Browse files Browse the repository at this point in the history
  • Loading branch information
janssensjelle committed Feb 11, 2025
2 parents d7887b6 + a232dbb commit ccef779
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 10 deletions.
54 changes: 46 additions & 8 deletions src/cmds/core/other.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import discord
from discord import ApplicationContext, Interaction, Message, Option, slash_command
from discord.ext import commands
from discord.ui import InputText, Modal
from discord.ui import Button, InputText, Modal, View
from slack_sdk.webhook import WebhookClient

from src.bot import Bot
Expand Down Expand Up @@ -61,8 +61,23 @@ class SpoilerModal(Modal):
def __init__(self, *args, **kwargs) -> None:
"""Initialize the Spoiler Modal with input fields."""
super().__init__(*args, **kwargs)
self.add_item(InputText(label="Description", placeholder="Description", required=False, style=discord.InputTextStyle.long))
self.add_item(InputText(label="URL", placeholder="Enter URL. Submitting malicious or fake links will result in consequences.", required=True, style=discord.InputTextStyle.paragraph))

self.add_item(
InputText(
label="Description",
placeholder="Description",
required=False,
style=discord.InputTextStyle.long
)
)
self.add_item(
InputText(
label="URL",
placeholder="Enter URL. Submitting malicious or fake links will result in consequences.",
required=True,
style=discord.InputTextStyle.paragraph
)
)

async def callback(self, interaction: discord.Interaction) -> None:
"""Handle the modal submission by sending the spoiler report to JIRA."""
Expand All @@ -75,19 +90,38 @@ async def callback(self, interaction: discord.Interaction) -> None:
await interaction.response.send_message("Thank you, the spoiler has been reported.", ephemeral=True)

user_name = interaction.user.display_name

webhook_url = settings.JIRA_WEBHOOK

data = {
"user": user_name,
"desc": desc,
"url": url,
"desc": desc,
"type": "spoiler"
}

await webhook.webhook_call(webhook_url, data)


class SpoilerConfirmationView(View):
"""A confirmation view before opening the SpoilerModal."""

def __init__(self, user: discord.Member):
super().__init__(timeout=60)
self.user = user

@discord.ui.button(label="Proceed", style=discord.ButtonStyle.danger)
async def proceed(self, button: Button, interaction: discord.Interaction) -> None:
"""Opens the spoiler modal after confirmation."""
if interaction.user.id != self.user.id:
await interaction.response.send_message("This confirmation is not for you.", ephemeral=True)
return

modal = SpoilerModal(title="Report Spoiler")
await interaction.response.send_modal(modal)
self.stop()


class OtherCog(commands.Cog):
"""Other commands related to the bot."""

Expand Down Expand Up @@ -118,10 +152,14 @@ async def support(
)

@slash_command(guild_ids=settings.guild_ids, description="Add a URL which contains a spoiler.")
async def spoiler(self, ctx: ApplicationContext) -> Interaction:
"""Report a URL that contains a spoiler."""
modal = SpoilerModal(title="Report Spoiler")
return await ctx.send_modal(modal)
async def spoiler(self, ctx: ApplicationContext) -> None:
"""Ask for confirmation before reporting a spoiler."""
view = SpoilerConfirmationView(ctx.user)
await ctx.respond(
"Thank you for taking the time to report a spoiler. \n ⚠️ **Warning:** Submitting malicious or fake links will result in consequences.",
view=view,
ephemeral=True
)

@slash_command(guild_ids=settings.guild_ids, description="Provide feedback to HTB.")
@commands.cooldown(1, 60, commands.BucketType.user)
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/ban.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ async def add_infraction(
f"Following is the reason given:\n>>> {reason}\n"
)
except Forbidden as ex:
message = "Could not DM member due to privacy settings, however will still attempt to ban them..."
message = "Could not DM member due to privacy settings, however the infraction was still added."
logger.warning(f"Forbidden, when trying to contact user with ID {member.id} about infraction.", exc_info=ex)
except HTTPException as ex:
message = "Here's a 400 Bad Request for you. Just like when you tried to ask me out, last week."
Expand Down
63 changes: 63 additions & 0 deletions tests/src/cmds/core/test_ban.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@
from unittest.mock import AsyncMock, patch

import pytest
from discord import Forbidden

from src.cmds.core import ban
from src.database.models import Ban, Infraction
from src.helpers.ban import add_infraction
from src.helpers.duration import parse_duration_str
from src.helpers.responses import SimpleResponse
from tests import helpers


class MockResponse:
def __init__(self, status):
self.status = status
self.reason = 'Forbidden'
self.code = status
self.text = "Cannot send messages to this user"

class TestBanCog:
"""Test the `Ban` cog."""

Expand Down Expand Up @@ -219,6 +228,60 @@ async def test_remove_infraction_success(self, ctx, bot):

ctx.respond.assert_called_once_with(f"Infraction record #{infraction_record.id} has been deleted.")


@pytest.mark.asyncio
async def test_add_infraction_success(self, ctx, guild, member, author, bot):
member.send = AsyncMock()
bot.get_member_or_user.return_value = member

# Patch the AsyncSessionLocal to simulate database interaction
async with AsyncMock() as mock_session:
with patch('src.helpers.ban.AsyncSessionLocal', return_value=mock_session):
response = await add_infraction(guild, member, 10, "Test infraction reason", author)

# Assertions
assert response.message == f"{member.mention} ({member.id}) has been warned with a strike weight of 10."
member.send.assert_called_once_with(
f"You have been warned on {guild.name} with a strike value of 10. "
f"After a total value of 3, permanent exclusion from the server may be enforced.\n"
f"Following is the reason given:\n>>> Test infraction reason\n"
)

@pytest.mark.asyncio
async def test_add_infraction_dm_forbidden(self, ctx, guild, member, author, bot):
member.send = AsyncMock(side_effect=Forbidden(
response=MockResponse(403),
message="Cannot send messages to this user"
))
bot.get_member_or_user.return_value = member

# Patch the AsyncSessionLocal to simulate database interaction
async with AsyncMock() as mock_session:
with patch('src.helpers.ban.AsyncSessionLocal', return_value=mock_session):
response = await add_infraction(guild, member, 10, "Test infraction reason", author)

# Assertions
assert response.message == "Could not DM member due to privacy settings, however the infraction was still added."
member.send.assert_called_once()

@pytest.mark.asyncio
async def test_add_infraction_no_reason(self, ctx, guild, member, author, bot):
member.send = AsyncMock()
bot.get_member_or_user.return_value = member

# Patch the AsyncSessionLocal to simulate database interaction
async with AsyncMock() as mock_session:
with patch('src.helpers.ban.AsyncSessionLocal', return_value=mock_session):
response = await add_infraction(guild, member, 10, "", author)

# Assertions
assert response.message == f"{member.mention} ({member.id}) has been warned with a strike weight of 10."
member.send.assert_called_once_with(
f"You have been warned on {guild.name} with a strike value of 10. "
f"After a total value of 3, permanent exclusion from the server may be enforced.\n"
f"Following is the reason given:\n>>> No reason given ...\n"
)

def test_setup(self, bot):
"""Test the setup method of the cog."""
# Invoke the command
Expand Down
2 changes: 1 addition & 1 deletion tests/src/cmds/core/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,4 @@ def test_setup(self, bot):
# Invoke the command
other.setup(bot)

bot.add_cog.assert_called_once()
bot.add_cog.assert_called_once()

0 comments on commit ccef779

Please sign in to comment.