mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
[i18n] Pass over modlog, permissions, reports, streams, trivia, warnings
Signed-off-by: Toby Harradine <tobyharradine@gmail.com>
This commit is contained in:
parent
fa692ccc0b
commit
443f2ca556
@ -386,7 +386,7 @@ class Mod(commands.Cog):
|
|||||||
async def ban(
|
async def ban(
|
||||||
self, ctx: commands.Context, user: discord.Member, days: str = None, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, days: str = None, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Ban a user from the current server.
|
"""Ban a user from this server.
|
||||||
|
|
||||||
Deletes `<days>` worth of messages.
|
Deletes `<days>` worth of messages.
|
||||||
|
|
||||||
@ -469,7 +469,7 @@ class Mod(commands.Cog):
|
|||||||
@commands.bot_has_permissions(ban_members=True)
|
@commands.bot_has_permissions(ban_members=True)
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
async def hackban(self, ctx: commands.Context, user_id: int, *, reason: str = None):
|
async def hackban(self, ctx: commands.Context, user_id: int, *, reason: str = None):
|
||||||
"""Pre-emptively ban a user from the current server.
|
"""Pre-emptively ban a user from this server.
|
||||||
|
|
||||||
A user ID needs to be provided in order to ban
|
A user ID needs to be provided in order to ban
|
||||||
using this command.
|
using this command.
|
||||||
@ -531,7 +531,7 @@ class Mod(commands.Cog):
|
|||||||
async def tempban(
|
async def tempban(
|
||||||
self, ctx: commands.Context, user: discord.Member, days: int = 1, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, days: int = 1, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Temporarily ban a user from the current server."""
|
"""Temporarily ban a user from this server."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
days_delta = timedelta(days=int(days))
|
days_delta = timedelta(days=int(days))
|
||||||
@ -672,7 +672,7 @@ class Mod(commands.Cog):
|
|||||||
@commands.bot_has_permissions(ban_members=True)
|
@commands.bot_has_permissions(ban_members=True)
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
async def unban(self, ctx: commands.Context, user_id: int, *, reason: str = None):
|
async def unban(self, ctx: commands.Context, user_id: int, *, reason: str = None):
|
||||||
"""Unban a user from the current server.
|
"""Unban a user from this server.
|
||||||
|
|
||||||
Requires specifying the target user's ID. To find this, you may either:
|
Requires specifying the target user's ID. To find this, you may either:
|
||||||
1. Copy it from the mod log case (if one was created), or
|
1. Copy it from the mod log case (if one was created), or
|
||||||
@ -1104,7 +1104,7 @@ class Mod(commands.Cog):
|
|||||||
async def unmute_channel(
|
async def unmute_channel(
|
||||||
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Unmute a user in the current channel."""
|
"""Unmute a user in this channel."""
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
@ -1137,7 +1137,7 @@ class Mod(commands.Cog):
|
|||||||
async def unmute_guild(
|
async def unmute_guild(
|
||||||
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Unmute a user in the current server."""
|
"""Unmute a user in this server."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
|
|
||||||
@ -1236,7 +1236,7 @@ class Mod(commands.Cog):
|
|||||||
@ignore.command(name="server", aliases=["guild"])
|
@ignore.command(name="server", aliases=["guild"])
|
||||||
@checks.admin_or_permissions(manage_guild=True)
|
@checks.admin_or_permissions(manage_guild=True)
|
||||||
async def ignore_guild(self, ctx: commands.Context):
|
async def ignore_guild(self, ctx: commands.Context):
|
||||||
"""Ignore commands in the current server."""
|
"""Ignore commands in this server."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if not await self.settings.guild(guild).ignored():
|
if not await self.settings.guild(guild).ignored():
|
||||||
await self.settings.guild(guild).ignored.set(True)
|
await self.settings.guild(guild).ignored.set(True)
|
||||||
@ -1270,7 +1270,7 @@ class Mod(commands.Cog):
|
|||||||
@unignore.command(name="server", aliases=["guild"])
|
@unignore.command(name="server", aliases=["guild"])
|
||||||
@checks.admin_or_permissions(manage_guild=True)
|
@checks.admin_or_permissions(manage_guild=True)
|
||||||
async def unignore_guild(self, ctx: commands.Context):
|
async def unignore_guild(self, ctx: commands.Context):
|
||||||
"""Remove the current server from the ignore list."""
|
"""Remove this server from the ignore list."""
|
||||||
guild = ctx.message.guild
|
guild = ctx.message.guild
|
||||||
if await self.settings.guild(guild).ignored():
|
if await self.settings.guild(guild).ignored():
|
||||||
await self.settings.guild(guild).ignored.set(False)
|
await self.settings.guild(guild).ignored.set(False)
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
from redbot.core import checks, modlog, commands
|
from redbot.core import checks, modlog, commands
|
||||||
@ -10,7 +12,7 @@ _ = Translator("ModLog", __file__)
|
|||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class ModLog(commands.Cog):
|
class ModLog(commands.Cog):
|
||||||
"""Log for mod actions"""
|
"""Manage log channels for moderation actions."""
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -19,23 +21,28 @@ class ModLog(commands.Cog):
|
|||||||
@commands.group()
|
@commands.group()
|
||||||
@checks.guildowner_or_permissions(administrator=True)
|
@checks.guildowner_or_permissions(administrator=True)
|
||||||
async def modlogset(self, ctx: commands.Context):
|
async def modlogset(self, ctx: commands.Context):
|
||||||
"""Settings for the mod log"""
|
"""Manage modlog settings."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@modlogset.command()
|
@modlogset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def modlog(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
async def modlog(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||||
"""Sets a channel as mod log
|
"""Set a channel as the modlog.
|
||||||
|
|
||||||
Leaving the channel parameter empty will deactivate it"""
|
Omit `<channel>` to disable the modlog.
|
||||||
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if channel:
|
if channel:
|
||||||
if channel.permissions_for(guild.me).send_messages:
|
if channel.permissions_for(guild.me).send_messages:
|
||||||
await modlog.set_modlog_channel(guild, channel)
|
await modlog.set_modlog_channel(guild, channel)
|
||||||
await ctx.send(_("Mod events will be sent to {}").format(channel.mention))
|
await ctx.send(
|
||||||
|
_("Mod events will be sent to {channel}").format(channel=channel.mention)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("I do not have permissions to send messages in {}!").format(channel.mention)
|
_("I do not have permissions to send messages in {channel}!").format(
|
||||||
|
channel=channel.mention
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@ -49,39 +56,36 @@ class ModLog(commands.Cog):
|
|||||||
@modlogset.command(name="cases")
|
@modlogset.command(name="cases")
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def set_cases(self, ctx: commands.Context, action: str = None):
|
async def set_cases(self, ctx: commands.Context, action: str = None):
|
||||||
"""Enables or disables case creation for each type of mod action"""
|
"""Enable or disable case creation for a mod action."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
if action is None: # No args given
|
if action is None: # No args given
|
||||||
casetypes = await modlog.get_all_casetypes(guild)
|
casetypes = await modlog.get_all_casetypes(guild)
|
||||||
await ctx.send_help()
|
await ctx.send_help()
|
||||||
title = _("Current settings:")
|
lines = []
|
||||||
msg = ""
|
|
||||||
for ct in casetypes:
|
for ct in casetypes:
|
||||||
enabled = await ct.is_enabled()
|
enabled = "enabled" if await ct.is_enabled() else "disabled"
|
||||||
value = "enabled" if enabled else "disabled"
|
lines.append(f"{ct.name} : {enabled}")
|
||||||
msg += "%s : %s\n" % (ct.name, value)
|
|
||||||
|
|
||||||
msg = title + "\n" + box(msg)
|
await ctx.send(_("Current settings:\n") + box("\n".join(lines)))
|
||||||
await ctx.send(msg)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
casetype = await modlog.get_casetype(action, guild)
|
casetype = await modlog.get_casetype(action, guild)
|
||||||
if not casetype:
|
if not casetype:
|
||||||
await ctx.send(_("That action is not registered"))
|
await ctx.send(_("That action is not registered"))
|
||||||
else:
|
else:
|
||||||
|
|
||||||
enabled = await casetype.is_enabled()
|
enabled = await casetype.is_enabled()
|
||||||
await casetype.set_enabled(True if not enabled else False)
|
await casetype.set_enabled(not enabled)
|
||||||
|
await ctx.send(
|
||||||
msg = _("Case creation for {} actions is now {}.").format(
|
_("Case creation for {action_name} actions is now {enabled}.").format(
|
||||||
action, "enabled" if not enabled else "disabled"
|
action_name=action, enabled="enabled" if not enabled else "disabled"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
await ctx.send(msg)
|
|
||||||
|
|
||||||
@modlogset.command()
|
@modlogset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def resetcases(self, ctx: commands.Context):
|
async def resetcases(self, ctx: commands.Context):
|
||||||
"""Resets modlog's cases"""
|
"""Reset all modlog cases in this server."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
await modlog.reset_cases(guild)
|
await modlog.reset_cases(guild)
|
||||||
await ctx.send(_("Cases have been reset."))
|
await ctx.send(_("Cases have been reset."))
|
||||||
@ -89,7 +93,7 @@ class ModLog(commands.Cog):
|
|||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def case(self, ctx: commands.Context, number: int):
|
async def case(self, ctx: commands.Context, number: int):
|
||||||
"""Shows the specified case"""
|
"""Show the specified case."""
|
||||||
try:
|
try:
|
||||||
case = await modlog.get_case(number, ctx.guild, self.bot)
|
case = await modlog.get_case(number, ctx.guild, self.bot)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
@ -101,24 +105,21 @@ class ModLog(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
await ctx.send(await case.message_content(embed=False))
|
await ctx.send(await case.message_content(embed=False))
|
||||||
|
|
||||||
@commands.command(usage="[case] <reason>")
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def reason(self, ctx: commands.Context, *, reason: str):
|
async def reason(self, ctx: commands.Context, case: Optional[int], *, reason: str):
|
||||||
"""Lets you specify a reason for mod-log's cases
|
"""Specify a reason for a modlog case.
|
||||||
|
|
||||||
Please note that you can only edit cases you are
|
Please note that you can only edit cases you are
|
||||||
the owner of unless you are a mod/admin or the server owner.
|
the owner of unless you are a mod, admin or server owner.
|
||||||
|
|
||||||
If no number is specified, the latest case will be used."""
|
If no case number is specified, the latest case will be used.
|
||||||
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
potential_case = reason.split()[0]
|
if case is None:
|
||||||
if potential_case.isdigit():
|
# get the latest case
|
||||||
case = int(potential_case)
|
case = int(await modlog.get_next_case_number(guild)) - 1
|
||||||
reason = reason.replace(potential_case, "")
|
|
||||||
else:
|
|
||||||
case = str(int(await modlog.get_next_case_number(guild)) - 1)
|
|
||||||
# latest case
|
|
||||||
try:
|
try:
|
||||||
case_before = await modlog.get_case(case, guild, self.bot)
|
case_before = await modlog.get_case(case, guild, self.bot)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
from typing import NamedTuple, Union, Optional
|
from typing import NamedTuple, Union, Optional, cast, Type
|
||||||
|
|
||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
|
from redbot.core.i18n import Translator
|
||||||
|
|
||||||
|
_ = Translator("PermissionsConverters", __file__)
|
||||||
|
|
||||||
|
|
||||||
class CogOrCommand(NamedTuple):
|
class CogOrCommand(NamedTuple):
|
||||||
@ -18,31 +22,24 @@ class CogOrCommand(NamedTuple):
|
|||||||
return cls(type="COMMAND", name=cmd.qualified_name, obj=cmd)
|
return cls(type="COMMAND", name=cmd.qualified_name, obj=cmd)
|
||||||
|
|
||||||
raise commands.BadArgument(
|
raise commands.BadArgument(
|
||||||
'Cog or command "{arg}" not found. Please note that this is case sensitive.'
|
_(
|
||||||
"".format(arg=arg)
|
'Cog or command "{name}" not found. Please note that this is case sensitive.'
|
||||||
|
).format(name=arg)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RuleType:
|
def RuleType(arg: str) -> bool:
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
|
||||||
@classmethod
|
|
||||||
async def convert(cls, ctx: commands.Context, arg: str) -> bool:
|
|
||||||
if arg.lower() in ("allow", "whitelist", "allowed"):
|
if arg.lower() in ("allow", "whitelist", "allowed"):
|
||||||
return True
|
return True
|
||||||
if arg.lower() in ("deny", "blacklist", "denied"):
|
if arg.lower() in ("deny", "blacklist", "denied"):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
raise commands.BadArgument(
|
raise commands.BadArgument(
|
||||||
'"{arg}" is not a valid rule. Valid rules are "allow" or "deny"'.format(arg=arg)
|
_('"{arg}" is not a valid rule. Valid rules are "allow" or "deny"').format(arg=arg)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ClearableRuleType:
|
def ClearableRuleType(arg: str) -> Optional[bool]:
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
|
||||||
@classmethod
|
|
||||||
async def convert(cls, ctx: commands.Context, arg: str) -> Optional[bool]:
|
|
||||||
if arg.lower() in ("allow", "whitelist", "allowed"):
|
if arg.lower() in ("allow", "whitelist", "allowed"):
|
||||||
return True
|
return True
|
||||||
if arg.lower() in ("deny", "blacklist", "denied"):
|
if arg.lower() in ("deny", "blacklist", "denied"):
|
||||||
@ -51,6 +48,8 @@ class ClearableRuleType:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
raise commands.BadArgument(
|
raise commands.BadArgument(
|
||||||
|
_(
|
||||||
'"{arg}" is not a valid rule. Valid rules are "allow" or "deny", or "clear" to '
|
'"{arg}" is not a valid rule. Valid rules are "allow" or "deny", or "clear" to '
|
||||||
"remove the rule".format(arg=arg)
|
"remove the rule"
|
||||||
|
).format(arg=arg)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import asyncio
|
|||||||
import io
|
import io
|
||||||
import textwrap
|
import textwrap
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from typing import Union, Optional, Dict, List, Tuple, Any, Iterator, ItemsView
|
from typing import Union, Optional, Dict, List, Tuple, Any, Iterator, ItemsView, cast
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import yaml
|
import yaml
|
||||||
@ -287,9 +287,11 @@ class Permissions(commands.Cog):
|
|||||||
`<who_or_what>` is the user, channel, role or server the rule
|
`<who_or_what>` is the user, channel, role or server the rule
|
||||||
is for.
|
is for.
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
|
||||||
await self._add_rule(
|
await self._add_rule(
|
||||||
rule=allow_or_deny, cog_or_cmd=cog_or_command, model_id=who_or_what.id, guild_id=0
|
rule=cast(bool, allow_or_deny),
|
||||||
|
cog_or_cmd=cog_or_command,
|
||||||
|
model_id=who_or_what.id,
|
||||||
|
guild_id=0,
|
||||||
)
|
)
|
||||||
await ctx.send(_("Rule added."))
|
await ctx.send(_("Rule added."))
|
||||||
|
|
||||||
@ -312,9 +314,8 @@ class Permissions(commands.Cog):
|
|||||||
|
|
||||||
`<who_or_what>` is the user, channel or role the rule is for.
|
`<who_or_what>` is the user, channel or role the rule is for.
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
|
||||||
await self._add_rule(
|
await self._add_rule(
|
||||||
rule=allow_or_deny,
|
rule=cast(bool, allow_or_deny),
|
||||||
cog_or_cmd=cog_or_command,
|
cog_or_cmd=cog_or_command,
|
||||||
model_id=who_or_what.id,
|
model_id=who_or_what.id,
|
||||||
guild_id=ctx.guild.id,
|
guild_id=ctx.guild.id,
|
||||||
@ -381,9 +382,10 @@ class Permissions(commands.Cog):
|
|||||||
`<cog_or_command>` is the cog or command to set the default
|
`<cog_or_command>` is the cog or command to set the default
|
||||||
rule for. This is case sensitive.
|
rule for. This is case sensitive.
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
|
||||||
await self._set_default_rule(
|
await self._set_default_rule(
|
||||||
rule=allow_or_deny, cog_or_cmd=cog_or_command, guild_id=ctx.guild.id
|
rule=cast(Optional[bool], allow_or_deny),
|
||||||
|
cog_or_cmd=cog_or_command,
|
||||||
|
guild_id=ctx.guild.id,
|
||||||
)
|
)
|
||||||
await ctx.send(_("Default set."))
|
await ctx.send(_("Default set."))
|
||||||
|
|
||||||
@ -403,9 +405,8 @@ class Permissions(commands.Cog):
|
|||||||
`<cog_or_command>` is the cog or command to set the default
|
`<cog_or_command>` is the cog or command to set the default
|
||||||
rule for. This is case sensitive.
|
rule for. This is case sensitive.
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
|
||||||
await self._set_default_rule(
|
await self._set_default_rule(
|
||||||
rule=allow_or_deny, cog_or_cmd=cog_or_command, guild_id=GLOBAL
|
rule=cast(Optional[bool], allow_or_deny), cog_or_cmd=cog_or_command, guild_id=GLOBAL
|
||||||
)
|
)
|
||||||
await ctx.send(_("Default set."))
|
await ctx.send(_("Default set."))
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Union
|
from typing import Union, List
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from copy import copy
|
from copy import copy
|
||||||
import contextlib
|
import contextlib
|
||||||
@ -60,23 +60,20 @@ class Reports(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@commands.group(name="reportset")
|
@commands.group(name="reportset")
|
||||||
async def reportset(self, ctx: commands.Context):
|
async def reportset(self, ctx: commands.Context):
|
||||||
"""
|
"""Manage Reports."""
|
||||||
Settings for the report system.
|
|
||||||
"""
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@checks.admin_or_permissions(manage_guild=True)
|
@checks.admin_or_permissions(manage_guild=True)
|
||||||
@reportset.command(name="output")
|
@reportset.command(name="output")
|
||||||
async def setoutput(self, ctx: commands.Context, channel: discord.TextChannel):
|
async def reportset_output(self, ctx: commands.Context, channel: discord.TextChannel):
|
||||||
"""Set the channel where reports will show up"""
|
"""Set the channel where reports will be sent."""
|
||||||
await self.config.guild(ctx.guild).output_channel.set(channel.id)
|
await self.config.guild(ctx.guild).output_channel.set(channel.id)
|
||||||
await ctx.send(_("The report channel has been set."))
|
await ctx.send(_("The report channel has been set."))
|
||||||
|
|
||||||
@checks.admin_or_permissions(manage_guild=True)
|
@checks.admin_or_permissions(manage_guild=True)
|
||||||
@reportset.command(name="toggle", aliases=["toggleactive"])
|
@reportset.command(name="toggle", aliases=["toggleactive"])
|
||||||
async def report_toggle(self, ctx: commands.Context):
|
async def reportset_toggle(self, ctx: commands.Context):
|
||||||
"""Enables or Disables reporting for the server"""
|
"""Enable or Disable reporting for this server."""
|
||||||
|
|
||||||
active = await self.config.guild(ctx.guild).active()
|
active = await self.config.guild(ctx.guild).active()
|
||||||
active = not active
|
active = not active
|
||||||
await self.config.guild(ctx.guild).active.set(active)
|
await self.config.guild(ctx.guild).active.set(active)
|
||||||
@ -168,7 +165,7 @@ class Reports(commands.Cog):
|
|||||||
if channel is None:
|
if channel is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
files = await Tunnel.files_from_attatch(msg)
|
files: List[discord.File] = await Tunnel.files_from_attatch(msg)
|
||||||
|
|
||||||
ticket_number = await self.config.guild(guild).next_ticket()
|
ticket_number = await self.config.guild(guild).next_ticket()
|
||||||
await self.config.guild(guild).next_ticket.set(ticket_number + 1)
|
await self.config.guild(guild).next_ticket.set(ticket_number + 1)
|
||||||
@ -204,11 +201,10 @@ class Reports(commands.Cog):
|
|||||||
|
|
||||||
@commands.group(name="report", invoke_without_command=True)
|
@commands.group(name="report", invoke_without_command=True)
|
||||||
async def report(self, ctx: commands.Context, *, _report: str = ""):
|
async def report(self, ctx: commands.Context, *, _report: str = ""):
|
||||||
"""
|
"""Send a report.
|
||||||
Send a report.
|
|
||||||
|
|
||||||
Use without arguments for interactive reporting, or do
|
Use without arguments for interactive reporting, or do
|
||||||
[p]report <text> to use it non-interactively.
|
`[p]report <text>` to use it non-interactively.
|
||||||
"""
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
@ -323,8 +319,7 @@ class Reports(commands.Cog):
|
|||||||
@checks.mod_or_permissions(manage_members=True)
|
@checks.mod_or_permissions(manage_members=True)
|
||||||
@report.command(name="interact")
|
@report.command(name="interact")
|
||||||
async def response(self, ctx, ticket_number: int):
|
async def response(self, ctx, ticket_number: int):
|
||||||
"""
|
"""Open a message tunnel.
|
||||||
Open a message tunnel.
|
|
||||||
|
|
||||||
This tunnel will forward things you say in this channel
|
This tunnel will forward things you say in this channel
|
||||||
to the ticket opener's direct messages.
|
to the ticket opener's direct messages.
|
||||||
@ -354,8 +349,7 @@ class Reports(commands.Cog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
big_topic = _(
|
big_topic = _(
|
||||||
"{who} opened a 2-way communication "
|
" Anything you say or upload here "
|
||||||
"about ticket number {ticketnum}. Anything you say or upload here "
|
|
||||||
"(8MB file size limitation on uploads) "
|
"(8MB file size limitation on uploads) "
|
||||||
"will be forwarded to them until the communication is closed.\n"
|
"will be forwarded to them until the communication is closed.\n"
|
||||||
"You can close a communication at any point by reacting with "
|
"You can close a communication at any point by reacting with "
|
||||||
@ -364,8 +358,12 @@ class Reports(commands.Cog):
|
|||||||
"\N{WHITE HEAVY CHECK MARK}.\n"
|
"\N{WHITE HEAVY CHECK MARK}.\n"
|
||||||
"Tunnels are not persistent across bot restarts."
|
"Tunnels are not persistent across bot restarts."
|
||||||
)
|
)
|
||||||
topic = big_topic.format(
|
topic = (
|
||||||
ticketnum=ticket_number, who=_("A moderator in `{guild.name}` has").format(guild=guild)
|
_(
|
||||||
|
"A moderator in the server `{guild.name}` has opened a 2-way communication about "
|
||||||
|
"ticket number {ticket_number}."
|
||||||
|
).format(guild=guild, ticket_number=ticket_number)
|
||||||
|
+ big_topic
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
m = await tun.communicate(message=ctx.message, topic=topic, skip_message_content=True)
|
m = await tun.communicate(message=ctx.message, topic=topic, skip_message_content=True)
|
||||||
@ -373,4 +371,9 @@ class Reports(commands.Cog):
|
|||||||
await ctx.send(_("That user has DMs disabled."))
|
await ctx.send(_("That user has DMs disabled."))
|
||||||
else:
|
else:
|
||||||
self.tunnel_store[(guild, ticket_number)] = {"tun": tun, "msgs": m}
|
self.tunnel_store[(guild, ticket_number)] = {"tun": tun, "msgs": m}
|
||||||
await ctx.send(big_topic.format(who=_("You have"), ticketnum=ticket_number))
|
await ctx.send(
|
||||||
|
_(
|
||||||
|
"You have opened a 2-way communication about ticket number {ticket_number}."
|
||||||
|
).format(ticket_number=ticket_number)
|
||||||
|
+ big_topic
|
||||||
|
)
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
import contextlib
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from redbot.core import Config, checks, commands
|
from redbot.core import Config, checks, commands
|
||||||
from redbot.core.utils.chat_formatting import pagify
|
from redbot.core.utils.chat_formatting import pagify
|
||||||
@ -22,7 +24,7 @@ from .errors import (
|
|||||||
StreamsError,
|
StreamsError,
|
||||||
InvalidTwitchCredentials,
|
InvalidTwitchCredentials,
|
||||||
)
|
)
|
||||||
from . import streamtypes as StreamClasses
|
from . import streamtypes as _streamtypes
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import asyncio
|
import asyncio
|
||||||
import re
|
import re
|
||||||
@ -76,14 +78,14 @@ class Streams(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def twitch(self, ctx: commands.Context, channel_name: str):
|
async def twitch(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Checks if a Twitch channel is live"""
|
"""Check if a Twitch channel is live."""
|
||||||
token = await self.db.tokens.get_raw(TwitchStream.__name__, default=None)
|
token = await self.db.tokens.get_raw(TwitchStream.__name__, default=None)
|
||||||
stream = TwitchStream(name=channel_name, token=token)
|
stream = TwitchStream(name=channel_name, token=token)
|
||||||
await self.check_online(ctx, stream)
|
await self.check_online(ctx, stream)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def youtube(self, ctx: commands.Context, channel_id_or_name: str):
|
async def youtube(self, ctx: commands.Context, channel_id_or_name: str):
|
||||||
"""Checks if a Youtube channel is live"""
|
"""Check if a YouTube channel is live."""
|
||||||
apikey = await self.db.tokens.get_raw(YoutubeStream.__name__, default=None)
|
apikey = await self.db.tokens.get_raw(YoutubeStream.__name__, default=None)
|
||||||
is_name = self.check_name_or_id(channel_id_or_name)
|
is_name = self.check_name_or_id(channel_id_or_name)
|
||||||
if is_name:
|
if is_name:
|
||||||
@ -94,23 +96,24 @@ class Streams(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def hitbox(self, ctx: commands.Context, channel_name: str):
|
async def hitbox(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Checks if a Hitbox channel is live"""
|
"""Check if a Hitbox channel is live."""
|
||||||
stream = HitboxStream(name=channel_name)
|
stream = HitboxStream(name=channel_name)
|
||||||
await self.check_online(ctx, stream)
|
await self.check_online(ctx, stream)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def mixer(self, ctx: commands.Context, channel_name: str):
|
async def mixer(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Checks if a Mixer channel is live"""
|
"""Check if a Mixer channel is live."""
|
||||||
stream = MixerStream(name=channel_name)
|
stream = MixerStream(name=channel_name)
|
||||||
await self.check_online(ctx, stream)
|
await self.check_online(ctx, stream)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def picarto(self, ctx: commands.Context, channel_name: str):
|
async def picarto(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Checks if a Picarto channel is live"""
|
"""Check if a Picarto channel is live."""
|
||||||
stream = PicartoStream(name=channel_name)
|
stream = PicartoStream(name=channel_name)
|
||||||
await self.check_online(ctx, stream)
|
await self.check_online(ctx, stream)
|
||||||
|
|
||||||
async def check_online(self, ctx: commands.Context, stream):
|
@staticmethod
|
||||||
|
async def check_online(ctx: commands.Context, stream):
|
||||||
try:
|
try:
|
||||||
embed = await stream.is_online()
|
embed = await stream.is_online()
|
||||||
except OfflineStream:
|
except OfflineStream:
|
||||||
@ -119,15 +122,17 @@ class Streams(commands.Cog):
|
|||||||
await ctx.send(_("That channel doesn't seem to exist."))
|
await ctx.send(_("That channel doesn't seem to exist."))
|
||||||
except InvalidTwitchCredentials:
|
except InvalidTwitchCredentials:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("The twitch token is either invalid or has not been set. See `{}`.").format(
|
_(
|
||||||
"{}streamset twitchtoken".format(ctx.prefix)
|
"The Twitch token is either invalid or has not been set. See "
|
||||||
)
|
"`{prefix}streamset twitchtoken`."
|
||||||
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
except InvalidYoutubeCredentials:
|
except InvalidYoutubeCredentials:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("Your Youtube API key is either invalid or has not been set. See {}.").format(
|
_(
|
||||||
"`{}streamset youtubekey`".format(ctx.prefix)
|
"The YouTube API key is either invalid or has not been set. See "
|
||||||
)
|
"`{prefix}streamset youtubekey`."
|
||||||
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
except APIError:
|
except APIError:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
@ -140,11 +145,12 @@ class Streams(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.mod()
|
@checks.mod()
|
||||||
async def streamalert(self, ctx: commands.Context):
|
async def streamalert(self, ctx: commands.Context):
|
||||||
|
"""Manage automated stream alerts."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@streamalert.group(name="twitch", invoke_without_command=True)
|
@streamalert.group(name="twitch", invoke_without_command=True)
|
||||||
async def _twitch(self, ctx: commands.Context, channel_name: str = None):
|
async def _twitch(self, ctx: commands.Context, channel_name: str = None):
|
||||||
"""Twitch stream alerts"""
|
"""Manage Twitch stream notifications."""
|
||||||
if channel_name is not None:
|
if channel_name is not None:
|
||||||
await ctx.invoke(self.twitch_alert_channel, channel_name)
|
await ctx.invoke(self.twitch_alert_channel, channel_name)
|
||||||
else:
|
else:
|
||||||
@ -152,7 +158,7 @@ class Streams(commands.Cog):
|
|||||||
|
|
||||||
@_twitch.command(name="channel")
|
@_twitch.command(name="channel")
|
||||||
async def twitch_alert_channel(self, ctx: commands.Context, channel_name: str):
|
async def twitch_alert_channel(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Sets a Twitch alert notification in the channel"""
|
"""Toggle alerts in this channel for a Twitch stream."""
|
||||||
if re.fullmatch(r"<#\d+>", channel_name):
|
if re.fullmatch(r"<#\d+>", channel_name):
|
||||||
await ctx.send("Please supply the name of a *Twitch* channel, not a Discord channel.")
|
await ctx.send("Please supply the name of a *Twitch* channel, not a Discord channel.")
|
||||||
return
|
return
|
||||||
@ -160,33 +166,39 @@ class Streams(commands.Cog):
|
|||||||
|
|
||||||
@_twitch.command(name="community")
|
@_twitch.command(name="community")
|
||||||
async def twitch_alert_community(self, ctx: commands.Context, community: str):
|
async def twitch_alert_community(self, ctx: commands.Context, community: str):
|
||||||
"""Sets an alert notification in the channel for the specified twitch community."""
|
"""Toggle alerts in this channel for a Twitch community."""
|
||||||
await self.community_alert(ctx, TwitchCommunity, community.lower())
|
await self.community_alert(ctx, TwitchCommunity, community.lower())
|
||||||
|
|
||||||
@streamalert.command(name="youtube")
|
@streamalert.command(name="youtube")
|
||||||
async def youtube_alert(self, ctx: commands.Context, channel_name_or_id: str):
|
async def youtube_alert(self, ctx: commands.Context, channel_name_or_id: str):
|
||||||
"""Sets a Youtube alert notification in the channel"""
|
"""Toggle alerts in this channel for a YouTube stream."""
|
||||||
await self.stream_alert(ctx, YoutubeStream, channel_name_or_id)
|
await self.stream_alert(ctx, YoutubeStream, channel_name_or_id)
|
||||||
|
|
||||||
@streamalert.command(name="hitbox")
|
@streamalert.command(name="hitbox")
|
||||||
async def hitbox_alert(self, ctx: commands.Context, channel_name: str):
|
async def hitbox_alert(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Sets a Hitbox alert notification in the channel"""
|
"""Toggle alerts in this channel for a Hitbox stream."""
|
||||||
await self.stream_alert(ctx, HitboxStream, channel_name)
|
await self.stream_alert(ctx, HitboxStream, channel_name)
|
||||||
|
|
||||||
@streamalert.command(name="mixer")
|
@streamalert.command(name="mixer")
|
||||||
async def mixer_alert(self, ctx: commands.Context, channel_name: str):
|
async def mixer_alert(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Sets a Mixer alert notification in the channel"""
|
"""Toggle alerts in this channel for a Mixer stream."""
|
||||||
await self.stream_alert(ctx, MixerStream, channel_name)
|
await self.stream_alert(ctx, MixerStream, channel_name)
|
||||||
|
|
||||||
@streamalert.command(name="picarto")
|
@streamalert.command(name="picarto")
|
||||||
async def picarto_alert(self, ctx: commands.Context, channel_name: str):
|
async def picarto_alert(self, ctx: commands.Context, channel_name: str):
|
||||||
"""Sets a Picarto alert notification in the channel"""
|
"""Toggle alerts in this channel for a Picarto stream."""
|
||||||
await self.stream_alert(ctx, PicartoStream, channel_name)
|
await self.stream_alert(ctx, PicartoStream, channel_name)
|
||||||
|
|
||||||
@streamalert.command(name="stop")
|
@streamalert.command(name="stop", usage="[disable_all=No]")
|
||||||
async def streamalert_stop(self, ctx: commands.Context, _all: bool = False):
|
async def streamalert_stop(self, ctx: commands.Context, _all: bool = False):
|
||||||
"""Stops all stream notifications in the channel
|
"""Disable all stream alerts in this channel or server.
|
||||||
Adding 'yes' will disable all notifications in the server"""
|
|
||||||
|
`[p]streamalert stop` will disable this channel's stream
|
||||||
|
alerts.
|
||||||
|
|
||||||
|
Do `[p]streamalert stop yes` to disable all stream alerts in
|
||||||
|
this server.
|
||||||
|
"""
|
||||||
streams = self.streams.copy()
|
streams = self.streams.copy()
|
||||||
local_channel_ids = [c.id for c in ctx.guild.channels]
|
local_channel_ids = [c.id for c in ctx.guild.channels]
|
||||||
to_remove = []
|
to_remove = []
|
||||||
@ -208,9 +220,10 @@ class Streams(commands.Cog):
|
|||||||
self.streams = streams
|
self.streams = streams
|
||||||
await self.save_streams()
|
await self.save_streams()
|
||||||
|
|
||||||
msg = _("All the alerts in the {} have been disabled.").format(
|
if _all:
|
||||||
"server" if _all else "channel"
|
msg = _("All the stream alerts in this server have been disabled.")
|
||||||
)
|
else:
|
||||||
|
msg = _("All the stream alerts in this channel have been disabled.")
|
||||||
|
|
||||||
await ctx.send(msg)
|
await ctx.send(msg)
|
||||||
|
|
||||||
@ -250,16 +263,18 @@ class Streams(commands.Cog):
|
|||||||
exists = await self.check_exists(stream)
|
exists = await self.check_exists(stream)
|
||||||
except InvalidTwitchCredentials:
|
except InvalidTwitchCredentials:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("Your twitch token is either invalid or has not been set. See {}.").format(
|
_(
|
||||||
"`{}streamset twitchtoken`".format(ctx.prefix)
|
"The Twitch token is either invalid or has not been set. See "
|
||||||
)
|
"`{prefix}streamset twitchtoken`."
|
||||||
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
except InvalidYoutubeCredentials:
|
except InvalidYoutubeCredentials:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"Your Youtube API key is either invalid or has not been set. See {}."
|
"The YouTube API key is either invalid or has not been set. See "
|
||||||
).format("`{}streamset youtubekey`".format(ctx.prefix))
|
"`{prefix}streamset youtubekey`."
|
||||||
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
except APIError:
|
except APIError:
|
||||||
@ -283,9 +298,10 @@ class Streams(commands.Cog):
|
|||||||
await community.get_community_streams()
|
await community.get_community_streams()
|
||||||
except InvalidTwitchCredentials:
|
except InvalidTwitchCredentials:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("The twitch token is either invalid or has not been set. See {}.").format(
|
_(
|
||||||
"`{}streamset twitchtoken`".format(ctx.prefix)
|
"The Twitch token is either invalid or has not been set. See "
|
||||||
)
|
"`{prefix}streamset twitchtoken`."
|
||||||
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
except CommunityNotFound:
|
except CommunityNotFound:
|
||||||
@ -309,7 +325,8 @@ class Streams(commands.Cog):
|
|||||||
@streamset.command()
|
@streamset.command()
|
||||||
@checks.is_owner()
|
@checks.is_owner()
|
||||||
async def twitchtoken(self, ctx: commands.Context, token: str):
|
async def twitchtoken(self, ctx: commands.Context, token: str):
|
||||||
"""Set the Client ID for twitch.
|
"""Set the Client ID for Twitch.
|
||||||
|
|
||||||
To do this, follow these steps:
|
To do this, follow these steps:
|
||||||
1. Go to this page: https://dev.twitch.tv/dashboard/apps.
|
1. Go to this page: https://dev.twitch.tv/dashboard/apps.
|
||||||
2. Click *Register Your Application*
|
2. Click *Register Your Application*
|
||||||
@ -325,64 +342,59 @@ class Streams(commands.Cog):
|
|||||||
@streamset.command()
|
@streamset.command()
|
||||||
@checks.is_owner()
|
@checks.is_owner()
|
||||||
async def youtubekey(self, ctx: commands.Context, key: str):
|
async def youtubekey(self, ctx: commands.Context, key: str):
|
||||||
"""Sets the API key for Youtube.
|
"""Set the API key for YouTube.
|
||||||
|
|
||||||
To get one, do the following:
|
To get one, do the following:
|
||||||
1. Create a project (see https://support.google.com/googleapi/answer/6251787 for details)
|
1. Create a project (see https://support.google.com/googleapi/answer/6251787 for details)
|
||||||
2. Enable the Youtube Data API v3 (see https://support.google.com/googleapi/answer/6158841 for instructions)
|
2. Enable the YouTube Data API v3 (see https://support.google.com/googleapi/answer/6158841
|
||||||
3. Set up your API key (see https://support.google.com/googleapi/answer/6158862 for instructions)
|
for instructions)
|
||||||
|
3. Set up your API key (see https://support.google.com/googleapi/answer/6158862 for
|
||||||
|
instructions)
|
||||||
4. Copy your API key and paste it into this command. Done!
|
4. Copy your API key and paste it into this command. Done!
|
||||||
"""
|
"""
|
||||||
await self.db.tokens.set_raw("YoutubeStream", value=key)
|
await self.db.tokens.set_raw("YoutubeStream", value=key)
|
||||||
await ctx.send(_("Youtube key set."))
|
await ctx.send(_("YouTube key set."))
|
||||||
|
|
||||||
@streamset.group()
|
@streamset.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def mention(self, ctx: commands.Context):
|
async def mention(self, ctx: commands.Context):
|
||||||
"""Sets mentions for alerts."""
|
"""Manage mention settings for stream alerts."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@mention.command(aliases=["everyone"])
|
@mention.command(aliases=["everyone"])
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def all(self, ctx: commands.Context):
|
async def all(self, ctx: commands.Context):
|
||||||
"""Toggles everyone mention"""
|
"""Toggle the `@\u200beveryone` mention."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
current_setting = await self.db.guild(guild).mention_everyone()
|
current_setting = await self.db.guild(guild).mention_everyone()
|
||||||
if current_setting:
|
if current_setting:
|
||||||
await self.db.guild(guild).mention_everyone.set(False)
|
await self.db.guild(guild).mention_everyone.set(False)
|
||||||
await ctx.send(
|
await ctx.send(_("`@\u200beveryone` will no longer be mentioned for stream alerts."))
|
||||||
_("{} will no longer be mentioned when a stream or community is live").format(
|
|
||||||
"@\u200beveryone"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
await self.db.guild(guild).mention_everyone.set(True)
|
await self.db.guild(guild).mention_everyone.set(True)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("When a stream or community " "is live, {} will be mentioned.").format(
|
_("When a stream or community is live, `@\u200beveryone` will be mentioned.")
|
||||||
"@\u200beveryone"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@mention.command(aliases=["here"])
|
@mention.command(aliases=["here"])
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def online(self, ctx: commands.Context):
|
async def online(self, ctx: commands.Context):
|
||||||
"""Toggles here mention"""
|
"""Toggle the `@\u200bhere` mention."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
current_setting = await self.db.guild(guild).mention_here()
|
current_setting = await self.db.guild(guild).mention_here()
|
||||||
if current_setting:
|
if current_setting:
|
||||||
await self.db.guild(guild).mention_here.set(False)
|
await self.db.guild(guild).mention_here.set(False)
|
||||||
await ctx.send(_("{} will no longer be mentioned for an alert.").format("@\u200bhere"))
|
await ctx.send(_("`@\u200bhere` will no longer be mentioned for stream alerts."))
|
||||||
else:
|
else:
|
||||||
await self.db.guild(guild).mention_here.set(True)
|
await self.db.guild(guild).mention_here.set(True)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("When a stream or community " "is live, {} will be mentioned.").format(
|
_("When a stream or community is live, `@\u200bhere` will be mentioned.")
|
||||||
"@\u200bhere"
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@mention.command()
|
@mention.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def role(self, ctx: commands.Context, *, role: discord.Role):
|
async def role(self, ctx: commands.Context, *, role: discord.Role):
|
||||||
"""Toggles role mention"""
|
"""Toggle a role mention."""
|
||||||
current_setting = await self.db.role(role).mention()
|
current_setting = await self.db.role(role).mention()
|
||||||
if not role.mentionable:
|
if not role.mentionable:
|
||||||
await ctx.send("That role is not mentionable!")
|
await ctx.send("That role is not mentionable!")
|
||||||
@ -390,27 +402,27 @@ class Streams(commands.Cog):
|
|||||||
if current_setting:
|
if current_setting:
|
||||||
await self.db.role(role).mention.set(False)
|
await self.db.role(role).mention.set(False)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("{} will no longer be mentioned for an alert.").format(
|
_("`@\u200b{role.name}` will no longer be mentioned for stream alerts.").format(
|
||||||
"@\u200b{}".format(role.name)
|
role=role
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await self.db.role(role).mention.set(True)
|
await self.db.role(role).mention.set(True)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("When a stream or community " "is live, {} will be mentioned." "").format(
|
_(
|
||||||
"@\u200b{}".format(role.name)
|
"When a stream or community is live, `@\u200b{role.name}` will be mentioned."
|
||||||
)
|
).format(role=role)
|
||||||
)
|
)
|
||||||
|
|
||||||
@streamset.command()
|
@streamset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def autodelete(self, ctx: commands.Context, on_off: bool):
|
async def autodelete(self, ctx: commands.Context, on_off: bool):
|
||||||
"""Toggles automatic deletion of notifications for streams that go offline"""
|
"""Toggle alert deletion for when streams go offline."""
|
||||||
await self.db.guild(ctx.guild).autodelete.set(on_off)
|
await self.db.guild(ctx.guild).autodelete.set(on_off)
|
||||||
if on_off:
|
if on_off:
|
||||||
await ctx.send("The notifications will be deleted once streams go offline.")
|
await ctx.send(_("The notifications will be deleted once streams go offline."))
|
||||||
else:
|
else:
|
||||||
await ctx.send("Notifications will never be deleted.")
|
await ctx.send(_("Notifications will no longer be deleted."))
|
||||||
|
|
||||||
async def add_or_remove(self, ctx: commands.Context, stream):
|
async def add_or_remove(self, ctx: commands.Context, stream):
|
||||||
if ctx.channel.id not in stream.channels:
|
if ctx.channel.id not in stream.channels:
|
||||||
@ -418,18 +430,18 @@ class Streams(commands.Cog):
|
|||||||
if stream not in self.streams:
|
if stream not in self.streams:
|
||||||
self.streams.append(stream)
|
self.streams.append(stream)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("I'll now send a notification in this channel when {} is live.").format(
|
_(
|
||||||
stream.name
|
"I'll now send a notification in this channel when {stream.name} is live."
|
||||||
)
|
).format(stream=stream)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
stream.channels.remove(ctx.channel.id)
|
stream.channels.remove(ctx.channel.id)
|
||||||
if not stream.channels:
|
if not stream.channels:
|
||||||
self.streams.remove(stream)
|
self.streams.remove(stream)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("I won't send notifications about {} in this channel anymore.").format(
|
_(
|
||||||
stream.name
|
"I won't send notifications about {stream.name} in this channel anymore."
|
||||||
)
|
).format(stream=stream)
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.save_streams()
|
await self.save_streams()
|
||||||
@ -442,9 +454,8 @@ class Streams(commands.Cog):
|
|||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"I'll send a notification in this channel when a "
|
"I'll send a notification in this channel when a "
|
||||||
"channel is live in the {} community."
|
"channel is live in the {community.name} community."
|
||||||
""
|
).format(community=community)
|
||||||
).format(community.name)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
community.channels.remove(ctx.channel.id)
|
community.channels.remove(ctx.channel.id)
|
||||||
@ -453,9 +464,8 @@ class Streams(commands.Cog):
|
|||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"I won't send notifications about channels streaming "
|
"I won't send notifications about channels streaming "
|
||||||
"in the {} community in this channel anymore."
|
"in the {community.name} community in this channel anymore."
|
||||||
""
|
).format(community=community)
|
||||||
).format(community.name)
|
|
||||||
)
|
)
|
||||||
await self.save_communities()
|
await self.save_communities()
|
||||||
|
|
||||||
@ -481,7 +491,8 @@ class Streams(commands.Cog):
|
|||||||
if community.type == _class.__name__ and community.name.lower() == name.lower():
|
if community.type == _class.__name__ and community.name.lower() == name.lower():
|
||||||
return community
|
return community
|
||||||
|
|
||||||
async def check_exists(self, stream):
|
@staticmethod
|
||||||
|
async def check_exists(stream):
|
||||||
try:
|
try:
|
||||||
await stream.is_online()
|
await stream.is_online()
|
||||||
except OfflineStream:
|
except OfflineStream:
|
||||||
@ -506,22 +517,19 @@ class Streams(commands.Cog):
|
|||||||
|
|
||||||
async def check_streams(self):
|
async def check_streams(self):
|
||||||
for stream in self.streams:
|
for stream in self.streams:
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
try:
|
try:
|
||||||
embed = await stream.is_online()
|
embed = await stream.is_online()
|
||||||
except OfflineStream:
|
except OfflineStream:
|
||||||
if not stream._messages_cache:
|
if not stream._messages_cache:
|
||||||
continue
|
continue
|
||||||
for message in stream._messages_cache:
|
for message in stream._messages_cache:
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
autodelete = await self.db.guild(message.guild).autodelete()
|
autodelete = await self.db.guild(message.guild).autodelete()
|
||||||
if autodelete:
|
if autodelete:
|
||||||
await message.delete()
|
await message.delete()
|
||||||
except:
|
|
||||||
pass
|
|
||||||
stream._messages_cache.clear()
|
stream._messages_cache.clear()
|
||||||
await self.save_streams()
|
await self.save_streams()
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
if stream._messages_cache:
|
if stream._messages_cache:
|
||||||
continue
|
continue
|
||||||
@ -530,16 +538,15 @@ class Streams(commands.Cog):
|
|||||||
mention_str = await self._get_mention_str(channel.guild)
|
mention_str = await self._get_mention_str(channel.guild)
|
||||||
|
|
||||||
if mention_str:
|
if mention_str:
|
||||||
content = "{}, {} is live!".format(mention_str, stream.name)
|
content = _("{mention}, {stream.name} is live!").format(
|
||||||
|
mention=mention_str, stream=stream
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
content = "{} is live!".format(stream.name)
|
content = _("{stream.name} is live!").format(stream=stream.name)
|
||||||
|
|
||||||
try:
|
|
||||||
m = await channel.send(content, embed=embed)
|
m = await channel.send(content, embed=embed)
|
||||||
stream._messages_cache.append(m)
|
stream._messages_cache.append(m)
|
||||||
await self.save_streams()
|
await self.save_streams()
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def _get_mention_str(self, guild: discord.Guild):
|
async def _get_mention_str(self, guild: discord.Guild):
|
||||||
settings = self.db.guild(guild)
|
settings = self.db.guild(guild)
|
||||||
@ -555,25 +562,26 @@ class Streams(commands.Cog):
|
|||||||
|
|
||||||
async def check_communities(self):
|
async def check_communities(self):
|
||||||
for community in self.communities:
|
for community in self.communities:
|
||||||
|
with contextlib.suppress(Exception):
|
||||||
try:
|
try:
|
||||||
stream_list = await community.get_community_streams()
|
stream_list = await community.get_community_streams()
|
||||||
except CommunityNotFound:
|
except CommunityNotFound:
|
||||||
print(_("The Community {} was not found!").format(community.name))
|
print(
|
||||||
|
_("The Community {community.name} was not found!").format(
|
||||||
|
community=community
|
||||||
|
)
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
except OfflineCommunity:
|
except OfflineCommunity:
|
||||||
if not community._messages_cache:
|
if not community._messages_cache:
|
||||||
continue
|
continue
|
||||||
for message in community._messages_cache:
|
for message in community._messages_cache:
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
autodelete = await self.db.guild(message.guild).autodelete()
|
autodelete = await self.db.guild(message.guild).autodelete()
|
||||||
if autodelete:
|
if autodelete:
|
||||||
await message.delete()
|
await message.delete()
|
||||||
except:
|
|
||||||
pass
|
|
||||||
community._messages_cache.clear()
|
community._messages_cache.clear()
|
||||||
await self.save_communities()
|
await self.save_communities()
|
||||||
except:
|
|
||||||
pass
|
|
||||||
else:
|
else:
|
||||||
for channel in community.channels:
|
for channel in community.channels:
|
||||||
chn = self.bot.get_channel(channel)
|
chn = self.bot.get_channel(channel)
|
||||||
@ -611,7 +619,7 @@ class Streams(commands.Cog):
|
|||||||
streams = []
|
streams = []
|
||||||
|
|
||||||
for raw_stream in await self.db.streams():
|
for raw_stream in await self.db.streams():
|
||||||
_class = getattr(StreamClasses, raw_stream["type"], None)
|
_class = getattr(_streamtypes, raw_stream["type"], None)
|
||||||
if not _class:
|
if not _class:
|
||||||
continue
|
continue
|
||||||
raw_msg_cache = raw_stream["messages"]
|
raw_msg_cache = raw_stream["messages"]
|
||||||
@ -631,7 +639,7 @@ class Streams(commands.Cog):
|
|||||||
communities = []
|
communities = []
|
||||||
|
|
||||||
for raw_community in await self.db.communities():
|
for raw_community in await self.db.communities():
|
||||||
_class = getattr(StreamClasses, raw_community["type"], None)
|
_class = getattr(_streamtypes, raw_community["type"], None)
|
||||||
if not _class:
|
if not _class:
|
||||||
continue
|
continue
|
||||||
raw_msg_cache = raw_community["messages"]
|
raw_msg_cache = raw_community["messages"]
|
||||||
|
|||||||
@ -4,20 +4,30 @@ import time
|
|||||||
import random
|
import random
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
import discord
|
import discord
|
||||||
from redbot.core.bank import deposit_credits
|
from redbot.core import bank
|
||||||
from redbot.core.utils.chat_formatting import box
|
from redbot.core.i18n import Translator
|
||||||
|
from redbot.core.utils.chat_formatting import box, bold, humanize_list
|
||||||
from redbot.core.utils.common_filters import normalize_smartquotes
|
from redbot.core.utils.common_filters import normalize_smartquotes
|
||||||
from .log import LOG
|
from .log import LOG
|
||||||
|
|
||||||
__all__ = ["TriviaSession"]
|
__all__ = ["TriviaSession"]
|
||||||
|
|
||||||
_REVEAL_MESSAGES = ("I know this one! {}!", "Easy: {}.", "Oh really? It's {} of course.")
|
T_ = Translator("TriviaSession", __file__)
|
||||||
_FAIL_MESSAGES = (
|
|
||||||
"To the next one I guess...",
|
|
||||||
"Moving on...",
|
_ = lambda s: s
|
||||||
"I'm sure you'll know the answer of the next one.",
|
_REVEAL_MESSAGES = (
|
||||||
"\N{PENSIVE FACE} Next one.",
|
_("I know this one! {answer}!"),
|
||||||
|
_("Easy: {answer}."),
|
||||||
|
_("Oh really? It's {answer} of course."),
|
||||||
)
|
)
|
||||||
|
_FAIL_MESSAGES = (
|
||||||
|
_("To the next one I guess..."),
|
||||||
|
_("Moving on..."),
|
||||||
|
_("I'm sure you'll know the answer of the next one."),
|
||||||
|
_("\N{PENSIVE FACE} Next one."),
|
||||||
|
)
|
||||||
|
_ = T_
|
||||||
|
|
||||||
|
|
||||||
class TriviaSession:
|
class TriviaSession:
|
||||||
@ -104,7 +114,7 @@ class TriviaSession:
|
|||||||
async with self.ctx.typing():
|
async with self.ctx.typing():
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
self.count += 1
|
self.count += 1
|
||||||
msg = "**Question number {}!**\n\n{}".format(self.count, question)
|
msg = bold(_("**Question number {num}!").format(num=self.count)) + "\n\n" + question
|
||||||
await self.ctx.send(msg)
|
await self.ctx.send(msg)
|
||||||
continue_ = await self.wait_for_answer(answers, delay, timeout)
|
continue_ = await self.wait_for_answer(answers, delay, timeout)
|
||||||
if continue_ is False:
|
if continue_ is False:
|
||||||
@ -113,7 +123,7 @@ class TriviaSession:
|
|||||||
await self.end_game()
|
await self.end_game()
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
await self.ctx.send("There are no more questions!")
|
await self.ctx.send(_("There are no more questions!"))
|
||||||
await self.end_game()
|
await self.end_game()
|
||||||
|
|
||||||
async def _send_startup_msg(self):
|
async def _send_startup_msg(self):
|
||||||
@ -121,20 +131,13 @@ class TriviaSession:
|
|||||||
for idx, tup in enumerate(self.settings["lists"].items()):
|
for idx, tup in enumerate(self.settings["lists"].items()):
|
||||||
name, author = tup
|
name, author = tup
|
||||||
if author:
|
if author:
|
||||||
title = "{} (by {})".format(name, author)
|
title = _("{trivia_list} (by {author})").format(trivia_list=name, author=author)
|
||||||
else:
|
else:
|
||||||
title = name
|
title = name
|
||||||
list_names.append(title)
|
list_names.append(title)
|
||||||
num_lists = len(list_names)
|
await self.ctx.send(
|
||||||
if num_lists > 2:
|
_("Starting Trivia: {list_names}").format(list_names=humanize_list(list_names))
|
||||||
# at least 3 lists, join all but last with comma
|
)
|
||||||
msg = ", ".join(list_names[: num_lists - 1])
|
|
||||||
# join onto last with "and"
|
|
||||||
msg = " and ".join((msg, list_names[num_lists - 1]))
|
|
||||||
else:
|
|
||||||
# either 1 or 2 lists, join together with "and"
|
|
||||||
msg = " and ".join(list_names)
|
|
||||||
await self.ctx.send("Starting Trivia: " + msg)
|
|
||||||
|
|
||||||
def _iter_questions(self):
|
def _iter_questions(self):
|
||||||
"""Iterate over questions and answers for this session.
|
"""Iterate over questions and answers for this session.
|
||||||
@ -179,20 +182,20 @@ class TriviaSession:
|
|||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
if time.time() - self._last_response >= timeout:
|
if time.time() - self._last_response >= timeout:
|
||||||
await self.ctx.send("Guys...? Well, I guess I'll stop then.")
|
await self.ctx.send(_("Guys...? Well, I guess I'll stop then."))
|
||||||
self.stop()
|
self.stop()
|
||||||
return False
|
return False
|
||||||
if self.settings["reveal_answer"]:
|
if self.settings["reveal_answer"]:
|
||||||
reply = random.choice(_REVEAL_MESSAGES).format(answers[0])
|
reply = T_(random.choice(_REVEAL_MESSAGES)).format(answer=answers[0])
|
||||||
else:
|
else:
|
||||||
reply = random.choice(_FAIL_MESSAGES)
|
reply = T_(random.choice(_FAIL_MESSAGES))
|
||||||
if self.settings["bot_plays"]:
|
if self.settings["bot_plays"]:
|
||||||
reply += " **+1** for me!"
|
reply += _(" **+1** for me!")
|
||||||
self.scores[self.ctx.guild.me] += 1
|
self.scores[self.ctx.guild.me] += 1
|
||||||
await self.ctx.send(reply)
|
await self.ctx.send(reply)
|
||||||
else:
|
else:
|
||||||
self.scores[message.author] += 1
|
self.scores[message.author] += 1
|
||||||
reply = "You got it {}! **+1** to you!".format(message.author.display_name)
|
reply = _("You got it {user}! **+1** to you!").format(user=message.author.display_name)
|
||||||
await self.ctx.send(reply)
|
await self.ctx.send(reply)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -282,10 +285,16 @@ class TriviaSession:
|
|||||||
amount = int(multiplier * score)
|
amount = int(multiplier * score)
|
||||||
if amount > 0:
|
if amount > 0:
|
||||||
LOG.debug("Paying trivia winner: %d credits --> %s", amount, str(winner))
|
LOG.debug("Paying trivia winner: %d credits --> %s", amount, str(winner))
|
||||||
await deposit_credits(winner, int(multiplier * score))
|
await bank.deposit_credits(winner, int(multiplier * score))
|
||||||
await self.ctx.send(
|
await self.ctx.send(
|
||||||
"Congratulations, {0}, you have received {1} credits"
|
_(
|
||||||
" for coming first.".format(winner.display_name, amount)
|
"Congratulations, {user}, you have received {num} {currency}"
|
||||||
|
" for coming first."
|
||||||
|
).format(
|
||||||
|
user=winner.display_name,
|
||||||
|
num=amount,
|
||||||
|
currency=await bank.get_currency_name(self.ctx.guild),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -313,9 +322,9 @@ def _parse_answers(answers):
|
|||||||
for answer in answers:
|
for answer in answers:
|
||||||
if isinstance(answer, bool):
|
if isinstance(answer, bool):
|
||||||
if answer is True:
|
if answer is True:
|
||||||
ret.extend(["True", "Yes"])
|
ret.extend(["True", "Yes", _("Yes")])
|
||||||
else:
|
else:
|
||||||
ret.extend(["False", "No"])
|
ret.extend(["False", "No", _("No")])
|
||||||
else:
|
else:
|
||||||
ret.append(str(answer))
|
ret.append(str(answer))
|
||||||
# Uniquify list
|
# Uniquify list
|
||||||
|
|||||||
@ -7,7 +7,8 @@ import discord
|
|||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
from redbot.core import Config, checks
|
from redbot.core import Config, checks
|
||||||
from redbot.core.data_manager import cog_data_path
|
from redbot.core.data_manager import cog_data_path
|
||||||
from redbot.core.utils.chat_formatting import box, pagify
|
from redbot.core.i18n import Translator, cog_i18n
|
||||||
|
from redbot.core.utils.chat_formatting import box, pagify, bold
|
||||||
from redbot.cogs.bank import check_global_setting_admin
|
from redbot.cogs.bank import check_global_setting_admin
|
||||||
from .log import LOG
|
from .log import LOG
|
||||||
from .session import TriviaSession
|
from .session import TriviaSession
|
||||||
@ -16,6 +17,8 @@ __all__ = ["Trivia", "UNIQUE_ID", "get_core_lists"]
|
|||||||
|
|
||||||
UNIQUE_ID = 0xB3C0E453
|
UNIQUE_ID = 0xB3C0E453
|
||||||
|
|
||||||
|
_ = Translator("Trivia", __file__)
|
||||||
|
|
||||||
|
|
||||||
class InvalidListError(Exception):
|
class InvalidListError(Exception):
|
||||||
"""A Trivia list file is in invalid format."""
|
"""A Trivia list file is in invalid format."""
|
||||||
@ -23,6 +26,7 @@ class InvalidListError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@cog_i18n(_)
|
||||||
class Trivia(commands.Cog):
|
class Trivia(commands.Cog):
|
||||||
"""Play trivia with friends!"""
|
"""Play trivia with friends!"""
|
||||||
|
|
||||||
@ -47,11 +51,12 @@ class Trivia(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.mod_or_permissions(administrator=True)
|
@checks.mod_or_permissions(administrator=True)
|
||||||
async def triviaset(self, ctx: commands.Context):
|
async def triviaset(self, ctx: commands.Context):
|
||||||
"""Manage trivia settings."""
|
"""Manage Trivia settings."""
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
settings_dict = await settings.all()
|
settings_dict = await settings.all()
|
||||||
msg = box(
|
msg = box(
|
||||||
|
_(
|
||||||
"**Current settings**\n"
|
"**Current settings**\n"
|
||||||
"Bot gains points: {bot_plays}\n"
|
"Bot gains points: {bot_plays}\n"
|
||||||
"Answer time limit: {delay} seconds\n"
|
"Answer time limit: {delay} seconds\n"
|
||||||
@ -60,7 +65,7 @@ class Trivia(commands.Cog):
|
|||||||
"Reveal answer on timeout: {reveal_answer}\n"
|
"Reveal answer on timeout: {reveal_answer}\n"
|
||||||
"Payout multiplier: {payout_multiplier}\n"
|
"Payout multiplier: {payout_multiplier}\n"
|
||||||
"Allow lists to override settings: {allow_override}"
|
"Allow lists to override settings: {allow_override}"
|
||||||
"".format(**settings_dict),
|
).format(**settings_dict),
|
||||||
lang="py",
|
lang="py",
|
||||||
)
|
)
|
||||||
await ctx.send(msg)
|
await ctx.send(msg)
|
||||||
@ -69,33 +74,34 @@ class Trivia(commands.Cog):
|
|||||||
async def triviaset_max_score(self, ctx: commands.Context, score: int):
|
async def triviaset_max_score(self, ctx: commands.Context, score: int):
|
||||||
"""Set the total points required to win."""
|
"""Set the total points required to win."""
|
||||||
if score < 0:
|
if score < 0:
|
||||||
await ctx.send("Score must be greater than 0.")
|
await ctx.send(_("Score must be greater than 0."))
|
||||||
return
|
return
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
await settings.max_score.set(score)
|
await settings.max_score.set(score)
|
||||||
await ctx.send("Done. Points required to win set to {}.".format(score))
|
await ctx.send(_("Done. Points required to win set to {num}.").format(num=score))
|
||||||
|
|
||||||
@triviaset.command(name="timelimit")
|
@triviaset.command(name="timelimit")
|
||||||
async def triviaset_timelimit(self, ctx: commands.Context, seconds: float):
|
async def triviaset_timelimit(self, ctx: commands.Context, seconds: float):
|
||||||
"""Set the maximum seconds permitted to answer a question."""
|
"""Set the maximum seconds permitted to answer a question."""
|
||||||
if seconds < 4.0:
|
if seconds < 4.0:
|
||||||
await ctx.send("Must be at least 4 seconds.")
|
await ctx.send(_("Must be at least 4 seconds."))
|
||||||
return
|
return
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
await settings.delay.set(seconds)
|
await settings.delay.set(seconds)
|
||||||
await ctx.send("Done. Maximum seconds to answer set to {}.".format(seconds))
|
await ctx.send(_("Done. Maximum seconds to answer set to {num}.").format(num=seconds))
|
||||||
|
|
||||||
@triviaset.command(name="stopafter")
|
@triviaset.command(name="stopafter")
|
||||||
async def triviaset_stopafter(self, ctx: commands.Context, seconds: float):
|
async def triviaset_stopafter(self, ctx: commands.Context, seconds: float):
|
||||||
"""Set how long until trivia stops due to no response."""
|
"""Set how long until trivia stops due to no response."""
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
if seconds < await settings.delay():
|
if seconds < await settings.delay():
|
||||||
await ctx.send("Must be larger than the answer time limit.")
|
await ctx.send(_("Must be larger than the answer time limit."))
|
||||||
return
|
return
|
||||||
await settings.timeout.set(seconds)
|
await settings.timeout.set(seconds)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"Done. Trivia sessions will now time out after {}"
|
_(
|
||||||
" seconds of no responses.".format(seconds)
|
"Done. Trivia sessions will now time out after {num} seconds of no responses."
|
||||||
|
).format(num=seconds)
|
||||||
)
|
)
|
||||||
|
|
||||||
@triviaset.command(name="override")
|
@triviaset.command(name="override")
|
||||||
@ -103,46 +109,46 @@ class Trivia(commands.Cog):
|
|||||||
"""Allow/disallow trivia lists to override settings."""
|
"""Allow/disallow trivia lists to override settings."""
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
await settings.allow_override.set(enabled)
|
await settings.allow_override.set(enabled)
|
||||||
enabled = "now" if enabled else "no longer"
|
if enabled:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"Done. Trivia lists can {} override the trivia settings"
|
_(
|
||||||
" for this server.".format(enabled)
|
"Done. Trivia lists can now override the trivia settings for this server."
|
||||||
|
).format(now=enabled)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await ctx.send(
|
||||||
|
_(
|
||||||
|
"Done. Trivia lists can no longer override the trivia settings for this "
|
||||||
|
"server."
|
||||||
|
).format(now=enabled)
|
||||||
)
|
)
|
||||||
|
|
||||||
@triviaset.command(name="botplays")
|
@triviaset.command(name="botplays", usage="<true_or_false>")
|
||||||
async def trivaset_bot_plays(self, ctx: commands.Context, true_or_false: bool):
|
async def trivaset_bot_plays(self, ctx: commands.Context, enabled: bool):
|
||||||
"""Set whether or not the bot gains points.
|
"""Set whether or not the bot gains points.
|
||||||
|
|
||||||
If enabled, the bot will gain a point if no one guesses correctly.
|
If enabled, the bot will gain a point if no one guesses correctly.
|
||||||
"""
|
"""
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
await settings.bot_plays.set(true_or_false)
|
await settings.bot_plays.set(enabled)
|
||||||
await ctx.send(
|
if enabled:
|
||||||
"Done. "
|
await ctx.send(_("Done. I'll now gain a point if users don't answer in time."))
|
||||||
+ (
|
else:
|
||||||
"I'll gain a point if users don't answer in time."
|
await ctx.send(_("Alright, I won't embarass you at trivia anymore."))
|
||||||
if true_or_false
|
|
||||||
else "Alright, I won't embarass you at trivia anymore."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@triviaset.command(name="revealanswer")
|
@triviaset.command(name="revealanswer", usage="<true_or_false>")
|
||||||
async def trivaset_reveal_answer(self, ctx: commands.Context, true_or_false: bool):
|
async def trivaset_reveal_answer(self, ctx: commands.Context, enabled: bool):
|
||||||
"""Set whether or not the answer is revealed.
|
"""Set whether or not the answer is revealed.
|
||||||
|
|
||||||
If enabled, the bot will reveal the answer if no one guesses correctly
|
If enabled, the bot will reveal the answer if no one guesses correctly
|
||||||
in time.
|
in time.
|
||||||
"""
|
"""
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
await settings.reveal_answer.set(true_or_false)
|
await settings.reveal_answer.set(enabled)
|
||||||
await ctx.send(
|
if enabled:
|
||||||
"Done. "
|
await ctx.send(_("Done. I'll reveal the answer if no one knows it."))
|
||||||
+ (
|
else:
|
||||||
"I'll reveal the answer if no one knows it."
|
await ctx.send(_("Alright, I won't reveal the answer to the questions anymore."))
|
||||||
if true_or_false
|
|
||||||
else "I won't reveal the answer to the questions anymore."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@triviaset.command(name="payout")
|
@triviaset.command(name="payout")
|
||||||
@check_global_setting_admin()
|
@check_global_setting_admin()
|
||||||
@ -158,13 +164,13 @@ class Trivia(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
settings = self.conf.guild(ctx.guild)
|
settings = self.conf.guild(ctx.guild)
|
||||||
if multiplier < 0:
|
if multiplier < 0:
|
||||||
await ctx.send("Multiplier must be at least 0.")
|
await ctx.send(_("Multiplier must be at least 0."))
|
||||||
return
|
return
|
||||||
await settings.payout_multiplier.set(multiplier)
|
await settings.payout_multiplier.set(multiplier)
|
||||||
if not multiplier:
|
if multiplier:
|
||||||
await ctx.send("Done. I will no longer reward the winner with a payout.")
|
await ctx.send(_("Done. Payout multiplier set to {num}.").format(num=multiplier))
|
||||||
return
|
else:
|
||||||
await ctx.send("Done. Payout multiplier set to {}.".format(multiplier))
|
await ctx.send(_("Done. I will no longer reward the winner with a payout."))
|
||||||
|
|
||||||
@commands.group(invoke_without_command=True)
|
@commands.group(invoke_without_command=True)
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@ -180,7 +186,7 @@ class Trivia(commands.Cog):
|
|||||||
categories = [c.lower() for c in categories]
|
categories = [c.lower() for c in categories]
|
||||||
session = self._get_trivia_session(ctx.channel)
|
session = self._get_trivia_session(ctx.channel)
|
||||||
if session is not None:
|
if session is not None:
|
||||||
await ctx.send("There is already an ongoing trivia session in this channel.")
|
await ctx.send(_("There is already an ongoing trivia session in this channel."))
|
||||||
return
|
return
|
||||||
trivia_dict = {}
|
trivia_dict = {}
|
||||||
authors = []
|
authors = []
|
||||||
@ -191,15 +197,17 @@ class Trivia(commands.Cog):
|
|||||||
dict_ = self.get_trivia_list(category)
|
dict_ = self.get_trivia_list(category)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"Invalid category `{0}`. See `{1}trivia list`"
|
_(
|
||||||
" for a list of trivia categories."
|
"Invalid category `{name}`. See `{prefix}trivia list` for a list of "
|
||||||
"".format(category, ctx.prefix)
|
"trivia categories."
|
||||||
|
).format(name=category, prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
except InvalidListError:
|
except InvalidListError:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"There was an error parsing the trivia list for"
|
_(
|
||||||
" the `{}` category. It may be formatted"
|
"There was an error parsing the trivia list for the `{name}` category. It "
|
||||||
" incorrectly.".format(category)
|
"may be formatted incorrectly."
|
||||||
|
).format(name=category)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
trivia_dict.update(dict_)
|
trivia_dict.update(dict_)
|
||||||
@ -208,7 +216,7 @@ class Trivia(commands.Cog):
|
|||||||
return
|
return
|
||||||
if not trivia_dict:
|
if not trivia_dict:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"The trivia list was parsed successfully, however it appears to be empty!"
|
_("The trivia list was parsed successfully, however it appears to be empty!")
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
settings = await self.conf.guild(ctx.guild).all()
|
settings = await self.conf.guild(ctx.guild).all()
|
||||||
@ -225,7 +233,7 @@ class Trivia(commands.Cog):
|
|||||||
"""Stop an ongoing trivia session."""
|
"""Stop an ongoing trivia session."""
|
||||||
session = self._get_trivia_session(ctx.channel)
|
session = self._get_trivia_session(ctx.channel)
|
||||||
if session is None:
|
if session is None:
|
||||||
await ctx.send("There is no ongoing trivia session in this channel.")
|
await ctx.send(_("There is no ongoing trivia session in this channel."))
|
||||||
return
|
return
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
auth_checks = (
|
auth_checks = (
|
||||||
@ -238,19 +246,27 @@ class Trivia(commands.Cog):
|
|||||||
if any(auth_checks):
|
if any(auth_checks):
|
||||||
await session.end_game()
|
await session.end_game()
|
||||||
session.force_stop()
|
session.force_stop()
|
||||||
await ctx.send("Trivia stopped.")
|
await ctx.send(_("Trivia stopped."))
|
||||||
else:
|
else:
|
||||||
await ctx.send("You are not allowed to do that.")
|
await ctx.send(_("You are not allowed to do that."))
|
||||||
|
|
||||||
@trivia.command(name="list")
|
@trivia.command(name="list")
|
||||||
async def trivia_list(self, ctx: commands.Context):
|
async def trivia_list(self, ctx: commands.Context):
|
||||||
"""List available trivia categories."""
|
"""List available trivia categories."""
|
||||||
lists = set(p.stem for p in self._all_lists())
|
lists = set(p.stem for p in self._all_lists())
|
||||||
|
if await ctx.embed_requested():
|
||||||
msg = box("**Available trivia lists**\n\n{}".format(", ".join(sorted(lists))))
|
await ctx.send(
|
||||||
|
embed=discord.Embed(
|
||||||
|
title=_("Available trivia lists"),
|
||||||
|
colour=await ctx.embed_colour(),
|
||||||
|
description=", ".join(sorted(lists)),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
msg = box(bold(_("Available trivia lists")) + "\n\n" + ", ".join(sorted(lists)))
|
||||||
if len(msg) > 1000:
|
if len(msg) > 1000:
|
||||||
await ctx.author.send(msg)
|
await ctx.author.send(msg)
|
||||||
return
|
else:
|
||||||
await ctx.send(msg)
|
await ctx.send(msg)
|
||||||
|
|
||||||
@trivia.group(name="leaderboard", aliases=["lboard"], autohelp=False)
|
@trivia.group(name="leaderboard", aliases=["lboard"], autohelp=False)
|
||||||
@ -273,19 +289,21 @@ class Trivia(commands.Cog):
|
|||||||
):
|
):
|
||||||
"""Leaderboard for this server.
|
"""Leaderboard for this server.
|
||||||
|
|
||||||
<sort_by> can be any of the following fields:
|
`<sort_by>` can be any of the following fields:
|
||||||
- wins : total wins
|
- `wins` : total wins
|
||||||
- avg : average score
|
- `avg` : average score
|
||||||
- total : total correct answers
|
- `total` : total correct answers
|
||||||
|
- `games` : total games played
|
||||||
|
|
||||||
<top> is the number of ranks to show on the leaderboard.
|
`<top>` is the number of ranks to show on the leaderboard.
|
||||||
"""
|
"""
|
||||||
key = self._get_sort_key(sort_by)
|
key = self._get_sort_key(sort_by)
|
||||||
if key is None:
|
if key is None:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"Unknown field `{}`, see `{}help trivia "
|
_(
|
||||||
"leaderboard server` for valid fields to sort by."
|
"Unknown field `{field_name}`, see `{prefix}help trivia leaderboard server` "
|
||||||
"".format(sort_by, ctx.prefix)
|
"for valid fields to sort by."
|
||||||
|
).format(field_name=sort_by, prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
@ -300,20 +318,21 @@ class Trivia(commands.Cog):
|
|||||||
):
|
):
|
||||||
"""Global trivia leaderboard.
|
"""Global trivia leaderboard.
|
||||||
|
|
||||||
<sort_by> can be any of the following fields:
|
`<sort_by>` can be any of the following fields:
|
||||||
- wins : total wins
|
- `wins` : total wins
|
||||||
- avg : average score
|
- `avg` : average score
|
||||||
- total : total correct answers from all sessions
|
- `total` : total correct answers from all sessions
|
||||||
- games : total games played
|
- `games` : total games played
|
||||||
|
|
||||||
<top> is the number of ranks to show on the leaderboard.
|
`<top>` is the number of ranks to show on the leaderboard.
|
||||||
"""
|
"""
|
||||||
key = self._get_sort_key(sort_by)
|
key = self._get_sort_key(sort_by)
|
||||||
if key is None:
|
if key is None:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
"Unknown field `{}`, see `{}help trivia "
|
_(
|
||||||
"leaderboard global` for valid fields to sort by."
|
"Unknown field `{field_name}`, see `{prefix}help trivia leaderboard server` "
|
||||||
"".format(sort_by, ctx.prefix)
|
"for valid fields to sort by."
|
||||||
|
).format(field_name=sort_by, prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
data = await self.conf.all_members()
|
data = await self.conf.all_members()
|
||||||
@ -365,7 +384,7 @@ class Trivia(commands.Cog):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
if not data:
|
if not data:
|
||||||
await ctx.send("There are no scores on record!")
|
await ctx.send(_("There are no scores on record!"))
|
||||||
return
|
return
|
||||||
leaderboard = self._get_leaderboard(data, key, top)
|
leaderboard = self._get_leaderboard(data, key, top)
|
||||||
ret = []
|
ret = []
|
||||||
@ -386,7 +405,7 @@ class Trivia(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
priority.remove(key)
|
priority.remove(key)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise ValueError("{} is not a valid key.".format(key))
|
raise ValueError(f"{key} is not a valid key.")
|
||||||
# Put key last in reverse priority
|
# Put key last in reverse priority
|
||||||
priority.append(key)
|
priority.append(key)
|
||||||
items = data.items()
|
items = data.items()
|
||||||
@ -395,16 +414,15 @@ class Trivia(commands.Cog):
|
|||||||
max_name_len = max(map(lambda m: len(str(m)), data.keys()))
|
max_name_len = max(map(lambda m: len(str(m)), data.keys()))
|
||||||
# Headers
|
# Headers
|
||||||
headers = (
|
headers = (
|
||||||
"Rank",
|
_("Rank"),
|
||||||
"Member{}".format(" " * (max_name_len - 6)),
|
_("Member") + " " * (max_name_len - 6),
|
||||||
"Wins",
|
_("Wins"),
|
||||||
"Games Played",
|
_("Games Played"),
|
||||||
"Total Score",
|
_("Total Score"),
|
||||||
"Average Score",
|
_("Average Score"),
|
||||||
)
|
)
|
||||||
lines = [" | ".join(headers)]
|
lines = [" | ".join(headers), " | ".join(("-" * len(h) for h in headers))]
|
||||||
# Header underlines
|
# Header underlines
|
||||||
lines.append(" | ".join(("-" * len(h) for h in headers)))
|
|
||||||
for rank, tup in enumerate(items, 1):
|
for rank, tup in enumerate(items, 1):
|
||||||
member, m_data = tup
|
member, m_data = tup
|
||||||
# Align fields to header width
|
# Align fields to header width
|
||||||
|
|||||||
@ -22,7 +22,7 @@ _ = Translator("Warnings", __file__)
|
|||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class Warnings(commands.Cog):
|
class Warnings(commands.Cog):
|
||||||
"""A warning system for Red"""
|
"""Warn misbehaving users and take automated actions."""
|
||||||
|
|
||||||
default_guild = {"actions": [], "reasons": {}, "allow_custom_reasons": False}
|
default_guild = {"actions": [], "reasons": {}, "allow_custom_reasons": False}
|
||||||
|
|
||||||
@ -48,31 +48,42 @@ class Warnings(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.guildowner_or_permissions(administrator=True)
|
@checks.guildowner_or_permissions(administrator=True)
|
||||||
async def warningset(self, ctx: commands.Context):
|
async def warningset(self, ctx: commands.Context):
|
||||||
"""Warning settings"""
|
"""Manage settings for Warnings."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@warningset.command()
|
@warningset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def allowcustomreasons(self, ctx: commands.Context, allowed: bool):
|
async def allowcustomreasons(self, ctx: commands.Context, allowed: bool):
|
||||||
"""Enable or Disable custom reasons for a warning"""
|
"""Enable or disable custom reasons for a warning."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
await self.config.guild(guild).allow_custom_reasons.set(allowed)
|
await self.config.guild(guild).allow_custom_reasons.set(allowed)
|
||||||
await ctx.send(
|
if allowed:
|
||||||
_("Custom reasons have been {}.").format(_("enabled") if allowed else _("disabled"))
|
await ctx.send(_("Custom reasons have been enabled."))
|
||||||
)
|
else:
|
||||||
|
await ctx.send(_("Custom reasons have been disabled."))
|
||||||
|
|
||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.guildowner_or_permissions(administrator=True)
|
@checks.guildowner_or_permissions(administrator=True)
|
||||||
async def warnaction(self, ctx: commands.Context):
|
async def warnaction(self, ctx: commands.Context):
|
||||||
"""Action management"""
|
"""Manage automated actions for Warnings.
|
||||||
|
|
||||||
|
Actions are essentially command macros. Any command can be run
|
||||||
|
when the action is initially triggered, and/or when the action
|
||||||
|
is lifted.
|
||||||
|
|
||||||
|
Actions must be given a name and a points threshold. When a
|
||||||
|
user is warned enough so that their points go over this
|
||||||
|
threshold, the action will be executed.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@warnaction.command(name="add")
|
@warnaction.command(name="add")
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def action_add(self, ctx: commands.Context, name: str, points: int):
|
async def action_add(self, ctx: commands.Context, name: str, points: int):
|
||||||
"""Create an action to be taken at a specified point count
|
"""Create an automated action.
|
||||||
Duplicate action names are not allowed
|
|
||||||
|
Duplicate action names are not allowed.
|
||||||
"""
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
@ -103,7 +114,7 @@ class Warnings(commands.Cog):
|
|||||||
@warnaction.command(name="del")
|
@warnaction.command(name="del")
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def action_del(self, ctx: commands.Context, action_name: str):
|
async def action_del(self, ctx: commands.Context, action_name: str):
|
||||||
"""Delete the point count action with the specified name"""
|
"""Delete the action with the specified name."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
guild_settings = self.config.guild(guild)
|
guild_settings = self.config.guild(guild)
|
||||||
async with guild_settings.actions() as registered_actions:
|
async with guild_settings.actions() as registered_actions:
|
||||||
@ -116,23 +127,29 @@ class Warnings(commands.Cog):
|
|||||||
registered_actions.remove(to_remove)
|
registered_actions.remove(to_remove)
|
||||||
await ctx.tick()
|
await ctx.tick()
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("No action named {} exists!").format(action_name))
|
await ctx.send(_("No action named {name} exists!").format(name=action_name))
|
||||||
|
|
||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.guildowner_or_permissions(administrator=True)
|
@checks.guildowner_or_permissions(administrator=True)
|
||||||
async def warnreason(self, ctx: commands.Context):
|
async def warnreason(self, ctx: commands.Context):
|
||||||
"""Add reasons for warnings"""
|
"""Manage warning reasons.
|
||||||
|
|
||||||
|
Reasons must be given a name, description and points value. The
|
||||||
|
name of the reason must be given when a user is warned.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@warnreason.command(name="add")
|
@warnreason.command(name="create", aliases=["add"])
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def reason_add(self, ctx: commands.Context, name: str, points: int, *, description: str):
|
async def reason_create(
|
||||||
"""Add a reason to be available for warnings"""
|
self, ctx: commands.Context, name: str, points: int, *, description: str
|
||||||
|
):
|
||||||
|
"""Create a warning reason."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
if name.lower() == "custom":
|
if name.lower() == "custom":
|
||||||
await ctx.send("That cannot be used as a reason name!")
|
await ctx.send(_("*Custom* cannot be used as a reason name!"))
|
||||||
return
|
return
|
||||||
to_add = {"points": points, "description": description}
|
to_add = {"points": points, "description": description}
|
||||||
completed = {name.lower(): to_add}
|
completed = {name.lower(): to_add}
|
||||||
@ -142,12 +159,12 @@ class Warnings(commands.Cog):
|
|||||||
async with guild_settings.reasons() as registered_reasons:
|
async with guild_settings.reasons() as registered_reasons:
|
||||||
registered_reasons.update(completed)
|
registered_reasons.update(completed)
|
||||||
|
|
||||||
await ctx.send(_("That reason has been registered."))
|
await ctx.send(_("The new reason has been registered."))
|
||||||
|
|
||||||
@warnreason.command(name="del")
|
@warnreason.command(name="del", aliases=["remove"])
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def reason_del(self, ctx: commands.Context, reason_name: str):
|
async def reason_del(self, ctx: commands.Context, reason_name: str):
|
||||||
"""Delete the reason with the specified name"""
|
"""Delete a warning reason."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
guild_settings = self.config.guild(guild)
|
guild_settings = self.config.guild(guild)
|
||||||
async with guild_settings.reasons() as registered_reasons:
|
async with guild_settings.reasons() as registered_reasons:
|
||||||
@ -160,7 +177,7 @@ class Warnings(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
async def reasonlist(self, ctx: commands.Context):
|
async def reasonlist(self, ctx: commands.Context):
|
||||||
"""List all configured reasons for warnings"""
|
"""List all configured reasons for Warnings."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
guild_settings = self.config.guild(guild)
|
guild_settings = self.config.guild(guild)
|
||||||
msg_list = []
|
msg_list = []
|
||||||
@ -174,9 +191,9 @@ class Warnings(commands.Cog):
|
|||||||
msg_list.append(em)
|
msg_list.append(em)
|
||||||
else:
|
else:
|
||||||
msg_list.append(
|
msg_list.append(
|
||||||
"Name: {}\nPoints: {}\nDescription: {}".format(
|
_(
|
||||||
r, v["points"], v["description"]
|
"Name: {reason_name}\nPoints: {points}\nDescription: {description}"
|
||||||
)
|
).format(reason_name=r, **v)
|
||||||
)
|
)
|
||||||
if msg_list:
|
if msg_list:
|
||||||
await menu(ctx, msg_list, DEFAULT_CONTROLS)
|
await menu(ctx, msg_list, DEFAULT_CONTROLS)
|
||||||
@ -187,7 +204,7 @@ class Warnings(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
async def actionlist(self, ctx: commands.Context):
|
async def actionlist(self, ctx: commands.Context):
|
||||||
"""List the actions to be taken at specific point values"""
|
"""List all configured automated actions for Warnings."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
guild_settings = self.config.guild(guild)
|
guild_settings = self.config.guild(guild)
|
||||||
msg_list = []
|
msg_list = []
|
||||||
@ -201,10 +218,10 @@ class Warnings(commands.Cog):
|
|||||||
msg_list.append(em)
|
msg_list.append(em)
|
||||||
else:
|
else:
|
||||||
msg_list.append(
|
msg_list.append(
|
||||||
"Name: {}\nPoints: {}\nExceed command: {}\n"
|
_(
|
||||||
"Drop command: {}".format(
|
"Name: {action_name}\nPoints: {points}\n"
|
||||||
r["action_name"], r["points"], r["exceed_command"], r["drop_command"]
|
"Exceed command: {exceed_command}\nDrop command: {drop_command}"
|
||||||
)
|
).format(**r)
|
||||||
)
|
)
|
||||||
if msg_list:
|
if msg_list:
|
||||||
await menu(ctx, msg_list, DEFAULT_CONTROLS)
|
await menu(ctx, msg_list, DEFAULT_CONTROLS)
|
||||||
@ -215,8 +232,10 @@ class Warnings(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
async def warn(self, ctx: commands.Context, user: discord.Member, reason: str):
|
async def warn(self, ctx: commands.Context, user: discord.Member, reason: str):
|
||||||
"""Warn the user for the specified reason
|
"""Warn the user for the specified reason.
|
||||||
Reason must be a registered reason, or "custom" if custom reasons are allowed
|
|
||||||
|
`<reason>` must be a registered reason name, or *custom* if
|
||||||
|
custom reasons are enabled.
|
||||||
"""
|
"""
|
||||||
if user == ctx.author:
|
if user == ctx.author:
|
||||||
await ctx.send(_("You cannot warn yourself."))
|
await ctx.send(_("You cannot warn yourself."))
|
||||||
@ -226,9 +245,9 @@ class Warnings(commands.Cog):
|
|||||||
if not custom_allowed:
|
if not custom_allowed:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"Custom reasons are not allowed! Please see {} for "
|
"Custom reasons are not allowed! Please see `{prefix}reasonlist` for "
|
||||||
"a complete list of valid reasons."
|
"a complete list of valid reasons."
|
||||||
).format("`{}reasonlist`".format(ctx.prefix))
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
reason_type = await self.custom_warning_reason(ctx)
|
reason_type = await self.custom_warning_reason(ctx)
|
||||||
@ -272,9 +291,7 @@ class Warnings(commands.Cog):
|
|||||||
await warning_points_add_check(self.config, ctx, user, current_point_count)
|
await warning_points_add_check(self.config, ctx, user, current_point_count)
|
||||||
try:
|
try:
|
||||||
em = discord.Embed(
|
em = discord.Embed(
|
||||||
title=_("Warning from {mod_name}#{mod_discrim}").format(
|
title=_("Warning from {user}").format(user=ctx.author),
|
||||||
mod_name=ctx.author.display_name, mod_discrim=ctx.author.discriminator
|
|
||||||
),
|
|
||||||
description=reason_type["description"],
|
description=reason_type["description"],
|
||||||
)
|
)
|
||||||
em.add_field(name=_("Points"), value=str(reason_type["points"]))
|
em.add_field(name=_("Points"), value=str(reason_type["points"]))
|
||||||
@ -286,19 +303,17 @@ class Warnings(commands.Cog):
|
|||||||
)
|
)
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
pass
|
pass
|
||||||
await ctx.send(
|
await ctx.send(_("User {user} has been warned.").format(user=user))
|
||||||
_("User {user_name}#{user_discrim} has been warned.").format(
|
|
||||||
user_name=user.display_name, user_discrim=user.discriminator
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def warnings(self, ctx: commands.Context, userid: int = None):
|
async def warnings(self, ctx: commands.Context, userid: int = None):
|
||||||
"""Show warnings for the specified user.
|
"""List the warnings for the specified user.
|
||||||
If userid is None, show warnings for the person running the command
|
|
||||||
|
Emit `<userid>` to see your own warnings.
|
||||||
|
|
||||||
Note that showing warnings for users other than yourself requires
|
Note that showing warnings for users other than yourself requires
|
||||||
appropriate permissions
|
appropriate permissions.
|
||||||
"""
|
"""
|
||||||
if userid is None:
|
if userid is None:
|
||||||
user = ctx.author
|
user = ctx.author
|
||||||
@ -326,18 +341,24 @@ class Warnings(commands.Cog):
|
|||||||
)
|
)
|
||||||
if mod is None:
|
if mod is None:
|
||||||
mod = await self.bot.get_user_info(user_warnings[key]["mod"])
|
mod = await self.bot.get_user_info(user_warnings[key]["mod"])
|
||||||
msg += "{} point warning {} issued by {} for {}\n".format(
|
msg += _(
|
||||||
user_warnings[key]["points"], key, mod, user_warnings[key]["description"]
|
"{num_points} point warning {reason_name} issued by {user} for "
|
||||||
|
"{description}\n"
|
||||||
|
).format(
|
||||||
|
num_points=user_warnings[key]["points"],
|
||||||
|
reason_name=key,
|
||||||
|
user=mod,
|
||||||
|
description=user_warnings[key]["description"],
|
||||||
)
|
)
|
||||||
await ctx.send_interactive(
|
await ctx.send_interactive(
|
||||||
pagify(msg, shorten_by=58), box_lang="Warnings for {}".format(user)
|
pagify(msg, shorten_by=58), box_lang=_("Warnings for {user}").format(user=user)
|
||||||
)
|
)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
async def unwarn(self, ctx: commands.Context, user_id: int, warn_id: str):
|
async def unwarn(self, ctx: commands.Context, user_id: int, warn_id: str):
|
||||||
"""Removes the specified warning from the user specified"""
|
"""Remove a warning from a user."""
|
||||||
if user_id == ctx.author.id:
|
if user_id == ctx.author.id:
|
||||||
await ctx.send(_("You cannot remove warnings from yourself."))
|
await ctx.send(_("You cannot remove warnings from yourself."))
|
||||||
return
|
return
|
||||||
@ -351,7 +372,7 @@ class Warnings(commands.Cog):
|
|||||||
await warning_points_remove_check(self.config, ctx, member, current_point_count)
|
await warning_points_remove_check(self.config, ctx, member, current_point_count)
|
||||||
async with member_settings.warnings() as user_warnings:
|
async with member_settings.warnings() as user_warnings:
|
||||||
if warn_id not in user_warnings.keys():
|
if warn_id not in user_warnings.keys():
|
||||||
await ctx.send("That warning doesn't exist!")
|
await ctx.send(_("That warning doesn't exist!"))
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
current_point_count -= user_warnings[warn_id]["points"]
|
current_point_count -= user_warnings[warn_id]["points"]
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from redbot.core.utils.chat_formatting import pagify
|
|||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
import weakref
|
import weakref
|
||||||
from typing import List
|
from typing import List, Optional
|
||||||
from .common_filters import filter_mass_mentions
|
from .common_filters import filter_mass_mentions
|
||||||
|
|
||||||
_instances = weakref.WeakValueDictionary({})
|
_instances = weakref.WeakValueDictionary({})
|
||||||
@ -86,7 +86,11 @@ class Tunnel(metaclass=TunnelMeta):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def message_forwarder(
|
async def message_forwarder(
|
||||||
*, destination: discord.abc.Messageable, content: str = None, embed=None, files=[]
|
*,
|
||||||
|
destination: discord.abc.Messageable,
|
||||||
|
content: str = None,
|
||||||
|
embed=None,
|
||||||
|
files: Optional[List[discord.File]] = None
|
||||||
) -> List[discord.Message]:
|
) -> List[discord.Message]:
|
||||||
"""
|
"""
|
||||||
This does the actual sending, use this instead of a full tunnel
|
This does the actual sending, use this instead of a full tunnel
|
||||||
@ -95,19 +99,19 @@ class Tunnel(metaclass=TunnelMeta):
|
|||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
destination: `discord.abc.Messageable`
|
destination: discord.abc.Messageable
|
||||||
Where to send
|
Where to send
|
||||||
content: `str`
|
content: str
|
||||||
The message content
|
The message content
|
||||||
embed: `discord.Embed`
|
embed: discord.Embed
|
||||||
The embed to send
|
The embed to send
|
||||||
files: `list` of `discord.File`
|
files: Optional[List[discord.File]]
|
||||||
A list of files to send.
|
A list of files to send.
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
list of `discord.Message`
|
List[discord.Message]
|
||||||
The `discord.Message`\ (s) sent as a result
|
The messages sent as a result.
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
@ -117,7 +121,6 @@ class Tunnel(metaclass=TunnelMeta):
|
|||||||
see `discord.abc.Messageable.send`
|
see `discord.abc.Messageable.send`
|
||||||
"""
|
"""
|
||||||
rets = []
|
rets = []
|
||||||
files = files if files else None
|
|
||||||
if content:
|
if content:
|
||||||
for page in pagify(content):
|
for page in pagify(content):
|
||||||
rets.append(await destination.send(page, files=files, embed=embed))
|
rets.append(await destination.send(page, files=files, embed=embed))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user