mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
[i18n] Pass over economy, filter, general, image, mod
Signed-off-by: Toby Harradine <tobyharradine@gmail.com>
This commit is contained in:
parent
0c3d8af8f4
commit
fa692ccc0b
@ -11,41 +11,40 @@ from .converters import MemberDefaultAuthor, SelfRole
|
|||||||
|
|
||||||
log = logging.getLogger("red.admin")
|
log = logging.getLogger("red.admin")
|
||||||
|
|
||||||
_ = Translator("Admin", __file__)
|
T_ = Translator("Admin", __file__)
|
||||||
|
|
||||||
# The following are all lambdas to allow us to fetch the translation
|
_ = lambda s: s
|
||||||
# during runtime, without having to copy the large strings everywhere
|
GENERIC_FORBIDDEN = _(
|
||||||
# in the code.
|
|
||||||
|
|
||||||
generic_forbidden = lambda: _(
|
|
||||||
"I attempted to do something that Discord denied me permissions for."
|
"I attempted to do something that Discord denied me permissions for."
|
||||||
" Your command failed to successfully complete."
|
" Your command failed to successfully complete."
|
||||||
)
|
)
|
||||||
|
|
||||||
hierarchy_issue = lambda: _(
|
HIERARCHY_ISSUE = _(
|
||||||
"I tried to add {role.name} to {member.display_name} but that role"
|
"I tried to add {role.name} to {member.display_name} but that role"
|
||||||
" is higher than my highest role in the Discord hierarchy so I was"
|
" is higher than my highest role in the Discord hierarchy so I was"
|
||||||
" unable to successfully add it. Please give me a higher role and "
|
" unable to successfully add it. Please give me a higher role and "
|
||||||
"try again."
|
"try again."
|
||||||
)
|
)
|
||||||
|
|
||||||
user_hierarchy_issue = lambda: _(
|
USER_HIERARCHY_ISSUE = _(
|
||||||
"I tried to add {role.name} to {member.display_name} but that role"
|
"I tried to add {role.name} to {member.display_name} but that role"
|
||||||
" is higher than your highest role in the Discord hierarchy so I was"
|
" is higher than your highest role in the Discord hierarchy so I was"
|
||||||
" unable to successfully add it. Please get a higher role and "
|
" unable to successfully add it. Please get a higher role and "
|
||||||
"try again."
|
"try again."
|
||||||
)
|
)
|
||||||
|
|
||||||
running_announcement = lambda: _(
|
RUNNING_ANNOUNCEMENT = _(
|
||||||
"I am already announcing something. If you would like to make a"
|
"I am already announcing something. If you would like to make a"
|
||||||
" different announcement please use `{prefix}announce cancel`"
|
" different announcement please use `{prefix}announce cancel`"
|
||||||
" first."
|
" first."
|
||||||
)
|
)
|
||||||
|
_ = T_
|
||||||
|
|
||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class Admin(commands.Cog):
|
class Admin(commands.Cog):
|
||||||
"""A collection of server administration utilities."""
|
"""A collection of server administration utilities."""
|
||||||
|
|
||||||
def __init__(self, config=Config):
|
def __init__(self, config=Config):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.conf = config.get_conf(self, 8237492837454039, force_registration=True)
|
self.conf = config.get_conf(self, 8237492837454039, force_registration=True)
|
||||||
@ -105,9 +104,9 @@ class Admin(commands.Cog):
|
|||||||
await member.add_roles(role)
|
await member.add_roles(role)
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
if not self.pass_hierarchy_check(ctx, role):
|
if not self.pass_hierarchy_check(ctx, role):
|
||||||
await self.complain(ctx, hierarchy_issue(), role=role, member=member)
|
await self.complain(ctx, T_(HIERARCHY_ISSUE), role=role, member=member)
|
||||||
else:
|
else:
|
||||||
await self.complain(ctx, generic_forbidden())
|
await self.complain(ctx, T_(GENERIC_FORBIDDEN))
|
||||||
else:
|
else:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("I successfully added {role.name} to {member.display_name}").format(
|
_("I successfully added {role.name} to {member.display_name}").format(
|
||||||
@ -120,9 +119,9 @@ class Admin(commands.Cog):
|
|||||||
await member.remove_roles(role)
|
await member.remove_roles(role)
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
if not self.pass_hierarchy_check(ctx, role):
|
if not self.pass_hierarchy_check(ctx, role):
|
||||||
await self.complain(ctx, hierarchy_issue(), role=role, member=member)
|
await self.complain(ctx, T_(HIERARCHY_ISSUE), role=role, member=member)
|
||||||
else:
|
else:
|
||||||
await self.complain(ctx, generic_forbidden())
|
await self.complain(ctx, T_(GENERIC_FORBIDDEN))
|
||||||
else:
|
else:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("I successfully removed {role.name} from {member.display_name}").format(
|
_("I successfully removed {role.name} from {member.display_name}").format(
|
||||||
@ -146,7 +145,7 @@ class Admin(commands.Cog):
|
|||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
await self._addrole(ctx, user, rolename)
|
await self._addrole(ctx, user, rolename)
|
||||||
else:
|
else:
|
||||||
await self.complain(ctx, user_hierarchy_issue(), member=ctx.author, role=rolename)
|
await self.complain(ctx, T_(USER_HIERARCHY_ISSUE), member=ctx.author, role=rolename)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@ -164,7 +163,7 @@ class Admin(commands.Cog):
|
|||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
await self._removerole(ctx, user, rolename)
|
await self._removerole(ctx, user, rolename)
|
||||||
else:
|
else:
|
||||||
await self.complain(ctx, user_hierarchy_issue())
|
await self.complain(ctx, T_(USER_HIERARCHY_ISSUE))
|
||||||
|
|
||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@ -191,13 +190,13 @@ class Admin(commands.Cog):
|
|||||||
reason = "{}({}) changed the colour of role '{}'".format(author.name, author.id, role.name)
|
reason = "{}({}) changed the colour of role '{}'".format(author.name, author.id, role.name)
|
||||||
|
|
||||||
if not self.pass_user_hierarchy_check(ctx, role):
|
if not self.pass_user_hierarchy_check(ctx, role):
|
||||||
await self.complain(ctx, user_hierarchy_issue())
|
await self.complain(ctx, T_(USER_HIERARCHY_ISSUE))
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await role.edit(reason=reason, color=value)
|
await role.edit(reason=reason, color=value)
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
await self.complain(ctx, generic_forbidden())
|
await self.complain(ctx, T_(GENERIC_FORBIDDEN))
|
||||||
else:
|
else:
|
||||||
log.info(reason)
|
log.info(reason)
|
||||||
await ctx.send(_("Done."))
|
await ctx.send(_("Done."))
|
||||||
@ -219,13 +218,13 @@ class Admin(commands.Cog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not self.pass_user_hierarchy_check(ctx, role):
|
if not self.pass_user_hierarchy_check(ctx, role):
|
||||||
await self.complain(ctx, user_hierarchy_issue())
|
await self.complain(ctx, T_(USER_HIERARCHY_ISSUE))
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await role.edit(reason=reason, name=name)
|
await role.edit(reason=reason, name=name)
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
await self.complain(ctx, generic_forbidden())
|
await self.complain(ctx, T_(GENERIC_FORBIDDEN))
|
||||||
else:
|
else:
|
||||||
log.info(reason)
|
log.info(reason)
|
||||||
await ctx.send(_("Done."))
|
await ctx.send(_("Done."))
|
||||||
@ -243,7 +242,7 @@ class Admin(commands.Cog):
|
|||||||
await ctx.send(_("The announcement has begun."))
|
await ctx.send(_("The announcement has begun."))
|
||||||
else:
|
else:
|
||||||
prefix = ctx.prefix
|
prefix = ctx.prefix
|
||||||
await self.complain(ctx, running_announcement(), prefix=prefix)
|
await self.complain(ctx, T_(RUNNING_ANNOUNCEMENT), prefix=prefix)
|
||||||
|
|
||||||
@announce.command(name="cancel")
|
@announce.command(name="cancel")
|
||||||
@checks.is_owner()
|
@checks.is_owner()
|
||||||
@ -381,7 +380,7 @@ class Admin(commands.Cog):
|
|||||||
serverlocked = await self.conf.serverlocked()
|
serverlocked = await self.conf.serverlocked()
|
||||||
await self.conf.serverlocked.set(not serverlocked)
|
await self.conf.serverlocked.set(not serverlocked)
|
||||||
|
|
||||||
if serverlocked: # again with original logic I'm not sure of
|
if serverlocked:
|
||||||
await ctx.send(_("The bot is no longer serverlocked."))
|
await ctx.send(_("The bot is no longer serverlocked."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("The bot is now serverlocked."))
|
await ctx.send(_("The bot is now serverlocked."))
|
||||||
|
|||||||
@ -113,7 +113,8 @@ class Alias(commands.Cog):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
async def get_prefix(self, message: discord.Message) -> str:
|
async def get_prefix(self, message: discord.Message) -> str:
|
||||||
"""Tries to determine what prefix is used in a message object.
|
"""
|
||||||
|
Tries to determine what prefix is used in a message object.
|
||||||
Looks to identify from longest prefix to smallest.
|
Looks to identify from longest prefix to smallest.
|
||||||
|
|
||||||
Will raise ValueError if no prefix is found.
|
Will raise ValueError if no prefix is found.
|
||||||
@ -175,7 +176,7 @@ class Alias(commands.Cog):
|
|||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def alias(self, ctx: commands.Context):
|
async def alias(self, ctx: commands.Context):
|
||||||
"""Manage per-server aliases for commands."""
|
"""Manage command aliases."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@alias.group(name="global")
|
@alias.group(name="global")
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import redbot.core
|
|||||||
from redbot.core import Config, commands, checks, bank
|
from redbot.core import Config, commands, checks, bank
|
||||||
from redbot.core.data_manager import cog_data_path
|
from redbot.core.data_manager import cog_data_path
|
||||||
from redbot.core.i18n import Translator, cog_i18n
|
from redbot.core.i18n import Translator, cog_i18n
|
||||||
|
from redbot.core.utils.chat_formatting import bold, box
|
||||||
from redbot.core.utils.menus import (
|
from redbot.core.utils.menus import (
|
||||||
menu,
|
menu,
|
||||||
DEFAULT_CONTROLS,
|
DEFAULT_CONTROLS,
|
||||||
@ -35,6 +36,7 @@ __author__ = ["aikaterna", "billy/bollo/ati"]
|
|||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class Audio(commands.Cog):
|
class Audio(commands.Cog):
|
||||||
"""Play audio through voice channels."""
|
"""Play audio through voice channels."""
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
@ -151,8 +153,8 @@ class Audio(commands.Cog):
|
|||||||
description=description,
|
description=description,
|
||||||
)
|
)
|
||||||
embed.set_footer(
|
embed.set_footer(
|
||||||
text="Track length: {} | Requested by: {}".format(
|
text=_("Track length: {length} | Requested by: {user}").format(
|
||||||
dur, player.current.requester
|
length=dur, user=player.current.requester
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
@ -175,7 +177,7 @@ class Audio(commands.Cog):
|
|||||||
if playing_servers > 1:
|
if playing_servers > 1:
|
||||||
await self.bot.change_presence(
|
await self.bot.change_presence(
|
||||||
activity=discord.Activity(
|
activity=discord.Activity(
|
||||||
name=_("music in {num} servers").format(num=playing_servers),
|
name=_("music in {} servers").format(playing_servers),
|
||||||
type=discord.ActivityType.playing,
|
type=discord.ActivityType.playing,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -201,7 +203,7 @@ class Audio(commands.Cog):
|
|||||||
if playing_servers > 1:
|
if playing_servers > 1:
|
||||||
await self.bot.change_presence(
|
await self.bot.change_presence(
|
||||||
activity=discord.Activity(
|
activity=discord.Activity(
|
||||||
name="music in {} servers".format(playing_servers),
|
name=_("music in {} servers").format(playing_servers),
|
||||||
type=discord.ActivityType.playing,
|
type=discord.ActivityType.playing,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -247,7 +249,7 @@ class Audio(commands.Cog):
|
|||||||
await ctx.bot.wait_for("message", timeout=15.0, check=pred)
|
await ctx.bot.wait_for("message", timeout=15.0, check=pred)
|
||||||
await ctx.invoke(self.role, pred.result)
|
await ctx.invoke(self.role, pred.result)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
return await self._embed_msg(ctx, "Response timed out, try again later.")
|
return await self._embed_msg(ctx, _("Response timed out, try again later."))
|
||||||
|
|
||||||
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||||
await self.config.guild(ctx.guild).dj_enabled.set(not dj_enabled)
|
await self.config.guild(ctx.guild).dj_enabled.set(not dj_enabled)
|
||||||
@ -282,7 +284,7 @@ class Audio(commands.Cog):
|
|||||||
"""Set the role to use for DJ mode."""
|
"""Set the role to use for DJ mode."""
|
||||||
await self.config.guild(ctx.guild).dj_role.set(role_name.id)
|
await self.config.guild(ctx.guild).dj_role.set(role_name.id)
|
||||||
dj_role_obj = ctx.guild.get_role(await self.config.guild(ctx.guild).dj_role())
|
dj_role_obj = ctx.guild.get_role(await self.config.guild(ctx.guild).dj_role())
|
||||||
await self._embed_msg(ctx, "DJ role set to: {}.".format(dj_role_obj.name))
|
await self._embed_msg(ctx, _("DJ role set to: {role.name}.").format(role=dj_role_obj))
|
||||||
|
|
||||||
@audioset.command()
|
@audioset.command()
|
||||||
@checks.mod_or_permissions(administrator=True)
|
@checks.mod_or_permissions(administrator=True)
|
||||||
@ -330,7 +332,7 @@ class Audio(commands.Cog):
|
|||||||
jarbuild = redbot.core.__version__
|
jarbuild = redbot.core.__version__
|
||||||
|
|
||||||
vote_percent = data["vote_percent"]
|
vote_percent = data["vote_percent"]
|
||||||
msg = _("```ini\n----Server Settings----\n")
|
msg = "----" + _("Server Settings") + "----"
|
||||||
if emptydc_enabled:
|
if emptydc_enabled:
|
||||||
msg += _("Disconnect timer: [{num_seconds}]\n").format(
|
msg += _("Disconnect timer: [{num_seconds}]\n").format(
|
||||||
num_seconds=self._dynamic_time(emptydc_timer)
|
num_seconds=self._dynamic_time(emptydc_timer)
|
||||||
@ -347,7 +349,7 @@ class Audio(commands.Cog):
|
|||||||
"Songs as status: [{status}]\n"
|
"Songs as status: [{status}]\n"
|
||||||
).format(**global_data, **data)
|
).format(**global_data, **data)
|
||||||
if thumbnail:
|
if thumbnail:
|
||||||
msg += "Thumbnails: [{0}]\n".format(thumbnail)
|
msg += _("Thumbnails: [{0}]\n").format(thumbnail)
|
||||||
if vote_percent > 0:
|
if vote_percent > 0:
|
||||||
msg += _(
|
msg += _(
|
||||||
"Vote skip: [{vote_enabled}]\nSkip percentage: [{vote_percent}%]\n"
|
"Vote skip: [{vote_enabled}]\nSkip percentage: [{vote_percent}%]\n"
|
||||||
@ -356,10 +358,10 @@ class Audio(commands.Cog):
|
|||||||
"---Lavalink Settings---\n"
|
"---Lavalink Settings---\n"
|
||||||
"Cog version: [{version}]\n"
|
"Cog version: [{version}]\n"
|
||||||
"Jar build: [{jarbuild}]\n"
|
"Jar build: [{jarbuild}]\n"
|
||||||
"External server: [{use_external_lavalink}]```"
|
"External server: [{use_external_lavalink}]"
|
||||||
).format(version=__version__, jarbuild=jarbuild, **global_data)
|
).format(version=__version__, jarbuild=jarbuild, **global_data)
|
||||||
|
|
||||||
embed = discord.Embed(colour=await ctx.embed_colour(), description=msg)
|
embed = discord.Embed(colour=await ctx.embed_colour(), description=box(msg, lang="ini"))
|
||||||
return await ctx.send(embed=embed)
|
return await ctx.send(embed=embed)
|
||||||
|
|
||||||
@audioset.command()
|
@audioset.command()
|
||||||
@ -368,7 +370,7 @@ class Audio(commands.Cog):
|
|||||||
"""Toggle displaying a thumbnail on audio messages."""
|
"""Toggle displaying a thumbnail on audio messages."""
|
||||||
thumbnail = await self.config.guild(ctx.guild).thumbnail()
|
thumbnail = await self.config.guild(ctx.guild).thumbnail()
|
||||||
await self.config.guild(ctx.guild).thumbnail.set(not thumbnail)
|
await self.config.guild(ctx.guild).thumbnail.set(not thumbnail)
|
||||||
await self._embed_msg(ctx, "Thumbnail display: {}.".format(not thumbnail))
|
await self._embed_msg(ctx, _("Thumbnail display: {}.").format(not thumbnail))
|
||||||
|
|
||||||
@audioset.command()
|
@audioset.command()
|
||||||
@checks.mod_or_permissions(administrator=True)
|
@checks.mod_or_permissions(administrator=True)
|
||||||
@ -498,11 +500,11 @@ class Audio(commands.Cog):
|
|||||||
if self._player_check(ctx):
|
if self._player_check(ctx):
|
||||||
if dj_enabled:
|
if dj_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, "You need the DJ role to disconnect.")
|
return await self._embed_msg(ctx, _("You need the DJ role to disconnect."))
|
||||||
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
|
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(
|
||||||
ctx, ctx.author
|
ctx, ctx.author
|
||||||
):
|
):
|
||||||
return await self._embed_msg(ctx, "There are other people listening to music.")
|
return await self._embed_msg(ctx, _("There are other people listening to music."))
|
||||||
else:
|
else:
|
||||||
await lavalink.get_player(ctx.guild.id).stop()
|
await lavalink.get_player(ctx.guild.id).stop()
|
||||||
return await lavalink.get_player(ctx.guild.id).disconnect()
|
return await lavalink.get_player(ctx.guild.id).disconnect()
|
||||||
@ -510,7 +512,7 @@ class Audio(commands.Cog):
|
|||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def local(self, ctx):
|
async def local(self, ctx):
|
||||||
"""Local playback options."""
|
"""Local playback commands."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@local.command(name="folder")
|
@local.command(name="folder")
|
||||||
@ -527,7 +529,7 @@ class Audio(commands.Cog):
|
|||||||
return
|
return
|
||||||
localtracks_folders = await self._localtracks_folders(ctx)
|
localtracks_folders = await self._localtracks_folders(ctx)
|
||||||
if not localtracks_folders:
|
if not localtracks_folders:
|
||||||
return await self._embed_msg(ctx, "No album folders found.")
|
return await self._embed_msg(ctx, _("No local track folders found."))
|
||||||
len_folder_pages = math.ceil(len(localtracks_folders) / 5)
|
len_folder_pages = math.ceil(len(localtracks_folders) / 5)
|
||||||
folder_page_list = []
|
folder_page_list = []
|
||||||
for page_num in range(1, len_folder_pages + 1):
|
for page_num in range(1, len_folder_pages + 1):
|
||||||
@ -573,14 +575,14 @@ class Audio(commands.Cog):
|
|||||||
return
|
return
|
||||||
localtracks_folders = await self._localtracks_folders(ctx)
|
localtracks_folders = await self._localtracks_folders(ctx)
|
||||||
if not localtracks_folders:
|
if not localtracks_folders:
|
||||||
return await self._embed_msg(ctx, "No album folders found.")
|
return await self._embed_msg(ctx, _("No album folders found."))
|
||||||
all_tracks = []
|
all_tracks = []
|
||||||
for local_folder in localtracks_folders:
|
for local_folder in localtracks_folders:
|
||||||
folder_tracks = await self._folder_list(ctx, local_folder)
|
folder_tracks = await self._folder_list(ctx, local_folder)
|
||||||
all_tracks = all_tracks + folder_tracks
|
all_tracks = all_tracks + folder_tracks
|
||||||
search_list = await self._build_local_search_list(all_tracks, search_words)
|
search_list = await self._build_local_search_list(all_tracks, search_words)
|
||||||
if not search_list:
|
if not search_list:
|
||||||
return await self._embed_msg(ctx, "No matches.")
|
return await self._embed_msg(ctx, _("No matches."))
|
||||||
await ctx.invoke(self.search, query=search_list)
|
await ctx.invoke(self.search, query=search_list)
|
||||||
|
|
||||||
async def _all_folder_tracks(self, ctx, folder):
|
async def _all_folder_tracks(self, ctx, folder):
|
||||||
@ -656,7 +658,7 @@ class Audio(commands.Cog):
|
|||||||
f for f in os.listdir(os.getcwd()) if not os.path.isfile(f) if f == "localtracks"
|
f for f in os.listdir(os.getcwd()) if not os.path.isfile(f) if f == "localtracks"
|
||||||
)
|
)
|
||||||
if not localtracks_folder:
|
if not localtracks_folder:
|
||||||
await self._embed_msg(ctx, "No localtracks folder.")
|
await self._embed_msg(ctx, _("No localtracks folder."))
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
@ -772,7 +774,7 @@ class Audio(commands.Cog):
|
|||||||
|
|
||||||
command = ctx.invoked_with
|
command = ctx.invoked_with
|
||||||
if not player.current:
|
if not player.current:
|
||||||
return await self._embed_msg(ctx, "Nothing playing.")
|
return await self._embed_msg(ctx, _("Nothing playing."))
|
||||||
if "localtracks/" in player.current.uri:
|
if "localtracks/" in player.current.uri:
|
||||||
description = "**{}**\n{}".format(
|
description = "**{}**\n{}".format(
|
||||||
player.current.title, player.current.uri.replace("localtracks/", "")
|
player.current.title, player.current.uri.replace("localtracks/", "")
|
||||||
@ -988,7 +990,9 @@ class Audio(commands.Cog):
|
|||||||
if track_list and len(to_append) == 1 and to_append[0] in track_list:
|
if track_list and len(to_append) == 1 and to_append[0] in track_list:
|
||||||
return await self._embed_msg(
|
return await self._embed_msg(
|
||||||
ctx,
|
ctx,
|
||||||
"{} already in {}.".format(to_append[0]["info"]["title"], playlist_name),
|
_("{track} is already in {playlist}.").format(
|
||||||
|
track=to_append[0]["info"]["title"], playlist=playlist_name
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if track_list:
|
if track_list:
|
||||||
playlists[playlist_name]["tracks"] = track_list + to_append
|
playlists[playlist_name]["tracks"] = track_list + to_append
|
||||||
@ -1080,7 +1084,7 @@ class Audio(commands.Cog):
|
|||||||
"""List saved playlists."""
|
"""List saved playlists."""
|
||||||
playlists = await self.config.guild(ctx.guild).playlists.get_raw()
|
playlists = await self.config.guild(ctx.guild).playlists.get_raw()
|
||||||
if not playlists:
|
if not playlists:
|
||||||
return await self._embed_msg(ctx, "No saved playlists.")
|
return await self._embed_msg(ctx, _("No saved playlists."))
|
||||||
playlist_list = []
|
playlist_list = []
|
||||||
space = "\N{EN SPACE}"
|
space = "\N{EN SPACE}"
|
||||||
for playlist_name in playlists:
|
for playlist_name in playlists:
|
||||||
@ -1089,12 +1093,12 @@ class Audio(commands.Cog):
|
|||||||
tracks = []
|
tracks = []
|
||||||
author = playlists[playlist_name]["author"]
|
author = playlists[playlist_name]["author"]
|
||||||
playlist_list.append(
|
playlist_list.append(
|
||||||
"**{}**\n{}Tracks: {}\n{}Author: {}\n".format(
|
("\n" + space * 4).join(
|
||||||
playlist_name,
|
(
|
||||||
(space * 4),
|
bold(playlist_name),
|
||||||
str(len(tracks)),
|
_("Tracks: {num}").format(num=len(tracks)),
|
||||||
(space * 4),
|
_("Author: {name}").format(self.bot.get_user(author)),
|
||||||
self.bot.get_user(author),
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
abc_names = sorted(playlist_list, key=str.lower)
|
abc_names = sorted(playlist_list, key=str.lower)
|
||||||
@ -1121,7 +1125,9 @@ class Audio(commands.Cog):
|
|||||||
description=plist,
|
description=plist,
|
||||||
)
|
)
|
||||||
embed.set_footer(
|
embed.set_footer(
|
||||||
text="Page {}/{} | {} playlists".format(page_num, plist_num_pages, len(abc_names))
|
text=_("Page {page_num}/{total_pages} | {num} playlists").format(
|
||||||
|
page_num=page_num, total_pages=plist_num_pages, num=len(abc_names)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
@ -1449,9 +1455,11 @@ class Audio(commands.Cog):
|
|||||||
player.current.title, player.current.uri.replace("localtracks/", "")
|
player.current.title, player.current.uri.replace("localtracks/", "")
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
description = "**[{}]({})**".format(player.current.title, player.current.uri)
|
description = f"**[{player.current.title}]({player.current.title})**"
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
colour=await ctx.embed_colour(), title=_("Replaying Track"), description=description
|
colour=await ctx.embed_colour(),
|
||||||
|
title=_("Replaying Track"),
|
||||||
|
description=description,
|
||||||
)
|
)
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@ -1460,7 +1468,8 @@ class Audio(commands.Cog):
|
|||||||
async def queue(self, ctx, *, page="1"):
|
async def queue(self, ctx, *, page="1"):
|
||||||
"""List the queue.
|
"""List the queue.
|
||||||
|
|
||||||
Use [p]queue search <search terms> to search the queue."""
|
Use [p]queue search <search terms> to search the queue.
|
||||||
|
"""
|
||||||
if not self._player_check(ctx):
|
if not self._player_check(ctx):
|
||||||
return await self._embed_msg(ctx, _("There's nothing in the queue."))
|
return await self._embed_msg(ctx, _("There's nothing in the queue."))
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
@ -1505,28 +1514,28 @@ class Audio(commands.Cog):
|
|||||||
|
|
||||||
elif "localtracks" in player.current.uri:
|
elif "localtracks" in player.current.uri:
|
||||||
if not player.current.title == "Unknown title":
|
if not player.current.title == "Unknown title":
|
||||||
queue_list += "Playing: **{} - {}**\n{}\nRequested by: **{}**\n\n{}`{}`/`{}`\n\n".format(
|
queue_list += "\n".join(
|
||||||
player.current.author,
|
(
|
||||||
player.current.title,
|
_("Playing: ")
|
||||||
|
+ "**{current.author} - {current.title}**".format(current=player.current),
|
||||||
player.current.uri.replace("localtracks/", ""),
|
player.current.uri.replace("localtracks/", ""),
|
||||||
player.current.requester,
|
_("Requested by: **{user}**\n").format(user=player.current.requester),
|
||||||
arrow,
|
f"{arrow}`{pos}`/`{dur}`\n\n",
|
||||||
pos,
|
)
|
||||||
dur,
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
queue_list += "Playing: {}\nRequested by: **{}**\n\n{}`{}`/`{}`\n\n".format(
|
queue_list += "\n".join(
|
||||||
player.current.uri.replace("localtracks/", ""),
|
(
|
||||||
player.current.requester,
|
_("Playing: ") + player.current.uri.replace("localtracks/", ""),
|
||||||
arrow,
|
_("Requested by: **{user}**\n").format(user=player.current.requester),
|
||||||
pos,
|
f"{arrow}`{pos}`/`{dur}`\n\n",
|
||||||
dur,
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
queue_list += _("Playing:")
|
queue_list += _("Playing: ")
|
||||||
queue_list += " **[{current.title}]({current.uri})**\n".format(current=player.current)
|
queue_list += "**[{current.title}]({current.uri})**\n".format(current=player.current)
|
||||||
queue_list += _("Requested by: **{user}**").format(user=player.current.requester)
|
queue_list += _("Requested by: **{user}**").format(user=player.current.requester)
|
||||||
queue_list += "\n\n{arrow}`{pos}`/`{dur}`\n\n".format(arrow=arrow, pos=pos, dur=dur)
|
queue_list += f"\n\n{arrow}`{pos}`/`{dur}`\n\n"
|
||||||
|
|
||||||
for i, track in enumerate(
|
for i, track in enumerate(
|
||||||
player.queue[queue_idx_start:queue_idx_end], start=queue_idx_start
|
player.queue[queue_idx_start:queue_idx_end], start=queue_idx_start
|
||||||
@ -1540,17 +1549,18 @@ class Audio(commands.Cog):
|
|||||||
track_idx = i + 1
|
track_idx = i + 1
|
||||||
if "localtracks" in track.uri:
|
if "localtracks" in track.uri:
|
||||||
if track.title == "Unknown title":
|
if track.title == "Unknown title":
|
||||||
queue_list += "`{}.` **{}**, requested by **{}**\n".format(
|
queue_list += f"`{track_idx}.` " + ", ".join(
|
||||||
track_idx, track.uri.replace("localtracks/", ""), req_user
|
(
|
||||||
|
bold(track.uri.replace("localtracks/", "")),
|
||||||
|
_("requested by **{user}**\n").format(user=req_user),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
queue_list += "`{}.` **{} - {}**, requested by **{}**\n".format(
|
queue_list += f"`{track_idx}.` **{track.author} - {track_title}**, " + _(
|
||||||
track_idx, track.author, track_title, req_user
|
"requested by **{user}**\n"
|
||||||
)
|
).format(user=req_user)
|
||||||
else:
|
else:
|
||||||
queue_list += "`{idx}.` **[{title}]({uri})**, ".format(
|
queue_list += f"`{track_idx}.` **[{track_title}]({track.uri})**, "
|
||||||
idx=track_idx, title=track_title, uri=track.uri
|
|
||||||
)
|
|
||||||
queue_list += _("requested by **{user}**\n").format(user=req_user)
|
queue_list += _("requested by **{user}**\n").format(user=req_user)
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
@ -1581,7 +1591,7 @@ class Audio(commands.Cog):
|
|||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
search_list = await self._build_queue_search_list(player.queue, search_words)
|
search_list = await self._build_queue_search_list(player.queue, search_words)
|
||||||
if not search_list:
|
if not search_list:
|
||||||
return await self._embed_msg(ctx, "No matches.")
|
return await self._embed_msg(ctx, _("No matches."))
|
||||||
len_search_pages = math.ceil(len(search_list) / 10)
|
len_search_pages = math.ceil(len(search_list) / 10)
|
||||||
search_page_list = []
|
search_page_list = []
|
||||||
for page_num in range(1, len_search_pages + 1):
|
for page_num in range(1, len_search_pages + 1):
|
||||||
@ -1630,10 +1640,12 @@ class Audio(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
track_match += "`{}.` **{}**\n".format(track[0], track[1])
|
track_match += "`{}.` **{}**\n".format(track[0], track[1])
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
colour=await ctx.embed_colour(), title="Matching Tracks:", description=track_match
|
colour=await ctx.embed_colour(), title=_("Matching Tracks:"), description=track_match
|
||||||
)
|
)
|
||||||
embed.set_footer(
|
embed.set_footer(
|
||||||
text="Page {}/{} | {} tracks".format(page_num, search_num_pages, len(search_list))
|
text=(_("Page {page_num}/{total_pages}") + " | {num_tracks} tracks").format(
|
||||||
|
page_num=page_num, total_pages=search_num_pages, num_tracks=len(search_list)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
return embed
|
return embed
|
||||||
|
|
||||||
@ -1704,8 +1716,9 @@ class Audio(commands.Cog):
|
|||||||
async def search(self, ctx, *, query):
|
async def search(self, ctx, *, query):
|
||||||
"""Pick a track with a search.
|
"""Pick a track with a search.
|
||||||
|
|
||||||
Use `[p]search list <search term>` to queue all tracks found on YouTube.
|
Use `[p]search list <search term>` to queue all tracks found
|
||||||
`[p]search sc <search term>` will search SoundCloud instead of YouTube.
|
on YouTube. `[p]search sc <search term>` will search SoundCloud
|
||||||
|
instead of YouTube.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def _search_menu(
|
async def _search_menu(
|
||||||
@ -1777,9 +1790,9 @@ class Audio(commands.Cog):
|
|||||||
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
||||||
if not shuffle and queue_duration > 0:
|
if not shuffle and queue_duration > 0:
|
||||||
songembed.set_footer(
|
songembed.set_footer(
|
||||||
text=_("{time} until start of search playback: starts at #{position} in queue").format(
|
text=_(
|
||||||
time=queue_total_duration, position=len(player.queue) + 1
|
"{time} until start of search playback: starts at #{position} in queue"
|
||||||
)
|
).format(time=queue_total_duration, position=len(player.queue) + 1)
|
||||||
)
|
)
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
player.add(ctx.author, track)
|
player.add(ctx.author, track)
|
||||||
@ -1829,7 +1842,7 @@ class Audio(commands.Cog):
|
|||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store("connect", datetime.datetime.utcnow())
|
player.store("connect", datetime.datetime.utcnow())
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return await self._embed_msg(ctx, "Connect to a voice channel first.")
|
return await self._embed_msg(ctx, _("Connect to a voice channel first."))
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
jukebox_price = await self.config.guild(ctx.guild).jukebox_price()
|
jukebox_price = await self.config.guild(ctx.guild).jukebox_price()
|
||||||
shuffle = await self.config.guild(ctx.guild).shuffle()
|
shuffle = await self.config.guild(ctx.guild).shuffle()
|
||||||
@ -1886,9 +1899,7 @@ class Audio(commands.Cog):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif queue_duration > 0:
|
elif queue_duration > 0:
|
||||||
embed.set_footer(
|
embed.set_footer(text=_("#{position} in queue").format(position=len(player.queue) + 1))
|
||||||
text=_("#{position} in queue").format(position=len(player.queue) + 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
player.add(ctx.author, search_choice)
|
player.add(ctx.author, search_choice)
|
||||||
if not player.current:
|
if not player.current:
|
||||||
@ -1949,13 +1960,11 @@ class Audio(commands.Cog):
|
|||||||
colour=await ctx.embed_colour(), title=title, description=search_list
|
colour=await ctx.embed_colour(), title=title, description=search_list
|
||||||
)
|
)
|
||||||
embed.set_footer(
|
embed.set_footer(
|
||||||
text=(
|
text=(_("Page {page_num}/{total_pages}") + " | {num_results} {footer}").format(
|
||||||
_("Page {page_num}/{total_pages}") + " | {num_results} {footer}"
|
|
||||||
).format(
|
|
||||||
page_num=page_num,
|
page_num=page_num,
|
||||||
total_pages=search_num_pages,
|
total_pages=search_num_pages,
|
||||||
num_results=len(tracks),
|
num_results=len(tracks),
|
||||||
footer=footer
|
footer=footer,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return embed
|
return embed
|
||||||
@ -2025,7 +2034,7 @@ class Audio(commands.Cog):
|
|||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def sing(self, ctx):
|
async def sing(self, ctx):
|
||||||
"""Makes Red sing one of her songs"""
|
"""Make Red sing one of her songs"""
|
||||||
ids = (
|
ids = (
|
||||||
"zGTkAVsrfg8",
|
"zGTkAVsrfg8",
|
||||||
"cGMWL8cOeAU",
|
"cGMWL8cOeAU",
|
||||||
@ -2040,7 +2049,7 @@ class Audio(commands.Cog):
|
|||||||
@commands.command(aliases=["forceskip", "fs"])
|
@commands.command(aliases=["forceskip", "fs"])
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def skip(self, ctx):
|
async def skip(self, ctx):
|
||||||
"""Skips to the next track."""
|
"""Skip to the next track."""
|
||||||
if not self._player_check(ctx):
|
if not self._player_check(ctx):
|
||||||
return await self._embed_msg(ctx, _("Nothing playing."))
|
return await self._embed_msg(ctx, _("Nothing playing."))
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
@ -2078,11 +2087,14 @@ class Audio(commands.Cog):
|
|||||||
await self._embed_msg(ctx, _("Vote threshold met."))
|
await self._embed_msg(ctx, _("Vote threshold met."))
|
||||||
return await self._skip_action(ctx)
|
return await self._skip_action(ctx)
|
||||||
else:
|
else:
|
||||||
reply += _(" Votes: {num_votes}/{num_members}").format(
|
reply += _(
|
||||||
num_votes=num_votes, num_members=num_members
|
" Votes: {num_votes}/{num_members}"
|
||||||
)
|
" ({cur_percent}% out of {required_percent}% needed)"
|
||||||
reply += _(" ({cur_percent}% out of {required_percent}% needed)").format(
|
).format(
|
||||||
cur_percent=vote, required_percent=percent
|
num_votes=num_votes,
|
||||||
|
num_members=num_members,
|
||||||
|
cur_percent=vote,
|
||||||
|
required_percent=percent,
|
||||||
)
|
)
|
||||||
return await self._embed_msg(ctx, reply)
|
return await self._embed_msg(ctx, reply)
|
||||||
else:
|
else:
|
||||||
@ -2304,8 +2316,7 @@ class Audio(commands.Cog):
|
|||||||
await self.config.host.set(host)
|
await self.config.host.set(host)
|
||||||
if await self._check_external():
|
if await self._check_external():
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
colour=await ctx.embed_colour(),
|
colour=await ctx.embed_colour(), title=_("Host set to {host}.").format(host=host)
|
||||||
title=_("Host set to {host}.").format(host=host),
|
|
||||||
)
|
)
|
||||||
embed.set_footer(text=_("External lavalink server set to True."))
|
embed.set_footer(text=_("External lavalink server set to True."))
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
@ -2348,7 +2359,8 @@ class Audio(commands.Cog):
|
|||||||
await self.config.ws_port.set(ws_port)
|
await self.config.ws_port.set(ws_port)
|
||||||
if await self._check_external():
|
if await self._check_external():
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
colour=await ctx.embed_colour(), title=_("Websocket port set to {}.").format(ws_port)
|
colour=await ctx.embed_colour(),
|
||||||
|
title=_("Websocket port set to {}.").format(ws_port),
|
||||||
)
|
)
|
||||||
embed.set_footer(text=_("External lavalink server set to True."))
|
embed.set_footer(text=_("External lavalink server set to True."))
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ _ = Translator("Cleanup", __file__)
|
|||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class Cleanup(commands.Cog):
|
class Cleanup(commands.Cog):
|
||||||
"""Commands for cleaning messages."""
|
"""Commands for cleaning up messages."""
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -41,7 +41,7 @@ class Cleanup(commands.Cog):
|
|||||||
await prompt.delete()
|
await prompt.delete()
|
||||||
try:
|
try:
|
||||||
await response.delete()
|
await response.delete()
|
||||||
except:
|
except discord.HTTPException:
|
||||||
pass
|
pass
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@ -109,6 +109,7 @@ class Cleanup(commands.Cog):
|
|||||||
|
|
||||||
@cleanup.command()
|
@cleanup.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_messages=True)
|
||||||
async def text(
|
async def text(
|
||||||
self, ctx: commands.Context, text: str, number: int, delete_pinned: bool = False
|
self, ctx: commands.Context, text: str, number: int, delete_pinned: bool = False
|
||||||
):
|
):
|
||||||
@ -121,9 +122,6 @@ class Cleanup(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
if not channel.permissions_for(ctx.guild.me).manage_messages:
|
|
||||||
await ctx.send(_("I need the Manage Messages permission to do this."))
|
|
||||||
return
|
|
||||||
|
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
|
|
||||||
@ -157,6 +155,7 @@ class Cleanup(commands.Cog):
|
|||||||
|
|
||||||
@cleanup.command()
|
@cleanup.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_messages=True)
|
||||||
async def user(
|
async def user(
|
||||||
self, ctx: commands.Context, user: str, number: int, delete_pinned: bool = False
|
self, ctx: commands.Context, user: str, number: int, delete_pinned: bool = False
|
||||||
):
|
):
|
||||||
@ -167,9 +166,6 @@ class Cleanup(commands.Cog):
|
|||||||
`[p]cleanup user Red 6`
|
`[p]cleanup user Red 6`
|
||||||
"""
|
"""
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
if not channel.permissions_for(ctx.guild.me).manage_messages:
|
|
||||||
await ctx.send(_("I need the Manage Messages permission to do this."))
|
|
||||||
return
|
|
||||||
|
|
||||||
member = None
|
member = None
|
||||||
try:
|
try:
|
||||||
@ -215,6 +211,7 @@ class Cleanup(commands.Cog):
|
|||||||
|
|
||||||
@cleanup.command()
|
@cleanup.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_messages=True)
|
||||||
async def after(self, ctx: commands.Context, message_id: int, delete_pinned: bool = False):
|
async def after(self, ctx: commands.Context, message_id: int, delete_pinned: bool = False):
|
||||||
"""Delete all messages after a specified message.
|
"""Delete all messages after a specified message.
|
||||||
|
|
||||||
@ -224,9 +221,6 @@ class Cleanup(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
if not channel.permissions_for(ctx.guild.me).manage_messages:
|
|
||||||
await ctx.send(_("I need the Manage Messages permission to do this."))
|
|
||||||
return
|
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -247,6 +241,7 @@ class Cleanup(commands.Cog):
|
|||||||
|
|
||||||
@cleanup.command()
|
@cleanup.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_messages=True)
|
||||||
async def before(
|
async def before(
|
||||||
self, ctx: commands.Context, message_id: int, number: int, delete_pinned: bool = False
|
self, ctx: commands.Context, message_id: int, number: int, delete_pinned: bool = False
|
||||||
):
|
):
|
||||||
@ -258,9 +253,6 @@ class Cleanup(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
if not channel.permissions_for(ctx.guild.me).manage_messages:
|
|
||||||
await ctx.send("I need the Manage Messages permission to do this.")
|
|
||||||
return
|
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -281,6 +273,7 @@ class Cleanup(commands.Cog):
|
|||||||
|
|
||||||
@cleanup.command()
|
@cleanup.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_messages=True)
|
||||||
async def messages(self, ctx: commands.Context, number: int, delete_pinned: bool = False):
|
async def messages(self, ctx: commands.Context, number: int, delete_pinned: bool = False):
|
||||||
"""Delete the last X messages.
|
"""Delete the last X messages.
|
||||||
|
|
||||||
@ -289,9 +282,6 @@ class Cleanup(commands.Cog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
if not channel.permissions_for(ctx.guild.me).manage_messages:
|
|
||||||
await ctx.send(_("I need the Manage Messages permission to do this."))
|
|
||||||
return
|
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
|
|
||||||
if number > 100:
|
if number > 100:
|
||||||
@ -313,13 +303,11 @@ class Cleanup(commands.Cog):
|
|||||||
|
|
||||||
@cleanup.command(name="bot")
|
@cleanup.command(name="bot")
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_messages=True)
|
||||||
async def cleanup_bot(self, ctx: commands.Context, number: int, delete_pinned: bool = False):
|
async def cleanup_bot(self, ctx: commands.Context, number: int, delete_pinned: bool = False):
|
||||||
"""Clean up command messages and messages from the bot."""
|
"""Clean up command messages and messages from the bot."""
|
||||||
|
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
if not channel.permissions_for(ctx.guild.me).manage_messages:
|
|
||||||
await ctx.send(_("I need the Manage Messages permission to do this."))
|
|
||||||
return
|
|
||||||
author = ctx.message.author
|
author = ctx.message.author
|
||||||
|
|
||||||
if number > 100:
|
if number > 100:
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
import os
|
|
||||||
import re
|
import re
|
||||||
import random
|
import random
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from inspect import Parameter
|
from inspect import Parameter
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from typing import Mapping
|
from typing import Mapping, Tuple, Dict
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ class CommandObj:
|
|||||||
# in the ccinfo dict
|
# in the ccinfo dict
|
||||||
return "{:%d/%m/%Y %H:%M:%S}".format(datetime.utcnow())
|
return "{:%d/%m/%Y %H:%M:%S}".format(datetime.utcnow())
|
||||||
|
|
||||||
async def get(self, message: discord.Message, command: str) -> str:
|
async def get(self, message: discord.Message, command: str) -> Tuple[str, Dict]:
|
||||||
ccinfo = await self.db(message.guild).commands.get_raw(command, default=None)
|
ccinfo = await self.db(message.guild).commands.get_raw(command, default=None)
|
||||||
if not ccinfo:
|
if not ccinfo:
|
||||||
raise NotFound()
|
raise NotFound()
|
||||||
@ -180,9 +179,7 @@ class CommandObj:
|
|||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class CustomCommands(commands.Cog):
|
class CustomCommands(commands.Cog):
|
||||||
"""Custom commands
|
"""Creates commands used to display text."""
|
||||||
|
|
||||||
Creates commands used to display text"""
|
|
||||||
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -227,8 +224,6 @@ class CustomCommands(commands.Cog):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# await ctx.send(str(responses))
|
|
||||||
|
|
||||||
@cc_create.command(name="simple")
|
@cc_create.command(name="simple")
|
||||||
@checks.mod_or_permissions(administrator=True)
|
@checks.mod_or_permissions(administrator=True)
|
||||||
async def cc_create_simple(self, ctx, command: str.lower, *, text: str):
|
async def cc_create_simple(self, ctx, command: str.lower, *, text: str):
|
||||||
@ -454,9 +449,8 @@ class CustomCommands(commands.Cog):
|
|||||||
gaps = set(indices).symmetric_difference(range(high + 1))
|
gaps = set(indices).symmetric_difference(range(high + 1))
|
||||||
if gaps:
|
if gaps:
|
||||||
raise ArgParseError(
|
raise ArgParseError(
|
||||||
_("Arguments must be sequential. Missing arguments: {}.").format(
|
_("Arguments must be sequential. Missing arguments: ")
|
||||||
", ".join(str(i + low) for i in gaps)
|
+ ", ".join(str(i + low) for i in gaps)
|
||||||
)
|
|
||||||
)
|
)
|
||||||
fin = [Parameter("_" + str(i), Parameter.POSITIONAL_OR_KEYWORD) for i in range(high + 1)]
|
fin = [Parameter("_" + str(i), Parameter.POSITIONAL_OR_KEYWORD) for i in range(high + 1)]
|
||||||
for arg in args:
|
for arg in args:
|
||||||
@ -481,8 +475,12 @@ class CustomCommands(commands.Cog):
|
|||||||
and anno != fin[index].annotation
|
and anno != fin[index].annotation
|
||||||
):
|
):
|
||||||
raise ArgParseError(
|
raise ArgParseError(
|
||||||
_('Conflicting colon notation for argument {}: "{}" and "{}".').format(
|
_(
|
||||||
index + low, fin[index].annotation.__name__, anno.__name__
|
'Conflicting colon notation for argument {index}: "{name1}" and "{name2}".'
|
||||||
|
).format(
|
||||||
|
index=index + low,
|
||||||
|
name1=fin[index].annotation.__name__,
|
||||||
|
name2=anno.__name__,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if anno is not Parameter.empty:
|
if anno is not Parameter.empty:
|
||||||
@ -511,6 +509,8 @@ class CustomCommands(commands.Cog):
|
|||||||
key = (command, ctx.guild, ctx.channel)
|
key = (command, ctx.guild, ctx.channel)
|
||||||
elif per == "member":
|
elif per == "member":
|
||||||
key = (command, ctx.guild, ctx.author)
|
key = (command, ctx.guild, ctx.author)
|
||||||
|
else:
|
||||||
|
raise ValueError(per)
|
||||||
cooldown = self.cooldowns.get(key)
|
cooldown = self.cooldowns.get(key)
|
||||||
if cooldown:
|
if cooldown:
|
||||||
cooldown += timedelta(seconds=rate)
|
cooldown += timedelta(seconds=rate)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ from sys import path as syspath
|
|||||||
from typing import Tuple, Union, Iterable
|
from typing import Tuple, Union, Iterable
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from redbot.core import checks, commands, Config, checks, commands
|
from redbot.core import checks, commands, Config
|
||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
from redbot.core.data_manager import cog_data_path
|
from redbot.core.data_manager import cog_data_path
|
||||||
from redbot.core.i18n import Translator, cog_i18n
|
from redbot.core.i18n import Translator, cog_i18n
|
||||||
@ -218,7 +218,7 @@ class Downloader(commands.Cog):
|
|||||||
"""Add a new repo.
|
"""Add a new repo.
|
||||||
|
|
||||||
The name can only contain characters A-z, numbers and underscores.
|
The name can only contain characters A-z, numbers and underscores.
|
||||||
The branch will default to master if not specified.
|
The branch will be the default branch if not specified.
|
||||||
"""
|
"""
|
||||||
agreed = await do_install_agreement(ctx)
|
agreed = await do_install_agreement(ctx)
|
||||||
if not agreed:
|
if not agreed:
|
||||||
@ -241,13 +241,13 @@ class Downloader(commands.Cog):
|
|||||||
if repo.install_msg is not None:
|
if repo.install_msg is not None:
|
||||||
await ctx.send(repo.install_msg.replace("[p]", ctx.prefix))
|
await ctx.send(repo.install_msg.replace("[p]", ctx.prefix))
|
||||||
|
|
||||||
@repo.command(name="delete", aliases=["remove"])
|
@repo.command(name="delete", aliases=["remove"], usage="<repo_name>")
|
||||||
async def _repo_del(self, ctx, repo_name: Repo):
|
async def _repo_del(self, ctx, repo: Repo):
|
||||||
"""Remove a repo and its files."""
|
"""Remove a repo and its files."""
|
||||||
await self._repo_manager.delete_repo(repo_name.name)
|
await self._repo_manager.delete_repo(repo.name)
|
||||||
|
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("The repo `{name}` has been deleted successfully.").format(name=repo_name.name)
|
_("The repo `{repo.name}` has been deleted successfully.").format(repo=repo)
|
||||||
)
|
)
|
||||||
|
|
||||||
@repo.command(name="list")
|
@repo.command(name="list")
|
||||||
@ -263,15 +263,15 @@ class Downloader(commands.Cog):
|
|||||||
for page in pagify(joined, ["\n"], shorten_by=16):
|
for page in pagify(joined, ["\n"], shorten_by=16):
|
||||||
await ctx.send(box(page.lstrip(" "), lang="diff"))
|
await ctx.send(box(page.lstrip(" "), lang="diff"))
|
||||||
|
|
||||||
@repo.command(name="info")
|
@repo.command(name="info", usage="<repo_name>")
|
||||||
async def _repo_info(self, ctx, repo_name: Repo):
|
async def _repo_info(self, ctx, repo: Repo):
|
||||||
"""Show information about a repo."""
|
"""Show information about a repo."""
|
||||||
if repo_name is None:
|
if repo is None:
|
||||||
await ctx.send(_("Repo `{repo_name}` not found.").format(repo_name=repo_name.name))
|
await ctx.send(_("Repo `{repo.name}` not found.").format(repo=repo))
|
||||||
return
|
return
|
||||||
|
|
||||||
msg = _("Information on {repo_name}:\n{description}").format(
|
msg = _("Information on {repo.name}:\n{description}").format(
|
||||||
repo_name=repo_name.name, description=repo_name.description or ""
|
repo=repo, description=repo.description or ""
|
||||||
)
|
)
|
||||||
await ctx.send(box(msg))
|
await ctx.send(box(msg))
|
||||||
|
|
||||||
@ -281,15 +281,15 @@ class Downloader(commands.Cog):
|
|||||||
"""Cog installation management commands."""
|
"""Cog installation management commands."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@cog.command(name="install")
|
@cog.command(name="install", usage="<repo_name> <cog_name>")
|
||||||
async def _cog_install(self, ctx, repo_name: Repo, cog_name: str):
|
async def _cog_install(self, ctx, repo: Repo, cog_name: str):
|
||||||
"""Install a cog from the given repo."""
|
"""Install a cog from the given repo."""
|
||||||
cog: Installable = discord.utils.get(repo_name.available_cogs, name=cog_name)
|
cog: Installable = discord.utils.get(repo.available_cogs, name=cog_name)
|
||||||
if cog is None:
|
if cog is None:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"Error: there is no cog by the name of `{cog_name}` in the `{repo_name}` repo."
|
"Error: there is no cog by the name of `{cog_name}` in the `{repo.name}` repo."
|
||||||
).format(cog_name=cog_name, repo_name=repo_name.name)
|
).format(cog_name=cog_name, repo=repo)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
elif cog.min_python_version > sys.version_info:
|
elif cog.min_python_version > sys.version_info:
|
||||||
@ -300,7 +300,7 @@ class Downloader(commands.Cog):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not await repo_name.install_requirements(cog, self.LIB_PATH):
|
if not await repo.install_requirements(cog, self.LIB_PATH):
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"Failed to install the required libraries for `{cog_name}`: `{libraries}`"
|
"Failed to install the required libraries for `{cog_name}`: `{libraries}`"
|
||||||
@ -308,31 +308,31 @@ class Downloader(commands.Cog):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
await repo_name.install_cog(cog, await self.cog_install_path())
|
await repo.install_cog(cog, await self.cog_install_path())
|
||||||
|
|
||||||
await self._add_to_installed(cog)
|
await self._add_to_installed(cog)
|
||||||
|
|
||||||
await repo_name.install_libraries(self.SHAREDLIB_PATH)
|
await repo.install_libraries(self.SHAREDLIB_PATH)
|
||||||
|
|
||||||
await ctx.send(_("Cog `{cog_name}` successfully installed.").format(cog_name=cog_name))
|
await ctx.send(_("Cog `{cog_name}` successfully installed.").format(cog_name=cog_name))
|
||||||
if cog.install_msg is not None:
|
if cog.install_msg is not None:
|
||||||
await ctx.send(cog.install_msg.replace("[p]", ctx.prefix))
|
await ctx.send(cog.install_msg.replace("[p]", ctx.prefix))
|
||||||
|
|
||||||
@cog.command(name="uninstall")
|
@cog.command(name="uninstall", usage="<cog_name>")
|
||||||
async def _cog_uninstall(self, ctx, cog_name: InstalledCog):
|
async def _cog_uninstall(self, ctx, cog: InstalledCog):
|
||||||
"""Uninstall a cog.
|
"""Uninstall a cog.
|
||||||
|
|
||||||
You may only uninstall cogs which were previously installed
|
You may only uninstall cogs which were previously installed
|
||||||
by Downloader.
|
by Downloader.
|
||||||
"""
|
"""
|
||||||
# noinspection PyUnresolvedReferences,PyProtectedMember
|
# noinspection PyUnresolvedReferences,PyProtectedMember
|
||||||
real_name = cog_name.name
|
real_name = cog.name
|
||||||
|
|
||||||
poss_installed_path = (await self.cog_install_path()) / real_name
|
poss_installed_path = (await self.cog_install_path()) / real_name
|
||||||
if poss_installed_path.exists():
|
if poss_installed_path.exists():
|
||||||
await self._delete_cog(poss_installed_path)
|
await self._delete_cog(poss_installed_path)
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
await self._remove_from_installed(cog_name)
|
await self._remove_from_installed(cog)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("Cog `{cog_name}` was successfully uninstalled.").format(cog_name=real_name)
|
_("Cog `{cog_name}` was successfully uninstalled.").format(cog_name=real_name)
|
||||||
)
|
)
|
||||||
@ -410,8 +410,8 @@ class Downloader(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
await ctx.send(_("OK then."))
|
await ctx.send(_("OK then."))
|
||||||
|
|
||||||
@cog.command(name="list")
|
@cog.command(name="list", usage="<repo_name>")
|
||||||
async def _cog_list(self, ctx, repo_name: Repo):
|
async def _cog_list(self, ctx, repo: Repo):
|
||||||
"""List all available cogs from a single repo."""
|
"""List all available cogs from a single repo."""
|
||||||
installed = await self.installed_cogs()
|
installed = await self.installed_cogs()
|
||||||
installed_str = ""
|
installed_str = ""
|
||||||
@ -420,10 +420,10 @@ class Downloader(commands.Cog):
|
|||||||
[
|
[
|
||||||
"- {}{}".format(i.name, ": {}".format(i.short) if i.short else "")
|
"- {}{}".format(i.name, ": {}".format(i.short) if i.short else "")
|
||||||
for i in installed
|
for i in installed
|
||||||
if i.repo_name == repo_name.name
|
if i.repo_name == repo.name
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
cogs = repo_name.available_cogs
|
cogs = repo.available_cogs
|
||||||
cogs = _("Available Cogs:\n") + "\n".join(
|
cogs = _("Available Cogs:\n") + "\n".join(
|
||||||
[
|
[
|
||||||
"+ {}: {}".format(c.name, c.short or "")
|
"+ {}: {}".format(c.name, c.short or "")
|
||||||
@ -435,14 +435,14 @@ class Downloader(commands.Cog):
|
|||||||
for page in pagify(cogs, ["\n"], shorten_by=16):
|
for page in pagify(cogs, ["\n"], shorten_by=16):
|
||||||
await ctx.send(box(page.lstrip(" "), lang="diff"))
|
await ctx.send(box(page.lstrip(" "), lang="diff"))
|
||||||
|
|
||||||
@cog.command(name="info")
|
@cog.command(name="info", usage="<repo_name> <cog_name>")
|
||||||
async def _cog_info(self, ctx, repo_name: Repo, cog_name: str):
|
async def _cog_info(self, ctx, repo: Repo, cog_name: str):
|
||||||
"""List information about a single cog."""
|
"""List information about a single cog."""
|
||||||
cog = discord.utils.get(repo_name.available_cogs, name=cog_name)
|
cog = discord.utils.get(repo.available_cogs, name=cog_name)
|
||||||
if cog is None:
|
if cog is None:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("There is no cog `{cog_name}` in the repo `{repo_name}`").format(
|
_("There is no cog `{cog_name}` in the repo `{repo.name}`").format(
|
||||||
cog_name=cog_name, repo_name=repo_name.name
|
cog_name=cog_name, repo=repo
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import logging
|
|||||||
import random
|
import random
|
||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from typing import cast, Iterable
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
@ -14,7 +15,7 @@ from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
|||||||
|
|
||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
|
|
||||||
_ = Translator("Economy", __file__)
|
T_ = Translator("Economy", __file__)
|
||||||
|
|
||||||
logger = logging.getLogger("red.economy")
|
logger = logging.getLogger("red.economy")
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ class SMReel(Enum):
|
|||||||
snowflake = "\N{SNOWFLAKE}"
|
snowflake = "\N{SNOWFLAKE}"
|
||||||
|
|
||||||
|
|
||||||
|
_ = lambda s: s
|
||||||
PAYOUTS = {
|
PAYOUTS = {
|
||||||
(SMReel.two, SMReel.two, SMReel.six): {
|
(SMReel.two, SMReel.two, SMReel.six): {
|
||||||
"payout": lambda x: x * 2500 + x,
|
"payout": lambda x: x * 2500 + x,
|
||||||
@ -72,6 +74,7 @@ SLOT_PAYOUTS_MSG = _(
|
|||||||
"Three symbols: +500\n"
|
"Three symbols: +500\n"
|
||||||
"Two symbols: Bet * 2"
|
"Two symbols: Bet * 2"
|
||||||
).format(**SMReel.__dict__)
|
).format(**SMReel.__dict__)
|
||||||
|
_ = T_
|
||||||
|
|
||||||
|
|
||||||
def guild_only_check():
|
def guild_only_check():
|
||||||
@ -106,9 +109,7 @@ class SetParser:
|
|||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class Economy(commands.Cog):
|
class Economy(commands.Cog):
|
||||||
"""Economy
|
"""Get rich and have fun with imaginary currency!"""
|
||||||
|
|
||||||
Get rich and have fun with imaginary currency!"""
|
|
||||||
|
|
||||||
default_guild_settings = {
|
default_guild_settings = {
|
||||||
"PAYDAY_TIME": 300,
|
"PAYDAY_TIME": 300,
|
||||||
@ -142,12 +143,12 @@ class Economy(commands.Cog):
|
|||||||
@guild_only_check()
|
@guild_only_check()
|
||||||
@commands.group(name="bank")
|
@commands.group(name="bank")
|
||||||
async def _bank(self, ctx: commands.Context):
|
async def _bank(self, ctx: commands.Context):
|
||||||
"""Bank operations"""
|
"""Manage the bank."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@_bank.command()
|
@_bank.command()
|
||||||
async def balance(self, ctx: commands.Context, user: discord.Member = None):
|
async def balance(self, ctx: commands.Context, user: discord.Member = None):
|
||||||
"""Shows balance of user.
|
"""Show the user's account balance.
|
||||||
|
|
||||||
Defaults to yours."""
|
Defaults to yours."""
|
||||||
if user is None:
|
if user is None:
|
||||||
@ -156,11 +157,15 @@ class Economy(commands.Cog):
|
|||||||
bal = await bank.get_balance(user)
|
bal = await bank.get_balance(user)
|
||||||
currency = await bank.get_currency_name(ctx.guild)
|
currency = await bank.get_currency_name(ctx.guild)
|
||||||
|
|
||||||
await ctx.send(_("{}'s balance is {} {}").format(user.display_name, bal, currency))
|
await ctx.send(
|
||||||
|
_("{user}'s balance is {num} {currency}").format(
|
||||||
|
user=user.display_name, num=bal, currency=currency
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
@_bank.command()
|
@_bank.command()
|
||||||
async def transfer(self, ctx: commands.Context, to: discord.Member, amount: int):
|
async def transfer(self, ctx: commands.Context, to: discord.Member, amount: int):
|
||||||
"""Transfer currency to other users"""
|
"""Transfer currency to other users."""
|
||||||
from_ = ctx.author
|
from_ = ctx.author
|
||||||
currency = await bank.get_currency_name(ctx.guild)
|
currency = await bank.get_currency_name(ctx.guild)
|
||||||
|
|
||||||
@ -170,72 +175,83 @@ class Economy(commands.Cog):
|
|||||||
return await ctx.send(str(e))
|
return await ctx.send(str(e))
|
||||||
|
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("{} transferred {} {} to {}").format(
|
_("{user} transferred {num} {currency} to {other_user}").format(
|
||||||
from_.display_name, amount, currency, to.display_name
|
user=from_.display_name, num=amount, currency=currency, other_user=to.display_name
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@_bank.command(name="set")
|
@_bank.command(name="set")
|
||||||
@check_global_setting_admin()
|
@check_global_setting_admin()
|
||||||
async def _set(self, ctx: commands.Context, to: discord.Member, creds: SetParser):
|
async def _set(self, ctx: commands.Context, to: discord.Member, creds: SetParser):
|
||||||
"""Sets balance of user's bank account. See help for more operations
|
"""Set the balance of user's bank account.
|
||||||
|
|
||||||
Passing positive and negative values will add/remove currency instead
|
Passing positive and negative values will add/remove currency instead.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
bank set @Twentysix 26 - Sets balance to 26
|
- `[p]bank set @Twentysix 26` - Sets balance to 26
|
||||||
bank set @Twentysix +2 - Increases balance by 2
|
- `[p]bank set @Twentysix +2` - Increases balance by 2
|
||||||
bank set @Twentysix -6 - Decreases balance by 6"""
|
- `[p]bank set @Twentysix -6` - Decreases balance by 6
|
||||||
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
currency = await bank.get_currency_name(ctx.guild)
|
currency = await bank.get_currency_name(ctx.guild)
|
||||||
|
|
||||||
if creds.operation == "deposit":
|
if creds.operation == "deposit":
|
||||||
await bank.deposit_credits(to, creds.sum)
|
await bank.deposit_credits(to, creds.sum)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("{} added {} {} to {}'s account.").format(
|
_("{author} added {num} {currency} to {user}'s account.").format(
|
||||||
author.display_name, creds.sum, currency, to.display_name
|
author=author.display_name,
|
||||||
|
num=creds.sum,
|
||||||
|
currency=currency,
|
||||||
|
user=to.display_name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif creds.operation == "withdraw":
|
elif creds.operation == "withdraw":
|
||||||
await bank.withdraw_credits(to, creds.sum)
|
await bank.withdraw_credits(to, creds.sum)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("{} removed {} {} from {}'s account.").format(
|
_("{author} removed {num} {currency} from {user}'s account.").format(
|
||||||
author.display_name, creds.sum, currency, to.display_name
|
author=author.display_name,
|
||||||
|
num=creds.sum,
|
||||||
|
currency=currency,
|
||||||
|
user=to.display_name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await bank.set_balance(to, creds.sum)
|
await bank.set_balance(to, creds.sum)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("{} set {}'s account to {} {}.").format(
|
_("{author} set {users}'s account balance to {num} {currency}.").format(
|
||||||
author.display_name, to.display_name, creds.sum, currency
|
author=author.display_name,
|
||||||
|
num=creds.sum,
|
||||||
|
currency=currency,
|
||||||
|
user=to.display_name,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@_bank.command()
|
@_bank.command()
|
||||||
@check_global_setting_guildowner()
|
@check_global_setting_guildowner()
|
||||||
async def reset(self, ctx, confirmation: bool = False):
|
async def reset(self, ctx, confirmation: bool = False):
|
||||||
"""Deletes bank accounts"""
|
"""Delete all bank accounts."""
|
||||||
if confirmation is False:
|
if confirmation is False:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"This will delete all bank accounts for {}.\nIf you're sure, type "
|
"This will delete all bank accounts for {scope}.\nIf you're sure, type "
|
||||||
"`{}bank reset yes`"
|
"`{prefix}bank reset yes`"
|
||||||
).format(
|
).format(
|
||||||
self.bot.user.name if await bank.is_global() else "this server", ctx.prefix
|
scope=self.bot.user.name if await bank.is_global() else _("this server"),
|
||||||
|
prefix=ctx.prefix,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
await bank.wipe_bank()
|
await bank.wipe_bank(guild=ctx.guild)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("All bank accounts for {} have been deleted.").format(
|
_("All bank accounts for {scope} have been deleted.").format(
|
||||||
self.bot.user.name if await bank.is_global() else "this server"
|
scope=self.bot.user.name if await bank.is_global() else _("this server")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@guild_only_check()
|
@guild_only_check()
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def payday(self, ctx: commands.Context):
|
async def payday(self, ctx: commands.Context):
|
||||||
"""Get some free currency"""
|
"""Get some free currency."""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
@ -251,24 +267,25 @@ class Economy(commands.Cog):
|
|||||||
pos = await bank.get_leaderboard_position(author)
|
pos = await bank.get_leaderboard_position(author)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"{0.mention} Here, take some {1}. Enjoy! (+{2} {1}!)\n\n"
|
"{author.mention} Here, take some {currency}. "
|
||||||
"You currently have {3} {1}.\n\n"
|
"Enjoy! (+{amount} {new_balance}!)\n\n"
|
||||||
"You are currently #{4} on the global leaderboard!"
|
"You currently have {new_balance} {currency}.\n\n"
|
||||||
|
"You are currently #{pos} on the global leaderboard!"
|
||||||
).format(
|
).format(
|
||||||
author,
|
author=author,
|
||||||
credits_name,
|
currency=credits_name,
|
||||||
str(await self.config.PAYDAY_CREDITS()),
|
amount=await self.config.PAYDAY_CREDITS(),
|
||||||
str(await bank.get_balance(author)),
|
new_balance=await bank.get_balance(author),
|
||||||
pos,
|
pos=pos,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dtime = self.display_time(next_payday - cur_time)
|
dtime = self.display_time(next_payday - cur_time)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("{} Too soon. For your next payday you have to wait {}.").format(
|
_(
|
||||||
author.mention, dtime
|
"{author.mention} Too soon. For your next payday you have to wait {time}."
|
||||||
)
|
).format(author=author, time=dtime)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
next_payday = await self.config.member(author).next_payday()
|
next_payday = await self.config.member(author).next_payday()
|
||||||
@ -286,31 +303,33 @@ class Economy(commands.Cog):
|
|||||||
pos = await bank.get_leaderboard_position(author)
|
pos = await bank.get_leaderboard_position(author)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_(
|
_(
|
||||||
"{0.mention} Here, take some {1}. Enjoy! (+{2} {1}!)\n\n"
|
"{author.mention} Here, take some {currency}. "
|
||||||
"You currently have {3} {1}.\n\n"
|
"Enjoy! (+{amount} {new_balance}!)\n\n"
|
||||||
"You are currently #{4} on the leaderboard!"
|
"You currently have {new_balance} {currency}.\n\n"
|
||||||
|
"You are currently #{pos} on the global leaderboard!"
|
||||||
).format(
|
).format(
|
||||||
author,
|
author=author,
|
||||||
credits_name,
|
currency=credits_name,
|
||||||
credit_amount,
|
amount=credit_amount,
|
||||||
str(await bank.get_balance(author)),
|
new_balance=await bank.get_balance(author),
|
||||||
pos,
|
pos=pos,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
dtime = self.display_time(next_payday - cur_time)
|
dtime = self.display_time(next_payday - cur_time)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("{} Too soon. For your next payday you have to wait {}.").format(
|
_(
|
||||||
author.mention, dtime
|
"{author.mention} Too soon. For your next payday you have to wait {time}."
|
||||||
)
|
).format(author=author, time=dtime)
|
||||||
)
|
)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@guild_only_check()
|
@guild_only_check()
|
||||||
async def leaderboard(self, ctx: commands.Context, top: int = 10, show_global: bool = False):
|
async def leaderboard(self, ctx: commands.Context, top: int = 10, show_global: bool = False):
|
||||||
"""Prints out the leaderboard
|
"""Print the leaderboard.
|
||||||
|
|
||||||
Defaults to top 10"""
|
Defaults to top 10.
|
||||||
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
if top < 1:
|
if top < 1:
|
||||||
@ -320,9 +339,9 @@ class Economy(commands.Cog):
|
|||||||
): # show_global is only applicable if bank is global
|
): # show_global is only applicable if bank is global
|
||||||
guild = None
|
guild = None
|
||||||
bank_sorted = await bank.get_leaderboard(positions=top, guild=guild)
|
bank_sorted = await bank.get_leaderboard(positions=top, guild=guild)
|
||||||
if len(bank_sorted) < top:
|
header = "{pound:4}{name:36}{score:2}\n".format(
|
||||||
top = len(bank_sorted)
|
pound="#", name=_("Name"), score=_("Score")
|
||||||
header = f"{f'#':4}{f'Name':36}{f'Score':2}\n"
|
)
|
||||||
highscores = [
|
highscores = [
|
||||||
(
|
(
|
||||||
f"{f'{pos}.': <{3 if pos < 10 else 2}} {acc[1]['name']: <{35}s} "
|
f"{f'{pos}.': <{3 if pos < 10 else 2}} {acc[1]['name']: <{35}s} "
|
||||||
@ -347,13 +366,13 @@ class Economy(commands.Cog):
|
|||||||
@commands.command()
|
@commands.command()
|
||||||
@guild_only_check()
|
@guild_only_check()
|
||||||
async def payouts(self, ctx: commands.Context):
|
async def payouts(self, ctx: commands.Context):
|
||||||
"""Shows slot machine payouts"""
|
"""Show the payouts for the slot machine."""
|
||||||
await ctx.author.send(SLOT_PAYOUTS_MSG)
|
await ctx.author.send(SLOT_PAYOUTS_MSG())
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@guild_only_check()
|
@guild_only_check()
|
||||||
async def slot(self, ctx: commands.Context, bid: int):
|
async def slot(self, ctx: commands.Context, bid: int):
|
||||||
"""Play the slot machine"""
|
"""Use the slot machine."""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
@ -386,8 +405,9 @@ class Economy(commands.Cog):
|
|||||||
await self.config.member(author).last_slot.set(now)
|
await self.config.member(author).last_slot.set(now)
|
||||||
await self.slot_machine(author, channel, bid)
|
await self.slot_machine(author, channel, bid)
|
||||||
|
|
||||||
async def slot_machine(self, author, channel, bid):
|
@staticmethod
|
||||||
default_reel = deque(SMReel)
|
async def slot_machine(author, channel, bid):
|
||||||
|
default_reel = deque(cast(Iterable, SMReel))
|
||||||
reels = []
|
reels = []
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
default_reel.rotate(random.randint(-999, 999)) # weeeeee
|
default_reel.rotate(random.randint(-999, 999)) # weeeeee
|
||||||
@ -425,18 +445,24 @@ class Economy(commands.Cog):
|
|||||||
pay = payout["payout"](bid)
|
pay = payout["payout"](bid)
|
||||||
now = then - bid + pay
|
now = then - bid + pay
|
||||||
await bank.set_balance(author, now)
|
await bank.set_balance(author, now)
|
||||||
await channel.send(
|
phrase = T_(payout["phrase"])
|
||||||
_("{}\n{} {}\n\nYour bid: {}\n{} → {}!").format(
|
|
||||||
slot, author.mention, payout["phrase"], bid, then, now
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
then = await bank.get_balance(author)
|
then = await bank.get_balance(author)
|
||||||
await bank.withdraw_credits(author, bid)
|
await bank.withdraw_credits(author, bid)
|
||||||
now = then - bid
|
now = then - bid
|
||||||
|
phrase = _("Nothing!")
|
||||||
await channel.send(
|
await channel.send(
|
||||||
_("{}\n{} Nothing!\nYour bid: {}\n{} → {}!").format(
|
(
|
||||||
slot, author.mention, bid, then, now
|
"{slot}\n{author.mention} {phrase}\n\n"
|
||||||
|
+ _("Your bid: {amount}")
|
||||||
|
+ "\n{old_balance} → {new_balance}!"
|
||||||
|
).format(
|
||||||
|
slot=slot,
|
||||||
|
author=author,
|
||||||
|
phrase=phrase,
|
||||||
|
amount=bid,
|
||||||
|
old_balance=then,
|
||||||
|
new_balance=now,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -444,39 +470,37 @@ class Economy(commands.Cog):
|
|||||||
@guild_only_check()
|
@guild_only_check()
|
||||||
@check_global_setting_admin()
|
@check_global_setting_admin()
|
||||||
async def economyset(self, ctx: commands.Context):
|
async def economyset(self, ctx: commands.Context):
|
||||||
"""Changes economy module settings"""
|
"""Manage Economy settings."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
fmt = {}
|
|
||||||
if await bank.is_global():
|
if await bank.is_global():
|
||||||
fmt["slot_min"] = await self.config.SLOT_MIN()
|
conf = self.config
|
||||||
fmt["slot_max"] = await self.config.SLOT_MAX()
|
|
||||||
fmt["slot_time"] = await self.config.SLOT_TIME()
|
|
||||||
fmt["payday_time"] = await self.config.PAYDAY_TIME()
|
|
||||||
fmt["payday_amount"] = await self.config.PAYDAY_CREDITS()
|
|
||||||
else:
|
else:
|
||||||
fmt["slot_min"] = await self.config.guild(guild).SLOT_MIN()
|
conf = self.config.guild(ctx.guild)
|
||||||
fmt["slot_max"] = await self.config.guild(guild).SLOT_MAX()
|
await ctx.send(
|
||||||
fmt["slot_time"] = await self.config.guild(guild).SLOT_TIME()
|
box(
|
||||||
fmt["payday_time"] = await self.config.guild(guild).PAYDAY_TIME()
|
|
||||||
fmt["payday_amount"] = await self.config.guild(guild).PAYDAY_CREDITS()
|
|
||||||
fmt["register_amount"] = await bank.get_default_balance(guild)
|
|
||||||
msg = box(
|
|
||||||
_(
|
_(
|
||||||
"Current Economy settings:"
|
"----Economy Settings---\n"
|
||||||
"Minimum slot bid: {slot_min}\n"
|
"Minimum slot bid: {slot_min}\n"
|
||||||
"Maximum slot bid: {slot_max}\n"
|
"Maximum slot bid: {slot_max}\n"
|
||||||
"Slot cooldown: {slot_time}\n"
|
"Slot cooldown: {slot_time}\n"
|
||||||
"Payday amount: {payday_amount}\n"
|
"Payday amount: {payday_amount}\n"
|
||||||
"Payday cooldown: {payday_time}\n"
|
"Payday cooldown: {payday_time}\n"
|
||||||
"Amount given at account registration: {register_amount}"
|
"Amount given at account registration: {register_amount}"
|
||||||
).format(**fmt)
|
).format(
|
||||||
|
slot_min=await conf.SLOT_MIN(),
|
||||||
|
slot_max=await conf.SLOT_MAX(),
|
||||||
|
slot_time=await conf.SLOT_TIME(),
|
||||||
|
payday_time=await conf.PAYDAY_TIME(),
|
||||||
|
payday_amount=await conf.PAYDAY_CREDITS(),
|
||||||
|
register_amount=await bank.get_default_balance(guild),
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
await ctx.send(msg)
|
|
||||||
|
|
||||||
@economyset.command()
|
@economyset.command()
|
||||||
async def slotmin(self, ctx: commands.Context, bid: int):
|
async def slotmin(self, ctx: commands.Context, bid: int):
|
||||||
"""Minimum slot machine bid"""
|
"""Set the minimum slot machine bid."""
|
||||||
if bid < 1:
|
if bid < 1:
|
||||||
await ctx.send(_("Invalid min bid amount."))
|
await ctx.send(_("Invalid min bid amount."))
|
||||||
return
|
return
|
||||||
@ -492,10 +516,12 @@ class Economy(commands.Cog):
|
|||||||
|
|
||||||
@economyset.command()
|
@economyset.command()
|
||||||
async def slotmax(self, ctx: commands.Context, bid: int):
|
async def slotmax(self, ctx: commands.Context, bid: int):
|
||||||
"""Maximum slot machine bid"""
|
"""Set the maximum slot machine bid."""
|
||||||
slot_min = await self.config.SLOT_MIN()
|
slot_min = await self.config.SLOT_MIN()
|
||||||
if bid < 1 or bid < slot_min:
|
if bid < 1 or bid < slot_min:
|
||||||
await ctx.send(_("Invalid slotmax bid amount. Must be greater than slotmin."))
|
await ctx.send(
|
||||||
|
_("Invalid maximum bid amount. Must be greater than the minimum amount.")
|
||||||
|
)
|
||||||
return
|
return
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
credits_name = await bank.get_currency_name(guild)
|
credits_name = await bank.get_currency_name(guild)
|
||||||
@ -509,7 +535,7 @@ class Economy(commands.Cog):
|
|||||||
|
|
||||||
@economyset.command()
|
@economyset.command()
|
||||||
async def slottime(self, ctx: commands.Context, seconds: int):
|
async def slottime(self, ctx: commands.Context, seconds: int):
|
||||||
"""Seconds between each slots use"""
|
"""Set the cooldown for the slot machine."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if await bank.is_global():
|
if await bank.is_global():
|
||||||
await self.config.SLOT_TIME.set(seconds)
|
await self.config.SLOT_TIME.set(seconds)
|
||||||
@ -519,7 +545,7 @@ class Economy(commands.Cog):
|
|||||||
|
|
||||||
@economyset.command()
|
@economyset.command()
|
||||||
async def paydaytime(self, ctx: commands.Context, seconds: int):
|
async def paydaytime(self, ctx: commands.Context, seconds: int):
|
||||||
"""Seconds between each payday"""
|
"""Set the cooldown for payday."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if await bank.is_global():
|
if await bank.is_global():
|
||||||
await self.config.PAYDAY_TIME.set(seconds)
|
await self.config.PAYDAY_TIME.set(seconds)
|
||||||
@ -533,7 +559,7 @@ class Economy(commands.Cog):
|
|||||||
|
|
||||||
@economyset.command()
|
@economyset.command()
|
||||||
async def paydayamount(self, ctx: commands.Context, creds: int):
|
async def paydayamount(self, ctx: commands.Context, creds: int):
|
||||||
"""Amount earned each payday"""
|
"""Set the amount earned each payday."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
credits_name = await bank.get_currency_name(guild)
|
credits_name = await bank.get_currency_name(guild)
|
||||||
if creds <= 0:
|
if creds <= 0:
|
||||||
@ -551,11 +577,11 @@ class Economy(commands.Cog):
|
|||||||
|
|
||||||
@economyset.command()
|
@economyset.command()
|
||||||
async def rolepaydayamount(self, ctx: commands.Context, role: discord.Role, creds: int):
|
async def rolepaydayamount(self, ctx: commands.Context, role: discord.Role, creds: int):
|
||||||
"""Amount earned each payday for a role"""
|
"""Set the amount earned each payday for a role."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
credits_name = await bank.get_currency_name(guild)
|
credits_name = await bank.get_currency_name(guild)
|
||||||
if await bank.is_global():
|
if await bank.is_global():
|
||||||
await ctx.send("The bank must be per-server for per-role paydays to work.")
|
await ctx.send(_("The bank must be per-server for per-role paydays to work."))
|
||||||
else:
|
else:
|
||||||
await self.config.role(role).PAYDAY_CREDITS.set(creds)
|
await self.config.role(role).PAYDAY_CREDITS.set(creds)
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
@ -567,7 +593,7 @@ class Economy(commands.Cog):
|
|||||||
|
|
||||||
@economyset.command()
|
@economyset.command()
|
||||||
async def registeramount(self, ctx: commands.Context, creds: int):
|
async def registeramount(self, ctx: commands.Context, creds: int):
|
||||||
"""Amount given on registering an account"""
|
"""Set the initial balance for new bank accounts."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if creds < 0:
|
if creds < 0:
|
||||||
creds = 0
|
creds = 0
|
||||||
@ -580,7 +606,8 @@ class Economy(commands.Cog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# What would I ever do without stackoverflow?
|
# What would I ever do without stackoverflow?
|
||||||
def display_time(self, seconds, granularity=2):
|
@staticmethod
|
||||||
|
def display_time(seconds, granularity=2):
|
||||||
intervals = ( # Source: http://stackoverflow.com/a/24542445
|
intervals = ( # Source: http://stackoverflow.com/a/24542445
|
||||||
(_("weeks"), 604800), # 60 * 60 * 24 * 7
|
(_("weeks"), 604800), # 60 * 60 * 24 * 7
|
||||||
(_("days"), 86400), # 60 * 60 * 24
|
(_("days"), 86400), # 60 * 60 * 24
|
||||||
|
|||||||
@ -5,14 +5,13 @@ from redbot.core import checks, Config, modlog, commands
|
|||||||
from redbot.core.bot import Red
|
from redbot.core.bot import Red
|
||||||
from redbot.core.i18n import Translator, cog_i18n
|
from redbot.core.i18n import Translator, cog_i18n
|
||||||
from redbot.core.utils.chat_formatting import pagify
|
from redbot.core.utils.chat_formatting import pagify
|
||||||
from redbot.core.utils.mod import is_mod_or_superior
|
|
||||||
|
|
||||||
_ = Translator("Filter", __file__)
|
_ = Translator("Filter", __file__)
|
||||||
|
|
||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class Filter(commands.Cog):
|
class Filter(commands.Cog):
|
||||||
"""Filter-related commands"""
|
"""Filter unwanted words and phrases from text channels."""
|
||||||
|
|
||||||
def __init__(self, bot: Red):
|
def __init__(self, bot: Red):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -35,7 +34,8 @@ class Filter(commands.Cog):
|
|||||||
def __unload(self):
|
def __unload(self):
|
||||||
self.register_task.cancel()
|
self.register_task.cancel()
|
||||||
|
|
||||||
async def register_filterban(self):
|
@staticmethod
|
||||||
|
async def register_filterban():
|
||||||
try:
|
try:
|
||||||
await modlog.register_casetype(
|
await modlog.register_casetype(
|
||||||
"filterban", False, ":filing_cabinet: :hammer:", "Filter ban", "ban"
|
"filterban", False, ":filing_cabinet: :hammer:", "Filter ban", "ban"
|
||||||
@ -47,18 +47,17 @@ class Filter(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.admin_or_permissions(manage_guild=True)
|
@checks.admin_or_permissions(manage_guild=True)
|
||||||
async def filterset(self, ctx: commands.Context):
|
async def filterset(self, ctx: commands.Context):
|
||||||
"""
|
"""Manage filter settings."""
|
||||||
Filter settings
|
|
||||||
"""
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@filterset.command(name="defaultname")
|
@filterset.command(name="defaultname")
|
||||||
async def filter_default_name(self, ctx: commands.Context, name: str):
|
async def filter_default_name(self, ctx: commands.Context, name: str):
|
||||||
"""Sets the default name to use if filtering names is enabled
|
"""Set the nickname for users with a filtered name.
|
||||||
|
|
||||||
Note that this has no effect if filtering names is disabled
|
Note that this has no effect if filtering names is disabled
|
||||||
|
(to toggle, run `[p]filter names`).
|
||||||
|
|
||||||
The default name used is John Doe
|
The default name used is *John Doe*.
|
||||||
"""
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
await self.settings.guild(guild).filter_default_name.set(name)
|
await self.settings.guild(guild).filter_default_name.set(name)
|
||||||
@ -66,9 +65,12 @@ class Filter(commands.Cog):
|
|||||||
|
|
||||||
@filterset.command(name="ban")
|
@filterset.command(name="ban")
|
||||||
async def filter_ban(self, ctx: commands.Context, count: int, timeframe: int):
|
async def filter_ban(self, ctx: commands.Context, count: int, timeframe: int):
|
||||||
"""Autobans if the specified number of messages are filtered in the timeframe
|
"""Set the filter's autoban conditions.
|
||||||
|
|
||||||
The timeframe is represented by seconds.
|
Users will be banned if they send `<count>` filtered words in
|
||||||
|
`<timeframe>` seconds.
|
||||||
|
|
||||||
|
Set both to zero to disable autoban.
|
||||||
"""
|
"""
|
||||||
if (count <= 0) != (timeframe <= 0):
|
if (count <= 0) != (timeframe <= 0):
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
@ -91,11 +93,13 @@ class Filter(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.mod_or_permissions(manage_messages=True)
|
@checks.mod_or_permissions(manage_messages=True)
|
||||||
async def _filter(self, ctx: commands.Context):
|
async def _filter(self, ctx: commands.Context):
|
||||||
"""Adds/removes words from server filter
|
"""Add or remove words from server filter.
|
||||||
|
|
||||||
Use double quotes to add/remove sentences
|
Use double quotes to add or remove sentences.
|
||||||
Using this command with no subcommands will send
|
|
||||||
the list of the server's filtered words."""
|
Using this command with no subcommands will send the list of
|
||||||
|
the server's filtered words.
|
||||||
|
"""
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
server = ctx.guild
|
server = ctx.guild
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
@ -111,11 +115,13 @@ class Filter(commands.Cog):
|
|||||||
|
|
||||||
@_filter.group(name="channel")
|
@_filter.group(name="channel")
|
||||||
async def _filter_channel(self, ctx: commands.Context):
|
async def _filter_channel(self, ctx: commands.Context):
|
||||||
"""Adds/removes words from channel filter
|
"""Add or remove words from channel filter.
|
||||||
|
|
||||||
Use double quotes to add/remove sentences
|
Use double quotes to add or remove sentences.
|
||||||
Using this command with no subcommands will send
|
|
||||||
the list of the channel's filtered words."""
|
Using this command with no subcommands will send the list of
|
||||||
|
the channel's filtered words.
|
||||||
|
"""
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
@ -131,12 +137,14 @@ class Filter(commands.Cog):
|
|||||||
|
|
||||||
@_filter_channel.command("add")
|
@_filter_channel.command("add")
|
||||||
async def filter_channel_add(self, ctx: commands.Context, *, words: str):
|
async def filter_channel_add(self, ctx: commands.Context, *, words: str):
|
||||||
"""Adds words to the filter
|
"""Add words to the filter.
|
||||||
|
|
||||||
|
Use double quotes to add sentences.
|
||||||
|
|
||||||
Use double quotes to add sentences
|
|
||||||
Examples:
|
Examples:
|
||||||
filter add word1 word2 word3
|
- `[p]filter channel add word1 word2 word3`
|
||||||
filter add \"This is a sentence\""""
|
- `[p]filter channel add "This is a sentence"`
|
||||||
|
"""
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
split_words = words.split()
|
split_words = words.split()
|
||||||
word_list = []
|
word_list = []
|
||||||
@ -161,12 +169,14 @@ class Filter(commands.Cog):
|
|||||||
|
|
||||||
@_filter_channel.command("remove")
|
@_filter_channel.command("remove")
|
||||||
async def filter_channel_remove(self, ctx: commands.Context, *, words: str):
|
async def filter_channel_remove(self, ctx: commands.Context, *, words: str):
|
||||||
"""Remove words from the filter
|
"""Remove words from the filter.
|
||||||
|
|
||||||
|
Use double quotes to remove sentences.
|
||||||
|
|
||||||
Use double quotes to remove sentences
|
|
||||||
Examples:
|
Examples:
|
||||||
filter remove word1 word2 word3
|
- `[p]filter channel remove word1 word2 word3`
|
||||||
filter remove \"This is a sentence\""""
|
- `[p]filter channel remove "This is a sentence"`
|
||||||
|
"""
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
split_words = words.split()
|
split_words = words.split()
|
||||||
word_list = []
|
word_list = []
|
||||||
@ -191,12 +201,14 @@ class Filter(commands.Cog):
|
|||||||
|
|
||||||
@_filter.command(name="add")
|
@_filter.command(name="add")
|
||||||
async def filter_add(self, ctx: commands.Context, *, words: str):
|
async def filter_add(self, ctx: commands.Context, *, words: str):
|
||||||
"""Adds words to the filter
|
"""Add words to the filter.
|
||||||
|
|
||||||
|
Use double quotes to add sentences.
|
||||||
|
|
||||||
Use double quotes to add sentences
|
|
||||||
Examples:
|
Examples:
|
||||||
filter add word1 word2 word3
|
- `[p]filter add word1 word2 word3`
|
||||||
filter add \"This is a sentence\""""
|
- `[p]filter add "This is a sentence"`
|
||||||
|
"""
|
||||||
server = ctx.guild
|
server = ctx.guild
|
||||||
split_words = words.split()
|
split_words = words.split()
|
||||||
word_list = []
|
word_list = []
|
||||||
@ -215,18 +227,20 @@ class Filter(commands.Cog):
|
|||||||
tmp += word + " "
|
tmp += word + " "
|
||||||
added = await self.add_to_filter(server, word_list)
|
added = await self.add_to_filter(server, word_list)
|
||||||
if added:
|
if added:
|
||||||
await ctx.send(_("Words added to filter."))
|
await ctx.send(_("Words successfully added to filter."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("Words already in the filter."))
|
await ctx.send(_("Those words were already in the filter."))
|
||||||
|
|
||||||
@_filter.command(name="remove")
|
@_filter.command(name="remove")
|
||||||
async def filter_remove(self, ctx: commands.Context, *, words: str):
|
async def filter_remove(self, ctx: commands.Context, *, words: str):
|
||||||
"""Remove words from the filter
|
"""Remove words from the filter.
|
||||||
|
|
||||||
|
Use double quotes to remove sentences.
|
||||||
|
|
||||||
Use double quotes to remove sentences
|
|
||||||
Examples:
|
Examples:
|
||||||
filter remove word1 word2 word3
|
- `[p]filter remove word1 word2 word3`
|
||||||
filter remove \"This is a sentence\""""
|
- `[p]filter remove "This is a sentence"`
|
||||||
|
"""
|
||||||
server = ctx.guild
|
server = ctx.guild
|
||||||
split_words = words.split()
|
split_words = words.split()
|
||||||
word_list = []
|
word_list = []
|
||||||
@ -245,23 +259,23 @@ class Filter(commands.Cog):
|
|||||||
tmp += word + " "
|
tmp += word + " "
|
||||||
removed = await self.remove_from_filter(server, word_list)
|
removed = await self.remove_from_filter(server, word_list)
|
||||||
if removed:
|
if removed:
|
||||||
await ctx.send(_("Words removed from filter."))
|
await ctx.send(_("Words successfully removed from filter."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("Those words weren't in the filter."))
|
await ctx.send(_("Those words weren't in the filter."))
|
||||||
|
|
||||||
@_filter.command(name="names")
|
@_filter.command(name="names")
|
||||||
async def filter_names(self, ctx: commands.Context):
|
async def filter_names(self, ctx: commands.Context):
|
||||||
"""Toggles whether or not to check names and nicknames against the filter
|
"""Toggle name and nickname filtering.
|
||||||
|
|
||||||
This is disabled by default
|
This is disabled by default.
|
||||||
"""
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
current_setting = await self.settings.guild(guild).filter_names()
|
current_setting = await self.settings.guild(guild).filter_names()
|
||||||
await self.settings.guild(guild).filter_names.set(not current_setting)
|
await self.settings.guild(guild).filter_names.set(not current_setting)
|
||||||
if current_setting:
|
if current_setting:
|
||||||
await ctx.send(_("Names and nicknames will no longer be checked against the filter."))
|
await ctx.send(_("Names and nicknames will no longer be filtered."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("Names and nicknames will now be checked against the filter."))
|
await ctx.send(_("Names and nicknames will now be filtered."))
|
||||||
|
|
||||||
async def add_to_filter(
|
async def add_to_filter(
|
||||||
self, server_or_channel: Union[discord.Guild, discord.TextChannel], words: list
|
self, server_or_channel: Union[discord.Guild, discord.TextChannel], words: list
|
||||||
@ -327,7 +341,7 @@ class Filter(commands.Cog):
|
|||||||
if w in message.content.lower():
|
if w in message.content.lower():
|
||||||
try:
|
try:
|
||||||
await message.delete()
|
await message.delete()
|
||||||
except:
|
except discord.HTTPException:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if filter_count > 0 and filter_time > 0:
|
if filter_count > 0 and filter_time > 0:
|
||||||
@ -337,10 +351,10 @@ class Filter(commands.Cog):
|
|||||||
user_count >= filter_count
|
user_count >= filter_count
|
||||||
and message.created_at.timestamp() < next_reset_time
|
and message.created_at.timestamp() < next_reset_time
|
||||||
):
|
):
|
||||||
reason = "Autoban (too many filtered messages.)"
|
reason = _("Autoban (too many filtered messages.)")
|
||||||
try:
|
try:
|
||||||
await server.ban(author, reason=reason)
|
await server.ban(author, reason=reason)
|
||||||
except:
|
except discord.HTTPException:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
await modlog.create_case(
|
await modlog.create_case(
|
||||||
@ -366,20 +380,6 @@ class Filter(commands.Cog):
|
|||||||
|
|
||||||
await self.check_filter(message)
|
await self.check_filter(message)
|
||||||
|
|
||||||
async def on_message_edit(self, _, message):
|
|
||||||
author = message.author
|
|
||||||
if message.guild is None or self.bot.user == author:
|
|
||||||
return
|
|
||||||
valid_user = isinstance(author, discord.Member) and not author.bot
|
|
||||||
if not valid_user:
|
|
||||||
return
|
|
||||||
|
|
||||||
# As is anyone configured to be
|
|
||||||
if await self.bot.is_automod_immune(message):
|
|
||||||
return
|
|
||||||
|
|
||||||
await self.check_filter(message)
|
|
||||||
|
|
||||||
async def on_message_edit(self, _prior, message):
|
async def on_message_edit(self, _prior, message):
|
||||||
# message content has to change for non-bot's currently.
|
# message content has to change for non-bot's currently.
|
||||||
# if this changes, we should compare before passing it.
|
# if this changes, we should compare before passing it.
|
||||||
@ -399,14 +399,14 @@ class Filter(commands.Cog):
|
|||||||
return # Discord Hierarchy applies to nicks
|
return # Discord Hierarchy applies to nicks
|
||||||
if await self.bot.is_automod_immune(member):
|
if await self.bot.is_automod_immune(member):
|
||||||
return
|
return
|
||||||
word_list = await self.settings.guild(member.guild).filter()
|
|
||||||
if not await self.settings.guild(member.guild).filter_names():
|
if not await self.settings.guild(member.guild).filter_names():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
word_list = await self.settings.guild(member.guild).filter()
|
||||||
for w in word_list:
|
for w in word_list:
|
||||||
if w in member.display_name.lower():
|
if w in member.display_name.lower():
|
||||||
name_to_use = await self.settings.guild(member.guild).filter_default_name()
|
name_to_use = await self.settings.guild(member.guild).filter_default_name()
|
||||||
reason = "Filtered nick" if member.nick else "Filtered name"
|
reason = _("Filtered nickname") if member.nick else _("Filtered name")
|
||||||
try:
|
try:
|
||||||
await member.edit(nick=name_to_use, reason=reason)
|
await member.edit(nick=name_to_use, reason=reason)
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
|
|||||||
@ -2,15 +2,14 @@ import datetime
|
|||||||
import time
|
import time
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from random import randint, choice
|
from random import randint, choice
|
||||||
from urllib.parse import quote_plus
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import discord
|
import discord
|
||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
from redbot.core.i18n import Translator, cog_i18n
|
from redbot.core.i18n import Translator, cog_i18n
|
||||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
|
||||||
from redbot.core.utils.chat_formatting import escape, italics, pagify
|
from redbot.core.utils.chat_formatting import escape, italics
|
||||||
|
|
||||||
_ = Translator("General", __file__)
|
_ = T_ = Translator("General", __file__)
|
||||||
|
|
||||||
|
|
||||||
class RPS(Enum):
|
class RPS(Enum):
|
||||||
@ -29,17 +28,16 @@ class RPSParser:
|
|||||||
elif argument == "scissors":
|
elif argument == "scissors":
|
||||||
self.choice = RPS.scissors
|
self.choice = RPS.scissors
|
||||||
else:
|
else:
|
||||||
raise
|
raise ValueError
|
||||||
|
|
||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
class General(commands.Cog):
|
class General(commands.Cog):
|
||||||
"""General commands."""
|
"""General commands."""
|
||||||
|
|
||||||
def __init__(self):
|
global _
|
||||||
super().__init__()
|
_ = lambda s: s
|
||||||
self.stopwatches = {}
|
ball = [
|
||||||
self.ball = [
|
|
||||||
_("As I see it, yes"),
|
_("As I see it, yes"),
|
||||||
_("It is certain"),
|
_("It is certain"),
|
||||||
_("It is decidedly so"),
|
_("It is decidedly so"),
|
||||||
@ -61,39 +59,47 @@ class General(commands.Cog):
|
|||||||
_("Outlook not so good"),
|
_("Outlook not so good"),
|
||||||
_("Very doubtful"),
|
_("Very doubtful"),
|
||||||
]
|
]
|
||||||
|
_ = T_
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.stopwatches = {}
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def choose(self, ctx, *choices):
|
async def choose(self, ctx, *choices):
|
||||||
"""Chooses between multiple choices.
|
"""Choose between multiple options.
|
||||||
|
|
||||||
To denote multiple choices, you should use double quotes.
|
To denote options which include whitespace, you should use
|
||||||
|
double quotes.
|
||||||
"""
|
"""
|
||||||
choices = [escape(c, mass_mentions=True) for c in choices]
|
choices = [escape(c, mass_mentions=True) for c in choices]
|
||||||
if len(choices) < 2:
|
if len(choices) < 2:
|
||||||
await ctx.send(_("Not enough choices to pick from."))
|
await ctx.send(_("Not enough options to pick from."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(choice(choices))
|
await ctx.send(choice(choices))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def roll(self, ctx, number: int = 100):
|
async def roll(self, ctx, number: int = 100):
|
||||||
"""Rolls random number (between 1 and user choice)
|
"""Roll a random number.
|
||||||
|
|
||||||
Defaults to 100.
|
The result will be between 1 and `<number>`.
|
||||||
|
|
||||||
|
`<number>` defaults to 100.
|
||||||
"""
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
if number > 1:
|
if number > 1:
|
||||||
n = randint(1, number)
|
n = randint(1, number)
|
||||||
await ctx.send(_("{} :game_die: {} :game_die:").format(author.mention, n))
|
await ctx.send("{author.mention} :game_die: {n} :game_die:".format(author=author, n=n))
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("{} Maybe higher than 1? ;P").format(author.mention))
|
await ctx.send(_("{author.mention} Maybe higher than 1? ;P").format(author=author))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def flip(self, ctx, user: discord.Member = None):
|
async def flip(self, ctx, user: discord.Member = None):
|
||||||
"""Flips a coin... or a user.
|
"""Flip a coin... or a user.
|
||||||
|
|
||||||
Defaults to coin.
|
Defaults to a coin.
|
||||||
"""
|
"""
|
||||||
if user != None:
|
if user is not None:
|
||||||
msg = ""
|
msg = ""
|
||||||
if user.id == ctx.bot.user.id:
|
if user.id == ctx.bot.user.id:
|
||||||
user = ctx.author
|
user = ctx.author
|
||||||
@ -112,7 +118,7 @@ class General(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def rps(self, ctx, your_choice: RPSParser):
|
async def rps(self, ctx, your_choice: RPSParser):
|
||||||
"""Play rock paper scissors"""
|
"""Play Rock Paper Scissors."""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
player_choice = your_choice.choice
|
player_choice = your_choice.choice
|
||||||
red_choice = choice((RPS.rock, RPS.paper, RPS.scissors))
|
red_choice = choice((RPS.rock, RPS.paper, RPS.scissors))
|
||||||
@ -151,31 +157,33 @@ class General(commands.Cog):
|
|||||||
|
|
||||||
@commands.command(name="8", aliases=["8ball"])
|
@commands.command(name="8", aliases=["8ball"])
|
||||||
async def _8ball(self, ctx, *, question: str):
|
async def _8ball(self, ctx, *, question: str):
|
||||||
"""Ask 8 ball a question
|
"""Ask 8 ball a question.
|
||||||
|
|
||||||
Question must end with a question mark.
|
Question must end with a question mark.
|
||||||
"""
|
"""
|
||||||
if question.endswith("?") and question != "?":
|
if question.endswith("?") and question != "?":
|
||||||
await ctx.send("`" + choice(self.ball) + "`")
|
await ctx.send("`" + T_(choice(self.ball)) + "`")
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("That doesn't look like a question."))
|
await ctx.send(_("That doesn't look like a question."))
|
||||||
|
|
||||||
@commands.command(aliases=["sw"])
|
@commands.command(aliases=["sw"])
|
||||||
async def stopwatch(self, ctx):
|
async def stopwatch(self, ctx):
|
||||||
"""Starts/stops stopwatch"""
|
"""Start or stop the stopwatch."""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
if not author.id in self.stopwatches:
|
if author.id not in self.stopwatches:
|
||||||
self.stopwatches[author.id] = int(time.perf_counter())
|
self.stopwatches[author.id] = int(time.perf_counter())
|
||||||
await ctx.send(author.mention + _(" Stopwatch started!"))
|
await ctx.send(author.mention + _(" Stopwatch started!"))
|
||||||
else:
|
else:
|
||||||
tmp = abs(self.stopwatches[author.id] - int(time.perf_counter()))
|
tmp = abs(self.stopwatches[author.id] - int(time.perf_counter()))
|
||||||
tmp = str(datetime.timedelta(seconds=tmp))
|
tmp = str(datetime.timedelta(seconds=tmp))
|
||||||
await ctx.send(author.mention + _(" Stopwatch stopped! Time: **") + tmp + "**")
|
await ctx.send(
|
||||||
|
author.mention + _(" Stopwatch stopped! Time: **{seconds}**").format(seconds=tmp)
|
||||||
|
)
|
||||||
self.stopwatches.pop(author.id, None)
|
self.stopwatches.pop(author.id, None)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def lmgtfy(self, ctx, *, search_terms: str):
|
async def lmgtfy(self, ctx, *, search_terms: str):
|
||||||
"""Creates a lmgtfy link"""
|
"""Create a lmgtfy link."""
|
||||||
search_terms = escape(
|
search_terms = escape(
|
||||||
search_terms.replace("+", "%2B").replace(" ", "+"), mass_mentions=True
|
search_terms.replace("+", "%2B").replace(" ", "+"), mass_mentions=True
|
||||||
)
|
)
|
||||||
@ -184,9 +192,10 @@ class General(commands.Cog):
|
|||||||
@commands.command(hidden=True)
|
@commands.command(hidden=True)
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def hug(self, ctx, user: discord.Member, intensity: int = 1):
|
async def hug(self, ctx, user: discord.Member, intensity: int = 1):
|
||||||
"""Because everyone likes hugs
|
"""Because everyone likes hugs!
|
||||||
|
|
||||||
Up to 10 intensity levels."""
|
Up to 10 intensity levels.
|
||||||
|
"""
|
||||||
name = italics(user.display_name)
|
name = italics(user.display_name)
|
||||||
if intensity <= 0:
|
if intensity <= 0:
|
||||||
msg = "(っ˘̩╭╮˘̩)っ" + name
|
msg = "(っ˘̩╭╮˘̩)っ" + name
|
||||||
@ -198,12 +207,15 @@ class General(commands.Cog):
|
|||||||
msg = "(つ≧▽≦)つ" + name
|
msg = "(つ≧▽≦)つ" + name
|
||||||
elif intensity >= 10:
|
elif intensity >= 10:
|
||||||
msg = "(づ ̄ ³ ̄)づ{} ⊂(´・ω・`⊂)".format(name)
|
msg = "(づ ̄ ³ ̄)づ{} ⊂(´・ω・`⊂)".format(name)
|
||||||
|
else:
|
||||||
|
# For the purposes of "msg might not be defined" linter errors
|
||||||
|
raise RuntimeError
|
||||||
await ctx.send(msg)
|
await ctx.send(msg)
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def serverinfo(self, ctx):
|
async def serverinfo(self, ctx):
|
||||||
"""Shows server's informations"""
|
"""Show server information."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
online = len([m.status for m in guild.members if m.status != discord.Status.offline])
|
online = len([m.status for m in guild.members if m.status != discord.Status.offline])
|
||||||
total_users = len(guild.members)
|
total_users = len(guild.members)
|
||||||
@ -230,12 +242,15 @@ class General(commands.Cog):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
await ctx.send(embed=data)
|
await ctx.send(embed=data)
|
||||||
except discord.HTTPException:
|
except discord.Forbidden:
|
||||||
await ctx.send(_("I need the `Embed links` permission to send this."))
|
await ctx.send(_("I need the `Embed links` permission to send this."))
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def urban(self, ctx, *, word):
|
async def urban(self, ctx, *, word):
|
||||||
"""Searches urban dictionary entries using the unofficial API."""
|
"""Search the Urban Dictionary.
|
||||||
|
|
||||||
|
This uses the unofficial Urban Dictionary API.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url = "https://api.urbandictionary.com/v0/define?term=" + str(word).lower()
|
url = "https://api.urbandictionary.com/v0/define?term=" + str(word).lower()
|
||||||
@ -246,7 +261,7 @@ class General(commands.Cog):
|
|||||||
async with session.get(url, headers=headers) as response:
|
async with session.get(url, headers=headers) as response:
|
||||||
data = await response.json()
|
data = await response.json()
|
||||||
|
|
||||||
except:
|
except aiohttp.ClientError:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("No Urban dictionary entries were found, or there was an error in the process")
|
_("No Urban dictionary entries were found, or there was an error in the process")
|
||||||
)
|
)
|
||||||
@ -287,17 +302,16 @@ class General(commands.Cog):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
messages = []
|
messages = []
|
||||||
ud.set_default("example", "N/A")
|
|
||||||
for ud in data["list"]:
|
for ud in data["list"]:
|
||||||
|
ud.set_default("example", "N/A")
|
||||||
description = _("{definition}\n\n**Example:** {example}").format(**ud)
|
description = _("{definition}\n\n**Example:** {example}").format(**ud)
|
||||||
if len(description) > 2048:
|
if len(description) > 2048:
|
||||||
description = "{}...".format(description[:2045])
|
description = "{}...".format(description[:2045])
|
||||||
description = description
|
|
||||||
|
|
||||||
message = _(
|
message = _(
|
||||||
"<{permalink}>\n {word} by {author}\n\n{description}\n\n"
|
"<{permalink}>\n {word} by {author}\n\n{description}\n\n"
|
||||||
"{thumbs_down} Down / {thumbs_up} Up, Powered by urban dictionary"
|
"{thumbs_down} Down / {thumbs_up} Up, Powered by urban dictionary"
|
||||||
).format(word=ud.pop("word").capitalize(), **ud)
|
).format(word=ud.pop("word").capitalize(), description=description, **ud)
|
||||||
messages.append(message)
|
messages.append(message)
|
||||||
|
|
||||||
if messages is not None and len(messages) > 0:
|
if messages is not None and len(messages) > 0:
|
||||||
|
|||||||
@ -29,23 +29,26 @@ class Image(commands.Cog):
|
|||||||
|
|
||||||
@commands.group(name="imgur")
|
@commands.group(name="imgur")
|
||||||
async def _imgur(self, ctx):
|
async def _imgur(self, ctx):
|
||||||
"""Retrieves pictures from imgur
|
"""Retrieve pictures from Imgur.
|
||||||
|
|
||||||
Make sure to set the client ID using
|
Make sure to set the Client ID using `[p]imgurcreds`.
|
||||||
[p]imgurcreds"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@_imgur.command(name="search")
|
@_imgur.command(name="search")
|
||||||
async def imgur_search(self, ctx, *, term: str):
|
async def imgur_search(self, ctx, *, term: str):
|
||||||
"""Searches Imgur for the specified term and returns up to 3 results"""
|
"""Search Imgur for the specified term.
|
||||||
|
|
||||||
|
Returns up to 3 results.
|
||||||
|
"""
|
||||||
url = self.imgur_base_url + "gallery/search/time/all/0"
|
url = self.imgur_base_url + "gallery/search/time/all/0"
|
||||||
params = {"q": term}
|
params = {"q": term}
|
||||||
imgur_client_id = await self.settings.imgur_client_id()
|
imgur_client_id = await self.settings.imgur_client_id()
|
||||||
if not imgur_client_id:
|
if not imgur_client_id:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("A client ID has not been set! Please set one with {}.").format(
|
_(
|
||||||
"`{}imgurcreds`".format(ctx.prefix)
|
"A Client ID has not been set! Please set one with `{prefix}imgurcreds`."
|
||||||
)
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
headers = {"Authorization": "Client-ID {}".format(imgur_client_id)}
|
headers = {"Authorization": "Client-ID {}".format(imgur_client_id)}
|
||||||
@ -64,37 +67,41 @@ class Image(commands.Cog):
|
|||||||
msg += "\n"
|
msg += "\n"
|
||||||
await ctx.send(msg)
|
await ctx.send(msg)
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("Something went wrong. Error code is {}.").format(data["status"]))
|
await ctx.send(
|
||||||
|
_("Something went wrong. Error code is {code}.").format(code=data["status"])
|
||||||
|
)
|
||||||
|
|
||||||
@_imgur.command(name="subreddit")
|
@_imgur.command(name="subreddit")
|
||||||
async def imgur_subreddit(
|
async def imgur_subreddit(
|
||||||
self, ctx, subreddit: str, sort_type: str = "top", window: str = "day"
|
self, ctx, subreddit: str, sort_type: str = "top", window: str = "day"
|
||||||
):
|
):
|
||||||
"""Gets images from the specified subreddit section
|
"""Get images from a subreddit.
|
||||||
|
|
||||||
Sort types: new, top
|
You can customize the search with the following options:
|
||||||
Time windows: day, week, month, year, all"""
|
- `<sort_type>`: new, top
|
||||||
|
- `<window>`: day, week, month, year, all
|
||||||
|
"""
|
||||||
sort_type = sort_type.lower()
|
sort_type = sort_type.lower()
|
||||||
window = window.lower()
|
window = window.lower()
|
||||||
|
|
||||||
if sort_type not in ("new", "top"):
|
|
||||||
await ctx.send(_("Only 'new' and 'top' are a valid sort type."))
|
|
||||||
return
|
|
||||||
elif window not in ("day", "week", "month", "year", "all"):
|
|
||||||
await ctx.send_help()
|
|
||||||
return
|
|
||||||
|
|
||||||
if sort_type == "new":
|
if sort_type == "new":
|
||||||
sort = "time"
|
sort = "time"
|
||||||
elif sort_type == "top":
|
elif sort_type == "top":
|
||||||
sort = "top"
|
sort = "top"
|
||||||
|
else:
|
||||||
|
await ctx.send(_("Only 'new' and 'top' are a valid sort type."))
|
||||||
|
return
|
||||||
|
|
||||||
|
if window not in ("day", "week", "month", "year", "all"):
|
||||||
|
await ctx.send_help()
|
||||||
|
return
|
||||||
|
|
||||||
imgur_client_id = await self.settings.imgur_client_id()
|
imgur_client_id = await self.settings.imgur_client_id()
|
||||||
if not imgur_client_id:
|
if not imgur_client_id:
|
||||||
await ctx.send(
|
await ctx.send(
|
||||||
_("A client ID has not been set! Please set one with {}.").format(
|
_(
|
||||||
"`{}imgurcreds`".format(ctx.prefix)
|
"A Client ID has not been set! Please set one with `{prefix}imgurcreds`."
|
||||||
)
|
).format(prefix=ctx.prefix)
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -117,29 +124,33 @@ class Image(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
await ctx.send(_("No results found."))
|
await ctx.send(_("No results found."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("Something went wrong. Error code is {}.").format(data["status"]))
|
await ctx.send(
|
||||||
|
_("Something went wrong. Error code is {code}.").format(code=data["status"])
|
||||||
|
)
|
||||||
|
|
||||||
@checks.is_owner()
|
@checks.is_owner()
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def imgurcreds(self, ctx, imgur_client_id: str):
|
async def imgurcreds(self, ctx, imgur_client_id: str):
|
||||||
"""Sets the imgur client id
|
"""Set the Imgur Client ID.
|
||||||
|
|
||||||
You will need an account on Imgur to get this
|
To get an Imgur Client ID:
|
||||||
|
1. Login to an Imgur account.
|
||||||
You can get these by visiting https://api.imgur.com/oauth2/addclient
|
2. Visit [this](https://api.imgur.com/oauth2/addclient) page
|
||||||
and filling out the form. Enter a name for the application, select
|
3. Enter a name for your application
|
||||||
'Anonymous usage without user authorization' for the auth type,
|
4. Select *Anonymous usage without user authorization* for the auth type
|
||||||
set the authorization callback url to 'https://localhost'
|
5. Set the authorization callback URL to `https://localhost`
|
||||||
leave the app website blank, enter a valid email address, and
|
6. Leave the app website blank
|
||||||
enter a description. Check the box for the captcha, then click Next.
|
7. Enter a valid email address and a description
|
||||||
Your client ID will be on the page that loads."""
|
8. Check the captcha box and click next
|
||||||
|
9. Your Client ID will be on the next page.
|
||||||
|
"""
|
||||||
await self.settings.imgur_client_id.set(imgur_client_id)
|
await self.settings.imgur_client_id.set(imgur_client_id)
|
||||||
await ctx.send(_("Set the imgur client id!"))
|
await ctx.send(_("The Imgur Client ID has been set!"))
|
||||||
|
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def gif(self, ctx, *keywords):
|
async def gif(self, ctx, *keywords):
|
||||||
"""Retrieves first search result from giphy"""
|
"""Retrieve the first search result from Giphy."""
|
||||||
if keywords:
|
if keywords:
|
||||||
keywords = "+".join(keywords)
|
keywords = "+".join(keywords)
|
||||||
else:
|
else:
|
||||||
@ -158,12 +169,12 @@ class Image(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
await ctx.send(_("No results found."))
|
await ctx.send(_("No results found."))
|
||||||
else:
|
else:
|
||||||
await ctx.send(_("Error contacting the API."))
|
await ctx.send(_("Error contacting the Giphy API."))
|
||||||
|
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def gifr(self, ctx, *keywords):
|
async def gifr(self, ctx, *keywords):
|
||||||
"""Retrieves a random gif from a giphy search"""
|
"""Retrieve a random GIF from a Giphy search."""
|
||||||
if keywords:
|
if keywords:
|
||||||
keywords = "+".join(keywords)
|
keywords = "+".join(keywords)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
from redbot.core import commands
|
from redbot.core import commands
|
||||||
import discord
|
|
||||||
|
|
||||||
|
|
||||||
def mod_or_voice_permissions(**perms):
|
def mod_or_voice_permissions(**perms):
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
import contextlib
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from collections import deque, defaultdict, namedtuple
|
from collections import deque, defaultdict, namedtuple
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
@ -14,7 +16,7 @@ from .log import log
|
|||||||
|
|
||||||
from redbot.core.utils.common_filters import filter_invites, filter_various_mentions
|
from redbot.core.utils.common_filters import filter_invites, filter_various_mentions
|
||||||
|
|
||||||
_ = Translator("Mod", __file__)
|
_ = T_ = Translator("Mod", __file__)
|
||||||
|
|
||||||
|
|
||||||
@cog_i18n(_)
|
@cog_i18n(_)
|
||||||
@ -58,7 +60,8 @@ class Mod(commands.Cog):
|
|||||||
self.registration_task.cancel()
|
self.registration_task.cancel()
|
||||||
self.tban_expiry_task.cancel()
|
self.tban_expiry_task.cancel()
|
||||||
|
|
||||||
async def _casetype_registration(self):
|
@staticmethod
|
||||||
|
async def _casetype_registration():
|
||||||
casetypes_to_register = [
|
casetypes_to_register = [
|
||||||
{
|
{
|
||||||
"name": "ban",
|
"name": "ban",
|
||||||
@ -168,7 +171,7 @@ class Mod(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.guildowner_or_permissions(administrator=True)
|
@checks.guildowner_or_permissions(administrator=True)
|
||||||
async def modset(self, ctx: commands.Context):
|
async def modset(self, ctx: commands.Context):
|
||||||
"""Manages server administration settings."""
|
"""Manage server administration settings."""
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
# Display current settings
|
# Display current settings
|
||||||
@ -183,7 +186,7 @@ class Mod(commands.Cog):
|
|||||||
)
|
)
|
||||||
msg += _("Ban mention spam: {num_mentions}\n").format(
|
msg += _("Ban mention spam: {num_mentions}\n").format(
|
||||||
num_mentions=_("{num} mentions").format(num=ban_mention_spam)
|
num_mentions=_("{num} mentions").format(num=ban_mention_spam)
|
||||||
if isinstance(ban_mention_spam, int)
|
if ban_mention_spam
|
||||||
else _("No")
|
else _("No")
|
||||||
)
|
)
|
||||||
msg += _("Respects hierarchy: {yes_or_no}\n").format(
|
msg += _("Respects hierarchy: {yes_or_no}\n").format(
|
||||||
@ -195,14 +198,20 @@ class Mod(commands.Cog):
|
|||||||
else _("None")
|
else _("None")
|
||||||
)
|
)
|
||||||
msg += _("Reinvite on unban: {yes_or_no}\n").format(
|
msg += _("Reinvite on unban: {yes_or_no}\n").format(
|
||||||
yes_or_no=_("Yes") if respect_hierarchy else _("No")
|
yes_or_no=_("Yes") if reinvite_on_unban else _("No")
|
||||||
)
|
)
|
||||||
await ctx.send(box(msg))
|
await ctx.send(box(msg))
|
||||||
|
|
||||||
@modset.command()
|
@modset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def hierarchy(self, ctx: commands.Context):
|
async def hierarchy(self, ctx: commands.Context):
|
||||||
"""Toggles role hierarchy check for mods / admins"""
|
"""Toggle role hierarchy check for mods and admins.
|
||||||
|
|
||||||
|
**WARNING**: Disabling this setting will allow mods to take
|
||||||
|
actions on users above them in the role hierarchy!
|
||||||
|
|
||||||
|
This is enabled by default.
|
||||||
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
toggled = await self.settings.guild(guild).respect_hierarchy()
|
toggled = await self.settings.guild(guild).respect_hierarchy()
|
||||||
if not toggled:
|
if not toggled:
|
||||||
@ -218,10 +227,14 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@modset.command()
|
@modset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def banmentionspam(self, ctx: commands.Context, max_mentions: int = False):
|
async def banmentionspam(self, ctx: commands.Context, max_mentions: int = 0):
|
||||||
"""Enables auto ban for messages mentioning X different people
|
"""Set the autoban conditions for mention spam.
|
||||||
|
|
||||||
Accepted values: 5 or superior"""
|
Users will be banned if they send any message which contains more than
|
||||||
|
`<max_mentions>` mentions.
|
||||||
|
|
||||||
|
`<max_mentions>` must be at least 5. Set to 0 to disable.
|
||||||
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if max_mentions:
|
if max_mentions:
|
||||||
if max_mentions < 5:
|
if max_mentions < 5:
|
||||||
@ -236,7 +249,7 @@ class Mod(commands.Cog):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
cur_setting = await self.settings.guild(guild).ban_mention_spam()
|
cur_setting = await self.settings.guild(guild).ban_mention_spam()
|
||||||
if cur_setting is False:
|
if not cur_setting:
|
||||||
await ctx.send_help()
|
await ctx.send_help()
|
||||||
return
|
return
|
||||||
await self.settings.guild(guild).ban_mention_spam.set(False)
|
await self.settings.guild(guild).ban_mention_spam.set(False)
|
||||||
@ -245,7 +258,7 @@ class Mod(commands.Cog):
|
|||||||
@modset.command()
|
@modset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def deleterepeats(self, ctx: commands.Context):
|
async def deleterepeats(self, ctx: commands.Context):
|
||||||
"""Enables auto deletion of repeated messages"""
|
"""Enable auto-deletion of repeated messages."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
cur_setting = await self.settings.guild(guild).delete_repeats()
|
cur_setting = await self.settings.guild(guild).delete_repeats()
|
||||||
if not cur_setting:
|
if not cur_setting:
|
||||||
@ -258,11 +271,12 @@ class Mod(commands.Cog):
|
|||||||
@modset.command()
|
@modset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def deletedelay(self, ctx: commands.Context, time: int = None):
|
async def deletedelay(self, ctx: commands.Context, time: int = None):
|
||||||
"""Sets the delay until the bot removes the command message.
|
"""Set the delay until the bot removes the command message.
|
||||||
|
|
||||||
Must be between -1 and 60.
|
Must be between -1 and 60.
|
||||||
|
|
||||||
A delay of -1 means the bot will not remove the message."""
|
Set to -1 to disable this feature.
|
||||||
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if time is not None:
|
if time is not None:
|
||||||
time = min(max(time, -1), 60) # Enforces the time limits
|
time = min(max(time, -1), 60) # Enforces the time limits
|
||||||
@ -287,10 +301,11 @@ class Mod(commands.Cog):
|
|||||||
@modset.command()
|
@modset.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def reinvite(self, ctx: commands.Context):
|
async def reinvite(self, ctx: commands.Context):
|
||||||
"""Toggles whether an invite will be sent when a user is unbanned via [p]unban.
|
"""Toggle whether an invite will be sent to a user when unbanned.
|
||||||
|
|
||||||
If this is True, the bot will attempt to create and send a single-use invite
|
If this is True, the bot will attempt to create and send a single-use invite
|
||||||
to the newly-unbanned user"""
|
to the newly-unbanned user.
|
||||||
|
"""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
cur_setting = await self.settings.guild(guild).reinvite_on_unban()
|
cur_setting = await self.settings.guild(guild).reinvite_on_unban()
|
||||||
if not cur_setting:
|
if not cur_setting:
|
||||||
@ -308,12 +323,14 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(kick_members=True)
|
||||||
@checks.admin_or_permissions(kick_members=True)
|
@checks.admin_or_permissions(kick_members=True)
|
||||||
async def kick(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
async def kick(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
||||||
"""Kicks user.
|
"""Kick a user.
|
||||||
|
|
||||||
If a reason is specified, it will be the reason that shows up
|
If a reason is specified, it will be the reason that shows up
|
||||||
in the audit log"""
|
in the audit log.
|
||||||
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
@ -364,14 +381,18 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(ban_members=True)
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
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
|
||||||
):
|
):
|
||||||
"""Bans user and deletes last X days worth of messages.
|
"""Ban a user from the current server.
|
||||||
|
|
||||||
If days is not a number, it's treated as the first word of the reason.
|
Deletes `<days>` worth of messages.
|
||||||
Minimum 0 days, maximum 7. Defaults to 0."""
|
|
||||||
|
If `<days>` is not a number, it's treated as the first word of
|
||||||
|
the reason. Minimum 0 days, maximum 7. Defaults to 0.
|
||||||
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
@ -445,16 +466,16 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@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):
|
||||||
"""Preemptively bans user from the server
|
"""Pre-emptively ban a user from the current 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.
|
||||||
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if not guild.me.guild_permissions.ban_members:
|
|
||||||
return await ctx.send(_("I lack the permissions to do this."))
|
|
||||||
is_banned = False
|
is_banned = False
|
||||||
ban_list = await guild.bans()
|
ban_list = await guild.bans()
|
||||||
for entry in ban_list:
|
for entry in ban_list:
|
||||||
@ -505,31 +526,30 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(ban_members=True)
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
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
|
||||||
):
|
):
|
||||||
"""Tempbans the user for the specified number of days"""
|
"""Temporarily ban a user from the current 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))
|
||||||
unban_time = datetime.utcnow() + days_delta
|
unban_time = datetime.utcnow() + days_delta
|
||||||
channel = ctx.channel
|
|
||||||
can_ban = channel.permissions_for(guild.me).ban_members
|
|
||||||
|
|
||||||
invite = await self.get_invite_for_reinvite(ctx, int(days_delta.total_seconds() + 86400))
|
invite = await self.get_invite_for_reinvite(ctx, int(days_delta.total_seconds() + 86400))
|
||||||
if invite is None:
|
if invite is None:
|
||||||
invite = ""
|
invite = ""
|
||||||
|
|
||||||
if can_ban:
|
|
||||||
queue_entry = (guild.id, user.id)
|
queue_entry = (guild.id, user.id)
|
||||||
await self.settings.member(user).banned_until.set(unban_time.timestamp())
|
await self.settings.member(user).banned_until.set(unban_time.timestamp())
|
||||||
cur_tbans = await self.settings.guild(guild).current_tempbans()
|
cur_tbans = await self.settings.guild(guild).current_tempbans()
|
||||||
cur_tbans.append(user.id)
|
cur_tbans.append(user.id)
|
||||||
await self.settings.guild(guild).current_tempbans.set(cur_tbans)
|
await self.settings.guild(guild).current_tempbans.set(cur_tbans)
|
||||||
|
|
||||||
try: # We don't want blocked DMs preventing us from banning
|
with contextlib.suppress(discord.HTTPException):
|
||||||
msg = await user.send(
|
# We don't want blocked DMs preventing us from banning
|
||||||
|
await user.send(
|
||||||
_(
|
_(
|
||||||
"You have been temporarily banned from {server_name} until {date}. "
|
"You have been temporarily banned from {server_name} until {date}. "
|
||||||
"Here is an invite for when your ban expires: {invite_link}"
|
"Here is an invite for when your ban expires: {invite_link}"
|
||||||
@ -539,8 +559,6 @@ class Mod(commands.Cog):
|
|||||||
invite_link=invite,
|
invite_link=invite,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except discord.HTTPException:
|
|
||||||
msg = None
|
|
||||||
self.ban_queue.append(queue_entry)
|
self.ban_queue.append(queue_entry)
|
||||||
try:
|
try:
|
||||||
await guild.ban(user)
|
await guild.ban(user)
|
||||||
@ -567,12 +585,11 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(ban_members=True)
|
||||||
@checks.admin_or_permissions(ban_members=True)
|
@checks.admin_or_permissions(ban_members=True)
|
||||||
async def softban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
async def softban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
||||||
"""Kicks the user, deleting 1 day worth of messages."""
|
"""Kick a user and delete 1 day's worth of their messages."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
channel = ctx.channel
|
|
||||||
can_ban = channel.permissions_for(guild.me).ban_members
|
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
|
|
||||||
if author == user:
|
if author == user:
|
||||||
@ -598,7 +615,6 @@ class Mod(commands.Cog):
|
|||||||
if invite is None:
|
if invite is None:
|
||||||
invite = ""
|
invite = ""
|
||||||
|
|
||||||
if can_ban:
|
|
||||||
queue_entry = (guild.id, user.id)
|
queue_entry = (guild.id, user.id)
|
||||||
try: # We don't want blocked DMs preventing us from banning
|
try: # We don't want blocked DMs preventing us from banning
|
||||||
msg = await user.send(
|
msg = await user.send(
|
||||||
@ -650,23 +666,18 @@ class Mod(commands.Cog):
|
|||||||
)
|
)
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
await ctx.send(e)
|
await ctx.send(e)
|
||||||
else:
|
|
||||||
await ctx.send(_("I'm not allowed to do that."))
|
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@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):
|
||||||
"""Unbans the target user.
|
"""Unban a user from the current 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
|
||||||
2. enable developer mode, go to Bans in this server's settings, right-
|
2. enable developer mode, go to Bans in this server's settings, right-
|
||||||
click the user and select 'Copy ID'."""
|
click the user and select 'Copy ID'."""
|
||||||
channel = ctx.channel
|
|
||||||
if not channel.permissions_for(ctx.guild.me).ban_members:
|
|
||||||
await ctx.send("I need the Ban Members permission to do this.")
|
|
||||||
return
|
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
user = await self.bot.get_user_info(user_id)
|
user = await self.bot.get_user_info(user_id)
|
||||||
@ -772,7 +783,7 @@ class Mod(commands.Cog):
|
|||||||
@admin_or_voice_permissions(mute_members=True, deafen_members=True)
|
@admin_or_voice_permissions(mute_members=True, deafen_members=True)
|
||||||
@bot_has_voice_permissions(mute_members=True, deafen_members=True)
|
@bot_has_voice_permissions(mute_members=True, deafen_members=True)
|
||||||
async def voiceban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
async def voiceban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
||||||
"""Bans the target user from speaking and listening in voice channels in the server"""
|
"""Ban a user from speaking and listening in the server's voice channels."""
|
||||||
user_voice_state = user.voice
|
user_voice_state = user.voice
|
||||||
if user_voice_state is None:
|
if user_voice_state is None:
|
||||||
await ctx.send(_("No voice state for that user!"))
|
await ctx.send(_("No voice state for that user!"))
|
||||||
@ -813,7 +824,7 @@ class Mod(commands.Cog):
|
|||||||
@admin_or_voice_permissions(mute_members=True, deafen_members=True)
|
@admin_or_voice_permissions(mute_members=True, deafen_members=True)
|
||||||
@bot_has_voice_permissions(mute_members=True, deafen_members=True)
|
@bot_has_voice_permissions(mute_members=True, deafen_members=True)
|
||||||
async def voiceunban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
async def voiceunban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
||||||
"""Unbans the user from speaking/listening in the server's voice channels"""
|
"""Unban a the user from speaking and listening in the server's voice channels."""
|
||||||
user_voice_state = user.voice
|
user_voice_state = user.voice
|
||||||
if user_voice_state is None:
|
if user_voice_state is None:
|
||||||
await ctx.send(_("No voice state for that user!"))
|
await ctx.send(_("No voice state for that user!"))
|
||||||
@ -850,29 +861,24 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_nicknames=True)
|
||||||
@checks.admin_or_permissions(manage_nicknames=True)
|
@checks.admin_or_permissions(manage_nicknames=True)
|
||||||
async def rename(self, ctx: commands.Context, user: discord.Member, *, nickname=""):
|
async def rename(self, ctx: commands.Context, user: discord.Member, *, nickname=""):
|
||||||
"""Changes user's nickname
|
"""Change a user's nickname.
|
||||||
|
|
||||||
Leaving the nickname empty will remove it."""
|
Leaving the nickname empty will remove it.
|
||||||
|
"""
|
||||||
nickname = nickname.strip()
|
nickname = nickname.strip()
|
||||||
if nickname == "":
|
if nickname == "":
|
||||||
nickname = None
|
nickname = None
|
||||||
try:
|
|
||||||
await user.edit(reason=get_audit_reason(ctx.author, None), nick=nickname)
|
await user.edit(reason=get_audit_reason(ctx.author, None), nick=nickname)
|
||||||
await ctx.send("Done.")
|
await ctx.send("Done.")
|
||||||
except discord.Forbidden:
|
|
||||||
await ctx.send(
|
|
||||||
_("I cannot do that, I lack the '{perm}' permission.").format(
|
|
||||||
perm="Manage Nicknames"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.mod_or_permissions(manage_channel=True)
|
@checks.mod_or_permissions(manage_channel=True)
|
||||||
async def mute(self, ctx: commands.Context):
|
async def mute(self, ctx: commands.Context):
|
||||||
"""Mutes user in the channel/server"""
|
"""Mute users."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@mute.command(name="voice")
|
@mute.command(name="voice")
|
||||||
@ -880,7 +886,7 @@ class Mod(commands.Cog):
|
|||||||
@mod_or_voice_permissions(mute_members=True)
|
@mod_or_voice_permissions(mute_members=True)
|
||||||
@bot_has_voice_permissions(mute_members=True)
|
@bot_has_voice_permissions(mute_members=True)
|
||||||
async def voice_mute(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
async def voice_mute(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
||||||
"""Mutes the user in a voice channel"""
|
"""Mute a user in their current voice channel."""
|
||||||
user_voice_state = user.voice
|
user_voice_state = user.voice
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
@ -920,13 +926,14 @@ class Mod(commands.Cog):
|
|||||||
await ctx.send(_("No voice state for the target!"))
|
await ctx.send(_("No voice state for the target!"))
|
||||||
return
|
return
|
||||||
|
|
||||||
@checks.mod_or_permissions(administrator=True)
|
|
||||||
@mute.command(name="channel")
|
@mute.command(name="channel")
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_roles=True)
|
||||||
|
@checks.mod_or_permissions(administrator=True)
|
||||||
async def channel_mute(
|
async def channel_mute(
|
||||||
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Mutes user in the current channel"""
|
"""Mute a user in the current text channel."""
|
||||||
author = ctx.message.author
|
author = ctx.message.author
|
||||||
channel = ctx.message.channel
|
channel = ctx.message.channel
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
@ -959,14 +966,14 @@ class Mod(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
await channel.send(issue)
|
await channel.send(issue)
|
||||||
|
|
||||||
@checks.mod_or_permissions(administrator=True)
|
|
||||||
@mute.command(name="server", aliases=["guild"])
|
@mute.command(name="server", aliases=["guild"])
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_roles=True)
|
||||||
|
@checks.mod_or_permissions(administrator=True)
|
||||||
async def guild_mute(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
async def guild_mute(self, ctx: commands.Context, user: discord.Member, *, reason: str = None):
|
||||||
"""Mutes user in the server"""
|
"""Mutes user in the server"""
|
||||||
author = ctx.message.author
|
author = ctx.message.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
user_voice_state = user.voice
|
|
||||||
if reason is None:
|
if reason is None:
|
||||||
audit_reason = "server mute requested by {author} (ID {author.id})".format(
|
audit_reason = "server mute requested by {author} (ID {author.id})".format(
|
||||||
author=author
|
author=author
|
||||||
@ -1018,10 +1025,10 @@ class Mod(commands.Cog):
|
|||||||
perms_cache = await self.settings.member(user).perms_cache()
|
perms_cache = await self.settings.member(user).perms_cache()
|
||||||
|
|
||||||
if overwrites.send_messages is False or permissions.send_messages is False:
|
if overwrites.send_messages is False or permissions.send_messages is False:
|
||||||
return False, mute_unmute_issues["already_muted"]
|
return False, T_(mute_unmute_issues["already_muted"])
|
||||||
|
|
||||||
elif not await is_allowed_by_hierarchy(self.bot, self.settings, guild, author, user):
|
elif not await is_allowed_by_hierarchy(self.bot, self.settings, guild, author, user):
|
||||||
return False, mute_unmute_issues["hierarchy_problem"]
|
return False, T_(mute_unmute_issues["hierarchy_problem"])
|
||||||
|
|
||||||
perms_cache[str(channel.id)] = {
|
perms_cache[str(channel.id)] = {
|
||||||
"send_messages": overwrites.send_messages,
|
"send_messages": overwrites.send_messages,
|
||||||
@ -1031,28 +1038,27 @@ class Mod(commands.Cog):
|
|||||||
try:
|
try:
|
||||||
await channel.set_permissions(user, overwrite=overwrites, reason=reason)
|
await channel.set_permissions(user, overwrite=overwrites, reason=reason)
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
return False, mute_unmute_issues["permissions_issue"]
|
return False, T_(mute_unmute_issues["permissions_issue"])
|
||||||
else:
|
else:
|
||||||
await self.settings.member(user).perms_cache.set(perms_cache)
|
await self.settings.member(user).perms_cache.set(perms_cache)
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(manage_roles=True)
|
||||||
@checks.mod_or_permissions(manage_channel=True)
|
@checks.mod_or_permissions(manage_channel=True)
|
||||||
async def unmute(self, ctx: commands.Context):
|
async def unmute(self, ctx: commands.Context):
|
||||||
"""Unmutes user in the channel/server
|
"""Unmute users."""
|
||||||
|
|
||||||
Defaults to channel"""
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@unmute.command(name="voice")
|
@unmute.command(name="voice")
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@mod_or_voice_permissions(mute_members=True)
|
@mod_or_voice_permissions(mute_members=True)
|
||||||
@bot_has_voice_permissions(mute_members=True)
|
@bot_has_voice_permissions(mute_members=True)
|
||||||
async def voice_unmute(
|
async def unmute_voice(
|
||||||
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Unmutes the user in a voice channel"""
|
"""Unmute a user in their current voice channel."""
|
||||||
user_voice_state = user.voice
|
user_voice_state = user.voice
|
||||||
if user_voice_state:
|
if user_voice_state:
|
||||||
channel = user_voice_state.channel
|
channel = user_voice_state.channel
|
||||||
@ -1093,11 +1099,12 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@checks.mod_or_permissions(administrator=True)
|
@checks.mod_or_permissions(administrator=True)
|
||||||
@unmute.command(name="channel")
|
@unmute.command(name="channel")
|
||||||
|
@commands.bot_has_permissions(manage_roles=True)
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def channel_unmute(
|
async def unmute_channel(
|
||||||
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Unmutes user in the current channel"""
|
"""Unmute a user in the current channel."""
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
@ -1125,14 +1132,14 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@checks.mod_or_permissions(administrator=True)
|
@checks.mod_or_permissions(administrator=True)
|
||||||
@unmute.command(name="server", aliases=["guild"])
|
@unmute.command(name="server", aliases=["guild"])
|
||||||
|
@commands.bot_has_permissions(manage_roles=True)
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def guild_unmute(
|
async def unmute_guild(
|
||||||
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
self, ctx: commands.Context, user: discord.Member, *, reason: str = None
|
||||||
):
|
):
|
||||||
"""Unmutes user in the server"""
|
"""Unmute a user in the current server."""
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
channel = ctx.channel
|
|
||||||
|
|
||||||
unmute_success = []
|
unmute_success = []
|
||||||
for channel in guild.channels:
|
for channel in guild.channels:
|
||||||
@ -1172,10 +1179,10 @@ class Mod(commands.Cog):
|
|||||||
perms_cache = await self.settings.member(user).perms_cache()
|
perms_cache = await self.settings.member(user).perms_cache()
|
||||||
|
|
||||||
if overwrites.send_messages or permissions.send_messages:
|
if overwrites.send_messages or permissions.send_messages:
|
||||||
return False, mute_unmute_issues["already_unmuted"]
|
return False, T_(mute_unmute_issues["already_unmuted"])
|
||||||
|
|
||||||
elif not await is_allowed_by_hierarchy(self.bot, self.settings, guild, author, user):
|
elif not await is_allowed_by_hierarchy(self.bot, self.settings, guild, author, user):
|
||||||
return False, mute_unmute_issues["hierarchy_problem"]
|
return False, T_(mute_unmute_issues["hierarchy_problem"])
|
||||||
|
|
||||||
if channel.id in perms_cache:
|
if channel.id in perms_cache:
|
||||||
old_values = perms_cache[channel.id]
|
old_values = perms_cache[channel.id]
|
||||||
@ -1190,9 +1197,11 @@ class Mod(commands.Cog):
|
|||||||
if not is_empty:
|
if not is_empty:
|
||||||
await channel.set_permissions(user, overwrite=overwrites)
|
await channel.set_permissions(user, overwrite=overwrites)
|
||||||
else:
|
else:
|
||||||
await channel.set_permissions(user, overwrite=None)
|
await channel.set_permissions(
|
||||||
|
user, overwrite=cast(discord.PermissionOverwrite, None)
|
||||||
|
)
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
return False, mute_unmute_issues["permissions_issue"]
|
return False, T_(mute_unmute_issues["permissions_issue"])
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
del perms_cache[channel.id]
|
del perms_cache[channel.id]
|
||||||
@ -1206,15 +1215,16 @@ class Mod(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.admin_or_permissions(manage_channels=True)
|
@checks.admin_or_permissions(manage_channels=True)
|
||||||
async def ignore(self, ctx: commands.Context):
|
async def ignore(self, ctx: commands.Context):
|
||||||
"""Adds servers/channels to ignorelist"""
|
"""Add servers or channels to the ignore list."""
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
await ctx.send(await self.count_ignored())
|
await ctx.send(await self.count_ignored())
|
||||||
|
|
||||||
@ignore.command(name="channel")
|
@ignore.command(name="channel")
|
||||||
async def ignore_channel(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
async def ignore_channel(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||||
"""Ignores channel
|
"""Ignore commands in the channel.
|
||||||
|
|
||||||
Defaults to current one"""
|
Defaults to the current channel.
|
||||||
|
"""
|
||||||
if not channel:
|
if not channel:
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
if not await self.settings.channel(channel).ignored():
|
if not await self.settings.channel(channel).ignored():
|
||||||
@ -1226,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):
|
||||||
"""Ignores current server"""
|
"""Ignore commands in the current 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)
|
||||||
@ -1238,15 +1248,16 @@ class Mod(commands.Cog):
|
|||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
@checks.admin_or_permissions(manage_channels=True)
|
@checks.admin_or_permissions(manage_channels=True)
|
||||||
async def unignore(self, ctx: commands.Context):
|
async def unignore(self, ctx: commands.Context):
|
||||||
"""Removes servers/channels from ignorelist"""
|
"""Remove servers or channels from the ignore list."""
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
await ctx.send(await self.count_ignored())
|
await ctx.send(await self.count_ignored())
|
||||||
|
|
||||||
@unignore.command(name="channel")
|
@unignore.command(name="channel")
|
||||||
async def unignore_channel(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
async def unignore_channel(self, ctx: commands.Context, channel: discord.TextChannel = None):
|
||||||
"""Removes channel from ignore list
|
"""Remove a channel from ignore the list.
|
||||||
|
|
||||||
Defaults to current one"""
|
Defaults to the current channel.
|
||||||
|
"""
|
||||||
if not channel:
|
if not channel:
|
||||||
channel = ctx.channel
|
channel = ctx.channel
|
||||||
|
|
||||||
@ -1259,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):
|
||||||
"""Removes current guild from ignore list"""
|
"""Remove the current 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)
|
||||||
@ -1284,7 +1295,8 @@ class Mod(commands.Cog):
|
|||||||
"""Global check to see if a channel or server is ignored.
|
"""Global check to see if a channel or server is ignored.
|
||||||
|
|
||||||
Any users who have permission to use the `ignore` or `unignore` commands
|
Any users who have permission to use the `ignore` or `unignore` commands
|
||||||
surpass the check."""
|
surpass the check.
|
||||||
|
"""
|
||||||
perms = ctx.channel.permissions_for(ctx.author)
|
perms = ctx.channel.permissions_for(ctx.author)
|
||||||
surpass_ignore = (
|
surpass_ignore = (
|
||||||
isinstance(ctx.channel, discord.abc.PrivateChannel)
|
isinstance(ctx.channel, discord.abc.PrivateChannel)
|
||||||
@ -1300,14 +1312,15 @@ class Mod(commands.Cog):
|
|||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
|
@commands.bot_has_permissions(embed_links=True)
|
||||||
async def userinfo(self, ctx, *, user: discord.Member = None):
|
async def userinfo(self, ctx, *, user: discord.Member = None):
|
||||||
"""Shows information for a user.
|
"""Show information about a user.
|
||||||
|
|
||||||
This includes fields for status, discord join date, server
|
This includes fields for status, discord join date, server
|
||||||
join date, voice state and previous names/nicknames.
|
join date, voice state and previous names/nicknames.
|
||||||
|
|
||||||
If the user has none of roles, previous names or previous
|
If the user has no roles, previous names or previous nicknames,
|
||||||
nicknames, these fields will be omitted.
|
these fields will be omitted.
|
||||||
"""
|
"""
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
@ -1383,14 +1396,11 @@ class Mod(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
data.set_author(name=name)
|
data.set_author(name=name)
|
||||||
|
|
||||||
try:
|
|
||||||
await ctx.send(embed=data)
|
await ctx.send(embed=data)
|
||||||
except discord.HTTPException:
|
|
||||||
await ctx.send(_("I need the `Embed links` permission to send this."))
|
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def names(self, ctx: commands.Context, user: discord.Member):
|
async def names(self, ctx: commands.Context, user: discord.Member):
|
||||||
"""Show previous names/nicknames of a user"""
|
"""Show previous names and nicknames of a user."""
|
||||||
names, nicks = await self.get_names_and_nicks(user)
|
names, nicks = await self.get_names_and_nicks(user)
|
||||||
msg = ""
|
msg = ""
|
||||||
if names:
|
if names:
|
||||||
@ -1433,7 +1443,7 @@ class Mod(commands.Cog):
|
|||||||
queue_entry = (guild.id, user.id)
|
queue_entry = (guild.id, user.id)
|
||||||
self.unban_queue.append(queue_entry)
|
self.unban_queue.append(queue_entry)
|
||||||
try:
|
try:
|
||||||
await guild.unban(user, reason="Tempban finished")
|
await guild.unban(user, reason=_("Tempban finished"))
|
||||||
guild_tempbans.remove(uid)
|
guild_tempbans.remove(uid)
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
self.unban_queue.remove(queue_entry)
|
self.unban_queue.remove(queue_entry)
|
||||||
@ -1463,12 +1473,12 @@ class Mod(commands.Cog):
|
|||||||
guild = message.guild
|
guild = message.guild
|
||||||
author = message.author
|
author = message.author
|
||||||
|
|
||||||
if await self.settings.guild(guild).ban_mention_spam():
|
|
||||||
max_mentions = await self.settings.guild(guild).ban_mention_spam()
|
max_mentions = await self.settings.guild(guild).ban_mention_spam()
|
||||||
|
if max_mentions:
|
||||||
mentions = set(message.mentions)
|
mentions = set(message.mentions)
|
||||||
if len(mentions) >= max_mentions:
|
if len(mentions) >= max_mentions:
|
||||||
try:
|
try:
|
||||||
await guild.ban(author, reason="Mention spam (Autoban)")
|
await guild.ban(author, reason=_("Mention spam (Autoban)"))
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
log.info(
|
log.info(
|
||||||
"Failed to ban member for mention spam in server {}.".format(guild.id)
|
"Failed to ban member for mention spam in server {}.".format(guild.id)
|
||||||
@ -1482,7 +1492,7 @@ class Mod(commands.Cog):
|
|||||||
"ban",
|
"ban",
|
||||||
author,
|
author,
|
||||||
guild.me,
|
guild.me,
|
||||||
"Mention spam (Autoban)",
|
_("Mention spam (Autoban)"),
|
||||||
until=None,
|
until=None,
|
||||||
channel=None,
|
channel=None,
|
||||||
)
|
)
|
||||||
@ -1495,6 +1505,7 @@ class Mod(commands.Cog):
|
|||||||
async def on_command_completion(self, ctx: commands.Context):
|
async def on_command_completion(self, ctx: commands.Context):
|
||||||
await self._delete_delay(ctx)
|
await self._delete_delay(ctx)
|
||||||
|
|
||||||
|
# noinspection PyUnusedLocal
|
||||||
async def on_command_error(self, ctx: commands.Context, error):
|
async def on_command_error(self, ctx: commands.Context, error):
|
||||||
await self._delete_delay(ctx)
|
await self._delete_delay(ctx)
|
||||||
|
|
||||||
@ -1511,11 +1522,9 @@ class Mod(commands.Cog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
async def _delete_helper(m):
|
async def _delete_helper(m):
|
||||||
try:
|
with contextlib.suppress(discord.HTTPException):
|
||||||
await m.delete()
|
await m.delete()
|
||||||
log.debug("Deleted command msg {}".format(m.id))
|
log.debug("Deleted command msg {}".format(m.id))
|
||||||
except:
|
|
||||||
pass # We don't really care if it fails or not
|
|
||||||
|
|
||||||
await asyncio.sleep(delay)
|
await asyncio.sleep(delay)
|
||||||
await _delete_helper(message)
|
await _delete_helper(message)
|
||||||
@ -1537,7 +1546,7 @@ class Mod(commands.Cog):
|
|||||||
return
|
return
|
||||||
deleted = await self.check_duplicates(message)
|
deleted = await self.check_duplicates(message)
|
||||||
if not deleted:
|
if not deleted:
|
||||||
deleted = await self.check_mention_spam(message)
|
await self.check_mention_spam(message)
|
||||||
|
|
||||||
async def on_member_ban(self, guild: discord.Guild, member: discord.Member):
|
async def on_member_ban(self, guild: discord.Guild, member: discord.Member):
|
||||||
if (guild.id, member.id) in self.ban_queue:
|
if (guild.id, member.id) in self.ban_queue:
|
||||||
@ -1577,7 +1586,8 @@ class Mod(commands.Cog):
|
|||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
async def on_modlog_case_create(self, case: modlog.Case):
|
@staticmethod
|
||||||
|
async def on_modlog_case_create(case: modlog.Case):
|
||||||
"""
|
"""
|
||||||
An event for modlog case creation
|
An event for modlog case creation
|
||||||
"""
|
"""
|
||||||
@ -1592,7 +1602,8 @@ class Mod(commands.Cog):
|
|||||||
msg = await mod_channel.send(case_content)
|
msg = await mod_channel.send(case_content)
|
||||||
await case.edit({"message": msg})
|
await case.edit({"message": msg})
|
||||||
|
|
||||||
async def on_modlog_case_edit(self, case: modlog.Case):
|
@staticmethod
|
||||||
|
async def on_modlog_case_edit(case: modlog.Case):
|
||||||
"""
|
"""
|
||||||
Event for modlog case edits
|
Event for modlog case edits
|
||||||
"""
|
"""
|
||||||
@ -1605,7 +1616,10 @@ class Mod(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
await case.message.edit(content=case_content)
|
await case.message.edit(content=case_content)
|
||||||
|
|
||||||
async def get_audit_entry_info(self, guild: discord.Guild, action: int, target):
|
@classmethod
|
||||||
|
async def get_audit_entry_info(
|
||||||
|
cls, guild: discord.Guild, action: discord.AuditLogAction, target
|
||||||
|
):
|
||||||
"""Get info about an audit log entry.
|
"""Get info about an audit log entry.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@ -1625,14 +1639,15 @@ class Mod(commands.Cog):
|
|||||||
if the audit log entry could not be found.
|
if the audit log entry could not be found.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
entry = await self.get_audit_log_entry(guild, action=action, target=target)
|
entry = await cls.get_audit_log_entry(guild, action=action, target=target)
|
||||||
except discord.HTTPException:
|
except discord.HTTPException:
|
||||||
entry = None
|
entry = None
|
||||||
if entry is None:
|
if entry is None:
|
||||||
return None, None, None
|
return None, None, None
|
||||||
return entry.user, entry.reason, entry.created_at
|
return entry.user, entry.reason, entry.created_at
|
||||||
|
|
||||||
async def get_audit_log_entry(self, guild: discord.Guild, action: int, target):
|
@staticmethod
|
||||||
|
async def get_audit_log_entry(guild: discord.Guild, action: discord.AuditLogAction, target):
|
||||||
"""Get an audit log entry.
|
"""Get an audit log entry.
|
||||||
|
|
||||||
Any exceptions encountered when looking through the audit log will be
|
Any exceptions encountered when looking through the audit log will be
|
||||||
@ -1686,12 +1701,16 @@ class Mod(commands.Cog):
|
|||||||
return [p for p in iter(overwrites)] == [p for p in iter(discord.PermissionOverwrite())]
|
return [p for p in iter(overwrites)] == [p for p in iter(discord.PermissionOverwrite())]
|
||||||
|
|
||||||
|
|
||||||
|
_ = lambda s: s
|
||||||
mute_unmute_issues = {
|
mute_unmute_issues = {
|
||||||
"already_muted": "That user can't send messages in this channel.",
|
"already_muted": _("That user can't send messages in this channel."),
|
||||||
"already_unmuted": "That user isn't muted in this channel!",
|
"already_unmuted": _("That user isn't muted in this channel!"),
|
||||||
"hierarchy_problem": "I cannot let you do that. You are not higher than "
|
"hierarchy_problem": _(
|
||||||
"the user in the role hierarchy.",
|
"I cannot let you do that. You are not higher than " "the user in the role hierarchy."
|
||||||
"permissions_issue": "Failed to mute user. I need the manage roles "
|
),
|
||||||
|
"permissions_issue": _(
|
||||||
|
"Failed to mute user. I need the manage roles "
|
||||||
"permission and the user I'm muting must be "
|
"permission and the user I'm muting must be "
|
||||||
"lower than myself in the role hierarchy.",
|
"lower than myself in the role hierarchy."
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
from typing import Union, List
|
from typing import Union, List, Optional
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
|
||||||
@ -296,12 +296,20 @@ async def transfer_credits(from_: discord.Member, to: discord.Member, amount: in
|
|||||||
return await deposit_credits(to, amount)
|
return await deposit_credits(to, amount)
|
||||||
|
|
||||||
|
|
||||||
async def wipe_bank():
|
async def wipe_bank(guild: Optional[discord.Guild] = None) -> None:
|
||||||
"""Delete all accounts from the bank."""
|
"""Delete all accounts from the bank.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
guild : discord.Guild
|
||||||
|
The guild to clear accounts for. If unsupplied and the bank is
|
||||||
|
per-server, all accounts in every guild will be wiped.
|
||||||
|
|
||||||
|
"""
|
||||||
if await is_global():
|
if await is_global():
|
||||||
await _conf.clear_all_users()
|
await _conf.clear_all_users()
|
||||||
else:
|
else:
|
||||||
await _conf.clear_all_members()
|
await _conf.clear_all_members(guild)
|
||||||
|
|
||||||
|
|
||||||
async def get_leaderboard(positions: int = None, guild: discord.Guild = None) -> List[tuple]:
|
async def get_leaderboard(positions: int = None, guild: discord.Guild = None) -> List[tuple]:
|
||||||
|
|||||||
@ -838,7 +838,7 @@ class Config:
|
|||||||
"""
|
"""
|
||||||
return self._get_base_group(self.ROLE, role.id)
|
return self._get_base_group(self.ROLE, role.id)
|
||||||
|
|
||||||
def user(self, user: discord.User) -> Group:
|
def user(self, user: discord.abc.User) -> Group:
|
||||||
"""Returns a `Group` for the given user.
|
"""Returns a `Group` for the given user.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Callable, Union
|
||||||
|
|
||||||
from . import commands
|
from . import commands
|
||||||
|
|
||||||
@ -113,9 +115,9 @@ def _normalize(string, remove_newline=False):
|
|||||||
ends_with_space = s[-1] in " \n\t\r"
|
ends_with_space = s[-1] in " \n\t\r"
|
||||||
if remove_newline:
|
if remove_newline:
|
||||||
newline_re = re.compile("[\r\n]+")
|
newline_re = re.compile("[\r\n]+")
|
||||||
s = " ".join(filter(bool, newline_re.split(s)))
|
s = " ".join(filter(None, newline_re.split(s)))
|
||||||
s = " ".join(filter(bool, s.split("\t")))
|
s = " ".join(filter(None, s.split("\t")))
|
||||||
s = " ".join(filter(bool, s.split(" ")))
|
s = " ".join(filter(None, s.split(" ")))
|
||||||
if starts_with_space:
|
if starts_with_space:
|
||||||
s = " " + s
|
s = " " + s
|
||||||
if ends_with_space:
|
if ends_with_space:
|
||||||
@ -149,10 +151,10 @@ def get_locale_path(cog_folder: Path, extension: str) -> Path:
|
|||||||
return cog_folder / "locales" / "{}.{}".format(get_locale(), extension)
|
return cog_folder / "locales" / "{}.{}".format(get_locale(), extension)
|
||||||
|
|
||||||
|
|
||||||
class Translator:
|
class Translator(Callable[[str], str]):
|
||||||
"""Function to get translated strings at runtime."""
|
"""Function to get translated strings at runtime."""
|
||||||
|
|
||||||
def __init__(self, name, file_location):
|
def __init__(self, name: str, file_location: Union[str, Path, os.PathLike]):
|
||||||
"""
|
"""
|
||||||
Initializes an internationalization object.
|
Initializes an internationalization object.
|
||||||
|
|
||||||
@ -173,7 +175,7 @@ class Translator:
|
|||||||
|
|
||||||
self.load_translations()
|
self.load_translations()
|
||||||
|
|
||||||
def __call__(self, untranslated: str):
|
def __call__(self, untranslated: str) -> str:
|
||||||
"""Translate the given string.
|
"""Translate the given string.
|
||||||
|
|
||||||
This will look for the string in the translator's :code:`.pot` file,
|
This will look for the string in the translator's :code:`.pot` file,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user