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
1 change: 0 additions & 1 deletion enforce/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,3 @@ Set ToS or Privacy Policy link.<br/>

- Usage: `[p]tosconfig set <tos_or_privacy> <url>`
- Restricted to: `BOT_OWNER`

26 changes: 17 additions & 9 deletions enforce/enforce.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
SOFTWARE.
"""

from datetime import datetime, timedelta
import asyncio
from datetime import timedelta
from typing import Final

import discord
Expand All @@ -41,7 +42,7 @@
class Enforce(commands.Cog):
"""Requires users to accept ToS and privacy policy before using any bot commands."""

__version__: Final[str] = "0.0.1b"
__version__: Final[str] = "0.0.1c"
__author__: Final[str] = "MAX"
__docs__: Final[str] = "https://github.com/ltzmax/maxcogs/tree/master/enforce/README.md"

Expand All @@ -64,6 +65,7 @@ def __init__(self, bot: Red):
self.config.register_user(accepted_tos=False, accepted_at=None)
self.tos_prompt_antispam = {}
self.bot.add_check(self.tos_global_check)
self._cleanup_task = self.bot.loop.create_task(self._cleanup_antispam())

def format_help_for_context(self, ctx: commands.Context) -> str:
"""Thanks Sinbad!"""
Expand All @@ -72,13 +74,11 @@ def format_help_for_context(self, ctx: commands.Context) -> str:

async def red_delete_data_for_user(self, *, requester: str, user_id: int) -> None:
"""Delete stored data for a user."""
user = self.bot.get_user(user_id)
if user is None:
return
await self.config.user(user).clear()
await self.config.user_from_id(user_id).clear()

def cog_unload(self):
self.bot.remove_check(self.tos_global_check)
self._cleanup_task.cancel()

async def tos_global_check(self, ctx: commands.Context) -> bool:
if ctx.author.bot:
Expand All @@ -91,13 +91,15 @@ async def tos_global_check(self, ctx: commands.Context) -> bool:
return True

if await self.config.user(ctx.author).accepted_tos():
self.tos_prompt_antispam.pop(ctx.author.id, None)
return True

user_id = ctx.author.id
if user_id not in self.tos_prompt_antispam:
self.tos_prompt_antispam[user_id] = AntiSpam([(timedelta(seconds=60), 1)])

antispam = self.tos_prompt_antispam[user_id]
antispam.stamp()
if antispam.spammy:
log.info(f"Skipping ToS prompt for {user_id} - antispam triggered")
return False
Expand All @@ -108,6 +110,13 @@ async def tos_global_check(self, ctx: commands.Context) -> bool:
log.warning(f"Failed to send ToS prompt to {ctx.author.id}: {e}")
return False

async def _cleanup_antispam(self):
"""Periodically remove stale antispam entries."""
await self.bot.wait_until_ready()
while not self.bot.is_closed():
await asyncio.sleep(300)
self.tos_prompt_antispam.clear()

async def send_tos_prompt(self, ctx: commands.Context):
tos_url = await self.config.tos_url()
privacy_url = await self.config.privacy_url()
Expand Down Expand Up @@ -156,7 +165,6 @@ async def toggle_enforcement(self, ctx):
await ctx.send(f"ToS enforcement {status}.")

@tosconfig.command(name="set")
@commands.is_owner()
async def set_link(self, ctx: commands.Context, tos_or_privacy: str, *, url: str):
"""
Set ToS or Privacy Policy link.
Expand Down Expand Up @@ -222,13 +230,13 @@ async def reset_settings(self, ctx):
)
await view.wait()
if view.result:
await self.config.clear_all()
await self.config.clear_all_globals()
await ctx.send("ToS settings reset to default.")
else:
await ctx.send("Reset cancelled.")

@tosconfig.command(name="resetuser")
async def reset_user(self, ctx, user: discord.User = None):
async def reset_user(self, ctx, user: discord.User | None = None):
"""
Reset ToS acceptance for a user.

Expand Down
2 changes: 1 addition & 1 deletion enforce/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


class AcceptView(discord.ui.View):
"""Persistent accept view with timeout"""
"""Accept view with timeout"""

def __init__(self, config: Config, author: discord.User):
super().__init__(timeout=120)
Expand Down
Loading