Shortmute introduced
very early in development, not production ready whatsoever yet
This commit is contained in:
parent
3b7e6ef548
commit
5621e6c737
4 changed files with 255 additions and 1 deletions
|
@ -35,7 +35,10 @@ Currently supports:
|
|||
|
||||
Also features user blacklists, both configurable per-server and globally.
|
||||
[^incomplete]:
|
||||
Podcast currently is non-functional. This notice will be removed once the Cog is completed.
|
||||
This cog currently is non-functional. This notice will be removed once the Cog is completed.
|
||||
|
||||
## Shortmutes **(WIP)**[^incomplete]
|
||||
Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature.
|
||||
|
||||
## Suggestions
|
||||
Allows users to submit suggestions to a specified channel. Highly cut-down modification of [SauriCog's Suggestions Cog](https://github.com/elijabesu/SauriCogs).
|
||||
|
|
5
shortmute/__init__.py
Normal file
5
shortmute/__init__.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from .shortmute import Shortmute
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Shortmute(bot))
|
9
shortmute/info.json
Normal file
9
shortmute/info.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"author" : ["SeaswimmerTheFsh"],
|
||||
"install_msg" : "Thank you for installing Shortmute!\nYou can find the source code of this cog here: https://github.com/SeaswimmerTheFsh/GalaxyCogs\nYou can find the original source code from sravan-cogs here: <https://github.com/sravan1946/sravan-cogs/tree/master/timeout>",
|
||||
"name" : "Shortmute",
|
||||
"short" : "Allows staff members to shortmute individuals for up to 30 minutes.",
|
||||
"description" : "Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature.",
|
||||
"tags" : ["moderation, mutes, timeouts"],
|
||||
"end_user_data_statement": "This cog stores no end user data."
|
||||
}
|
237
shortmute/shortmute.py
Normal file
237
shortmute/shortmute.py
Normal file
|
@ -0,0 +1,237 @@
|
|||
import contextlib
|
||||
import datetime
|
||||
from typing import List, Literal, Optional, Union
|
||||
|
||||
import discord
|
||||
import humanize
|
||||
from discord.http import Route
|
||||
from redbot.core import Config, commands, modlog
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.commands.converter import TimedeltaConverter
|
||||
|
||||
RequestType = Literal["discord_deleted_user", "owner", "user", "user_strict"]
|
||||
|
||||
class Shortmute(commands.Cog):
|
||||
"""Allows staff members to shortmute individuals for up to 30 minutes, using Discord's Timeouts feature.
|
||||
Maintained by SeaswimmerTheFsh.
|
||||
Original source code by sravan1946."""
|
||||
|
||||
def __init__(self, bot: Red) -> None:
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(self, identifier=2354731, force_registration=True)
|
||||
self.config.register_guild(
|
||||
dm = True
|
||||
)
|
||||
|
||||
def format_help_for_context(self, ctx: commands.Context) -> str:
|
||||
"""
|
||||
Thanks Sinbad!
|
||||
"""
|
||||
pre_processed = super().format_help_for_context(ctx)
|
||||
return f"{pre_processed}\n\nAuthors: {', '.join(self.__author__)}\nCog Version: {self.__version__}"
|
||||
|
||||
async def red_delete_data_for_user(
|
||||
self, *, requester: RequestType, user_id: int
|
||||
) -> None:
|
||||
# TODO: Replace this with the proper end user data removal handling.
|
||||
super().red_delete_data_for_user(requester=requester, user_id=user_id)
|
||||
|
||||
async def is_user_timed_out(self, member: discord.Member) -> bool:
|
||||
r = Route(
|
||||
"GET",
|
||||
"/guilds/{guild_id}/members/{user_id}",
|
||||
guild_id=member.guild.id,
|
||||
user_id=member.id,
|
||||
)
|
||||
try:
|
||||
data = await self.bot.http.request(r)
|
||||
except discord.NotFound:
|
||||
return False
|
||||
return data["communication_disabled_until"] is not None
|
||||
|
||||
async def pre_load(self):
|
||||
with contextlib.suppress(RuntimeError):
|
||||
await modlog.register_casetype(
|
||||
name="timeout",
|
||||
default_setting=True,
|
||||
image=":mute:",
|
||||
case_str="Timeout",
|
||||
)
|
||||
await modlog.register_casetype(
|
||||
name="untimeout",
|
||||
default_setting=True,
|
||||
image=":sound:",
|
||||
case_str="Untimeout",
|
||||
)
|
||||
|
||||
async def timeout_user(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
member: discord.Member,
|
||||
time: Optional[datetime.timedelta],
|
||||
reason: Optional[str] = None,
|
||||
) -> None:
|
||||
r = Route(
|
||||
"PATCH",
|
||||
"/guilds/{guild_id}/members/{user_id}",
|
||||
guild_id=ctx.guild.id,
|
||||
user_id=member.id,
|
||||
)
|
||||
|
||||
payload = {
|
||||
"communication_disabled_until": str(
|
||||
datetime.datetime.now(datetime.timezone.utc) + time
|
||||
)
|
||||
if time
|
||||
else None
|
||||
}
|
||||
|
||||
await ctx.bot.http.request(r, json=payload, reason=reason)
|
||||
await modlog.create_case(
|
||||
bot=ctx.bot,
|
||||
guild=ctx.guild,
|
||||
created_at=datetime.datetime.now(datetime.timezone.utc),
|
||||
action_type="timeout" if time else "untimeout",
|
||||
user=member,
|
||||
moderator=ctx.author,
|
||||
reason=reason,
|
||||
until=(datetime.datetime.now(datetime.timezone.utc) + time)
|
||||
if time
|
||||
else None,
|
||||
channel=ctx.channel,
|
||||
)
|
||||
if await self.config.guild(member.guild).dm():
|
||||
with contextlib.suppress(discord.HTTPException):
|
||||
message = "You have been"
|
||||
message += (
|
||||
f" timed out for {humanize.naturaldelta(time)}"
|
||||
if time
|
||||
else " untimedout"
|
||||
)
|
||||
message += f" in {ctx.guild.name}"
|
||||
message += f" for reason: {reason}" if reason else ""
|
||||
await member.send(message)
|
||||
|
||||
async def timeout_role(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
role: discord.Role,
|
||||
time: datetime.timedelta,
|
||||
reason: Optional[str] = None,
|
||||
) -> List[discord.Member]:
|
||||
failed = []
|
||||
members = list(role.members)
|
||||
for member in members:
|
||||
try:
|
||||
await self.timeout_user(ctx, member, time, reason)
|
||||
except discord.HTTPException:
|
||||
failed.append(member)
|
||||
return failed
|
||||
|
||||
@commands.command(aliases=["tt"])
|
||||
@commands.guild_only()
|
||||
@commands.cooldown(1, 1, commands.BucketType.user)
|
||||
@commands.mod_or_permissions(administrator=True)
|
||||
async def timeout(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
member_or_role: Union[discord.Member, discord.Role],
|
||||
time: TimedeltaConverter(
|
||||
minimum=datetime.timedelta(minutes=1),
|
||||
maximum=datetime.timedelta(days=28),
|
||||
default_unit="minutes",
|
||||
allowed_units=["minutes", "seconds", "hours", "days"],
|
||||
) = None,
|
||||
*,
|
||||
reason: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Timeout users.
|
||||
`<member_or_role>` is the username/rolename, ID or mention. If provided a role,
|
||||
everyone with that role will be timedout.
|
||||
`[time]` is the time to mute for. Time is any valid time length such as `45 minutes`
|
||||
or `3 days`. If nothing is provided the timeout will be 60 seconds default.
|
||||
`[reason]` is the reason for the timeout. Defaults to `None` if nothing is provided.
|
||||
Examples:
|
||||
`[p]timeout @member 5m talks too much`
|
||||
`[p]timeout @member 10m`
|
||||
"""
|
||||
if not time:
|
||||
time = datetime.timedelta(seconds=60)
|
||||
timestamp = datetime.datetime.now(datetime.timezone.utc) + time
|
||||
timestamp = int(datetime.datetime.timestamp(timestamp))
|
||||
if isinstance(member_or_role, discord.Member):
|
||||
check = await is_allowed_by_hierarchy(ctx.bot, ctx.author, member_or_role)
|
||||
if not check:
|
||||
return await ctx.send("You cannot timeout this user due to hierarchy.")
|
||||
if member_or_role.permissions_in(ctx.channel).administrator:
|
||||
return await ctx.send("You can't timeout an administrator.")
|
||||
await self.timeout_user(ctx, member_or_role, time, reason)
|
||||
return await ctx.send(
|
||||
f"{member_or_role.mention} has been timed out till <t:{timestamp}:f>."
|
||||
)
|
||||
if isinstance(member_or_role, discord.Role):
|
||||
await ctx.send(
|
||||
f"Timeing out {len(member_or_role.members)} members till <t:{timestamp}:f>."
|
||||
)
|
||||
failed = await self.timeout_role(ctx, member_or_role, time, reason)
|
||||
return await ctx.send(f"Failed to timeout {len(failed)} members.")
|
||||
|
||||
@commands.command(aliases=["utt"])
|
||||
@commands.guild_only()
|
||||
@commands.cooldown(1, 1, commands.BucketType.user)
|
||||
@commands.mod_or_permissions(administrator=True)
|
||||
async def untimeout(
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
member_or_role: Union[discord.Member, discord.Role],
|
||||
*,
|
||||
reason: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
Untimeout users.
|
||||
`<member_or_role>` is the username/rolename, ID or mention. If
|
||||
provided a role, everyone with that role will be untimed.
|
||||
`[reason]` is the reason for the untimeout. Defaults to `None`
|
||||
if nothing is provided.
|
||||
"""
|
||||
if isinstance(member_or_role, discord.Member):
|
||||
is_timedout = await self.is_user_timed_out(member_or_role)
|
||||
if not is_timedout:
|
||||
return await ctx.send("This user is not timed out.")
|
||||
await self.timeout_user(ctx, member_or_role, None, reason)
|
||||
return await ctx.send(f"Removed timeout from {member_or_role.mention}")
|
||||
if isinstance(member_or_role, discord.Role):
|
||||
await ctx.send(
|
||||
f"Removing timeout from {len(member_or_role.members)} members."
|
||||
)
|
||||
members = list(member_or_role.members)
|
||||
for member in members:
|
||||
if await self.is_user_timed_out(member):
|
||||
await self.timeout_user(ctx, member, None, reason)
|
||||
return await ctx.send(f"Removed timeout from {len(members)} members.")
|
||||
|
||||
@commands.group()
|
||||
@commands.guild_only()
|
||||
@commands.admin_or_permissions(manage_guild=True)
|
||||
async def timeoutset(self, ctx: commands.Context):
|
||||
"""Manage timeout settings."""
|
||||
|
||||
@timeoutset.command(name="dm")
|
||||
async def timeoutset_dm(self, ctx: commands.Context):
|
||||
"""Change whether to DM the user when they are timed out."""
|
||||
current = await self.config.guild(ctx.guild).dm()
|
||||
await self.config.guild(ctx.guild).dm.set(not current)
|
||||
w = "Will not" if current else "Will"
|
||||
await ctx.send(f"I {w} DM the user when they are timed out.")
|
||||
|
||||
|
||||
# https://github.com/phenom4n4n/phen-cogs/blob/8727d6ee74b40709c7eb9300713dc22b88a17915/roleutils/utils.py#L34
|
||||
async def is_allowed_by_hierarchy(
|
||||
bot: Red, user: discord.Member, member: discord.Member
|
||||
) -> bool:
|
||||
return (
|
||||
user.guild.owner_id == user.id
|
||||
or user.top_role > member.top_role
|
||||
or await bot.is_owner(user)
|
||||
)
|
Loading…
Reference in a new issue