mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-20 18:06:08 -05:00
[V3] Update code standards (black code format pass) (#1650)
* ran black: code formatter against `redbot/` with `-l 99` * badge
This commit is contained in:
@@ -40,18 +40,16 @@ RUNNING_ANNOUNCEMENT = (
|
||||
|
||||
|
||||
class Admin:
|
||||
def __init__(self, config=Config):
|
||||
self.conf = config.get_conf(self, 8237492837454039,
|
||||
force_registration=True)
|
||||
|
||||
self.conf.register_global(
|
||||
serverlocked=False
|
||||
)
|
||||
def __init__(self, config=Config):
|
||||
self.conf = config.get_conf(self, 8237492837454039, force_registration=True)
|
||||
|
||||
self.conf.register_global(serverlocked=False)
|
||||
|
||||
self.conf.register_guild(
|
||||
announce_ignore=False,
|
||||
announce_channel=None, # Integer ID
|
||||
selfroles=[] # List of integer ID's
|
||||
selfroles=[], # List of integer ID's
|
||||
)
|
||||
|
||||
self.__current_announcer = None
|
||||
@@ -63,8 +61,7 @@ class Admin:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
async def complain(ctx: commands.Context, message: str,
|
||||
**kwargs):
|
||||
async def complain(ctx: commands.Context, message: str, **kwargs):
|
||||
await ctx.send(message.format(**kwargs))
|
||||
|
||||
def is_announcing(self) -> bool:
|
||||
@@ -78,8 +75,7 @@ class Admin:
|
||||
return self.__current_announcer.active or False
|
||||
|
||||
@staticmethod
|
||||
def pass_heirarchy_check(ctx: commands.Context,
|
||||
role: discord.Role) -> bool:
|
||||
def pass_heirarchy_check(ctx: commands.Context, role: discord.Role) -> bool:
|
||||
"""
|
||||
Determines if the bot has a higher role than the given one.
|
||||
:param ctx:
|
||||
@@ -89,8 +85,7 @@ class Admin:
|
||||
return ctx.guild.me.top_role > role
|
||||
|
||||
@staticmethod
|
||||
def pass_user_heirarchy_check(ctx: commands.Context,
|
||||
role: discord.Role) -> bool:
|
||||
def pass_user_heirarchy_check(ctx: commands.Context, role: discord.Role) -> bool:
|
||||
"""
|
||||
Determines if a user is allowed to add/remove/edit the given role.
|
||||
:param ctx:
|
||||
@@ -99,43 +94,40 @@ class Admin:
|
||||
"""
|
||||
return ctx.author.top_role > role
|
||||
|
||||
async def _addrole(self, ctx: commands.Context, member: discord.Member,
|
||||
role: discord.Role):
|
||||
async def _addrole(self, ctx: commands.Context, member: discord.Member, role: discord.Role):
|
||||
try:
|
||||
await member.add_roles(role)
|
||||
except discord.Forbidden:
|
||||
if not self.pass_heirarchy_check(ctx, role):
|
||||
await self.complain(ctx, HIERARCHY_ISSUE, role=role,
|
||||
member=member)
|
||||
await self.complain(ctx, HIERARCHY_ISSUE, role=role, member=member)
|
||||
else:
|
||||
await self.complain(ctx, GENERIC_FORBIDDEN)
|
||||
else:
|
||||
await ctx.send("I successfully added {role.name} to"
|
||||
" {member.display_name}".format(
|
||||
role=role, member=member
|
||||
))
|
||||
await ctx.send(
|
||||
"I successfully added {role.name} to"
|
||||
" {member.display_name}".format(role=role, member=member)
|
||||
)
|
||||
|
||||
async def _removerole(self, ctx: commands.Context, member: discord.Member,
|
||||
role: discord.Role):
|
||||
async def _removerole(self, ctx: commands.Context, member: discord.Member, role: discord.Role):
|
||||
try:
|
||||
await member.remove_roles(role)
|
||||
except discord.Forbidden:
|
||||
if not self.pass_heirarchy_check(ctx, role):
|
||||
await self.complain(ctx, HIERARCHY_ISSUE, role=role,
|
||||
member=member)
|
||||
await self.complain(ctx, HIERARCHY_ISSUE, role=role, member=member)
|
||||
else:
|
||||
await self.complain(ctx, GENERIC_FORBIDDEN)
|
||||
else:
|
||||
await ctx.send("I successfully removed {role.name} from"
|
||||
" {member.display_name}".format(
|
||||
role=role, member=member
|
||||
))
|
||||
await ctx.send(
|
||||
"I successfully removed {role.name} from"
|
||||
" {member.display_name}".format(role=role, member=member)
|
||||
)
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@checks.admin_or_permissions(manage_roles=True)
|
||||
async def addrole(self, ctx: commands.Context, rolename: discord.Role, *,
|
||||
user: MemberDefaultAuthor=None):
|
||||
async def addrole(
|
||||
self, ctx: commands.Context, rolename: discord.Role, *, user: MemberDefaultAuthor = None
|
||||
):
|
||||
"""
|
||||
Adds a role to a user. If user is left blank it defaults to the
|
||||
author of the command.
|
||||
@@ -151,8 +143,9 @@ class Admin:
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@checks.admin_or_permissions(manage_roles=True)
|
||||
async def removerole(self, ctx: commands.Context, rolename: discord.Role, *,
|
||||
user: MemberDefaultAuthor=None):
|
||||
async def removerole(
|
||||
self, ctx: commands.Context, rolename: discord.Role, *, user: MemberDefaultAuthor = None
|
||||
):
|
||||
"""
|
||||
Removes a role from a user. If user is left blank it defaults to the
|
||||
author of the command.
|
||||
@@ -173,9 +166,10 @@ class Admin:
|
||||
if ctx.invoked_subcommand is None:
|
||||
await ctx.send_help()
|
||||
|
||||
@editrole.command(name="colour", aliases=["color", ])
|
||||
async def editrole_colour(self, ctx: commands.Context, role: discord.Role,
|
||||
value: discord.Colour):
|
||||
@editrole.command(name="colour", aliases=["color"])
|
||||
async def editrole_colour(
|
||||
self, ctx: commands.Context, role: discord.Role, value: discord.Colour
|
||||
):
|
||||
"""Edits a role's colour
|
||||
|
||||
Use double quotes if the role contains spaces.
|
||||
@@ -185,8 +179,7 @@ class Admin:
|
||||
!editrole colour \"The Transistor\" #ff0000
|
||||
!editrole colour Test #ff9900"""
|
||||
author = ctx.author
|
||||
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_heirarchy_check(ctx, role):
|
||||
await self.complain(ctx, USER_HIERARCHY_ISSUE)
|
||||
@@ -211,7 +204,8 @@ class Admin:
|
||||
author = ctx.message.author
|
||||
old_name = role.name
|
||||
reason = "{}({}) changed the name of role '{}' to '{}'".format(
|
||||
author.name, author.id, old_name, name)
|
||||
author.name, author.id, old_name, name
|
||||
)
|
||||
|
||||
if not self.pass_user_heirarchy_check(ctx, role):
|
||||
await self.complain(ctx, USER_HIERARCHY_ISSUE)
|
||||
@@ -240,8 +234,7 @@ class Admin:
|
||||
await ctx.send("The announcement has begun.")
|
||||
else:
|
||||
prefix = ctx.prefix
|
||||
await self.complain(ctx, RUNNING_ANNOUNCEMENT,
|
||||
prefix=prefix)
|
||||
await self.complain(ctx, RUNNING_ANNOUNCEMENT, prefix=prefix)
|
||||
|
||||
@announce.command(name="cancel")
|
||||
@checks.is_owner()
|
||||
@@ -259,7 +252,7 @@ class Admin:
|
||||
@announce.command(name="channel")
|
||||
@commands.guild_only()
|
||||
@checks.guildowner_or_permissions(administrator=True)
|
||||
async def announce_channel(self, ctx, *, channel: discord.TextChannel=None):
|
||||
async def announce_channel(self, ctx, *, channel: discord.TextChannel = None):
|
||||
"""
|
||||
Changes the channel on which the bot makes announcements.
|
||||
"""
|
||||
@@ -267,14 +260,12 @@ class Admin:
|
||||
channel = ctx.channel
|
||||
await self.conf.guild(ctx.guild).announce_channel.set(channel.id)
|
||||
|
||||
await ctx.send("The announcement channel has been set to {}".format(
|
||||
channel.mention
|
||||
))
|
||||
await ctx.send("The announcement channel has been set to {}".format(channel.mention))
|
||||
|
||||
@announce.command(name="ignore")
|
||||
@commands.guild_only()
|
||||
@checks.guildowner_or_permissions(administrator=True)
|
||||
async def announce_ignore(self, ctx, *, guild: discord.Guild=None):
|
||||
async def announce_ignore(self, ctx, *, guild: discord.Guild = None):
|
||||
"""
|
||||
Toggles whether the announcements will ignore the given server.
|
||||
Defaults to the current server if none is provided.
|
||||
@@ -287,9 +278,7 @@ class Admin:
|
||||
|
||||
verb = "will" if ignored else "will not"
|
||||
|
||||
await ctx.send("The server {} {} receive announcements.".format(
|
||||
guild.name, verb
|
||||
))
|
||||
await ctx.send("The server {} {} receive announcements.".format(guild.name, verb))
|
||||
|
||||
async def _valid_selfroles(self, guild: discord.Guild) -> Tuple[discord.Role]:
|
||||
"""
|
||||
@@ -384,8 +373,10 @@ class Admin:
|
||||
|
||||
await ctx.send("The bot {} serverlocked.".format(verb))
|
||||
|
||||
# region Event Handlers
|
||||
# region Event Handlers
|
||||
async def on_guild_join(self, guild: discord.Guild):
|
||||
if await self._serverlock_check(guild):
|
||||
return
|
||||
|
||||
|
||||
# endregion
|
||||
|
||||
@@ -5,9 +5,8 @@ from discord.ext import commands
|
||||
|
||||
|
||||
class Announcer:
|
||||
def __init__(self, ctx: commands.Context,
|
||||
message: str,
|
||||
config=None):
|
||||
|
||||
def __init__(self, ctx: commands.Context, message: str, config=None):
|
||||
"""
|
||||
:param ctx:
|
||||
:param message:
|
||||
@@ -65,10 +64,7 @@ class Announcer:
|
||||
try:
|
||||
await channel.send(self.message)
|
||||
except discord.Forbidden:
|
||||
await bot_owner.send("I could not announce to server: {}".format(
|
||||
g.id
|
||||
))
|
||||
await bot_owner.send("I could not announce to server: {}".format(g.id))
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
self.active = False
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from discord.ext import commands
|
||||
|
||||
|
||||
class MemberDefaultAuthor(commands.Converter):
|
||||
|
||||
async def convert(self, ctx: commands.Context, arg: str) -> discord.Member:
|
||||
member_converter = commands.MemberConverter()
|
||||
try:
|
||||
@@ -16,6 +17,7 @@ class MemberDefaultAuthor(commands.Converter):
|
||||
|
||||
|
||||
class SelfRole(commands.Converter):
|
||||
|
||||
async def convert(self, ctx: commands.Context, arg: str) -> discord.Role:
|
||||
admin = ctx.command.instance
|
||||
if admin is None:
|
||||
@@ -28,6 +30,5 @@ class SelfRole(commands.Converter):
|
||||
role = await role_converter.convert(ctx, arg)
|
||||
|
||||
if role.id not in selfroles:
|
||||
raise commands.BadArgument("The provided role is not a valid"
|
||||
" selfrole.")
|
||||
raise commands.BadArgument("The provided role is not a valid" " selfrole.")
|
||||
return role
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../admin.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../admin.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -26,14 +26,9 @@ class Alias:
|
||||
and append them to the stored alias
|
||||
"""
|
||||
|
||||
default_global_settings = {
|
||||
"entries": []
|
||||
}
|
||||
default_global_settings = {"entries": []}
|
||||
|
||||
default_guild_settings = {
|
||||
"enabled": False,
|
||||
"entries": [] # Going to be a list of dicts
|
||||
}
|
||||
default_guild_settings = {"enabled": False, "entries": []} # Going to be a list of dicts
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
@@ -49,14 +44,17 @@ class Alias:
|
||||
return (AliasEntry.from_json(d) for d in (await self._aliases.entries()))
|
||||
|
||||
async def loaded_aliases(self, guild: discord.Guild) -> Generator[AliasEntry, None, None]:
|
||||
return (AliasEntry.from_json(d, bot=self.bot)
|
||||
for d in (await self._aliases.guild(guild).entries()))
|
||||
return (
|
||||
AliasEntry.from_json(d, bot=self.bot)
|
||||
for d in (await self._aliases.guild(guild).entries())
|
||||
)
|
||||
|
||||
async def loaded_global_aliases(self) -> Generator[AliasEntry, None, None]:
|
||||
return (AliasEntry.from_json(d, bot=self.bot) for d in (await self._aliases.entries()))
|
||||
|
||||
async def is_alias(self, guild: discord.Guild, alias_name: str,
|
||||
server_aliases: Iterable[AliasEntry]=()) -> (bool, AliasEntry):
|
||||
async def is_alias(
|
||||
self, guild: discord.Guild, alias_name: str, server_aliases: Iterable[AliasEntry] = ()
|
||||
) -> (bool, AliasEntry):
|
||||
|
||||
if not server_aliases:
|
||||
server_aliases = await self.unloaded_aliases(guild)
|
||||
@@ -76,10 +74,11 @@ class Alias:
|
||||
|
||||
@staticmethod
|
||||
def is_valid_alias_name(alias_name: str) -> bool:
|
||||
return not bool(search(r'\s', alias_name)) and alias_name.isprintable()
|
||||
return not bool(search(r"\s", alias_name)) and alias_name.isprintable()
|
||||
|
||||
async def add_alias(self, ctx: commands.Context, alias_name: str,
|
||||
command: Tuple[str], global_: bool=False) -> AliasEntry:
|
||||
async def add_alias(
|
||||
self, ctx: commands.Context, alias_name: str, command: Tuple[str], global_: bool = False
|
||||
) -> AliasEntry:
|
||||
alias = AliasEntry(alias_name, command, ctx.author, global_=global_)
|
||||
|
||||
if global_:
|
||||
@@ -93,8 +92,9 @@ class Alias:
|
||||
|
||||
return alias
|
||||
|
||||
async def delete_alias(self, ctx: commands.Context, alias_name: str,
|
||||
global_: bool=False) -> bool:
|
||||
async def delete_alias(
|
||||
self, ctx: commands.Context, alias_name: str, global_: bool = False
|
||||
) -> bool:
|
||||
if global_:
|
||||
settings = self._aliases
|
||||
else:
|
||||
@@ -120,16 +120,15 @@ class Alias:
|
||||
"""
|
||||
content = message.content
|
||||
prefix_list = await self.bot.command_prefix(self.bot, message)
|
||||
prefixes = sorted(prefix_list,
|
||||
key=lambda pfx: len(pfx),
|
||||
reverse=True)
|
||||
prefixes = sorted(prefix_list, key=lambda pfx: len(pfx), reverse=True)
|
||||
for p in prefixes:
|
||||
if content.startswith(p):
|
||||
return p
|
||||
raise ValueError(_("No prefix found."))
|
||||
|
||||
def get_extra_args_from_alias(self, message: discord.Message, prefix: str,
|
||||
alias: AliasEntry) -> str:
|
||||
def get_extra_args_from_alias(
|
||||
self, message: discord.Message, prefix: str, alias: AliasEntry
|
||||
) -> str:
|
||||
"""
|
||||
When an alias is executed by a user in chat this function tries
|
||||
to get any extra arguments passed in with the call.
|
||||
@@ -143,8 +142,9 @@ class Alias:
|
||||
extra = message.content[known_content_length:].strip()
|
||||
return extra
|
||||
|
||||
async def maybe_call_alias(self, message: discord.Message,
|
||||
aliases: Iterable[AliasEntry]=None):
|
||||
async def maybe_call_alias(
|
||||
self, message: discord.Message, aliases: Iterable[AliasEntry] = None
|
||||
):
|
||||
try:
|
||||
prefix = await self.get_prefix(message)
|
||||
except ValueError:
|
||||
@@ -155,13 +155,14 @@ class Alias:
|
||||
except IndexError:
|
||||
return False
|
||||
|
||||
is_alias, alias = await self.is_alias(message.guild, potential_alias, server_aliases=aliases)
|
||||
is_alias, alias = await self.is_alias(
|
||||
message.guild, potential_alias, server_aliases=aliases
|
||||
)
|
||||
|
||||
if is_alias:
|
||||
await self.call_alias(message, prefix, alias)
|
||||
|
||||
async def call_alias(self, message: discord.Message, prefix: str,
|
||||
alias: AliasEntry):
|
||||
async def call_alias(self, message: discord.Message, prefix: str, alias: AliasEntry):
|
||||
new_message = copy(message)
|
||||
args = self.get_extra_args_from_alias(message, prefix, alias)
|
||||
|
||||
@@ -181,83 +182,118 @@ class Alias:
|
||||
"""
|
||||
Manage global aliases.
|
||||
"""
|
||||
if ctx.invoked_subcommand is None or \
|
||||
isinstance(ctx.invoked_subcommand, commands.Group):
|
||||
if ctx.invoked_subcommand is None or isinstance(ctx.invoked_subcommand, commands.Group):
|
||||
await ctx.send_help()
|
||||
|
||||
@alias.command(name="add")
|
||||
@commands.guild_only()
|
||||
async def _add_alias(self, ctx: commands.Context,
|
||||
alias_name: str, *, command):
|
||||
async def _add_alias(self, ctx: commands.Context, alias_name: str, *, command):
|
||||
"""
|
||||
Add an alias for a command.
|
||||
"""
|
||||
# region Alias Add Validity Checking
|
||||
# region Alias Add Validity Checking
|
||||
is_command = self.is_command(alias_name)
|
||||
if is_command:
|
||||
await ctx.send(_("You attempted to create a new alias"
|
||||
" with the name {} but that"
|
||||
" name is already a command on this bot.").format(alias_name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"You attempted to create a new alias"
|
||||
" with the name {} but that"
|
||||
" name is already a command on this bot."
|
||||
).format(
|
||||
alias_name
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
is_alias, something_useless = await self.is_alias(ctx.guild, alias_name)
|
||||
if is_alias:
|
||||
await ctx.send(_("You attempted to create a new alias"
|
||||
" with the name {} but that"
|
||||
" alias already exists on this server.").format(alias_name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"You attempted to create a new alias"
|
||||
" with the name {} but that"
|
||||
" alias already exists on this server."
|
||||
).format(
|
||||
alias_name
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
is_valid_name = self.is_valid_alias_name(alias_name)
|
||||
if not is_valid_name:
|
||||
await ctx.send(_("You attempted to create a new alias"
|
||||
" with the name {} but that"
|
||||
" name is an invalid alias name. Alias"
|
||||
" names may not contain spaces.").format(alias_name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"You attempted to create a new alias"
|
||||
" with the name {} but that"
|
||||
" name is an invalid alias name. Alias"
|
||||
" names may not contain spaces."
|
||||
).format(
|
||||
alias_name
|
||||
)
|
||||
)
|
||||
return
|
||||
# endregion
|
||||
# endregion
|
||||
|
||||
# At this point we know we need to make a new alias
|
||||
# and that the alias name is valid.
|
||||
|
||||
await self.add_alias(ctx, alias_name, command)
|
||||
|
||||
await ctx.send(_("A new alias with the trigger `{}`"
|
||||
" has been created.").format(alias_name))
|
||||
await ctx.send(
|
||||
_("A new alias with the trigger `{}`" " has been created.").format(alias_name)
|
||||
)
|
||||
|
||||
@global_.command(name="add")
|
||||
async def _add_global_alias(self, ctx: commands.Context,
|
||||
alias_name: str, *, command):
|
||||
async def _add_global_alias(self, ctx: commands.Context, alias_name: str, *, command):
|
||||
"""
|
||||
Add a global alias for a command.
|
||||
"""
|
||||
# region Alias Add Validity Checking
|
||||
# region Alias Add Validity Checking
|
||||
is_command = self.is_command(alias_name)
|
||||
if is_command:
|
||||
await ctx.send(_("You attempted to create a new global alias"
|
||||
" with the name {} but that"
|
||||
" name is already a command on this bot.").format(alias_name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"You attempted to create a new global alias"
|
||||
" with the name {} but that"
|
||||
" name is already a command on this bot."
|
||||
).format(
|
||||
alias_name
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
is_alias, something_useless = await self.is_alias(ctx.guild, alias_name)
|
||||
if is_alias:
|
||||
await ctx.send(_("You attempted to create a new global alias"
|
||||
" with the name {} but that"
|
||||
" alias already exists on this server.").format(alias_name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"You attempted to create a new global alias"
|
||||
" with the name {} but that"
|
||||
" alias already exists on this server."
|
||||
).format(
|
||||
alias_name
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
is_valid_name = self.is_valid_alias_name(alias_name)
|
||||
if not is_valid_name:
|
||||
await ctx.send(_("You attempted to create a new global alias"
|
||||
" with the name {} but that"
|
||||
" name is an invalid alias name. Alias"
|
||||
" names may not contain spaces.").format(alias_name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"You attempted to create a new global alias"
|
||||
" with the name {} but that"
|
||||
" name is an invalid alias name. Alias"
|
||||
" names may not contain spaces."
|
||||
).format(
|
||||
alias_name
|
||||
)
|
||||
)
|
||||
return
|
||||
# endregion
|
||||
# endregion
|
||||
|
||||
await self.add_alias(ctx, alias_name, command, global_=True)
|
||||
|
||||
await ctx.send(_("A new global alias with the trigger `{}`"
|
||||
" has been created.").format(alias_name))
|
||||
await ctx.send(
|
||||
_("A new global alias with the trigger `{}`" " has been created.").format(alias_name)
|
||||
)
|
||||
|
||||
@alias.command(name="help")
|
||||
@commands.guild_only()
|
||||
@@ -280,8 +316,11 @@ class Alias:
|
||||
is_alias, alias = await self.is_alias(ctx.guild, alias_name)
|
||||
|
||||
if is_alias:
|
||||
await ctx.send(_("The `{}` alias will execute the"
|
||||
" command `{}`").format(alias_name, alias.command))
|
||||
await ctx.send(
|
||||
_("The `{}` alias will execute the" " command `{}`").format(
|
||||
alias_name, alias.command
|
||||
)
|
||||
)
|
||||
else:
|
||||
await ctx.send(_("There is no alias with the name `{}`").format(alias_name))
|
||||
|
||||
@@ -299,8 +338,9 @@ class Alias:
|
||||
return
|
||||
|
||||
if await self.delete_alias(ctx, alias_name):
|
||||
await ctx.send(_("Alias with the name `{}` was successfully"
|
||||
" deleted.").format(alias_name))
|
||||
await ctx.send(
|
||||
_("Alias with the name `{}` was successfully" " deleted.").format(alias_name)
|
||||
)
|
||||
else:
|
||||
await ctx.send(_("Alias with name `{}` was not found.").format(alias_name))
|
||||
|
||||
@@ -317,8 +357,9 @@ class Alias:
|
||||
return
|
||||
|
||||
if await self.delete_alias(ctx, alias_name, global_=True):
|
||||
await ctx.send(_("Alias with the name `{}` was successfully"
|
||||
" deleted.").format(alias_name))
|
||||
await ctx.send(
|
||||
_("Alias with the name `{}` was successfully" " deleted.").format(alias_name)
|
||||
)
|
||||
else:
|
||||
await ctx.send(_("Alias with name `{}` was not found.").format(alias_name))
|
||||
|
||||
@@ -328,7 +369,9 @@ class Alias:
|
||||
"""
|
||||
Lists the available aliases on this server.
|
||||
"""
|
||||
names = [_("Aliases:"), ] + sorted(["+ " + a.name for a in (await self.unloaded_aliases(ctx.guild))])
|
||||
names = [_("Aliases:")] + sorted(
|
||||
["+ " + a.name for a in (await self.unloaded_aliases(ctx.guild))]
|
||||
)
|
||||
if len(names) == 0:
|
||||
await ctx.send(_("There are no aliases on this server."))
|
||||
else:
|
||||
@@ -339,7 +382,9 @@ class Alias:
|
||||
"""
|
||||
Lists the available global aliases on this bot.
|
||||
"""
|
||||
names = [_("Aliases:"), ] + sorted(["+ " + a.name for a in await self.unloaded_global_aliases()])
|
||||
names = [_("Aliases:")] + sorted(
|
||||
["+ " + a.name for a in await self.unloaded_global_aliases()]
|
||||
)
|
||||
if len(names) == 0:
|
||||
await ctx.send(_("There are no aliases on this server."))
|
||||
else:
|
||||
|
||||
@@ -5,8 +5,10 @@ from redbot.core import commands
|
||||
|
||||
|
||||
class AliasEntry:
|
||||
def __init__(self, name: str, command: Tuple[str],
|
||||
creator: discord.Member, global_: bool=False):
|
||||
|
||||
def __init__(
|
||||
self, name: str, command: Tuple[str], creator: discord.Member, global_: bool = False
|
||||
):
|
||||
super().__init__()
|
||||
self.has_real_data = False
|
||||
self.name = name
|
||||
@@ -43,13 +45,12 @@ class AliasEntry:
|
||||
"creator": creator,
|
||||
"guild": guild,
|
||||
"global": self.global_,
|
||||
"uses": self.uses
|
||||
"uses": self.uses,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, data: dict, bot: commands.Bot=None):
|
||||
ret = cls(data["name"], data["command"],
|
||||
data["creator"], global_=data["global"])
|
||||
def from_json(cls, data: dict, bot: commands.Bot = None):
|
||||
ret = cls(data["name"], data["command"], data["creator"], global_=data["global"])
|
||||
|
||||
if bot:
|
||||
ret.has_real_data = True
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../alias.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../alias.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -9,9 +9,10 @@ from redbot.core.data_manager import cog_data_path
|
||||
import redbot.core
|
||||
|
||||
LAVALINK_DOWNLOAD_URL = (
|
||||
"https://github.com/Cog-Creators/Red-DiscordBot/"
|
||||
"releases/download/{}/Lavalink.jar"
|
||||
).format(redbot.core.__version__)
|
||||
"https://github.com/Cog-Creators/Red-DiscordBot/" "releases/download/{}/Lavalink.jar"
|
||||
).format(
|
||||
redbot.core.__version__
|
||||
)
|
||||
|
||||
LAVALINK_DOWNLOAD_DIR = cog_data_path(raw_name="Audio")
|
||||
LAVALINK_JAR_FILE = LAVALINK_DOWNLOAD_DIR / "Lavalink.jar"
|
||||
@@ -21,7 +22,7 @@ BUNDLED_APP_YML_FILE = Path(__file__).parent / "application.yml"
|
||||
|
||||
|
||||
async def download_lavalink(session):
|
||||
with LAVALINK_JAR_FILE.open(mode='wb') as f:
|
||||
with LAVALINK_JAR_FILE.open(mode="wb") as f:
|
||||
async with session.get(LAVALINK_DOWNLOAD_URL) as resp:
|
||||
while True:
|
||||
chunk = await resp.content.read(512)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../audio.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../audio.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -5,7 +5,7 @@ from subprocess import Popen, DEVNULL, PIPE
|
||||
import os
|
||||
import logging
|
||||
|
||||
log = logging.getLogger('red.audio.manager')
|
||||
log = logging.getLogger("red.audio.manager")
|
||||
|
||||
proc = None
|
||||
SHUTDOWN = asyncio.Event()
|
||||
@@ -13,7 +13,8 @@ SHUTDOWN = asyncio.Event()
|
||||
|
||||
def has_java_error(pid):
|
||||
from . import LAVALINK_DOWNLOAD_DIR
|
||||
poss_error_file = LAVALINK_DOWNLOAD_DIR / 'hs_err_pid{}.log'.format(pid)
|
||||
|
||||
poss_error_file = LAVALINK_DOWNLOAD_DIR / "hs_err_pid{}.log".format(pid)
|
||||
return poss_error_file.exists()
|
||||
|
||||
|
||||
@@ -29,14 +30,14 @@ async def monitor_lavalink_server(loop):
|
||||
log.info("Restarting Lavalink jar.")
|
||||
await start_lavalink_server(loop)
|
||||
else:
|
||||
log.error("Your Java is borked. Please find the hs_err_pid{}.log file"
|
||||
" in the Audio data folder and report this issue.".format(
|
||||
proc.pid
|
||||
))
|
||||
log.error(
|
||||
"Your Java is borked. Please find the hs_err_pid{}.log file"
|
||||
" in the Audio data folder and report this issue.".format(proc.pid)
|
||||
)
|
||||
|
||||
|
||||
async def has_java(loop):
|
||||
java_available = shutil.which('java') is not None
|
||||
java_available = shutil.which("java") is not None
|
||||
if not java_available:
|
||||
return False
|
||||
|
||||
@@ -48,20 +49,18 @@ async def get_java_version(loop):
|
||||
"""
|
||||
This assumes we've already checked that java exists.
|
||||
"""
|
||||
proc = Popen(
|
||||
shlex.split("java -version", posix=os.name == 'posix'),
|
||||
stdout=PIPE, stderr=PIPE
|
||||
)
|
||||
proc = Popen(shlex.split("java -version", posix=os.name == "posix"), stdout=PIPE, stderr=PIPE)
|
||||
_, err = proc.communicate()
|
||||
|
||||
version_info = str(err, encoding='utf-8')
|
||||
version_info = str(err, encoding="utf-8")
|
||||
|
||||
version_line = version_info.split('\n')[0]
|
||||
version_line = version_info.split("\n")[0]
|
||||
version_start = version_line.find('"')
|
||||
version_string = version_line[version_start + 1:-1]
|
||||
major, minor = version_string.split('.')[:2]
|
||||
major, minor = version_string.split(".")[:2]
|
||||
return int(major), int(minor)
|
||||
|
||||
|
||||
async def start_lavalink_server(loop):
|
||||
java_available, java_version = await has_java(loop)
|
||||
if not java_available:
|
||||
@@ -72,13 +71,15 @@ async def start_lavalink_server(loop):
|
||||
extra_flags = "-Dsun.zip.disableMemoryMapping=true"
|
||||
|
||||
from . import LAVALINK_DOWNLOAD_DIR, LAVALINK_JAR_FILE
|
||||
|
||||
start_cmd = "java {} -jar {}".format(extra_flags, LAVALINK_JAR_FILE.resolve())
|
||||
|
||||
global proc
|
||||
proc = Popen(
|
||||
shlex.split(start_cmd, posix=os.name == 'posix'),
|
||||
shlex.split(start_cmd, posix=os.name == "posix"),
|
||||
cwd=str(LAVALINK_DOWNLOAD_DIR),
|
||||
stdout=DEVNULL, stderr=DEVNULL
|
||||
stdout=DEVNULL,
|
||||
stderr=DEVNULL,
|
||||
)
|
||||
|
||||
log.info("Lavalink jar started. PID: {}".format(proc.pid))
|
||||
|
||||
@@ -6,7 +6,7 @@ from redbot.core.i18n import Translator, cog_i18n
|
||||
|
||||
from redbot.core.bot import Red # Only used for type hints
|
||||
|
||||
_ = Translator('Bank', __file__)
|
||||
_ = Translator("Bank", __file__)
|
||||
|
||||
|
||||
def check_global_setting_guildowner():
|
||||
@@ -14,6 +14,7 @@ def check_global_setting_guildowner():
|
||||
Command decorator. If the bank is not global, it checks if the author is
|
||||
either the guildowner or has the administrator permission.
|
||||
"""
|
||||
|
||||
async def pred(ctx: commands.Context):
|
||||
author = ctx.author
|
||||
if await ctx.bot.is_owner(author):
|
||||
@@ -32,6 +33,7 @@ def check_global_setting_admin():
|
||||
Command decorator. If the bank is not global, it checks if the author is
|
||||
either a bot admin or has the manage_guild permission.
|
||||
"""
|
||||
|
||||
async def pred(ctx: commands.Context):
|
||||
author = ctx.author
|
||||
if await ctx.bot.is_owner(author):
|
||||
@@ -73,19 +75,23 @@ class Bank:
|
||||
currency_name = await bank._conf.guild(ctx.guild).currency()
|
||||
default_balance = await bank._conf.guild(ctx.guild).default_balance()
|
||||
|
||||
settings = (_(
|
||||
"Bank settings:\n\n"
|
||||
"Bank name: {}\n"
|
||||
"Currency: {}\n"
|
||||
"Default balance: {}"
|
||||
"").format(bank_name, currency_name, default_balance)
|
||||
settings = (
|
||||
_(
|
||||
"Bank settings:\n\n"
|
||||
"Bank name: {}\n"
|
||||
"Currency: {}\n"
|
||||
"Default balance: {}"
|
||||
""
|
||||
).format(
|
||||
bank_name, currency_name, default_balance
|
||||
)
|
||||
)
|
||||
await ctx.send(box(settings))
|
||||
await ctx.send_help()
|
||||
|
||||
@bankset.command(name="toggleglobal")
|
||||
@checks.is_owner()
|
||||
async def bankset_toggleglobal(self, ctx: commands.Context, confirm: bool=False):
|
||||
async def bankset_toggleglobal(self, ctx: commands.Context, confirm: bool = False):
|
||||
"""Toggles whether the bank is global or not
|
||||
If the bank is global, it will become per-server
|
||||
If the bank is per-server, it will become global"""
|
||||
@@ -94,8 +100,10 @@ class Bank:
|
||||
word = _("per-server") if cur_setting else _("global")
|
||||
if confirm is False:
|
||||
await ctx.send(
|
||||
_("This will toggle the bank to be {}, deleting all accounts "
|
||||
"in the process! If you're sure, type `{}`").format(
|
||||
_(
|
||||
"This will toggle the bank to be {}, deleting all accounts "
|
||||
"in the process! If you're sure, type `{}`"
|
||||
).format(
|
||||
word, "{}bankset toggleglobal yes".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
class BankError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BankNotGlobal(BankError):
|
||||
pass
|
||||
|
||||
@@ -34,4 +35,4 @@ class NegativeValue(BankError):
|
||||
|
||||
|
||||
class SameSenderAndReceiver(BankError):
|
||||
pass
|
||||
pass
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../bank.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../bank.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -27,13 +27,16 @@ class Cleanup:
|
||||
|
||||
Tries its best to cleanup after itself if the response is positive.
|
||||
"""
|
||||
|
||||
def author_check(message):
|
||||
return message.author == ctx.author
|
||||
|
||||
prompt = await ctx.send(_('Are you sure you want to delete {} messages? (y/n)').format(number))
|
||||
response = await ctx.bot.wait_for('message', check=author_check)
|
||||
prompt = await ctx.send(
|
||||
_("Are you sure you want to delete {} messages? (y/n)").format(number)
|
||||
)
|
||||
response = await ctx.bot.wait_for("message", check=author_check)
|
||||
|
||||
if response.content.lower().startswith('y'):
|
||||
if response.content.lower().startswith("y"):
|
||||
await prompt.delete()
|
||||
try:
|
||||
await response.delete()
|
||||
@@ -41,14 +44,19 @@ class Cleanup:
|
||||
pass
|
||||
return True
|
||||
else:
|
||||
await ctx.send(_('Cancelled.'))
|
||||
await ctx.send(_("Cancelled."))
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
async def get_messages_for_deletion(
|
||||
ctx: commands.Context, channel: discord.TextChannel, number,
|
||||
check=lambda x: True, limit=100, before=None, after=None,
|
||||
delete_pinned=False
|
||||
ctx: commands.Context,
|
||||
channel: discord.TextChannel,
|
||||
number,
|
||||
check=lambda x: True,
|
||||
limit=100,
|
||||
before=None,
|
||||
after=None,
|
||||
delete_pinned=False,
|
||||
) -> list:
|
||||
"""
|
||||
Gets a list of messages meeting the requirements to be deleted.
|
||||
@@ -65,9 +73,7 @@ class Cleanup:
|
||||
|
||||
while not too_old and len(to_delete) - 1 < number:
|
||||
message = None
|
||||
async for message in channel.history(limit=limit,
|
||||
before=before,
|
||||
after=after):
|
||||
async for message in channel.history(limit=limit, before=before, after=after):
|
||||
if (
|
||||
(not number or len(to_delete) - 1 < number)
|
||||
and check(message)
|
||||
@@ -96,7 +102,9 @@ class Cleanup:
|
||||
@cleanup.command()
|
||||
@commands.guild_only()
|
||||
@commands.bot_has_permissions(manage_messages=True)
|
||||
async def text(self, ctx: commands.Context, text: str, number: int, delete_pinned: bool=False):
|
||||
async def text(
|
||||
self, ctx: commands.Context, text: str, number: int, delete_pinned: bool = False
|
||||
):
|
||||
"""Deletes last X messages matching the specified text.
|
||||
|
||||
Example:
|
||||
@@ -122,12 +130,18 @@ class Cleanup:
|
||||
return False
|
||||
|
||||
to_delete = await self.get_messages_for_deletion(
|
||||
ctx, channel, number, check=check, limit=1000, before=ctx.message,
|
||||
delete_pinned=delete_pinned)
|
||||
ctx,
|
||||
channel,
|
||||
number,
|
||||
check=check,
|
||||
limit=1000,
|
||||
before=ctx.message,
|
||||
delete_pinned=delete_pinned,
|
||||
)
|
||||
|
||||
reason = "{}({}) deleted {} messages "\
|
||||
" containing '{}' in channel {}.".format(author.name,
|
||||
author.id, len(to_delete), text, channel.id)
|
||||
reason = "{}({}) deleted {} messages " " containing '{}' in channel {}.".format(
|
||||
author.name, author.id, len(to_delete), text, channel.id
|
||||
)
|
||||
log.info(reason)
|
||||
|
||||
if is_bot:
|
||||
@@ -138,7 +152,9 @@ class Cleanup:
|
||||
@cleanup.command()
|
||||
@commands.guild_only()
|
||||
@commands.bot_has_permissions(manage_messages=True)
|
||||
async def user(self, ctx: commands.Context, user: str, number: int, delete_pinned: bool=False):
|
||||
async def user(
|
||||
self, ctx: commands.Context, user: str, number: int, delete_pinned: bool = False
|
||||
):
|
||||
"""Deletes last X messages from specified user.
|
||||
|
||||
Examples:
|
||||
@@ -174,13 +190,17 @@ class Cleanup:
|
||||
return False
|
||||
|
||||
to_delete = await self.get_messages_for_deletion(
|
||||
ctx, channel, number, check=check, limit=1000, before=ctx.message,
|
||||
delete_pinned=delete_pinned
|
||||
ctx,
|
||||
channel,
|
||||
number,
|
||||
check=check,
|
||||
limit=1000,
|
||||
before=ctx.message,
|
||||
delete_pinned=delete_pinned,
|
||||
)
|
||||
reason = "{}({}) deleted {} messages " " made by {}({}) in channel {}." "".format(
|
||||
author.name, author.id, len(to_delete), member or "???", _id, channel.name
|
||||
)
|
||||
reason = "{}({}) deleted {} messages "\
|
||||
" made by {}({}) in channel {}."\
|
||||
"".format(author.name, author.id, len(to_delete),
|
||||
member or '???', _id, channel.name)
|
||||
log.info(reason)
|
||||
|
||||
if is_bot:
|
||||
@@ -192,7 +212,7 @@ class Cleanup:
|
||||
@cleanup.command()
|
||||
@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):
|
||||
"""Deletes all messages after specified message.
|
||||
|
||||
To get a message id, enable developer mode in Discord's
|
||||
@@ -207,8 +227,7 @@ class Cleanup:
|
||||
is_bot = self.bot.user.bot
|
||||
|
||||
if not is_bot:
|
||||
await ctx.send(_("This command can only be used on bots with "
|
||||
"bot accounts."))
|
||||
await ctx.send(_("This command can only be used on bots with " "bot accounts."))
|
||||
return
|
||||
|
||||
after = await channel.get_message(message_id)
|
||||
@@ -221,9 +240,9 @@ class Cleanup:
|
||||
ctx, channel, 0, limit=None, after=after, delete_pinned=delete_pinned
|
||||
)
|
||||
|
||||
reason = "{}({}) deleted {} messages in channel {}."\
|
||||
"".format(author.name, author.id,
|
||||
len(to_delete), channel.name)
|
||||
reason = "{}({}) deleted {} messages in channel {}." "".format(
|
||||
author.name, author.id, len(to_delete), channel.name
|
||||
)
|
||||
log.info(reason)
|
||||
|
||||
await mass_purge(to_delete, channel)
|
||||
@@ -231,7 +250,7 @@ class Cleanup:
|
||||
@cleanup.command()
|
||||
@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):
|
||||
"""Deletes last X messages.
|
||||
|
||||
Example:
|
||||
@@ -248,14 +267,13 @@ class Cleanup:
|
||||
return
|
||||
|
||||
to_delete = await self.get_messages_for_deletion(
|
||||
ctx, channel, number, limit=1000, before=ctx.message,
|
||||
delete_pinned=delete_pinned
|
||||
ctx, channel, number, limit=1000, before=ctx.message, delete_pinned=delete_pinned
|
||||
)
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
reason = "{}({}) deleted {} messages in channel {}."\
|
||||
"".format(author.name, author.id,
|
||||
number, channel.name)
|
||||
reason = "{}({}) deleted {} messages in channel {}." "".format(
|
||||
author.name, author.id, number, channel.name
|
||||
)
|
||||
log.info(reason)
|
||||
|
||||
if is_bot:
|
||||
@@ -263,10 +281,10 @@ class Cleanup:
|
||||
else:
|
||||
await slow_deletion(to_delete)
|
||||
|
||||
@cleanup.command(name='bot')
|
||||
@cleanup.command(name="bot")
|
||||
@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):
|
||||
"""Cleans up command messages and messages from the bot."""
|
||||
|
||||
channel = ctx.message.channel
|
||||
@@ -278,13 +296,13 @@ class Cleanup:
|
||||
if not cont:
|
||||
return
|
||||
|
||||
prefixes = await self.bot.get_prefix(ctx.message) # This returns all server prefixes
|
||||
prefixes = await self.bot.get_prefix(ctx.message) # This returns all server prefixes
|
||||
if isinstance(prefixes, str):
|
||||
prefixes = [prefixes]
|
||||
|
||||
# In case some idiot sets a null prefix
|
||||
if '' in prefixes:
|
||||
prefixes.remove('')
|
||||
if "" in prefixes:
|
||||
prefixes.remove("")
|
||||
|
||||
def check(m):
|
||||
if m.author.id == self.bot.user.id:
|
||||
@@ -293,20 +311,24 @@ class Cleanup:
|
||||
return True
|
||||
p = discord.utils.find(m.content.startswith, prefixes)
|
||||
if p and len(p) > 0:
|
||||
cmd_name = m.content[len(p):].split(' ')[0]
|
||||
cmd_name = m.content[len(p):].split(" ")[0]
|
||||
return bool(self.bot.get_command(cmd_name))
|
||||
return False
|
||||
|
||||
to_delete = await self.get_messages_for_deletion(
|
||||
ctx, channel, number, check=check, limit=1000, before=ctx.message,
|
||||
delete_pinned=delete_pinned
|
||||
ctx,
|
||||
channel,
|
||||
number,
|
||||
check=check,
|
||||
limit=1000,
|
||||
before=ctx.message,
|
||||
delete_pinned=delete_pinned,
|
||||
)
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
reason = "{}({}) deleted {} "\
|
||||
" command messages in channel {}."\
|
||||
"".format(author.name, author.id, len(to_delete),
|
||||
channel.name)
|
||||
reason = "{}({}) deleted {} " " command messages in channel {}." "".format(
|
||||
author.name, author.id, len(to_delete), channel.name
|
||||
)
|
||||
log.info(reason)
|
||||
|
||||
if is_bot:
|
||||
@@ -314,10 +336,14 @@ class Cleanup:
|
||||
else:
|
||||
await slow_deletion(to_delete)
|
||||
|
||||
@cleanup.command(name='self')
|
||||
@cleanup.command(name="self")
|
||||
async def cleanup_self(
|
||||
self, ctx: commands.Context, number: int,
|
||||
match_pattern: str = None, delete_pinned: bool=False):
|
||||
self,
|
||||
ctx: commands.Context,
|
||||
number: int,
|
||||
match_pattern: str = None,
|
||||
delete_pinned: bool = False,
|
||||
):
|
||||
"""Cleans up messages owned by the bot.
|
||||
|
||||
By default, all messages are cleaned. If a third argument is specified,
|
||||
@@ -343,8 +369,7 @@ class Cleanup:
|
||||
me = ctx.guild.me
|
||||
can_mass_purge = channel.permissions_for(me).manage_messages
|
||||
|
||||
use_re = (match_pattern and match_pattern.startswith('r(') and
|
||||
match_pattern.endswith(')'))
|
||||
use_re = (match_pattern and match_pattern.startswith("r(") and match_pattern.endswith(")"))
|
||||
|
||||
if use_re:
|
||||
match_pattern = match_pattern[1:] # strip 'r'
|
||||
@@ -352,10 +377,14 @@ class Cleanup:
|
||||
|
||||
def content_match(c):
|
||||
return bool(match_re.match(c))
|
||||
|
||||
elif match_pattern:
|
||||
|
||||
def content_match(c):
|
||||
return match_pattern in c
|
||||
|
||||
else:
|
||||
|
||||
def content_match(_):
|
||||
return True
|
||||
|
||||
@@ -367,8 +396,13 @@ class Cleanup:
|
||||
return False
|
||||
|
||||
to_delete = await self.get_messages_for_deletion(
|
||||
ctx, channel, number, check=check, limit=1000, before=ctx.message,
|
||||
delete_pinned=delete_pinned
|
||||
ctx,
|
||||
channel,
|
||||
number,
|
||||
check=check,
|
||||
limit=1000,
|
||||
before=ctx.message,
|
||||
delete_pinned=delete_pinned,
|
||||
)
|
||||
|
||||
# Selfbot convenience, delete trigger message
|
||||
@@ -376,14 +410,13 @@ class Cleanup:
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
if channel.name:
|
||||
channel_name = 'channel ' + channel.name
|
||||
channel_name = "channel " + channel.name
|
||||
else:
|
||||
channel_name = str(channel)
|
||||
|
||||
reason = "{}({}) deleted {} messages "\
|
||||
"sent by the bot in {}."\
|
||||
"".format(author.name, author.id, len(to_delete),
|
||||
channel_name)
|
||||
reason = "{}({}) deleted {} messages " "sent by the bot in {}." "".format(
|
||||
author.name, author.id, len(to_delete), channel_name
|
||||
)
|
||||
log.info(reason)
|
||||
|
||||
if is_bot and can_mass_purge:
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../cleanup.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../cleanup.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -27,8 +27,8 @@ class AlreadyExists(CCError):
|
||||
class CommandObj:
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
config = kwargs.get('config')
|
||||
self.bot = kwargs.get('bot')
|
||||
config = kwargs.get("config")
|
||||
self.bot = kwargs.get("bot")
|
||||
self.db = config.guild
|
||||
|
||||
@staticmethod
|
||||
@@ -40,22 +40,27 @@ class CommandObj:
|
||||
return customcommands
|
||||
|
||||
async def get_responses(self, ctx):
|
||||
intro = (_("Welcome to the interactive random {} maker!\n"
|
||||
"Every message you send will be added as one of the random "
|
||||
"response to choose from once this {} is "
|
||||
"triggered. To exit this interactive menu, type `{}`").format(
|
||||
"customcommand", "customcommand", "exit()"
|
||||
))
|
||||
intro = (
|
||||
_(
|
||||
"Welcome to the interactive random {} maker!\n"
|
||||
"Every message you send will be added as one of the random "
|
||||
"response to choose from once this {} is "
|
||||
"triggered. To exit this interactive menu, type `{}`"
|
||||
).format(
|
||||
"customcommand", "customcommand", "exit()"
|
||||
)
|
||||
)
|
||||
await ctx.send(intro)
|
||||
|
||||
def check(m):
|
||||
return m.channel == ctx.channel and m.author == ctx.message.author
|
||||
|
||||
responses = []
|
||||
while True:
|
||||
await ctx.send(_("Add a random response:"))
|
||||
msg = await self.bot.wait_for('message', check=check)
|
||||
msg = await self.bot.wait_for("message", check=check)
|
||||
|
||||
if msg.content.lower() == 'exit()':
|
||||
if msg.content.lower() == "exit()":
|
||||
break
|
||||
else:
|
||||
responses.append(msg.content)
|
||||
@@ -64,44 +69,31 @@ class CommandObj:
|
||||
def get_now(self) -> str:
|
||||
# Get current time as a string, for 'created_at' and 'edited_at' fields
|
||||
# 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) -> str:
|
||||
ccinfo = await self.db(message.guild).commands.get_raw(command, default=None)
|
||||
if not ccinfo:
|
||||
raise NotFound
|
||||
else:
|
||||
return ccinfo['response']
|
||||
return ccinfo["response"]
|
||||
|
||||
async def create(self,
|
||||
ctx: commands.Context,
|
||||
command: str,
|
||||
response):
|
||||
async def create(self, ctx: commands.Context, command: str, response):
|
||||
"""Create a customcommand"""
|
||||
# Check if this command is already registered as a customcommand
|
||||
if await self.db(ctx.guild).commands.get_raw(command, default=None):
|
||||
raise AlreadyExists()
|
||||
author = ctx.message.author
|
||||
ccinfo = {
|
||||
'author': {
|
||||
'id': author.id,
|
||||
'name': author.name
|
||||
},
|
||||
'command': command,
|
||||
'created_at': self.get_now(),
|
||||
'editors': [],
|
||||
'response': response
|
||||
|
||||
"author": {"id": author.id, "name": author.name},
|
||||
"command": command,
|
||||
"created_at": self.get_now(),
|
||||
"editors": [],
|
||||
"response": response,
|
||||
}
|
||||
await self.db(ctx.guild).commands.set_raw(
|
||||
command, value=ccinfo)
|
||||
await self.db(ctx.guild).commands.set_raw(command, value=ccinfo)
|
||||
|
||||
async def edit(self,
|
||||
ctx: commands.Context,
|
||||
command: str,
|
||||
response: None):
|
||||
async def edit(self, ctx: commands.Context, command: str, response: None):
|
||||
"""Edit an already existing custom command"""
|
||||
# Check if this command is registered
|
||||
if not await self.db(ctx.guild).commands.get_raw(command, default=None):
|
||||
@@ -114,41 +106,31 @@ class CommandObj:
|
||||
return m.channel == ctx.channel and m.author == ctx.message.author
|
||||
|
||||
if not response:
|
||||
await ctx.send(
|
||||
_("Do you want to create a 'randomized' cc? {}").format("y/n")
|
||||
)
|
||||
await ctx.send(_("Do you want to create a 'randomized' cc? {}").format("y/n"))
|
||||
|
||||
msg = await self.bot.wait_for('message', check=check)
|
||||
if msg.content.lower() == 'y':
|
||||
msg = await self.bot.wait_for("message", check=check)
|
||||
if msg.content.lower() == "y":
|
||||
response = await self.get_responses(ctx=ctx)
|
||||
else:
|
||||
await ctx.send(_("What response do you want?"))
|
||||
response = (await self.bot.wait_for(
|
||||
'message', check=check)
|
||||
).content
|
||||
response = (await self.bot.wait_for("message", check=check)).content
|
||||
|
||||
ccinfo['response'] = response
|
||||
ccinfo['edited_at'] = self.get_now()
|
||||
ccinfo["response"] = response
|
||||
ccinfo["edited_at"] = self.get_now()
|
||||
|
||||
if author.id not in ccinfo['editors']:
|
||||
if author.id not in ccinfo["editors"]:
|
||||
# Add the person who invoked the `edit` coroutine to the list of
|
||||
# editors, if the person is not yet in there
|
||||
ccinfo['editors'].append(
|
||||
author.id
|
||||
)
|
||||
ccinfo["editors"].append(author.id)
|
||||
|
||||
await self.db(ctx.guild).commands.set_raw(
|
||||
command, value=ccinfo)
|
||||
await self.db(ctx.guild).commands.set_raw(command, value=ccinfo)
|
||||
|
||||
async def delete(self,
|
||||
ctx: commands.Context,
|
||||
command: str):
|
||||
async def delete(self, ctx: commands.Context, command: str):
|
||||
"""Delete an already exisiting custom command"""
|
||||
# Check if this command is registered
|
||||
if not await self.db(ctx.guild).commands.get_raw(command, default=None):
|
||||
raise NotFound()
|
||||
await self.db(ctx.guild).commands.set_raw(
|
||||
command, value=None)
|
||||
await self.db(ctx.guild).commands.set_raw(command, value=None)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
@@ -159,24 +141,20 @@ class CustomCommands:
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.key = 414589031223512
|
||||
self.config = Config.get_conf(self,
|
||||
self.key)
|
||||
self.config = Config.get_conf(self, self.key)
|
||||
self.config.register_guild(commands={})
|
||||
self.commandobj = CommandObj(config=self.config,
|
||||
bot=self.bot)
|
||||
self.commandobj = CommandObj(config=self.config, bot=self.bot)
|
||||
|
||||
@commands.group(aliases=["cc"], no_pm=True)
|
||||
@commands.guild_only()
|
||||
async def customcom(self,
|
||||
ctx: commands.Context):
|
||||
async def customcom(self, ctx: commands.Context):
|
||||
"""Custom commands management"""
|
||||
if not ctx.invoked_subcommand:
|
||||
await ctx.send_help()
|
||||
|
||||
@customcom.group(name="add")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def cc_add(self,
|
||||
ctx: commands.Context):
|
||||
async def cc_add(self, ctx: commands.Context):
|
||||
"""
|
||||
CCs can be enhanced with arguments:
|
||||
|
||||
@@ -192,15 +170,12 @@ class CustomCommands:
|
||||
|
||||
{server} message.guild
|
||||
"""
|
||||
if not ctx.invoked_subcommand or isinstance(ctx.invoked_subcommand,
|
||||
commands.Group):
|
||||
if not ctx.invoked_subcommand or isinstance(ctx.invoked_subcommand, commands.Group):
|
||||
await ctx.send_help()
|
||||
|
||||
@cc_add.command(name='random')
|
||||
@cc_add.command(name="random")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def cc_add_random(self,
|
||||
ctx: commands.Context,
|
||||
command: str):
|
||||
async def cc_add_random(self, ctx: commands.Context, command: str):
|
||||
"""
|
||||
Create a CC where it will randomly choose a response!
|
||||
Note: This is interactive
|
||||
@@ -210,26 +185,20 @@ class CustomCommands:
|
||||
|
||||
responses = await self.commandobj.get_responses(ctx=ctx)
|
||||
try:
|
||||
await self.commandobj.create(ctx=ctx,
|
||||
command=command,
|
||||
response=responses)
|
||||
await self.commandobj.create(ctx=ctx, command=command, response=responses)
|
||||
await ctx.send(_("Custom command successfully added."))
|
||||
except AlreadyExists:
|
||||
await ctx.send(_(
|
||||
"This command already exists. Use "
|
||||
"`{}` to edit it.").format(
|
||||
await ctx.send(
|
||||
_("This command already exists. Use " "`{}` to edit it.").format(
|
||||
"{}customcom edit".format(ctx.prefix)
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
# await ctx.send(str(responses))
|
||||
|
||||
@cc_add.command(name="simple")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def cc_add_simple(self,
|
||||
ctx,
|
||||
command: str,
|
||||
*,
|
||||
text):
|
||||
async def cc_add_simple(self, ctx, command: str, *, text):
|
||||
"""Adds a simple custom command
|
||||
Example:
|
||||
[p]customcom add simple yourcommand Text you want
|
||||
@@ -240,24 +209,18 @@ class CustomCommands:
|
||||
await ctx.send(_("That command is already a standard command."))
|
||||
return
|
||||
try:
|
||||
await self.commandobj.create(ctx=ctx,
|
||||
command=command,
|
||||
response=text)
|
||||
await self.commandobj.create(ctx=ctx, command=command, response=text)
|
||||
await ctx.send(_("Custom command successfully added."))
|
||||
except AlreadyExists:
|
||||
await ctx.send(_(
|
||||
"This command already exists. Use "
|
||||
"`{}` to edit it.").format(
|
||||
await ctx.send(
|
||||
_("This command already exists. Use " "`{}` to edit it.").format(
|
||||
"{}customcom edit".format(ctx.prefix)
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
@customcom.command(name="edit")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def cc_edit(self,
|
||||
ctx,
|
||||
command: str,
|
||||
*,
|
||||
text=None):
|
||||
async def cc_edit(self, ctx, command: str, *, text=None):
|
||||
"""Edits a custom command
|
||||
Example:
|
||||
[p]customcom edit yourcommand Text you want
|
||||
@@ -266,61 +229,57 @@ class CustomCommands:
|
||||
command = command.lower()
|
||||
|
||||
try:
|
||||
await self.commandobj.edit(ctx=ctx,
|
||||
command=command,
|
||||
response=text)
|
||||
await self.commandobj.edit(ctx=ctx, command=command, response=text)
|
||||
await ctx.send(_("Custom command successfully edited."))
|
||||
except NotFound:
|
||||
await ctx.send(_(
|
||||
"That command doesn't exist. Use "
|
||||
"`{}` to add it.").format(
|
||||
await ctx.send(
|
||||
_("That command doesn't exist. Use " "`{}` to add it.").format(
|
||||
"{}customcom add".format(ctx.prefix)
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
@customcom.command(name="delete")
|
||||
@checks.mod_or_permissions(administrator=True)
|
||||
async def cc_delete(self,
|
||||
ctx,
|
||||
command: str):
|
||||
async def cc_delete(self, ctx, command: str):
|
||||
"""Deletes a custom command
|
||||
Example:
|
||||
[p]customcom delete yourcommand"""
|
||||
guild = ctx.message.guild
|
||||
command = command.lower()
|
||||
try:
|
||||
await self.commandobj.delete(ctx=ctx,
|
||||
command=command)
|
||||
await self.commandobj.delete(ctx=ctx, command=command)
|
||||
await ctx.send(_("Custom command successfully deleted."))
|
||||
except NotFound:
|
||||
await ctx.send(_("That command doesn't exist."))
|
||||
|
||||
@customcom.command(name="list")
|
||||
async def cc_list(self,
|
||||
ctx):
|
||||
async def cc_list(self, ctx):
|
||||
"""Shows custom commands list"""
|
||||
|
||||
response = await CommandObj.get_commands(self.config.guild(ctx.guild))
|
||||
|
||||
if not response:
|
||||
await ctx.send(_(
|
||||
"There are no custom commands in this server."
|
||||
" Use `{}` to start adding some.").format(
|
||||
await ctx.send(
|
||||
_(
|
||||
"There are no custom commands in this server."
|
||||
" Use `{}` to start adding some."
|
||||
).format(
|
||||
"{}customcom add".format(ctx.prefix)
|
||||
))
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
results = []
|
||||
|
||||
for command, body in response.items():
|
||||
responses = body['response']
|
||||
responses = body["response"]
|
||||
if isinstance(responses, list):
|
||||
result = ", ".join(responses)
|
||||
elif isinstance(responses, str):
|
||||
result = responses
|
||||
else:
|
||||
continue
|
||||
results.append("{command:<15} : {result}".format(command=command,
|
||||
result=result))
|
||||
results.append("{command:<15} : {result}".format(command=command, result=result))
|
||||
|
||||
commands = "\n".join(results)
|
||||
|
||||
@@ -330,14 +289,13 @@ class CustomCommands:
|
||||
for page in pagify(commands, delims=[" ", "\n"]):
|
||||
await ctx.author.send(box(page))
|
||||
|
||||
async def on_message(self,
|
||||
message):
|
||||
async def on_message(self, message):
|
||||
is_private = isinstance(message.channel, discord.abc.PrivateChannel)
|
||||
if len(message.content) < 2 or is_private:
|
||||
return
|
||||
|
||||
guild = message.guild
|
||||
prefixes = await self.bot.db.guild(guild).get_raw('prefix', default=[])
|
||||
prefixes = await self.bot.db.guild(guild).get_raw("prefix", default=[])
|
||||
|
||||
if len(prefixes) < 1:
|
||||
def_prefixes = await self.bot.get_prefix(message)
|
||||
@@ -358,8 +316,7 @@ class CustomCommands:
|
||||
if user_allowed:
|
||||
cmd = message.content[len(prefix):]
|
||||
try:
|
||||
c = await self.commandobj.get(message=message,
|
||||
command=cmd)
|
||||
c = await self.commandobj.get(message=message, command=cmd)
|
||||
if isinstance(c, list):
|
||||
command = random.choice(c)
|
||||
elif isinstance(c, str):
|
||||
@@ -371,18 +328,14 @@ class CustomCommands:
|
||||
response = self.format_cc(command, message)
|
||||
await message.channel.send(response)
|
||||
|
||||
def format_cc(self,
|
||||
command,
|
||||
message) -> str:
|
||||
def format_cc(self, command, message) -> str:
|
||||
results = re.findall("\{([^}]+)\}", command)
|
||||
for result in results:
|
||||
param = self.transform_parameter(result, message)
|
||||
command = command.replace("{" + result + "}", param)
|
||||
return command
|
||||
|
||||
def transform_parameter(self,
|
||||
result,
|
||||
message) -> str:
|
||||
def transform_parameter(self, result, message) -> str:
|
||||
"""
|
||||
For security reasons only specific objects are allowed
|
||||
Internals are ignored
|
||||
@@ -393,7 +346,7 @@ class CustomCommands:
|
||||
"author": message.author,
|
||||
"channel": message.channel,
|
||||
"guild": message.guild,
|
||||
"server": message.guild
|
||||
"server": message.guild,
|
||||
}
|
||||
if result in objects:
|
||||
return str(objects[result])
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../customcom.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../customcom.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -16,49 +16,49 @@ class SpecResolver(object):
|
||||
self.v2path = path
|
||||
self.resolved = set()
|
||||
self.available_core_conversions = {
|
||||
'Bank Accounts': {
|
||||
'cfg': ('Bank', None, 384734293238749),
|
||||
'file': self.v2path / 'data' / 'economy' / 'bank.json',
|
||||
'converter': self.bank_accounts_conv_spec
|
||||
"Bank Accounts": {
|
||||
"cfg": ("Bank", None, 384734293238749),
|
||||
"file": self.v2path / "data" / "economy" / "bank.json",
|
||||
"converter": self.bank_accounts_conv_spec,
|
||||
},
|
||||
'Economy Settings': {
|
||||
'cfg': ('Economy', 'config', 1256844281),
|
||||
'file': self.v2path / 'data' / 'economy' / 'settings.json',
|
||||
'converter': self.economy_conv_spec
|
||||
"Economy Settings": {
|
||||
"cfg": ("Economy", "config", 1256844281),
|
||||
"file": self.v2path / "data" / "economy" / "settings.json",
|
||||
"converter": self.economy_conv_spec,
|
||||
},
|
||||
'Mod Log Cases': {
|
||||
'cfg': ('ModLog', None, 1354799444),
|
||||
'file': self.v2path / 'data' / 'mod' / 'modlog.json',
|
||||
'converter': None # prevents from showing as available
|
||||
"Mod Log Cases": {
|
||||
"cfg": ("ModLog", None, 1354799444),
|
||||
"file": self.v2path / "data" / "mod" / "modlog.json",
|
||||
"converter": None, # prevents from showing as available
|
||||
},
|
||||
'Filter': {
|
||||
'cfg': ('Filter', 'settings', 4766951341),
|
||||
'file': self.v2path / 'data' / 'mod' / 'filter.json',
|
||||
'converter': self.filter_conv_spec
|
||||
"Filter": {
|
||||
"cfg": ("Filter", "settings", 4766951341),
|
||||
"file": self.v2path / "data" / "mod" / "filter.json",
|
||||
"converter": self.filter_conv_spec,
|
||||
},
|
||||
'Past Names': {
|
||||
'cfg': ('Mod', 'settings', 4961522000),
|
||||
'file': self.v2path / 'data' / 'mod' / 'past_names.json',
|
||||
'converter': self.past_names_conv_spec
|
||||
"Past Names": {
|
||||
"cfg": ("Mod", "settings", 4961522000),
|
||||
"file": self.v2path / "data" / "mod" / "past_names.json",
|
||||
"converter": self.past_names_conv_spec,
|
||||
},
|
||||
'Past Nicknames': {
|
||||
'cfg': ('Mod', 'settings', 4961522000),
|
||||
'file': self.v2path / 'data' / 'mod' / 'past_nicknames.json',
|
||||
'converter': self.past_nicknames_conv_spec
|
||||
"Past Nicknames": {
|
||||
"cfg": ("Mod", "settings", 4961522000),
|
||||
"file": self.v2path / "data" / "mod" / "past_nicknames.json",
|
||||
"converter": self.past_nicknames_conv_spec,
|
||||
},
|
||||
"Custom Commands": {
|
||||
"cfg": ("CustomCommands", "config", 414589031223512),
|
||||
"file": self.v2path / "data" / "customcom" / "commands.json",
|
||||
"converter": self.customcom_conv_spec,
|
||||
},
|
||||
'Custom Commands': {
|
||||
'cfg': ('CustomCommands', 'config', 414589031223512),
|
||||
'file': self.v2path / 'data' / 'customcom' / 'commands.json',
|
||||
'converter': self.customcom_conv_spec
|
||||
}
|
||||
}
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
return sorted(
|
||||
k for k, v in self.available_core_conversions.items()
|
||||
if v['file'].is_file() and v['converter'] is not None
|
||||
and k not in self.resolved
|
||||
k
|
||||
for k, v in self.available_core_conversions.items()
|
||||
if v["file"].is_file() and v["converter"] is not None and k not in self.resolved
|
||||
)
|
||||
|
||||
def unpack(self, parent_key, parent_value):
|
||||
@@ -75,15 +75,8 @@ class SpecResolver(object):
|
||||
"""Flatten a nested dictionary structure"""
|
||||
dictionary = {(key,): value for key, value in dictionary.items()}
|
||||
while True:
|
||||
dictionary = dict(
|
||||
chain.from_iterable(
|
||||
starmap(self.unpack, dictionary.items())
|
||||
)
|
||||
)
|
||||
if not any(
|
||||
isinstance(value, dict)
|
||||
for value in dictionary.values()
|
||||
):
|
||||
dictionary = dict(chain.from_iterable(starmap(self.unpack, dictionary.items())))
|
||||
if not any(isinstance(value, dict) for value in dictionary.values()):
|
||||
break
|
||||
return dictionary
|
||||
|
||||
@@ -97,11 +90,8 @@ class SpecResolver(object):
|
||||
outerkey, innerkey = tuple(k[:-1]), (k[-1],)
|
||||
if outerkey not in ret:
|
||||
ret[outerkey] = {}
|
||||
if innerkey[0] == 'created_at':
|
||||
x = int(
|
||||
datetime.strptime(
|
||||
v, "%Y-%m-%d %H:%M:%S").timestamp()
|
||||
)
|
||||
if innerkey[0] == "created_at":
|
||||
x = int(datetime.strptime(v, "%Y-%m-%d %H:%M:%S").timestamp())
|
||||
ret[outerkey].update({innerkey: x})
|
||||
else:
|
||||
ret[outerkey].update({innerkey: v})
|
||||
@@ -121,16 +111,10 @@ class SpecResolver(object):
|
||||
raise NotImplementedError("This one isn't ready yet")
|
||||
|
||||
def filter_conv_spec(self, data: dict):
|
||||
return {
|
||||
(Config.GUILD, k): {('filter',): v}
|
||||
for k, v in data.items()
|
||||
}
|
||||
return {(Config.GUILD, k): {("filter",): v} for k, v in data.items()}
|
||||
|
||||
def past_names_conv_spec(self, data: dict):
|
||||
return {
|
||||
(Config.USER, k): {('past_names',): v}
|
||||
for k, v in data.items()
|
||||
}
|
||||
return {(Config.USER, k): {("past_names",): v} for k, v in data.items()}
|
||||
|
||||
def past_nicknames_conv_spec(self, data: dict):
|
||||
flatscoped = self.apply_scope(Config.MEMBER, self.flatten_dict(data))
|
||||
@@ -146,19 +130,16 @@ class SpecResolver(object):
|
||||
flatscoped = self.apply_scope(Config.GUILD, self.flatten_dict(data))
|
||||
ret = {}
|
||||
for k, v in flatscoped.items():
|
||||
outerkey, innerkey = (*k[:-1],), ('commands', k[-1])
|
||||
outerkey, innerkey = (*k[:-1],), ("commands", k[-1])
|
||||
if outerkey not in ret:
|
||||
ret[outerkey] = {}
|
||||
|
||||
ccinfo = {
|
||||
'author': {
|
||||
'id': 42,
|
||||
'name': 'Converted from a v2 instance'
|
||||
},
|
||||
'command': k[-1],
|
||||
'created_at': '{:%d/%m/%Y %H:%M:%S}'.format(datetime.utcnow()),
|
||||
'editors': [],
|
||||
'response': v
|
||||
"author": {"id": 42, "name": "Converted from a v2 instance"},
|
||||
"command": k[-1],
|
||||
"created_at": "{:%d/%m/%Y %H:%M:%S}".format(datetime.utcnow()),
|
||||
"editors": [],
|
||||
"response": v,
|
||||
}
|
||||
ret[outerkey].update({innerkey: ccinfo})
|
||||
return ret
|
||||
@@ -168,8 +149,8 @@ class SpecResolver(object):
|
||||
raise NotImplementedError("No Conversion Specs for this")
|
||||
|
||||
info = self.available_core_conversions[prettyname]
|
||||
filepath, converter = info['file'], info['converter']
|
||||
(cogname, attr, _id) = info['cfg']
|
||||
filepath, converter = info["file"], info["converter"]
|
||||
(cogname, attr, _id) = info["cfg"]
|
||||
try:
|
||||
config = getattr(bot.get_cog(cogname), attr)
|
||||
except (TypeError, AttributeError):
|
||||
|
||||
@@ -7,7 +7,7 @@ from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.cogs.dataconverter.core_specs import SpecResolver
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
|
||||
_ = Translator('DataConverter', __file__)
|
||||
_ = Translator("DataConverter", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
@@ -34,13 +34,14 @@ class DataConverter:
|
||||
|
||||
if not resolver.available:
|
||||
return await ctx.send(
|
||||
_("There don't seem to be any data files I know how to "
|
||||
"handle here. Are you sure you gave me the base "
|
||||
"installation path?")
|
||||
_(
|
||||
"There don't seem to be any data files I know how to "
|
||||
"handle here. Are you sure you gave me the base "
|
||||
"installation path?"
|
||||
)
|
||||
)
|
||||
while resolver.available:
|
||||
menu = _("Please select a set of data to import by number"
|
||||
", or 'exit' to exit")
|
||||
menu = _("Please select a set of data to import by number" ", or 'exit' to exit")
|
||||
for index, entry in enumerate(resolver.available, 1):
|
||||
menu += "\n{}. {}".format(index, entry)
|
||||
|
||||
@@ -50,24 +51,17 @@ class DataConverter:
|
||||
return m.channel == ctx.channel and m.author == ctx.author
|
||||
|
||||
try:
|
||||
message = await self.bot.wait_for(
|
||||
'message', check=pred, timeout=60
|
||||
)
|
||||
message = await self.bot.wait_for("message", check=pred, timeout=60)
|
||||
except asyncio.TimeoutError:
|
||||
return await ctx.send(
|
||||
_('Try this again when you are more ready'))
|
||||
return await ctx.send(_("Try this again when you are more ready"))
|
||||
else:
|
||||
if message.content.strip().lower() in [
|
||||
'quit', 'exit', '-1', 'q', 'cancel'
|
||||
]:
|
||||
if message.content.strip().lower() in ["quit", "exit", "-1", "q", "cancel"]:
|
||||
return await ctx.tick()
|
||||
try:
|
||||
message = int(message.content.strip())
|
||||
to_conv = resolver.available[message - 1]
|
||||
except (ValueError, IndexError):
|
||||
await ctx.send(
|
||||
_("That wasn't a valid choice.")
|
||||
)
|
||||
await ctx.send(_("That wasn't a valid choice."))
|
||||
continue
|
||||
else:
|
||||
async with ctx.typing():
|
||||
@@ -76,6 +70,8 @@ class DataConverter:
|
||||
await menu_message.delete()
|
||||
else:
|
||||
return await ctx.send(
|
||||
_("There isn't anything else I know how to convert here."
|
||||
"\nThere might be more things I can convert in the future.")
|
||||
_(
|
||||
"There isn't anything else I know how to convert here."
|
||||
"\nThere might be more things I can convert in the future."
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../dataconverter.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../dataconverter.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -3,7 +3,7 @@ import asyncio
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
__all__ = ["install_agreement", ]
|
||||
__all__ = ["install_agreement"]
|
||||
|
||||
REPO_INSTALL_MSG = (
|
||||
"You're about to add a 3rd party repository. The creator of Red"
|
||||
@@ -17,29 +17,28 @@ REPO_INSTALL_MSG = (
|
||||
|
||||
|
||||
def install_agreement():
|
||||
|
||||
async def pred(ctx: commands.Context):
|
||||
downloader = ctx.command.instance
|
||||
if downloader is None:
|
||||
return True
|
||||
elif downloader.already_agreed:
|
||||
return True
|
||||
elif ctx.invoked_subcommand is None or \
|
||||
isinstance(ctx.invoked_subcommand, commands.Group):
|
||||
elif ctx.invoked_subcommand is None or isinstance(ctx.invoked_subcommand, commands.Group):
|
||||
return True
|
||||
|
||||
def does_agree(msg: discord.Message):
|
||||
return ctx.author == msg.author and \
|
||||
ctx.channel == msg.channel and \
|
||||
msg.content == "I agree"
|
||||
return ctx.author == msg.author and ctx.channel == msg.channel and msg.content == "I agree"
|
||||
|
||||
await ctx.send(REPO_INSTALL_MSG)
|
||||
|
||||
try:
|
||||
await ctx.bot.wait_for('message', check=does_agree, timeout=30)
|
||||
await ctx.bot.wait_for("message", check=does_agree, timeout=30)
|
||||
except asyncio.TimeoutError:
|
||||
await ctx.send("Your response has timed out, please try again.")
|
||||
return False
|
||||
|
||||
downloader.already_agreed = True
|
||||
return True
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
@@ -5,6 +5,7 @@ from .installable import Installable
|
||||
|
||||
|
||||
class InstalledCog(commands.Converter):
|
||||
|
||||
async def convert(self, ctx: commands.Context, arg: str) -> Installable:
|
||||
downloader = ctx.bot.get_cog("Downloader")
|
||||
if downloader is None:
|
||||
@@ -12,8 +13,6 @@ class InstalledCog(commands.Converter):
|
||||
|
||||
cog = discord.utils.get(await downloader.installed_cogs(), name=arg)
|
||||
if cog is None:
|
||||
raise commands.BadArgument(
|
||||
"That cog is not installed"
|
||||
)
|
||||
raise commands.BadArgument("That cog is not installed")
|
||||
|
||||
return cog
|
||||
|
||||
@@ -22,20 +22,18 @@ from .installable import Installable
|
||||
from .log import log
|
||||
from .repo_manager import RepoManager, Repo
|
||||
|
||||
_ = Translator('Downloader', __file__)
|
||||
_ = Translator("Downloader", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
class Downloader:
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
|
||||
self.conf = Config.get_conf(self, identifier=998240343,
|
||||
force_registration=True)
|
||||
self.conf = Config.get_conf(self, identifier=998240343, force_registration=True)
|
||||
|
||||
self.conf.register_global(
|
||||
installed=[]
|
||||
)
|
||||
self.conf.register_global(installed=[])
|
||||
|
||||
self.already_agreed = False
|
||||
|
||||
@@ -46,7 +44,7 @@ class Downloader:
|
||||
self.LIB_PATH.mkdir(parents=True, exist_ok=True)
|
||||
self.SHAREDLIB_PATH.mkdir(parents=True, exist_ok=True)
|
||||
if not self.SHAREDLIB_INIT.exists():
|
||||
with self.SHAREDLIB_INIT.open(mode='w', encoding='utf-8') as _:
|
||||
with self.SHAREDLIB_INIT.open(mode="w", encoding="utf-8") as _:
|
||||
pass
|
||||
|
||||
if str(self.LIB_PATH) not in syspath:
|
||||
@@ -170,7 +168,7 @@ class Downloader:
|
||||
for repo, reqs in has_reqs:
|
||||
for req in reqs:
|
||||
# noinspection PyTypeChecker
|
||||
ret = ret and await repo.install_raw_requirements([req, ], self.LIB_PATH)
|
||||
ret = ret and await repo.install_raw_requirements([req], self.LIB_PATH)
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
@@ -200,8 +198,12 @@ class Downloader:
|
||||
if success:
|
||||
await ctx.send(_("Libraries installed."))
|
||||
else:
|
||||
await ctx.send(_("Some libraries failed to install. Please check"
|
||||
" your logs for a complete list."))
|
||||
await ctx.send(
|
||||
_(
|
||||
"Some libraries failed to install. Please check"
|
||||
" your logs for a complete list."
|
||||
)
|
||||
)
|
||||
|
||||
@commands.group()
|
||||
@checks.is_owner()
|
||||
@@ -214,7 +216,7 @@ class Downloader:
|
||||
|
||||
@repo.command(name="add")
|
||||
@install_agreement()
|
||||
async def _repo_add(self, ctx, name: str, repo_url: str, branch: str=None):
|
||||
async def _repo_add(self, ctx, name: str, repo_url: str, branch: str = None):
|
||||
"""
|
||||
Add a new repo to Downloader.
|
||||
|
||||
@@ -223,11 +225,7 @@ class Downloader:
|
||||
"""
|
||||
try:
|
||||
# noinspection PyTypeChecker
|
||||
repo = await self._repo_manager.add_repo(
|
||||
name=name,
|
||||
url=repo_url,
|
||||
branch=branch
|
||||
)
|
||||
repo = await self._repo_manager.add_repo(name=name, url=repo_url, branch=branch)
|
||||
except ExistingGitRepo:
|
||||
await ctx.send(_("That git repo has already been added under another name."))
|
||||
except CloningError:
|
||||
@@ -275,20 +273,28 @@ class Downloader:
|
||||
"""
|
||||
cog = discord.utils.get(repo_name.available_cogs, name=cog_name) # type: Installable
|
||||
if cog is None:
|
||||
await ctx.send(_("Error, there is no cog by the name of"
|
||||
" `{}` in the `{}` repo.").format(cog_name, repo_name.name))
|
||||
await ctx.send(
|
||||
_("Error, there is no cog by the name of" " `{}` in the `{}` repo.").format(
|
||||
cog_name, repo_name.name
|
||||
)
|
||||
)
|
||||
return
|
||||
elif cog.min_python_version > sys.version_info:
|
||||
await ctx.send(_(
|
||||
"This cog requires at least python version {}, aborting install.".format(
|
||||
'.'.join([str(n) for n in cog.min_python_version])
|
||||
await ctx.send(
|
||||
_(
|
||||
"This cog requires at least python version {}, aborting install.".format(
|
||||
".".join([str(n) for n in cog.min_python_version])
|
||||
)
|
||||
)
|
||||
))
|
||||
)
|
||||
return
|
||||
|
||||
if not await repo_name.install_requirements(cog, self.LIB_PATH):
|
||||
await ctx.send(_("Failed to install the required libraries for"
|
||||
" `{}`: `{}`").format(cog.name, cog.requirements))
|
||||
await ctx.send(
|
||||
_("Failed to install the required libraries for" " `{}`: `{}`").format(
|
||||
cog.name, cog.requirements
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
await repo_name.install_cog(cog, await self.cog_install_path())
|
||||
@@ -317,12 +323,16 @@ class Downloader:
|
||||
await self._remove_from_installed(cog_name)
|
||||
await ctx.send(_("`{}` was successfully removed.").format(real_name))
|
||||
else:
|
||||
await ctx.send(_("That cog was installed but can no longer"
|
||||
" be located. You may need to remove it's"
|
||||
" files manually if it is still usable."))
|
||||
await ctx.send(
|
||||
_(
|
||||
"That cog was installed but can no longer"
|
||||
" be located. You may need to remove it's"
|
||||
" files manually if it is still usable."
|
||||
)
|
||||
)
|
||||
|
||||
@cog.command(name="update")
|
||||
async def _cog_update(self, ctx, cog_name: InstalledCog=None):
|
||||
async def _cog_update(self, ctx, cog_name: InstalledCog = None):
|
||||
"""
|
||||
Updates all cogs or one of your choosing.
|
||||
"""
|
||||
@@ -358,7 +368,8 @@ class Downloader:
|
||||
"""
|
||||
cogs = repo_name.available_cogs
|
||||
cogs = _("Available Cogs:\n") + "\n".join(
|
||||
["+ {}: {}".format(c.name, c.short or "") for c in cogs])
|
||||
["+ {}: {}".format(c.name, c.short or "") for c in cogs]
|
||||
)
|
||||
|
||||
await ctx.send(box(cogs, lang="diff"))
|
||||
|
||||
@@ -369,9 +380,9 @@ class Downloader:
|
||||
"""
|
||||
cog = discord.utils.get(repo_name.available_cogs, name=cog_name)
|
||||
if cog is None:
|
||||
await ctx.send(_("There is no cog `{}` in the repo `{}`").format(
|
||||
cog_name, repo_name.name
|
||||
))
|
||||
await ctx.send(
|
||||
_("There is no cog `{}` in the repo `{}`").format(cog_name, repo_name.name)
|
||||
)
|
||||
return
|
||||
|
||||
msg = _("Information on {}:\n{}").format(cog.name, cog.description or "")
|
||||
@@ -397,8 +408,9 @@ class Downloader:
|
||||
return True, installable
|
||||
return False, None
|
||||
|
||||
def format_findcog_info(self, command_name: str,
|
||||
cog_installable: Union[Installable, object]=None) -> str:
|
||||
def format_findcog_info(
|
||||
self, command_name: str, cog_installable: Union[Installable, object] = None
|
||||
) -> str:
|
||||
"""Format a cog's info for output to discord.
|
||||
|
||||
Parameters
|
||||
@@ -444,7 +456,7 @@ class Downloader:
|
||||
The name of the cog according to Downloader..
|
||||
|
||||
"""
|
||||
splitted = instance.__module__.split('.')
|
||||
splitted = instance.__module__.split(".")
|
||||
return splitted[-2]
|
||||
|
||||
@commands.command()
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
__all__ = ["DownloaderException", "GitException", "InvalidRepoName", "ExistingGitRepo",
|
||||
"MissingGitRepo", "CloningError", "CurrentHashError", "HardResetError",
|
||||
"UpdateError", "GitDiffError", "PipError"]
|
||||
__all__ = [
|
||||
"DownloaderException",
|
||||
"GitException",
|
||||
"InvalidRepoName",
|
||||
"ExistingGitRepo",
|
||||
"MissingGitRepo",
|
||||
"CloningError",
|
||||
"CurrentHashError",
|
||||
"HardResetError",
|
||||
"UpdateError",
|
||||
"GitDiffError",
|
||||
"PipError",
|
||||
]
|
||||
|
||||
|
||||
class DownloaderException(Exception):
|
||||
|
||||
@@ -56,6 +56,7 @@ class Installable(RepoJSONMixin):
|
||||
:class:`InstallationType`.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, location: Path):
|
||||
"""Base installable initializer.
|
||||
|
||||
@@ -114,13 +115,9 @@ class Installable(RepoJSONMixin):
|
||||
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
copy_func(
|
||||
src=str(self._location),
|
||||
dst=str(target_dir / self._location.stem)
|
||||
)
|
||||
copy_func(src=str(self._location), dst=str(target_dir / self._location.stem))
|
||||
except:
|
||||
log.exception("Error occurred when copying path:"
|
||||
" {}".format(self._location))
|
||||
log.exception("Error occurred when copying path:" " {}".format(self._location))
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -130,7 +127,7 @@ class Installable(RepoJSONMixin):
|
||||
if self._info_file.exists():
|
||||
self._process_info_file()
|
||||
|
||||
def _process_info_file(self, info_file_path: Path=None) -> MutableMapping[str, Any]:
|
||||
def _process_info_file(self, info_file_path: Path = None) -> MutableMapping[str, Any]:
|
||||
"""
|
||||
Processes an information file. Loads dependencies among other
|
||||
information into this object.
|
||||
@@ -144,13 +141,14 @@ class Installable(RepoJSONMixin):
|
||||
raise ValueError("No valid information file path was found.")
|
||||
|
||||
info = {}
|
||||
with info_file_path.open(encoding='utf-8') as f:
|
||||
with info_file_path.open(encoding="utf-8") as f:
|
||||
try:
|
||||
info = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
info = {}
|
||||
log.exception("Invalid JSON information file at path:"
|
||||
" {}".format(info_file_path))
|
||||
log.exception(
|
||||
"Invalid JSON information file at path:" " {}".format(info_file_path)
|
||||
)
|
||||
else:
|
||||
self._info = info
|
||||
|
||||
@@ -167,7 +165,7 @@ class Installable(RepoJSONMixin):
|
||||
self.bot_version = bot_version
|
||||
|
||||
try:
|
||||
min_python_version = tuple(info.get('min_python_version', [3, 5, 1]))
|
||||
min_python_version = tuple(info.get("min_python_version", [3, 5, 1]))
|
||||
except ValueError:
|
||||
min_python_version = self.min_python_version
|
||||
self.min_python_version = min_python_version
|
||||
@@ -200,15 +198,12 @@ class Installable(RepoJSONMixin):
|
||||
return info
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"repo_name": self.repo_name,
|
||||
"cog_name": self.name
|
||||
}
|
||||
return {"repo_name": self.repo_name, "cog_name": self.name}
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, data: dict, repo_mgr: "RepoManager"):
|
||||
repo_name = data['repo_name']
|
||||
cog_name = data['cog_name']
|
||||
repo_name = data["repo_name"]
|
||||
cog_name = data["cog_name"]
|
||||
|
||||
repo = repo_mgr.get_repo(repo_name)
|
||||
if repo is not None:
|
||||
|
||||
@@ -24,7 +24,7 @@ class RepoJSONMixin:
|
||||
return
|
||||
|
||||
try:
|
||||
with self._info_file.open(encoding='utf-8') as f:
|
||||
with self._info_file.open(encoding="utf-8") as f:
|
||||
info = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
return
|
||||
@@ -34,4 +34,4 @@ class RepoJSONMixin:
|
||||
self.author = info.get("author")
|
||||
self.install_msg = info.get("install_msg")
|
||||
self.short = info.get("short")
|
||||
self.description = info.get("description")
|
||||
self.description = info.get("description")
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../downloader.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../downloader.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("red.downloader")
|
||||
log = logging.getLogger("red.downloader")
|
||||
|
||||
@@ -27,16 +27,23 @@ class Repo(RepoJSONMixin):
|
||||
GIT_LATEST_COMMIT = "git -C {path} rev-parse {branch}"
|
||||
GIT_HARD_RESET = "git -C {path} reset --hard origin/{branch} -q"
|
||||
GIT_PULL = "git -C {path} pull -q --ff-only"
|
||||
GIT_DIFF_FILE_STATUS = ("git -C {path} diff --no-commit-id --name-status"
|
||||
" {old_hash} {new_hash}")
|
||||
GIT_LOG = ("git -C {path} log --relative-date --reverse {old_hash}.."
|
||||
" {relative_file_path}")
|
||||
GIT_DIFF_FILE_STATUS = (
|
||||
"git -C {path} diff --no-commit-id --name-status" " {old_hash} {new_hash}"
|
||||
)
|
||||
GIT_LOG = ("git -C {path} log --relative-date --reverse {old_hash}.." " {relative_file_path}")
|
||||
GIT_DISCOVER_REMOTE_URL = "git -C {path} config --get remote.origin.url"
|
||||
|
||||
PIP_INSTALL = "{python} -m pip install -U -t {target_dir} {reqs}"
|
||||
|
||||
def __init__(self, name: str, url: str, branch: str, folder_path: Path,
|
||||
available_modules: Tuple[Installable]=(), loop: asyncio.AbstractEventLoop=None):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
url: str,
|
||||
branch: str,
|
||||
folder_path: Path,
|
||||
available_modules: Tuple[Installable] = (),
|
||||
loop: asyncio.AbstractEventLoop = None,
|
||||
):
|
||||
self.url = url
|
||||
self.branch = branch
|
||||
|
||||
@@ -71,11 +78,12 @@ class Repo(RepoJSONMixin):
|
||||
return poss_repo
|
||||
|
||||
def _existing_git_repo(self) -> (bool, Path):
|
||||
git_path = self.folder_path / '.git'
|
||||
git_path = self.folder_path / ".git"
|
||||
return git_path.exists(), git_path
|
||||
|
||||
async def _get_file_update_statuses(
|
||||
self, old_hash: str, new_hash: str) -> MutableMapping[str, str]:
|
||||
self, old_hash: str, new_hash: str
|
||||
) -> MutableMapping[str, str]:
|
||||
"""
|
||||
Gets the file update status letters for each changed file between
|
||||
the two hashes.
|
||||
@@ -85,29 +93,25 @@ class Repo(RepoJSONMixin):
|
||||
"""
|
||||
p = await self._run(
|
||||
self.GIT_DIFF_FILE_STATUS.format(
|
||||
path=self.folder_path,
|
||||
old_hash=old_hash,
|
||||
new_hash=new_hash
|
||||
path=self.folder_path, old_hash=old_hash, new_hash=new_hash
|
||||
)
|
||||
)
|
||||
|
||||
if p.returncode != 0:
|
||||
raise GitDiffError("Git diff failed for repo at path:"
|
||||
" {}".format(self.folder_path))
|
||||
raise GitDiffError("Git diff failed for repo at path:" " {}".format(self.folder_path))
|
||||
|
||||
stdout = p.stdout.strip().decode().split('\n')
|
||||
stdout = p.stdout.strip().decode().split("\n")
|
||||
|
||||
ret = {}
|
||||
|
||||
for filename in stdout:
|
||||
# TODO: filter these filenames by ones in self.available_modules
|
||||
status, _, filepath = filename.partition('\t')
|
||||
status, _, filepath = filename.partition("\t")
|
||||
ret[filepath] = status
|
||||
|
||||
return ret
|
||||
|
||||
async def _get_commit_notes(self, old_commit_hash: str,
|
||||
relative_file_path: str) -> str:
|
||||
async def _get_commit_notes(self, old_commit_hash: str, relative_file_path: str) -> str:
|
||||
"""
|
||||
Gets the commit notes from git log.
|
||||
:param old_commit_hash: Point in time to start getting messages
|
||||
@@ -119,13 +123,15 @@ class Repo(RepoJSONMixin):
|
||||
self.GIT_LOG.format(
|
||||
path=self.folder_path,
|
||||
old_hash=old_commit_hash,
|
||||
relative_file_path=relative_file_path
|
||||
relative_file_path=relative_file_path,
|
||||
)
|
||||
)
|
||||
|
||||
if p.returncode != 0:
|
||||
raise GitException("An exception occurred while executing git log on"
|
||||
" this repo: {}".format(self.folder_path))
|
||||
raise GitException(
|
||||
"An exception occurred while executing git log on"
|
||||
" this repo: {}".format(self.folder_path)
|
||||
)
|
||||
|
||||
return p.stdout.decode().strip()
|
||||
|
||||
@@ -146,10 +152,8 @@ class Repo(RepoJSONMixin):
|
||||
Installable(location=name)
|
||||
)
|
||||
"""
|
||||
for file_finder, name, is_pkg in pkgutil.walk_packages(path=[str(self.folder_path), ]):
|
||||
curr_modules.append(
|
||||
Installable(location=self.folder_path / name)
|
||||
)
|
||||
for file_finder, name, is_pkg in pkgutil.walk_packages(path=[str(self.folder_path)]):
|
||||
curr_modules.append(Installable(location=self.folder_path / name))
|
||||
self.available_modules = curr_modules
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
@@ -157,12 +161,11 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
async def _run(self, *args, **kwargs):
|
||||
env = os.environ.copy()
|
||||
env['GIT_TERMINAL_PROMPT'] = '0'
|
||||
kwargs['env'] = env
|
||||
env["GIT_TERMINAL_PROMPT"] = "0"
|
||||
kwargs["env"] = env
|
||||
async with self._repo_lock:
|
||||
return await self._loop.run_in_executor(
|
||||
self._executor,
|
||||
functools.partial(sp_run, *args, stdout=PIPE, **kwargs)
|
||||
self._executor, functools.partial(sp_run, *args, stdout=PIPE, **kwargs)
|
||||
)
|
||||
|
||||
async def clone(self) -> Tuple[str]:
|
||||
@@ -176,24 +179,17 @@ class Repo(RepoJSONMixin):
|
||||
"""
|
||||
exists, path = self._existing_git_repo()
|
||||
if exists:
|
||||
raise ExistingGitRepo(
|
||||
"A git repo already exists at path: {}".format(path)
|
||||
)
|
||||
raise ExistingGitRepo("A git repo already exists at path: {}".format(path))
|
||||
|
||||
if self.branch is not None:
|
||||
p = await self._run(
|
||||
self.GIT_CLONE.format(
|
||||
branch=self.branch,
|
||||
url=self.url,
|
||||
folder=self.folder_path
|
||||
branch=self.branch, url=self.url, folder=self.folder_path
|
||||
).split()
|
||||
)
|
||||
else:
|
||||
p = await self._run(
|
||||
self.GIT_CLONE_NO_BRANCH.format(
|
||||
url=self.url,
|
||||
folder=self.folder_path
|
||||
).split()
|
||||
self.GIT_CLONE_NO_BRANCH.format(url=self.url, folder=self.folder_path).split()
|
||||
)
|
||||
|
||||
if p.returncode != 0:
|
||||
@@ -217,23 +213,18 @@ class Repo(RepoJSONMixin):
|
||||
"""
|
||||
exists, _ = self._existing_git_repo()
|
||||
if not exists:
|
||||
raise MissingGitRepo(
|
||||
"A git repo does not exist at path: {}".format(self.folder_path)
|
||||
)
|
||||
raise MissingGitRepo("A git repo does not exist at path: {}".format(self.folder_path))
|
||||
|
||||
p = await self._run(
|
||||
self.GIT_CURRENT_BRANCH.format(
|
||||
path=self.folder_path
|
||||
).split()
|
||||
)
|
||||
p = await self._run(self.GIT_CURRENT_BRANCH.format(path=self.folder_path).split())
|
||||
|
||||
if p.returncode != 0:
|
||||
raise GitException("Could not determine current branch"
|
||||
" at path: {}".format(self.folder_path))
|
||||
raise GitException(
|
||||
"Could not determine current branch" " at path: {}".format(self.folder_path)
|
||||
)
|
||||
|
||||
return p.stdout.decode().strip()
|
||||
|
||||
async def current_commit(self, branch: str=None) -> str:
|
||||
async def current_commit(self, branch: str = None) -> str:
|
||||
"""Determine the current commit hash of the repo.
|
||||
|
||||
Parameters
|
||||
@@ -252,15 +243,10 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
exists, _ = self._existing_git_repo()
|
||||
if not exists:
|
||||
raise MissingGitRepo(
|
||||
"A git repo does not exist at path: {}".format(self.folder_path)
|
||||
)
|
||||
raise MissingGitRepo("A git repo does not exist at path: {}".format(self.folder_path))
|
||||
|
||||
p = await self._run(
|
||||
self.GIT_LATEST_COMMIT.format(
|
||||
path=self.folder_path,
|
||||
branch=branch
|
||||
).split()
|
||||
self.GIT_LATEST_COMMIT.format(path=self.folder_path, branch=branch).split()
|
||||
)
|
||||
|
||||
if p.returncode != 0:
|
||||
@@ -268,7 +254,7 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
return p.stdout.decode().strip()
|
||||
|
||||
async def current_url(self, folder: Path=None) -> str:
|
||||
async def current_url(self, folder: Path = None) -> str:
|
||||
"""
|
||||
Discovers the FETCH URL for a Git repo.
|
||||
|
||||
@@ -290,18 +276,14 @@ class Repo(RepoJSONMixin):
|
||||
if folder is None:
|
||||
folder = self.folder_path
|
||||
|
||||
p = await self._run(
|
||||
Repo.GIT_DISCOVER_REMOTE_URL.format(
|
||||
path=folder
|
||||
).split()
|
||||
)
|
||||
p = await self._run(Repo.GIT_DISCOVER_REMOTE_URL.format(path=folder).split())
|
||||
|
||||
if p.returncode != 0:
|
||||
raise RuntimeError("Unable to discover a repo URL.")
|
||||
|
||||
return p.stdout.decode().strip()
|
||||
|
||||
async def hard_reset(self, branch: str=None) -> None:
|
||||
async def hard_reset(self, branch: str = None) -> None:
|
||||
"""Perform a hard reset on the current repo.
|
||||
|
||||
Parameters
|
||||
@@ -315,21 +297,18 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
exists, _ = self._existing_git_repo()
|
||||
if not exists:
|
||||
raise MissingGitRepo(
|
||||
"A git repo does not exist at path: {}".format(self.folder_path)
|
||||
)
|
||||
raise MissingGitRepo("A git repo does not exist at path: {}".format(self.folder_path))
|
||||
|
||||
p = await self._run(
|
||||
self.GIT_HARD_RESET.format(
|
||||
path=self.folder_path,
|
||||
branch=branch
|
||||
).split()
|
||||
self.GIT_HARD_RESET.format(path=self.folder_path, branch=branch).split()
|
||||
)
|
||||
|
||||
if p.returncode != 0:
|
||||
raise HardResetError("Some error occurred when trying to"
|
||||
" execute a hard reset on the repo at"
|
||||
" the following path: {}".format(self.folder_path))
|
||||
raise HardResetError(
|
||||
"Some error occurred when trying to"
|
||||
" execute a hard reset on the repo at"
|
||||
" the following path: {}".format(self.folder_path)
|
||||
)
|
||||
|
||||
async def update(self) -> (str, str):
|
||||
"""Update the current branch of this repo.
|
||||
@@ -345,15 +324,13 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
await self.hard_reset(branch=curr_branch)
|
||||
|
||||
p = await self._run(
|
||||
self.GIT_PULL.format(
|
||||
path=self.folder_path
|
||||
).split()
|
||||
)
|
||||
p = await self._run(self.GIT_PULL.format(path=self.folder_path).split())
|
||||
|
||||
if p.returncode != 0:
|
||||
raise UpdateError("Git pull returned a non zero exit code"
|
||||
" for the repo located at path: {}".format(self.folder_path))
|
||||
raise UpdateError(
|
||||
"Git pull returned a non zero exit code"
|
||||
" for the repo located at path: {}".format(self.folder_path)
|
||||
)
|
||||
|
||||
new_commit = await self.current_commit(branch=curr_branch)
|
||||
|
||||
@@ -389,7 +366,9 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
return await cog.copy_to(target_dir=target_dir)
|
||||
|
||||
async def install_libraries(self, target_dir: Path, libraries: Tuple[Installable]=()) -> bool:
|
||||
async def install_libraries(
|
||||
self, target_dir: Path, libraries: Tuple[Installable] = ()
|
||||
) -> bool:
|
||||
"""Install shared libraries to the target directory.
|
||||
|
||||
If :code:`libraries` is not specified, all shared libraries in the repo
|
||||
@@ -469,16 +448,16 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
p = await self._run(
|
||||
self.PIP_INSTALL.format(
|
||||
python=executable,
|
||||
target_dir=target_dir,
|
||||
reqs=" ".join(requirements)
|
||||
python=executable, target_dir=target_dir, reqs=" ".join(requirements)
|
||||
).split()
|
||||
)
|
||||
|
||||
if p.returncode != 0:
|
||||
log.error("Something went wrong when installing"
|
||||
" the following requirements:"
|
||||
" {}".format(", ".join(requirements)))
|
||||
log.error(
|
||||
"Something went wrong when installing"
|
||||
" the following requirements:"
|
||||
" {}".format(", ".join(requirements))
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -490,8 +469,7 @@ class Repo(RepoJSONMixin):
|
||||
"""
|
||||
# noinspection PyTypeChecker
|
||||
return tuple(
|
||||
[m for m in self.available_modules
|
||||
if m.type == InstallableType.COG and not m.hidden]
|
||||
[m for m in self.available_modules if m.type == InstallableType.COG and not m.hidden]
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -501,8 +479,7 @@ class Repo(RepoJSONMixin):
|
||||
"""
|
||||
# noinspection PyTypeChecker
|
||||
return tuple(
|
||||
[m for m in self.available_modules
|
||||
if m.type == InstallableType.SHARED_LIBRARY]
|
||||
[m for m in self.available_modules if m.type == InstallableType.SHARED_LIBRARY]
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -515,6 +492,7 @@ class Repo(RepoJSONMixin):
|
||||
|
||||
|
||||
class RepoManager:
|
||||
|
||||
def __init__(self, downloader_config: Config):
|
||||
self.downloader_config = downloader_config
|
||||
|
||||
@@ -526,7 +504,7 @@ class RepoManager:
|
||||
@property
|
||||
def repos_folder(self) -> Path:
|
||||
data_folder = data_manager.cog_data_path(self)
|
||||
return data_folder / 'repos'
|
||||
return data_folder / "repos"
|
||||
|
||||
def does_repo_exist(self, name: str) -> bool:
|
||||
return name in self._repos
|
||||
@@ -537,7 +515,7 @@ class RepoManager:
|
||||
raise InvalidRepoName("Not a valid Python variable name.")
|
||||
return name.lower()
|
||||
|
||||
async def add_repo(self, url: str, name: str, branch: str="master") -> Repo:
|
||||
async def add_repo(self, url: str, name: str, branch: str = "master") -> Repo:
|
||||
"""Add and clone a git repository.
|
||||
|
||||
Parameters
|
||||
@@ -557,13 +535,11 @@ class RepoManager:
|
||||
"""
|
||||
if self.does_repo_exist(name):
|
||||
raise InvalidRepoName(
|
||||
"That repo name you provided already exists."
|
||||
" Please choose another."
|
||||
"That repo name you provided already exists." " Please choose another."
|
||||
)
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
r = Repo(url=url, name=name, branch=branch,
|
||||
folder_path=self.repos_folder / name)
|
||||
r = Repo(url=url, name=name, branch=branch, folder_path=self.repos_folder / name)
|
||||
await r.clone()
|
||||
|
||||
self._repos[name] = r
|
||||
|
||||
@@ -36,45 +36,44 @@ class SMReel(Enum):
|
||||
PAYOUTS = {
|
||||
(SMReel.two, SMReel.two, SMReel.six): {
|
||||
"payout": lambda x: x * 2500 + x,
|
||||
"phrase": _("JACKPOT! 226! Your bid has been multiplied * 2500!")
|
||||
"phrase": _("JACKPOT! 226! Your bid has been multiplied * 2500!"),
|
||||
},
|
||||
(SMReel.flc, SMReel.flc, SMReel.flc): {
|
||||
"payout": lambda x: x + 1000,
|
||||
"phrase": _("4LC! +1000!")
|
||||
"payout": lambda x: x + 1000, "phrase": _("4LC! +1000!")
|
||||
},
|
||||
(SMReel.cherries, SMReel.cherries, SMReel.cherries): {
|
||||
"payout": lambda x: x + 800,
|
||||
"phrase": _("Three cherries! +800!")
|
||||
"payout": lambda x: x + 800, "phrase": _("Three cherries! +800!")
|
||||
},
|
||||
(SMReel.two, SMReel.six): {
|
||||
"payout": lambda x: x * 4 + x,
|
||||
"phrase": _("2 6! Your bid has been multiplied * 4!")
|
||||
"payout": lambda x: x * 4 + x, "phrase": _("2 6! Your bid has been multiplied * 4!")
|
||||
},
|
||||
(SMReel.cherries, SMReel.cherries): {
|
||||
"payout": lambda x: x * 3 + x,
|
||||
"phrase": _("Two cherries! Your bid has been multiplied * 3!")
|
||||
},
|
||||
"3 symbols": {
|
||||
"payout": lambda x: x + 500,
|
||||
"phrase": _("Three symbols! +500!")
|
||||
"phrase": _("Two cherries! Your bid has been multiplied * 3!"),
|
||||
},
|
||||
"3 symbols": {"payout": lambda x: x + 500, "phrase": _("Three symbols! +500!")},
|
||||
"2 symbols": {
|
||||
"payout": lambda x: x * 2 + x,
|
||||
"phrase": _("Two consecutive symbols! Your bid has been multiplied * 2!")
|
||||
"phrase": _("Two consecutive symbols! Your bid has been multiplied * 2!"),
|
||||
},
|
||||
}
|
||||
|
||||
SLOT_PAYOUTS_MSG = _("Slot machine payouts:\n"
|
||||
"{two.value} {two.value} {six.value} Bet * 2500\n"
|
||||
"{flc.value} {flc.value} {flc.value} +1000\n"
|
||||
"{cherries.value} {cherries.value} {cherries.value} +800\n"
|
||||
"{two.value} {six.value} Bet * 4\n"
|
||||
"{cherries.value} {cherries.value} Bet * 3\n\n"
|
||||
"Three symbols: +500\n"
|
||||
"Two symbols: Bet * 2").format(**SMReel.__dict__)
|
||||
SLOT_PAYOUTS_MSG = _(
|
||||
"Slot machine payouts:\n"
|
||||
"{two.value} {two.value} {six.value} Bet * 2500\n"
|
||||
"{flc.value} {flc.value} {flc.value} +1000\n"
|
||||
"{cherries.value} {cherries.value} {cherries.value} +800\n"
|
||||
"{two.value} {six.value} Bet * 4\n"
|
||||
"{cherries.value} {cherries.value} Bet * 3\n\n"
|
||||
"Three symbols: +500\n"
|
||||
"Two symbols: Bet * 2"
|
||||
).format(
|
||||
**SMReel.__dict__
|
||||
)
|
||||
|
||||
|
||||
def guild_only_check():
|
||||
|
||||
async def pred(ctx: commands.Context):
|
||||
if await bank.is_global():
|
||||
return True
|
||||
@@ -82,10 +81,12 @@ def guild_only_check():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
|
||||
class SetParser:
|
||||
|
||||
def __init__(self, argument):
|
||||
allowed = ("+", "-")
|
||||
self.sum = int(argument)
|
||||
@@ -115,19 +116,14 @@ class Economy:
|
||||
"SLOT_MIN": 5,
|
||||
"SLOT_MAX": 100,
|
||||
"SLOT_TIME": 0,
|
||||
"REGISTER_CREDITS": 0
|
||||
"REGISTER_CREDITS": 0,
|
||||
}
|
||||
|
||||
default_global_settings = default_guild_settings
|
||||
|
||||
default_member_settings = {
|
||||
"next_payday": 0,
|
||||
"last_slot": 0
|
||||
}
|
||||
default_member_settings = {"next_payday": 0, "last_slot": 0}
|
||||
|
||||
default_role_settings = {
|
||||
"PAYDAY_CREDITS": 0
|
||||
}
|
||||
default_role_settings = {"PAYDAY_CREDITS": 0}
|
||||
|
||||
default_user_settings = default_member_settings
|
||||
|
||||
@@ -159,8 +155,7 @@ class Economy:
|
||||
bal = await bank.get_balance(user)
|
||||
currency = await bank.get_currency_name(ctx.guild)
|
||||
|
||||
await ctx.send(_("{}'s balance is {} {}").format(
|
||||
user.display_name, bal, currency))
|
||||
await ctx.send(_("{}'s balance is {} {}").format(user.display_name, bal, currency))
|
||||
|
||||
@_bank.command()
|
||||
async def transfer(self, ctx: commands.Context, to: discord.Member, amount: int):
|
||||
@@ -173,9 +168,11 @@ class Economy:
|
||||
except ValueError as e:
|
||||
await ctx.send(str(e))
|
||||
|
||||
await ctx.send(_("{} transferred {} {} to {}").format(
|
||||
from_.display_name, amount, currency, to.display_name
|
||||
))
|
||||
await ctx.send(
|
||||
_("{} transferred {} {} to {}").format(
|
||||
from_.display_name, amount, currency, to.display_name
|
||||
)
|
||||
)
|
||||
|
||||
@_bank.command(name="set")
|
||||
@check_global_setting_admin()
|
||||
@@ -193,19 +190,25 @@ class Economy:
|
||||
|
||||
if creds.operation == "deposit":
|
||||
await bank.deposit_credits(to, creds.sum)
|
||||
await ctx.send(_("{} added {} {} to {}'s account.").format(
|
||||
author.display_name, creds.sum, currency, to.display_name
|
||||
))
|
||||
await ctx.send(
|
||||
_("{} added {} {} to {}'s account.").format(
|
||||
author.display_name, creds.sum, currency, to.display_name
|
||||
)
|
||||
)
|
||||
elif creds.operation == "withdraw":
|
||||
await bank.withdraw_credits(to, creds.sum)
|
||||
await ctx.send(_("{} removed {} {} from {}'s account.").format(
|
||||
author.display_name, creds.sum, currency, to.display_name
|
||||
))
|
||||
await ctx.send(
|
||||
_("{} removed {} {} from {}'s account.").format(
|
||||
author.display_name, creds.sum, currency, to.display_name
|
||||
)
|
||||
)
|
||||
else:
|
||||
await bank.set_balance(to, creds.sum)
|
||||
await ctx.send(_("{} set {}'s account to {} {}.").format(
|
||||
author.display_name, to.display_name, creds.sum, currency
|
||||
))
|
||||
await ctx.send(
|
||||
_("{} set {}'s account to {} {}.").format(
|
||||
author.display_name, to.display_name, creds.sum, currency
|
||||
)
|
||||
)
|
||||
|
||||
@_bank.command()
|
||||
@guild_only_check()
|
||||
@@ -214,19 +217,20 @@ class Economy:
|
||||
"""Deletes bank accounts"""
|
||||
if confirmation is False:
|
||||
await ctx.send(
|
||||
_("This will delete all bank accounts for {}.\nIf you're sure, type "
|
||||
"`{}bank reset yes`").format(
|
||||
self.bot.user.name if await bank.is_global() else "this server",
|
||||
ctx.prefix
|
||||
_(
|
||||
"This will delete all bank accounts for {}.\nIf you're sure, type "
|
||||
"`{}bank reset yes`"
|
||||
).format(
|
||||
self.bot.user.name if await bank.is_global() else "this server", ctx.prefix
|
||||
)
|
||||
)
|
||||
else:
|
||||
await bank.wipe_bank()
|
||||
await ctx.send(_("All bank accounts for {} have been "
|
||||
"deleted.").format(
|
||||
self.bot.user.name if await bank.is_global() else "this server"
|
||||
)
|
||||
)
|
||||
await ctx.send(
|
||||
_("All bank accounts for {} have been " "deleted.").format(
|
||||
self.bot.user.name if await bank.is_global() else "this server"
|
||||
)
|
||||
)
|
||||
|
||||
@commands.command()
|
||||
@guild_only_check()
|
||||
@@ -245,50 +249,65 @@ class Economy:
|
||||
await self.config.user(author).next_payday.set(next_payday)
|
||||
|
||||
pos = await bank.get_leaderboard_position(author)
|
||||
await ctx.send(_(
|
||||
"{0.mention} Here, take some {1}. Enjoy! (+{2} {1}!)\n\n"
|
||||
"You currently have {3} {1}.\n\n"
|
||||
"You are currently #{4} on the leaderboard!"
|
||||
).format(
|
||||
author, credits_name, str(await self.config.PAYDAY_CREDITS()),
|
||||
str(await bank.get_balance(author)), pos
|
||||
))
|
||||
await ctx.send(
|
||||
_(
|
||||
"{0.mention} Here, take some {1}. Enjoy! (+{2} {1}!)\n\n"
|
||||
"You currently have {3} {1}.\n\n"
|
||||
"You are currently #{4} on the leaderboard!"
|
||||
).format(
|
||||
author,
|
||||
credits_name,
|
||||
str(await self.config.PAYDAY_CREDITS()),
|
||||
str(await bank.get_balance(author)),
|
||||
pos,
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
dtime = self.display_time(next_payday - cur_time)
|
||||
await ctx.send(
|
||||
_("{} Too soon. For your next payday you have to"
|
||||
" wait {}.").format(author.mention, dtime)
|
||||
_("{} Too soon. For your next payday you have to" " wait {}.").format(
|
||||
author.mention, dtime
|
||||
)
|
||||
)
|
||||
else:
|
||||
next_payday = await self.config.member(author).next_payday()
|
||||
if cur_time >= next_payday:
|
||||
credit_amount = await self.config.guild(guild).PAYDAY_CREDITS()
|
||||
for role in author.roles:
|
||||
role_credits = await self.config.role(role).PAYDAY_CREDITS() # Nice variable name
|
||||
role_credits = await self.config.role(
|
||||
role
|
||||
).PAYDAY_CREDITS() # Nice variable name
|
||||
if role_credits > credit_amount:
|
||||
credit_amount = role_credits
|
||||
await bank.deposit_credits(author, credit_amount)
|
||||
next_payday = cur_time + await self.config.guild(guild).PAYDAY_TIME()
|
||||
await self.config.member(author).next_payday.set(next_payday)
|
||||
pos = await bank.get_leaderboard_position(author)
|
||||
await ctx.send(_(
|
||||
"{0.mention} Here, take some {1}. Enjoy! (+{2} {1}!)\n\n"
|
||||
"You currently have {3} {1}.\n\n"
|
||||
"You are currently #{4} on the leaderboard!"
|
||||
).format(
|
||||
author, credits_name, credit_amount,
|
||||
str(await bank.get_balance(author)), pos
|
||||
))
|
||||
await ctx.send(
|
||||
_(
|
||||
"{0.mention} Here, take some {1}. Enjoy! (+{2} {1}!)\n\n"
|
||||
"You currently have {3} {1}.\n\n"
|
||||
"You are currently #{4} on the leaderboard!"
|
||||
).format(
|
||||
author,
|
||||
credits_name,
|
||||
credit_amount,
|
||||
str(await bank.get_balance(author)),
|
||||
pos,
|
||||
)
|
||||
)
|
||||
else:
|
||||
dtime = self.display_time(next_payday - cur_time)
|
||||
await ctx.send(
|
||||
_("{} Too soon. For your next payday you have to"
|
||||
" wait {}.").format(author.mention, dtime))
|
||||
_("{} Too soon. For your next payday you have to" " wait {}.").format(
|
||||
author.mention, dtime
|
||||
)
|
||||
)
|
||||
|
||||
@commands.command()
|
||||
@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
|
||||
|
||||
Defaults to top 10"""
|
||||
@@ -296,7 +315,9 @@ class Economy:
|
||||
guild = ctx.guild
|
||||
if top < 1:
|
||||
top = 10
|
||||
if await bank.is_global() and show_global: # show_global is only applicable if bank is global
|
||||
if (
|
||||
await bank.is_global() and show_global
|
||||
): # show_global is only applicable if bank is global
|
||||
guild = None
|
||||
bank_sorted = await bank.get_leaderboard(positions=top, guild=guild)
|
||||
if len(bank_sorted) < top:
|
||||
@@ -310,8 +331,12 @@ class Economy:
|
||||
balance = acc[1]["balance"]
|
||||
balwidth = 2
|
||||
highscore += "{pos: <{poswidth}} {name: <{namewidth}s} {balance: >{balwidth}}\n".format(
|
||||
pos=pos, poswidth=poswidth, name=name, namewidth=namewidth,
|
||||
balance=balance, balwidth=balwidth
|
||||
pos=pos,
|
||||
poswidth=poswidth,
|
||||
name=name,
|
||||
namewidth=namewidth,
|
||||
balance=balance,
|
||||
balwidth=balwidth,
|
||||
)
|
||||
if highscore != "":
|
||||
for page in pagify(highscore, shorten_by=12):
|
||||
@@ -337,7 +362,11 @@ class Economy:
|
||||
slot_time = await self.config.SLOT_TIME()
|
||||
last_slot = await self.config.user(author).last_slot()
|
||||
else:
|
||||
valid_bid = await self.config.guild(guild).SLOT_MIN() <= bid <= await self.config.guild(guild).SLOT_MAX()
|
||||
valid_bid = await self.config.guild(
|
||||
guild
|
||||
).SLOT_MIN() <= bid <= await self.config.guild(
|
||||
guild
|
||||
).SLOT_MAX()
|
||||
slot_time = await self.config.guild(guild).SLOT_TIME()
|
||||
last_slot = await self.config.member(author).last_slot()
|
||||
now = calendar.timegm(ctx.message.created_at.utctimetuple())
|
||||
@@ -364,9 +393,11 @@ class Economy:
|
||||
default_reel.rotate(random.randint(-999, 999)) # weeeeee
|
||||
new_reel = deque(default_reel, maxlen=3) # we need only 3 symbols
|
||||
reels.append(new_reel) # for each reel
|
||||
rows = ((reels[0][0], reels[1][0], reels[2][0]),
|
||||
(reels[0][1], reels[1][1], reels[2][1]),
|
||||
(reels[0][2], reels[1][2], reels[2][2]))
|
||||
rows = (
|
||||
(reels[0][0], reels[1][0], reels[2][0]),
|
||||
(reels[0][1], reels[1][1], reels[2][1]),
|
||||
(reels[0][2], reels[1][2], reels[2][2]),
|
||||
)
|
||||
|
||||
slot = "~~\n~~" # Mobile friendly
|
||||
for i, row in enumerate(rows): # Let's build the slot to show
|
||||
@@ -378,8 +409,7 @@ class Economy:
|
||||
payout = PAYOUTS.get(rows[1])
|
||||
if not payout:
|
||||
# Checks for two-consecutive-symbols special rewards
|
||||
payout = PAYOUTS.get((rows[1][0], rows[1][1]),
|
||||
PAYOUTS.get((rows[1][1], rows[1][2])))
|
||||
payout = PAYOUTS.get((rows[1][0], rows[1][1]), PAYOUTS.get((rows[1][1], rows[1][2])))
|
||||
if not payout:
|
||||
# Still nothing. Let's check for 3 generic same symbols
|
||||
# or 2 consecutive symbols
|
||||
@@ -395,15 +425,20 @@ class Economy:
|
||||
pay = payout["payout"](bid)
|
||||
now = then - bid + pay
|
||||
await bank.set_balance(author, now)
|
||||
await channel.send(_("{}\n{} {}\n\nYour bid: {}\n{} → {}!"
|
||||
"").format(slot, author.mention,
|
||||
payout["phrase"], bid, then, now))
|
||||
await channel.send(
|
||||
_("{}\n{} {}\n\nYour bid: {}\n{} → {}!" "").format(
|
||||
slot, author.mention, payout["phrase"], bid, then, now
|
||||
)
|
||||
)
|
||||
else:
|
||||
then = await bank.get_balance(author)
|
||||
await bank.withdraw_credits(author, bid)
|
||||
now = then - bid
|
||||
await channel.send(_("{}\n{} Nothing!\nYour bid: {}\n{} → {}!"
|
||||
"").format(slot, author.mention, bid, then, now))
|
||||
await channel.send(
|
||||
_("{}\n{} Nothing!\nYour bid: {}\n{} → {}!" "").format(
|
||||
slot, author.mention, bid, then, now
|
||||
)
|
||||
)
|
||||
|
||||
@commands.group()
|
||||
@guild_only_check()
|
||||
@@ -427,17 +462,18 @@ class Economy:
|
||||
payday_amount = await self.config.guild(guild).PAYDAY_CREDITS()
|
||||
register_amount = await bank.get_default_balance(guild)
|
||||
msg = box(
|
||||
_("Minimum slot bid: {}\n"
|
||||
"Maximum slot bid: {}\n"
|
||||
"Slot cooldown: {}\n"
|
||||
"Payday amount: {}\n"
|
||||
"Payday cooldown: {}\n"
|
||||
"Amount given at account registration: {}"
|
||||
"").format(
|
||||
slot_min, slot_max, slot_time,
|
||||
payday_amount, payday_time, register_amount
|
||||
_(
|
||||
"Minimum slot bid: {}\n"
|
||||
"Maximum slot bid: {}\n"
|
||||
"Slot cooldown: {}\n"
|
||||
"Payday amount: {}\n"
|
||||
"Payday cooldown: {}\n"
|
||||
"Amount given at account registration: {}"
|
||||
""
|
||||
).format(
|
||||
slot_min, slot_max, slot_time, payday_amount, payday_time, register_amount
|
||||
),
|
||||
_("Current Economy settings:")
|
||||
_("Current Economy settings:"),
|
||||
)
|
||||
await ctx.send(msg)
|
||||
|
||||
@@ -445,7 +481,7 @@ class Economy:
|
||||
async def slotmin(self, ctx: commands.Context, bid: int):
|
||||
"""Minimum slot machine bid"""
|
||||
if bid < 1:
|
||||
await ctx.send(_('Invalid min bid amount.'))
|
||||
await ctx.send(_("Invalid min bid amount."))
|
||||
return
|
||||
guild = ctx.guild
|
||||
if await bank.is_global():
|
||||
@@ -460,8 +496,7 @@ class Economy:
|
||||
"""Maximum slot machine bid"""
|
||||
slot_min = await self.config.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 slotmax bid amount. Must be greater" " than slotmin."))
|
||||
return
|
||||
guild = ctx.guild
|
||||
credits_name = await bank.get_currency_name(guild)
|
||||
@@ -489,8 +524,11 @@ class Economy:
|
||||
await self.config.PAYDAY_TIME.set(seconds)
|
||||
else:
|
||||
await self.config.guild(guild).PAYDAY_TIME.set(seconds)
|
||||
await ctx.send(_("Value modified. At least {} seconds must pass "
|
||||
"between each payday.").format(seconds))
|
||||
await ctx.send(
|
||||
_("Value modified. At least {} seconds must pass " "between each payday.").format(
|
||||
seconds
|
||||
)
|
||||
)
|
||||
|
||||
@economyset.command()
|
||||
async def paydayamount(self, ctx: commands.Context, creds: int):
|
||||
@@ -504,8 +542,7 @@ class Economy:
|
||||
await self.config.PAYDAY_CREDITS.set(creds)
|
||||
else:
|
||||
await self.config.guild(guild).PAYDAY_CREDITS.set(creds)
|
||||
await ctx.send(_("Every payday will now give {} {}."
|
||||
"").format(creds, credits_name))
|
||||
await ctx.send(_("Every payday will now give {} {}." "").format(creds, credits_name))
|
||||
|
||||
@economyset.command()
|
||||
async def rolepaydayamount(self, ctx: commands.Context, role: discord.Role, creds: int):
|
||||
@@ -516,8 +553,11 @@ class Economy:
|
||||
await ctx.send("The bank must be per-server for per-role paydays to work.")
|
||||
else:
|
||||
await self.config.role(role).PAYDAY_CREDITS.set(creds)
|
||||
await ctx.send(_("Every payday will now give {} {} to people with the role {}."
|
||||
"").format(creds, credits_name, role.name))
|
||||
await ctx.send(
|
||||
_("Every payday will now give {} {} to people with the role {}." "").format(
|
||||
creds, credits_name, role.name
|
||||
)
|
||||
)
|
||||
|
||||
@economyset.command()
|
||||
async def registeramount(self, ctx: commands.Context, creds: int):
|
||||
@@ -527,17 +567,18 @@ class Economy:
|
||||
creds = 0
|
||||
credits_name = await bank.get_currency_name(guild)
|
||||
await bank.set_default_balance(creds, guild)
|
||||
await ctx.send(_("Registering an account will now give {} {}."
|
||||
"").format(creds, credits_name))
|
||||
await ctx.send(
|
||||
_("Registering an account will now give {} {}." "").format(creds, credits_name)
|
||||
)
|
||||
|
||||
# What would I ever do without stackoverflow?
|
||||
def display_time(self, seconds, granularity=2):
|
||||
intervals = ( # Source: http://stackoverflow.com/a/24542445
|
||||
(_('weeks'), 604800), # 60 * 60 * 24 * 7
|
||||
(_('days'), 86400), # 60 * 60 * 24
|
||||
(_('hours'), 3600), # 60 * 60
|
||||
(_('minutes'), 60),
|
||||
(_('seconds'), 1),
|
||||
(_("weeks"), 604800), # 60 * 60 * 24 * 7
|
||||
(_("days"), 86400), # 60 * 60 * 24
|
||||
(_("hours"), 3600), # 60 * 60
|
||||
(_("minutes"), 60),
|
||||
(_("seconds"), 1),
|
||||
)
|
||||
|
||||
result = []
|
||||
@@ -547,6 +588,6 @@ class Economy:
|
||||
if value:
|
||||
seconds -= value * count
|
||||
if value == 1:
|
||||
name = name.rstrip('s')
|
||||
name = name.rstrip("s")
|
||||
result.append("{} {}".format(value, name))
|
||||
return ', '.join(result[:granularity])
|
||||
return ", ".join(result[:granularity])
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../economy.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../economy.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -21,12 +21,9 @@ class Filter:
|
||||
"filterban_count": 0,
|
||||
"filterban_time": 0,
|
||||
"filter_names": False,
|
||||
"filter_default_name": "John Doe"
|
||||
}
|
||||
default_member_settings = {
|
||||
"filter_count": 0,
|
||||
"next_reset_time": 0
|
||||
"filter_default_name": "John Doe",
|
||||
}
|
||||
default_member_settings = {"filter_count": 0, "next_reset_time": 0}
|
||||
self.settings.register_guild(**default_guild_settings)
|
||||
self.settings.register_member(**default_member_settings)
|
||||
self.register_task = self.bot.loop.create_task(self.register_filterban())
|
||||
@@ -37,8 +34,7 @@ class Filter:
|
||||
async def register_filterban(self):
|
||||
try:
|
||||
await modlog.register_casetype(
|
||||
"filterban", False, ":filing_cabinet: :hammer:",
|
||||
"Filter ban", "ban"
|
||||
"filterban", False, ":filing_cabinet: :hammer:", "Filter ban", "ban"
|
||||
)
|
||||
except RuntimeError:
|
||||
pass
|
||||
@@ -79,13 +75,12 @@ class Filter:
|
||||
word_list = []
|
||||
tmp = ""
|
||||
for word in split_words:
|
||||
if not word.startswith("\"")\
|
||||
and not word.endswith("\"") and not tmp:
|
||||
if not word.startswith('"') and not word.endswith('"') and not tmp:
|
||||
word_list.append(word)
|
||||
else:
|
||||
if word.startswith("\""):
|
||||
if word.startswith('"'):
|
||||
tmp += word[1:]
|
||||
elif word.endswith("\""):
|
||||
elif word.endswith('"'):
|
||||
tmp += word[:-1]
|
||||
word_list.append(tmp)
|
||||
tmp = ""
|
||||
@@ -110,13 +105,12 @@ class Filter:
|
||||
word_list = []
|
||||
tmp = ""
|
||||
for word in split_words:
|
||||
if not word.startswith("\"")\
|
||||
and not word.endswith("\"") and not tmp:
|
||||
if not word.startswith('"') and not word.endswith('"') and not tmp:
|
||||
word_list.append(word)
|
||||
else:
|
||||
if word.startswith("\""):
|
||||
if word.startswith('"'):
|
||||
tmp += word[1:]
|
||||
elif word.endswith("\""):
|
||||
elif word.endswith('"'):
|
||||
tmp += word[:-1]
|
||||
word_list.append(tmp)
|
||||
tmp = ""
|
||||
@@ -139,14 +133,10 @@ class Filter:
|
||||
await self.settings.guild(guild).filter_names.set(not current_setting)
|
||||
if current_setting:
|
||||
await ctx.send(
|
||||
_("Names and nicknames will no longer be "
|
||||
"checked against the filter")
|
||||
_("Names and nicknames will no longer be " "checked against the filter")
|
||||
)
|
||||
else:
|
||||
await ctx.send(
|
||||
_("Names and nicknames will now be checked against "
|
||||
"the filter")
|
||||
)
|
||||
await ctx.send(_("Names and nicknames will now be checked against " "the filter"))
|
||||
|
||||
@_filter.command(name="defaultname")
|
||||
async def filter_default_name(self, ctx: commands.Context, name: str):
|
||||
@@ -160,17 +150,17 @@ class Filter:
|
||||
await ctx.send(_("The name to use on filtered names has been set"))
|
||||
|
||||
@_filter.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):
|
||||
"""
|
||||
Sets up an autoban if the specified number of messages are
|
||||
filtered in the specified amount of time (in seconds)
|
||||
"""
|
||||
if (count <= 0) != (timeframe <= 0):
|
||||
await ctx.send(
|
||||
_("Count and timeframe either both need to be 0 "
|
||||
"or both need to be greater than 0!"
|
||||
)
|
||||
_(
|
||||
"Count and timeframe either both need to be 0 "
|
||||
"or both need to be greater than 0!"
|
||||
)
|
||||
)
|
||||
return
|
||||
elif count == 0 and timeframe == 0:
|
||||
@@ -213,9 +203,7 @@ class Filter:
|
||||
if filter_count > 0 and filter_time > 0:
|
||||
if message.created_at.timestamp() >= next_reset_time:
|
||||
next_reset_time = message.created_at.timestamp() + filter_time
|
||||
await self.settings.member(author).next_reset_time.set(
|
||||
next_reset_time
|
||||
)
|
||||
await self.settings.member(author).next_reset_time.set(next_reset_time)
|
||||
if user_count > 0:
|
||||
user_count = 0
|
||||
await self.settings.member(author).filter_count.set(user_count)
|
||||
@@ -231,8 +219,10 @@ class Filter:
|
||||
if filter_count > 0 and filter_time > 0:
|
||||
user_count += 1
|
||||
await self.settings.member(author).filter_count.set(user_count)
|
||||
if user_count >= filter_count and \
|
||||
message.created_at.timestamp() < next_reset_time:
|
||||
if (
|
||||
user_count >= filter_count
|
||||
and message.created_at.timestamp() < next_reset_time
|
||||
):
|
||||
reason = "Autoban (too many filtered messages)"
|
||||
try:
|
||||
await server.ban(author, reason=reason)
|
||||
@@ -240,8 +230,13 @@ class Filter:
|
||||
pass
|
||||
else:
|
||||
await modlog.create_case(
|
||||
self.bot, server, message.created_at,
|
||||
"filterban", author, server.me, reason
|
||||
self.bot,
|
||||
server,
|
||||
message.created_at,
|
||||
"filterban",
|
||||
author,
|
||||
server.me,
|
||||
reason,
|
||||
)
|
||||
|
||||
async def on_message(self, message: discord.Message):
|
||||
@@ -251,7 +246,7 @@ class Filter:
|
||||
valid_user = isinstance(author, discord.Member) and not author.bot
|
||||
if not valid_user:
|
||||
return
|
||||
|
||||
|
||||
# Bots and mods or superior are ignored from the filter
|
||||
mod_or_superior = await is_mod_or_superior(self.bot, obj=author)
|
||||
if mod_or_superior:
|
||||
@@ -266,7 +261,7 @@ class Filter:
|
||||
valid_user = isinstance(author, discord.Member) and not author.bot
|
||||
if not valid_user:
|
||||
return
|
||||
|
||||
|
||||
# Bots and mods or superior are ignored from the filter
|
||||
mod_or_superior = await is_mod_or_superior(self.bot, obj=author)
|
||||
if mod_or_superior:
|
||||
@@ -323,4 +318,3 @@ class Filter:
|
||||
except:
|
||||
pass
|
||||
break
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../filter.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../filter.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -15,12 +15,13 @@ _ = Translator("General", __file__)
|
||||
|
||||
|
||||
class RPS(Enum):
|
||||
rock = "\N{MOYAI}"
|
||||
paper = "\N{PAGE FACING UP}"
|
||||
rock = "\N{MOYAI}"
|
||||
paper = "\N{PAGE FACING UP}"
|
||||
scissors = "\N{BLACK SCISSORS}"
|
||||
|
||||
|
||||
class RPSParser:
|
||||
|
||||
def __init__(self, argument):
|
||||
argument = argument.lower()
|
||||
if argument == "rock":
|
||||
@@ -40,13 +41,26 @@ class General:
|
||||
def __init__(self):
|
||||
self.stopwatches = {}
|
||||
self.ball = [
|
||||
_("As I see it, yes"), _("It is certain"), _("It is decidedly so"),
|
||||
_("Most likely"), _("Outlook good"), _("Signs point to yes"),
|
||||
_("Without a doubt"), _("Yes"), _("Yes – definitely"), _("You may rely on it"),
|
||||
_("Reply hazy, try again"), _("Ask again later"),
|
||||
_("Better not tell you now"), _("Cannot predict now"),
|
||||
_("Concentrate and ask again"), _("Don't count on it"), _("My reply is no"),
|
||||
_("My sources say no"), _("Outlook not so good"), _("Very doubtful")
|
||||
_("As I see it, yes"),
|
||||
_("It is certain"),
|
||||
_("It is decidedly so"),
|
||||
_("Most likely"),
|
||||
_("Outlook good"),
|
||||
_("Signs point to yes"),
|
||||
_("Without a doubt"),
|
||||
_("Yes"),
|
||||
_("Yes – definitely"),
|
||||
_("You may rely on it"),
|
||||
_("Reply hazy, try again"),
|
||||
_("Ask again later"),
|
||||
_("Better not tell you now"),
|
||||
_("Cannot predict now"),
|
||||
_("Concentrate and ask again"),
|
||||
_("Don't count on it"),
|
||||
_("My reply is no"),
|
||||
_("My sources say no"),
|
||||
_("Outlook not so good"),
|
||||
_("Very doubtful"),
|
||||
]
|
||||
|
||||
@commands.command()
|
||||
@@ -57,12 +71,12 @@ class General:
|
||||
"""
|
||||
choices = [escape(c, mass_mentions=True) for c in choices]
|
||||
if len(choices) < 2:
|
||||
await ctx.send(_('Not enough choices to pick from.'))
|
||||
await ctx.send(_("Not enough choices to pick from."))
|
||||
else:
|
||||
await ctx.send(choice(choices))
|
||||
|
||||
@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)
|
||||
|
||||
Defaults to 100.
|
||||
@@ -70,14 +84,12 @@ class General:
|
||||
author = ctx.author
|
||||
if number > 1:
|
||||
n = randint(1, number)
|
||||
await ctx.send(
|
||||
_("{} :game_die: {} :game_die:").format(author.mention, n)
|
||||
)
|
||||
await ctx.send(_("{} :game_die: {} :game_die:").format(author.mention, n))
|
||||
else:
|
||||
await ctx.send(_("{} Maybe higher than 1? ;P").format(author.mention))
|
||||
|
||||
@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.
|
||||
|
||||
Defaults to coin.
|
||||
@@ -86,8 +98,7 @@ class General:
|
||||
msg = ""
|
||||
if user.id == ctx.bot.user.id:
|
||||
user = ctx.author
|
||||
msg = _("Nice try. You think this is funny?\n"
|
||||
"How about *this* instead:\n\n")
|
||||
msg = _("Nice try. You think this is funny?\n" "How about *this* instead:\n\n")
|
||||
char = "abcdefghijklmnopqrstuvwxyz"
|
||||
tran = "ɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz"
|
||||
table = str.maketrans(char, tran)
|
||||
@@ -98,45 +109,37 @@ class General:
|
||||
name = name.translate(table)
|
||||
await ctx.send(msg + "(╯°□°)╯︵ " + name[::-1])
|
||||
else:
|
||||
await ctx.send(
|
||||
_("*flips a coin and... ") + choice([_("HEADS!*"), _("TAILS!*")])
|
||||
)
|
||||
await ctx.send(_("*flips a coin and... ") + choice([_("HEADS!*"), _("TAILS!*")]))
|
||||
|
||||
@commands.command()
|
||||
async def rps(self, ctx, your_choice : RPSParser):
|
||||
async def rps(self, ctx, your_choice: RPSParser):
|
||||
"""Play rock paper scissors"""
|
||||
author = ctx.author
|
||||
player_choice = your_choice.choice
|
||||
red_choice = choice((RPS.rock, RPS.paper, RPS.scissors))
|
||||
cond = {
|
||||
(RPS.rock, RPS.paper) : False,
|
||||
(RPS.rock, RPS.scissors) : True,
|
||||
(RPS.paper, RPS.rock) : True,
|
||||
(RPS.paper, RPS.scissors) : False,
|
||||
(RPS.scissors, RPS.rock) : False,
|
||||
(RPS.scissors, RPS.paper) : True
|
||||
}
|
||||
(RPS.rock, RPS.paper): False,
|
||||
(RPS.rock, RPS.scissors): True,
|
||||
(RPS.paper, RPS.rock): True,
|
||||
(RPS.paper, RPS.scissors): False,
|
||||
(RPS.scissors, RPS.rock): False,
|
||||
(RPS.scissors, RPS.paper): True,
|
||||
}
|
||||
|
||||
if red_choice == player_choice:
|
||||
outcome = None # Tie
|
||||
outcome = None # Tie
|
||||
else:
|
||||
outcome = cond[(player_choice, red_choice)]
|
||||
|
||||
if outcome is True:
|
||||
await ctx.send(_("{} You win {}!").format(
|
||||
red_choice.value, author.mention
|
||||
))
|
||||
await ctx.send(_("{} You win {}!").format(red_choice.value, author.mention))
|
||||
elif outcome is False:
|
||||
await ctx.send(_("{} You lose {}!").format(
|
||||
red_choice.value, author.mention
|
||||
))
|
||||
await ctx.send(_("{} You lose {}!").format(red_choice.value, author.mention))
|
||||
else:
|
||||
await ctx.send(_("{} We're square {}!").format(
|
||||
red_choice.value, author.mention
|
||||
))
|
||||
await ctx.send(_("{} We're square {}!").format(red_choice.value, author.mention))
|
||||
|
||||
@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
|
||||
|
||||
Question must end with a question mark.
|
||||
@@ -160,14 +163,14 @@ class General:
|
||||
self.stopwatches.pop(author.id, None)
|
||||
|
||||
@commands.command()
|
||||
async def lmgtfy(self, ctx, *, search_terms : str):
|
||||
async def lmgtfy(self, ctx, *, search_terms: str):
|
||||
"""Creates a lmgtfy link"""
|
||||
search_terms = escape(search_terms.replace(" ", "+"), mass_mentions=True)
|
||||
await ctx.send("https://lmgtfy.com/?q={}".format(search_terms))
|
||||
|
||||
@commands.command(hidden=True)
|
||||
@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
|
||||
|
||||
Up to 10 intensity levels."""
|
||||
@@ -186,7 +189,7 @@ class General:
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
async def userinfo(self, ctx, *, user: discord.Member=None):
|
||||
async def userinfo(self, ctx, *, user: discord.Member = None):
|
||||
"""Shows users's informations"""
|
||||
author = ctx.author
|
||||
guild = ctx.guild
|
||||
@@ -196,8 +199,7 @@ class General:
|
||||
|
||||
# A special case for a special someone :^)
|
||||
special_date = datetime.datetime(2016, 1, 10, 6, 8, 4, 443000)
|
||||
is_special = (user.id == 96130341705637888 and
|
||||
guild.id == 133049272517001216)
|
||||
is_special = (user.id == 96130341705637888 and guild.id == 133049272517001216)
|
||||
|
||||
roles = sorted(user.roles)[1:]
|
||||
|
||||
@@ -206,12 +208,11 @@ class General:
|
||||
since_joined = (ctx.message.created_at - joined_at).days
|
||||
user_joined = joined_at.strftime("%d %b %Y %H:%M")
|
||||
user_created = user.created_at.strftime("%d %b %Y %H:%M")
|
||||
member_number = sorted(guild.members,
|
||||
key=lambda m: m.joined_at).index(user) + 1
|
||||
member_number = sorted(guild.members, key=lambda m: m.joined_at).index(user) + 1
|
||||
|
||||
created_on = _("{}\n({} days ago)").format(user_created, since_created)
|
||||
joined_on = _("{}\n({} days ago)").format(user_joined, since_joined)
|
||||
|
||||
|
||||
activity = _("Chilling in {} status").format(user.status)
|
||||
if user.activity is None: # Default status
|
||||
pass
|
||||
@@ -233,15 +234,14 @@ class General:
|
||||
data.add_field(name=_("Joined Discord on"), value=created_on)
|
||||
data.add_field(name=_("Joined this server on"), value=joined_on)
|
||||
data.add_field(name=_("Roles"), value=roles, inline=False)
|
||||
data.set_footer(text=_("Member #{} | User ID: {}"
|
||||
"").format(member_number, user.id))
|
||||
data.set_footer(text=_("Member #{} | User ID: {}" "").format(member_number, user.id))
|
||||
|
||||
name = str(user)
|
||||
name = " ~ ".join((name, user.nick)) if user.nick else name
|
||||
|
||||
if user.avatar:
|
||||
avatar = user.avatar_url
|
||||
avatar = avatar.replace('webp', 'png')
|
||||
avatar = avatar.replace("webp", "png")
|
||||
data.set_author(name=name, url=avatar)
|
||||
data.set_thumbnail(url=avatar)
|
||||
else:
|
||||
@@ -250,31 +250,34 @@ class General:
|
||||
try:
|
||||
await ctx.send(embed=data)
|
||||
except discord.HTTPException:
|
||||
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.guild_only()
|
||||
async def serverinfo(self, ctx):
|
||||
"""Shows server's informations"""
|
||||
guild = ctx.guild
|
||||
online = len([m.status for m in guild.members
|
||||
if m.status == discord.Status.online or
|
||||
m.status == discord.Status.idle])
|
||||
online = len(
|
||||
[
|
||||
m.status
|
||||
for m in guild.members
|
||||
if m.status == discord.Status.online or m.status == discord.Status.idle
|
||||
]
|
||||
)
|
||||
total_users = len(guild.members)
|
||||
text_channels = len(guild.text_channels)
|
||||
voice_channels = len(guild.voice_channels)
|
||||
passed = (ctx.message.created_at - guild.created_at).days
|
||||
created_at = (_("Since {}. That's over {} days ago!"
|
||||
"").format(guild.created_at.strftime("%d %b %Y %H:%M"),
|
||||
passed))
|
||||
created_at = (
|
||||
_("Since {}. That's over {} days ago!" "").format(
|
||||
guild.created_at.strftime("%d %b %Y %H:%M"), passed
|
||||
)
|
||||
)
|
||||
|
||||
colour = ''.join([choice('0123456789ABCDEF') for x in range(6)])
|
||||
colour = "".join([choice("0123456789ABCDEF") for x in range(6)])
|
||||
colour = randint(0, 0xFFFFFF)
|
||||
|
||||
data = discord.Embed(
|
||||
description=created_at,
|
||||
colour=discord.Colour(value=colour))
|
||||
data = discord.Embed(description=created_at, colour=discord.Colour(value=colour))
|
||||
data.add_field(name=_("Region"), value=str(guild.region))
|
||||
data.add_field(name=_("Users"), value="{}/{}".format(online, total_users))
|
||||
data.add_field(name=_("Text Channels"), value=text_channels)
|
||||
@@ -292,16 +295,16 @@ class General:
|
||||
try:
|
||||
await ctx.send(embed=data)
|
||||
except discord.HTTPException:
|
||||
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()
|
||||
async def urban(self, ctx, *, search_terms: str, definition_number: int=1):
|
||||
async def urban(self, ctx, *, search_terms: str, definition_number: int = 1):
|
||||
"""Urban Dictionary search
|
||||
|
||||
Definition number must be between 1 and 10"""
|
||||
|
||||
def encode(s):
|
||||
return quote_plus(s, encoding='utf-8', errors='replace')
|
||||
return quote_plus(s, encoding="utf-8", errors="replace")
|
||||
|
||||
# definition_number is just there to show up in the help
|
||||
# all this mess is to avoid forcing double quotes on the user
|
||||
@@ -313,8 +316,8 @@ class General:
|
||||
search_terms = search_terms[:-1]
|
||||
else:
|
||||
pos = 0
|
||||
if pos not in range(0, 11): # API only provides the
|
||||
pos = 0 # top 10 definitions
|
||||
if pos not in range(0, 11): # API only provides the
|
||||
pos = 0 # top 10 definitions
|
||||
except ValueError:
|
||||
pos = 0
|
||||
|
||||
@@ -326,18 +329,19 @@ class General:
|
||||
result = await r.json()
|
||||
item_list = result["list"]
|
||||
if item_list:
|
||||
definition = item_list[pos]['definition']
|
||||
example = item_list[pos]['example']
|
||||
definition = item_list[pos]["definition"]
|
||||
example = item_list[pos]["example"]
|
||||
defs = len(item_list)
|
||||
msg = ("**Definition #{} out of {}:\n**{}\n\n"
|
||||
"**Example:\n**{}".format(pos+1, defs, definition,
|
||||
example))
|
||||
msg = (
|
||||
"**Definition #{} out of {}:\n**{}\n\n"
|
||||
"**Example:\n**{}".format(pos + 1, defs, definition, example)
|
||||
)
|
||||
msg = pagify(msg, ["\n"])
|
||||
for page in msg:
|
||||
await ctx.send(page)
|
||||
else:
|
||||
await ctx.send(_("Your search terms gave no results."))
|
||||
except IndexError:
|
||||
await ctx.send(_("There is no definition #{}").format(pos+1))
|
||||
await ctx.send(_("There is no definition #{}").format(pos + 1))
|
||||
except:
|
||||
await ctx.send(_("Error."))
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../general.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../general.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -13,9 +13,7 @@ GIPHY_API_KEY = "dc6zaTOxFJmzC"
|
||||
@cog_i18n(_)
|
||||
class Image:
|
||||
"""Image related commands."""
|
||||
default_global = {
|
||||
"imgur_client_id": None
|
||||
}
|
||||
default_global = {"imgur_client_id": None}
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
@@ -45,7 +43,9 @@ class Image:
|
||||
if not imgur_client_id:
|
||||
await ctx.send(
|
||||
_("A client ID has not been set! Please set one with {}").format(
|
||||
"`{}imgurcreds`".format(ctx.prefix)))
|
||||
"`{}imgurcreds`".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
return
|
||||
headers = {"Authorization": "Client-ID {}".format(imgur_client_id)}
|
||||
async with self.session.get(url, headers=headers, params=params) as search_get:
|
||||
@@ -66,7 +66,9 @@ class Image:
|
||||
await ctx.send(_("Something went wrong. Error code is {}").format(data["status"]))
|
||||
|
||||
@_imgur.command(name="subreddit")
|
||||
async def imgur_subreddit(self, ctx, subreddit: str, sort_type: str="top", window: str="day"):
|
||||
async def imgur_subreddit(
|
||||
self, ctx, subreddit: str, sort_type: str = "top", window: str = "day"
|
||||
):
|
||||
"""Gets images from the specified subreddit section
|
||||
|
||||
Sort types: new, top
|
||||
@@ -90,7 +92,9 @@ class Image:
|
||||
if not imgur_client_id:
|
||||
await ctx.send(
|
||||
_("A client ID has not been set! Please set one with {}").format(
|
||||
"`{}imgurcreds`".format(ctx.prefix)))
|
||||
"`{}imgurcreds`".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
links = []
|
||||
@@ -139,8 +143,10 @@ class Image:
|
||||
await ctx.send_help()
|
||||
return
|
||||
|
||||
url = ("http://api.giphy.com/v1/gifs/search?&api_key={}&q={}"
|
||||
"".format(GIPHY_API_KEY, keywords))
|
||||
url = (
|
||||
"http://api.giphy.com/v1/gifs/search?&api_key={}&q={}"
|
||||
"".format(GIPHY_API_KEY, keywords)
|
||||
)
|
||||
|
||||
async with self.session.get(url) as r:
|
||||
result = await r.json()
|
||||
@@ -161,8 +167,10 @@ class Image:
|
||||
await ctx.send_help()
|
||||
return
|
||||
|
||||
url = ("http://api.giphy.com/v1/gifs/random?&api_key={}&tag={}"
|
||||
"".format(GIPHY_API_KEY, keywords))
|
||||
url = (
|
||||
"http://api.giphy.com/v1/gifs/random?&api_key={}&tag={}"
|
||||
"".format(GIPHY_API_KEY, keywords)
|
||||
)
|
||||
|
||||
async with self.session.get(url) as r:
|
||||
result = await r.json()
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../image.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../image.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -3,6 +3,7 @@ import discord
|
||||
|
||||
|
||||
def mod_or_voice_permissions(**perms):
|
||||
|
||||
async def pred(ctx: commands.Context):
|
||||
author = ctx.author
|
||||
guild = ctx.guild
|
||||
@@ -23,10 +24,12 @@ def mod_or_voice_permissions(**perms):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
|
||||
def admin_or_voice_permissions(**perms):
|
||||
|
||||
async def pred(ctx: commands.Context):
|
||||
author = ctx.author
|
||||
guild = ctx.guild
|
||||
@@ -42,10 +45,12 @@ def admin_or_voice_permissions(**perms):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
|
||||
def bot_has_voice_permissions(**perms):
|
||||
|
||||
async def pred(ctx: commands.Context):
|
||||
guild = ctx.guild
|
||||
for vc in guild.voice_channels:
|
||||
@@ -55,4 +60,5 @@ def bot_has_voice_permissions(**perms):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
return commands.check(pred)
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../mod.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../mod.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../modlog.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../modlog.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -5,7 +5,7 @@ from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from redbot.core.utils.chat_formatting import box
|
||||
|
||||
_ = Translator('ModLog', __file__)
|
||||
_ = Translator("ModLog", __file__)
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
@@ -32,15 +32,12 @@ class ModLog:
|
||||
if channel:
|
||||
if channel.permissions_for(guild.me).send_messages:
|
||||
await modlog.set_modlog_channel(guild, channel)
|
||||
await ctx.send(
|
||||
_("Mod events will be sent to {}").format(
|
||||
channel.mention
|
||||
)
|
||||
)
|
||||
await ctx.send(_("Mod events will be sent to {}").format(channel.mention))
|
||||
else:
|
||||
await ctx.send(
|
||||
_("I do not have permissions to "
|
||||
"send messages in {}!").format(channel.mention)
|
||||
_("I do not have permissions to " "send messages in {}!").format(
|
||||
channel.mention
|
||||
)
|
||||
)
|
||||
else:
|
||||
try:
|
||||
@@ -51,7 +48,7 @@ class ModLog:
|
||||
await modlog.set_modlog_channel(guild, None)
|
||||
await ctx.send(_("Mod log deactivated."))
|
||||
|
||||
@modlogset.command(name='cases')
|
||||
@modlogset.command(name="cases")
|
||||
@commands.guild_only()
|
||||
async def set_cases(self, ctx: commands.Context, action: str = None):
|
||||
"""Enables or disables case creation for each type of mod action"""
|
||||
@@ -64,8 +61,8 @@ class ModLog:
|
||||
msg = ""
|
||||
for ct in casetypes:
|
||||
enabled = await ct.is_enabled()
|
||||
value = 'enabled' if enabled else 'disabled'
|
||||
msg += '%s : %s\n' % (ct.name, value)
|
||||
value = "enabled" if enabled else "disabled"
|
||||
msg += "%s : %s\n" % (ct.name, value)
|
||||
|
||||
msg = title + "\n" + box(msg)
|
||||
await ctx.send(msg)
|
||||
@@ -79,8 +76,8 @@ class ModLog:
|
||||
await casetype.set_enabled(True if not enabled else False)
|
||||
|
||||
msg = (
|
||||
_('Case creation for {} actions is now {}.').format(
|
||||
action, 'enabled' if not enabled else 'disabled'
|
||||
_("Case creation for {} actions is now {}.").format(
|
||||
action, "enabled" if not enabled else "disabled"
|
||||
)
|
||||
)
|
||||
await ctx.send(msg)
|
||||
@@ -133,8 +130,10 @@ class ModLog:
|
||||
if audit_type:
|
||||
audit_case = None
|
||||
async for entry in guild.audit_logs(action=audit_type):
|
||||
if entry.target.id == case_before.user.id and \
|
||||
entry.action == audit_type:
|
||||
if (
|
||||
entry.target.id == case_before.user.id
|
||||
and entry.action == audit_type
|
||||
):
|
||||
audit_case = entry
|
||||
break
|
||||
if audit_case:
|
||||
@@ -145,9 +144,7 @@ class ModLog:
|
||||
if not (is_guild_owner or is_case_author or author_is_mod):
|
||||
await ctx.send(_("You are not authorized to modify that case!"))
|
||||
return
|
||||
to_modify = {
|
||||
"reason": reason,
|
||||
}
|
||||
to_modify = {"reason": reason}
|
||||
if case_before.moderator != author:
|
||||
to_modify["amended_by"] = author
|
||||
to_modify["modified_at"] = ctx.message.created_at.timestamp()
|
||||
|
||||
@@ -22,15 +22,9 @@ log = logging.getLogger("red.reports")
|
||||
@cog_i18n(_)
|
||||
class Reports:
|
||||
|
||||
default_guild_settings = {
|
||||
"output_channel": None,
|
||||
"active": False,
|
||||
"next_ticket": 1
|
||||
}
|
||||
default_guild_settings = {"output_channel": None, "active": False, "next_ticket": 1}
|
||||
|
||||
default_report = {
|
||||
'report': {}
|
||||
}
|
||||
default_report = {"report": {}}
|
||||
|
||||
# This can be made configureable later if it
|
||||
# becomes an issue.
|
||||
@@ -42,15 +36,14 @@ class Reports:
|
||||
(timedelta(seconds=5), 1),
|
||||
(timedelta(minutes=5), 3),
|
||||
(timedelta(hours=1), 10),
|
||||
(timedelta(days=1), 24)
|
||||
(timedelta(days=1), 24),
|
||||
]
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.bot = bot
|
||||
self.config = Config.get_conf(
|
||||
self, 78631113035100160, force_registration=True)
|
||||
self.config = Config.get_conf(self, 78631113035100160, force_registration=True)
|
||||
self.config.register_guild(**self.default_guild_settings)
|
||||
self.config.register_custom('REPORT', **self.default_report)
|
||||
self.config.register_custom("REPORT", **self.default_report)
|
||||
self.antispam = {}
|
||||
self.user_cache = []
|
||||
self.tunnel_store = {}
|
||||
@@ -59,9 +52,7 @@ class Reports:
|
||||
|
||||
@property
|
||||
def tunnels(self):
|
||||
return [
|
||||
x['tun'] for x in self.tunnel_store.values()
|
||||
]
|
||||
return [x["tun"] for x in self.tunnel_store.values()]
|
||||
|
||||
@checks.admin_or_permissions(manage_guild=True)
|
||||
@commands.guild_only()
|
||||
@@ -99,9 +90,7 @@ class Reports:
|
||||
admin_role = discord.utils.get(
|
||||
guild.roles, id=await self.bot.db.guild(guild).admin_role()
|
||||
)
|
||||
mod_role = discord.utils.get(
|
||||
guild.roles, id=await self.bot.db.guild(guild).mod_role()
|
||||
)
|
||||
mod_role = discord.utils.get(guild.roles, id=await self.bot.db.guild(guild).mod_role())
|
||||
ret |= any(r in m.roles for r in (mod_role, admin_role))
|
||||
if perms:
|
||||
ret |= m.guild_permissions >= perms
|
||||
@@ -111,10 +100,13 @@ class Reports:
|
||||
return ret
|
||||
|
||||
async def discover_guild(
|
||||
self, author: discord.User, *,
|
||||
mod: bool=False,
|
||||
permissions: Union[discord.Permissions, dict]=None,
|
||||
prompt: str=""):
|
||||
self,
|
||||
author: discord.User,
|
||||
*,
|
||||
mod: bool = False,
|
||||
permissions: Union[discord.Permissions, dict] = None,
|
||||
prompt: str = ""
|
||||
):
|
||||
"""
|
||||
discovers which of shared guilds between the bot
|
||||
and provided user based on conditions (mod or permissions is an or)
|
||||
@@ -151,13 +143,9 @@ class Reports:
|
||||
return m.author == author and m.channel == dm.channel
|
||||
|
||||
try:
|
||||
message = await self.bot.wait_for(
|
||||
'message', check=pred, timeout=45
|
||||
)
|
||||
message = await self.bot.wait_for("message", check=pred, timeout=45)
|
||||
except asyncio.TimeoutError:
|
||||
await author.send(
|
||||
_("You took too long to select. Try again later.")
|
||||
)
|
||||
await author.send(_("You took too long to select. Try again later."))
|
||||
return None
|
||||
|
||||
try:
|
||||
@@ -187,35 +175,31 @@ class Reports:
|
||||
if await self.bot.embed_requested(channel, author):
|
||||
em = discord.Embed(description=report)
|
||||
em.set_author(
|
||||
name=_('Report from {0.display_name}').format(author),
|
||||
icon_url=author.avatar_url
|
||||
name=_("Report from {0.display_name}").format(author), icon_url=author.avatar_url
|
||||
)
|
||||
em.set_footer(text=_("Report #{}").format(ticket_number))
|
||||
send_content = None
|
||||
else:
|
||||
em = None
|
||||
send_content = _(
|
||||
'Report from {author.mention} (Ticket #{number})'
|
||||
).format(author=author, number=ticket_number)
|
||||
send_content = _("Report from {author.mention} (Ticket #{number})").format(
|
||||
author=author, number=ticket_number
|
||||
)
|
||||
send_content += "\n" + report
|
||||
|
||||
try:
|
||||
await Tunnel.message_forwarder(
|
||||
destination=channel,
|
||||
content=send_content,
|
||||
embed=em,
|
||||
files=files
|
||||
destination=channel, content=send_content, embed=em, files=files
|
||||
)
|
||||
except (discord.Forbidden, discord.HTTPException):
|
||||
return None
|
||||
|
||||
await self.config.custom('REPORT', guild.id, ticket_number).report.set(
|
||||
{'user_id': author.id, 'report': report}
|
||||
await self.config.custom("REPORT", guild.id, ticket_number).report.set(
|
||||
{"user_id": author.id, "report": report}
|
||||
)
|
||||
return ticket_number
|
||||
|
||||
@commands.group(name="report", invoke_without_command=True)
|
||||
async def report(self, ctx: commands.Context, *, _report: str=""):
|
||||
async def report(self, ctx: commands.Context, *, _report: str = ""):
|
||||
"""
|
||||
Follow the prompts to make a report
|
||||
|
||||
@@ -226,8 +210,7 @@ class Reports:
|
||||
guild = ctx.guild
|
||||
if guild is None:
|
||||
guild = await self.discover_guild(
|
||||
author,
|
||||
prompt=_("Select a server to make a report in by number.")
|
||||
author, prompt=_("Select a server to make a report in by number.")
|
||||
)
|
||||
else:
|
||||
try:
|
||||
@@ -238,24 +221,23 @@ class Reports:
|
||||
return
|
||||
g_active = await self.config.guild(guild).active()
|
||||
if not g_active:
|
||||
return await author.send(
|
||||
_("Reporting has not been enabled for this server")
|
||||
)
|
||||
return await author.send(_("Reporting has not been enabled for this server"))
|
||||
if guild.id not in self.antispam:
|
||||
self.antispam[guild.id] = {}
|
||||
if author.id not in self.antispam[guild.id]:
|
||||
self.antispam[guild.id][author.id] = AntiSpam(self.intervals)
|
||||
if self.antispam[guild.id][author.id].spammy:
|
||||
return await author.send(
|
||||
_("You've sent a few too many of these recently. "
|
||||
"Contact a server admin to resolve this, or try again "
|
||||
"later.")
|
||||
_(
|
||||
"You've sent a few too many of these recently. "
|
||||
"Contact a server admin to resolve this, or try again "
|
||||
"later."
|
||||
)
|
||||
)
|
||||
|
||||
if author.id in self.user_cache:
|
||||
return await author.send(
|
||||
_("Finish making your prior report "
|
||||
"before making an additional one")
|
||||
_("Finish making your prior report " "before making an additional one")
|
||||
)
|
||||
|
||||
if ctx.guild:
|
||||
@@ -273,13 +255,13 @@ class Reports:
|
||||
else:
|
||||
try:
|
||||
dm = await author.send(
|
||||
_("Please respond to this message with your Report."
|
||||
"\nYour report should be a single message")
|
||||
_(
|
||||
"Please respond to this message with your Report."
|
||||
"\nYour report should be a single message"
|
||||
)
|
||||
)
|
||||
except discord.Forbidden:
|
||||
await ctx.send(
|
||||
_("This requires DMs enabled.")
|
||||
)
|
||||
await ctx.send(_("This requires DMs enabled."))
|
||||
self.user_cache.remove(author.id)
|
||||
return
|
||||
|
||||
@@ -287,25 +269,17 @@ class Reports:
|
||||
return m.author == author and m.channel == dm.channel
|
||||
|
||||
try:
|
||||
message = await self.bot.wait_for(
|
||||
'message', check=pred, timeout=180
|
||||
)
|
||||
message = await self.bot.wait_for("message", check=pred, timeout=180)
|
||||
except asyncio.TimeoutError:
|
||||
await author.send(
|
||||
_("You took too long. Try again later.")
|
||||
)
|
||||
await author.send(_("You took too long. Try again later."))
|
||||
else:
|
||||
val = await self.send_report(message, guild)
|
||||
|
||||
with contextlib.suppress(discord.Forbidden, discord.HTTPException):
|
||||
if val is None:
|
||||
await author.send(
|
||||
_("There was an error sending your report.")
|
||||
)
|
||||
await author.send(_("There was an error sending your report."))
|
||||
else:
|
||||
await author.send(
|
||||
_("Your report was submitted. (Ticket #{})").format(val)
|
||||
)
|
||||
await author.send(_("Your report was submitted. (Ticket #{})").format(val))
|
||||
self.antispam[guild.id][author.id].stamp()
|
||||
|
||||
self.user_cache.remove(author.id)
|
||||
@@ -318,18 +292,14 @@ class Reports:
|
||||
return
|
||||
|
||||
_id = payload.message_id
|
||||
t = next(filter(
|
||||
lambda x: _id in x[1]['msgs'],
|
||||
self.tunnel_store.items()
|
||||
), None)
|
||||
t = next(filter(lambda x: _id in x[1]["msgs"], self.tunnel_store.items()), None)
|
||||
|
||||
if t is None:
|
||||
return
|
||||
tun = t[1]['tun']
|
||||
tun = t[1]["tun"]
|
||||
if payload.user_id in [x.id for x in tun.members]:
|
||||
await tun.react_close(
|
||||
uid=payload.user_id,
|
||||
message=_("{closer} has closed the correspondence")
|
||||
uid=payload.user_id, message=_("{closer} has closed the correspondence")
|
||||
)
|
||||
self.tunnel_store.pop(t[0], None)
|
||||
|
||||
@@ -337,12 +307,12 @@ class Reports:
|
||||
for k, v in self.tunnel_store.items():
|
||||
topic = _("Re: ticket# {1} in {0.name}").format(*k)
|
||||
# Tunnels won't forward unintended messages, this is safe
|
||||
msgs = await v['tun'].communicate(message=message, topic=topic)
|
||||
msgs = await v["tun"].communicate(message=message, topic=topic)
|
||||
if msgs:
|
||||
self.tunnel_store[k]['msgs'] = msgs
|
||||
self.tunnel_store[k]["msgs"] = msgs
|
||||
|
||||
@checks.mod_or_permissions(manage_members=True)
|
||||
@report.command(name='interact')
|
||||
@report.command(name="interact")
|
||||
async def response(self, ctx, ticket_number: int):
|
||||
"""
|
||||
opens a message tunnel between things you say in this channel
|
||||
@@ -353,27 +323,24 @@ class Reports:
|
||||
|
||||
# note, mod_or_permissions is an implicit guild_only
|
||||
guild = ctx.guild
|
||||
rec = await self.config.custom(
|
||||
'REPORT', guild.id, ticket_number).report()
|
||||
rec = await self.config.custom("REPORT", guild.id, ticket_number).report()
|
||||
|
||||
try:
|
||||
user = guild.get_member(rec.get('user_id'))
|
||||
user = guild.get_member(rec.get("user_id"))
|
||||
except KeyError:
|
||||
return await ctx.send(
|
||||
_("That ticket doesn't seem to exist")
|
||||
)
|
||||
return await ctx.send(_("That ticket doesn't seem to exist"))
|
||||
|
||||
if user is None:
|
||||
return await ctx.send(
|
||||
_("That user isn't here anymore.")
|
||||
)
|
||||
return await ctx.send(_("That user isn't here anymore."))
|
||||
|
||||
tun = Tunnel(recipient=user, origin=ctx.channel, sender=ctx.author)
|
||||
|
||||
if tun is None:
|
||||
return await ctx.send(
|
||||
_("Either you or the user you are trying to reach already "
|
||||
"has an open communication.")
|
||||
_(
|
||||
"Either you or the user you are trying to reach already "
|
||||
"has an open communication."
|
||||
)
|
||||
)
|
||||
|
||||
big_topic = _(
|
||||
@@ -387,18 +354,13 @@ class Reports:
|
||||
"\nTunnels are not persistent across bot restarts."
|
||||
)
|
||||
topic = big_topic.format(
|
||||
ticketnum=ticket_number,
|
||||
who=_("A moderator in `{guild.name}` has").format(guild=guild)
|
||||
ticketnum=ticket_number, who=_("A moderator in `{guild.name}` has").format(guild=guild)
|
||||
)
|
||||
try:
|
||||
m = await tun.communicate(
|
||||
message=ctx.message, topic=topic, skip_message_content=True
|
||||
)
|
||||
m = await tun.communicate(message=ctx.message, topic=topic, skip_message_content=True)
|
||||
except discord.Forbidden:
|
||||
await ctx.send(_("User has disabled DMs."))
|
||||
tun.close()
|
||||
else:
|
||||
self.tunnel_store[(guild, ticket_number)] = {'tun': tun, 'msgs': m}
|
||||
await ctx.send(
|
||||
big_topic.format(who=_("You have"), ticketnum=ticket_number)
|
||||
)
|
||||
self.tunnel_store[(guild, ticket_number)] = {"tun": tun, "msgs": m}
|
||||
await ctx.send(big_topic.format(who=_("You have"), ticketnum=ticket_number))
|
||||
|
||||
@@ -27,4 +27,4 @@ class OfflineStream(StreamsError):
|
||||
|
||||
|
||||
class OfflineCommunity(StreamsError):
|
||||
pass
|
||||
pass
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../mod.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../mod.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -3,9 +3,24 @@ from redbot.core import Config, checks, commands
|
||||
from redbot.core.utils.chat_formatting import pagify
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
from .streamtypes import TwitchStream, HitboxStream, MixerStream, PicartoStream, TwitchCommunity, YoutubeStream
|
||||
from .errors import (OfflineStream, StreamNotFound, APIError, InvalidYoutubeCredentials,
|
||||
CommunityNotFound, OfflineCommunity, StreamsError, InvalidTwitchCredentials)
|
||||
from .streamtypes import (
|
||||
TwitchStream,
|
||||
HitboxStream,
|
||||
MixerStream,
|
||||
PicartoStream,
|
||||
TwitchCommunity,
|
||||
YoutubeStream,
|
||||
)
|
||||
from .errors import (
|
||||
OfflineStream,
|
||||
StreamNotFound,
|
||||
APIError,
|
||||
InvalidYoutubeCredentials,
|
||||
CommunityNotFound,
|
||||
OfflineCommunity,
|
||||
StreamsError,
|
||||
InvalidTwitchCredentials,
|
||||
)
|
||||
from . import streamtypes as StreamClasses
|
||||
from collections import defaultdict
|
||||
import asyncio
|
||||
@@ -20,21 +35,11 @@ _ = Translator("Streams", __file__)
|
||||
@cog_i18n(_)
|
||||
class Streams:
|
||||
|
||||
global_defaults = {
|
||||
"tokens": {},
|
||||
"streams": [],
|
||||
"communities": []
|
||||
}
|
||||
global_defaults = {"tokens": {}, "streams": [], "communities": []}
|
||||
|
||||
guild_defaults = {
|
||||
"autodelete": False,
|
||||
"mention_everyone": False,
|
||||
"mention_here": False
|
||||
}
|
||||
guild_defaults = {"autodelete": False, "mention_everyone": False, "mention_here": False}
|
||||
|
||||
role_defaults = {
|
||||
"mention": False
|
||||
}
|
||||
role_defaults = {"mention": False}
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.db = Config.get_conf(self, 26262626)
|
||||
@@ -67,8 +72,7 @@ class Streams:
|
||||
async def twitch(self, ctx: commands.Context, channel_name: str):
|
||||
"""Checks if a Twitch channel is streaming"""
|
||||
token = await self.db.tokens.get_raw(TwitchStream.__name__, default=None)
|
||||
stream = TwitchStream(name=channel_name,
|
||||
token=token)
|
||||
stream = TwitchStream(name=channel_name, token=token)
|
||||
await self.check_online(ctx, stream)
|
||||
|
||||
@commands.command()
|
||||
@@ -110,14 +114,21 @@ class Streams:
|
||||
except StreamNotFound:
|
||||
await ctx.send(_("The channel doesn't seem to exist."))
|
||||
except InvalidTwitchCredentials:
|
||||
await ctx.send(_("The twitch token is either invalid or has not been set. "
|
||||
"See `{}`.").format("{}streamset twitchtoken".format(ctx.prefix)))
|
||||
await ctx.send(
|
||||
_("The twitch token is either invalid or has not been set. " "See `{}`.").format(
|
||||
"{}streamset twitchtoken".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
except InvalidYoutubeCredentials:
|
||||
await ctx.send(_("The Youtube API key is either invalid or has not been set. "
|
||||
"See {}.").format("`{}streamset youtubekey`".format(ctx.prefix)))
|
||||
await ctx.send(
|
||||
_("The Youtube API key is either invalid or has not been set. " "See {}.").format(
|
||||
"`{}streamset youtubekey`".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
except APIError:
|
||||
await ctx.send(_("Something went wrong whilst trying to contact the "
|
||||
"stream service's API."))
|
||||
await ctx.send(
|
||||
_("Something went wrong whilst trying to contact the " "stream service's API.")
|
||||
)
|
||||
else:
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@@ -166,7 +177,7 @@ class Streams:
|
||||
await self.stream_alert(ctx, PicartoStream, channel_name)
|
||||
|
||||
@streamalert.command(name="stop")
|
||||
async def streamalert_stop(self, ctx: commands.Context, _all: bool=False):
|
||||
async def streamalert_stop(self, ctx: commands.Context, _all: bool = False):
|
||||
"""Stops all stream notifications in the channel
|
||||
|
||||
Adding 'yes' will disable all notifications in the server"""
|
||||
@@ -191,8 +202,9 @@ class Streams:
|
||||
self.streams = streams
|
||||
await self.save_streams()
|
||||
|
||||
msg = _("All {}'s stream alerts have been disabled."
|
||||
"").format("server" if _all else "channel")
|
||||
msg = _("All {}'s stream alerts have been disabled." "").format(
|
||||
"server" if _all else "channel"
|
||||
)
|
||||
|
||||
await ctx.send(msg)
|
||||
|
||||
@@ -226,23 +238,29 @@ class Streams:
|
||||
if is_yt and not self.check_name_or_id(channel_name):
|
||||
stream = _class(id=channel_name, token=token)
|
||||
else:
|
||||
stream = _class(name=channel_name,
|
||||
token=token)
|
||||
stream = _class(name=channel_name, token=token)
|
||||
try:
|
||||
exists = await self.check_exists(stream)
|
||||
except InvalidTwitchCredentials:
|
||||
await ctx.send(
|
||||
_("The twitch token is either invalid or has not been set. "
|
||||
"See {}.").format("`{}streamset twitchtoken`".format(ctx.prefix)))
|
||||
_("The twitch token is either invalid or has not been set. " "See {}.").format(
|
||||
"`{}streamset twitchtoken`".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
return
|
||||
except InvalidYoutubeCredentials:
|
||||
await ctx.send(_("The Youtube API key is either invalid or has not been set. "
|
||||
"See {}.").format("`{}streamset youtubekey`".format(ctx.prefix)))
|
||||
await ctx.send(
|
||||
_(
|
||||
"The Youtube API key is either invalid or has not been set. " "See {}."
|
||||
).format(
|
||||
"`{}streamset youtubekey`".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
return
|
||||
except APIError:
|
||||
await ctx.send(
|
||||
_("Something went wrong whilst trying to contact the "
|
||||
"stream service's API."))
|
||||
_("Something went wrong whilst trying to contact the " "stream service's API.")
|
||||
)
|
||||
return
|
||||
else:
|
||||
if not exists:
|
||||
@@ -260,16 +278,18 @@ class Streams:
|
||||
await community.get_community_streams()
|
||||
except InvalidTwitchCredentials:
|
||||
await ctx.send(
|
||||
_("The twitch token is either invalid or has not been set. "
|
||||
"See {}.").format("`{}streamset twitchtoken`".format(ctx.prefix)))
|
||||
_("The twitch token is either invalid or has not been set. " "See {}.").format(
|
||||
"`{}streamset twitchtoken`".format(ctx.prefix)
|
||||
)
|
||||
)
|
||||
return
|
||||
except CommunityNotFound:
|
||||
await ctx.send(_("That community doesn't seem to exist."))
|
||||
return
|
||||
except APIError:
|
||||
await ctx.send(
|
||||
_("Something went wrong whilst trying to contact the "
|
||||
"stream service's API."))
|
||||
_("Something went wrong whilst trying to contact the " "stream service's API.")
|
||||
)
|
||||
return
|
||||
except OfflineCommunity:
|
||||
pass
|
||||
@@ -331,12 +351,21 @@ class Streams:
|
||||
current_setting = await self.db.guild(guild).mention_everyone()
|
||||
if current_setting:
|
||||
await self.db.guild(guild).mention_everyone.set(False)
|
||||
await ctx.send(_("{} will no longer be mentioned "
|
||||
"for a stream alert.").format("@\u200beveryone"))
|
||||
await ctx.send(
|
||||
_("{} will no longer be mentioned " "for a stream alert.").format(
|
||||
"@\u200beveryone"
|
||||
)
|
||||
)
|
||||
else:
|
||||
await self.db.guild(guild).mention_everyone.set(True)
|
||||
await ctx.send(_("When a stream configured for stream alerts "
|
||||
"comes online, {} will be mentioned").format("@\u200beveryone"))
|
||||
await ctx.send(
|
||||
_(
|
||||
"When a stream configured for stream alerts "
|
||||
"comes online, {} will be mentioned"
|
||||
).format(
|
||||
"@\u200beveryone"
|
||||
)
|
||||
)
|
||||
|
||||
@mention.command(aliases=["here"])
|
||||
@commands.guild_only()
|
||||
@@ -346,12 +375,19 @@ class Streams:
|
||||
current_setting = await self.db.guild(guild).mention_here()
|
||||
if current_setting:
|
||||
await self.db.guild(guild).mention_here.set(False)
|
||||
await ctx.send(_("{} will no longer be mentioned "
|
||||
"for a stream alert.").format("@\u200bhere"))
|
||||
await ctx.send(
|
||||
_("{} will no longer be mentioned " "for a stream alert.").format("@\u200bhere")
|
||||
)
|
||||
else:
|
||||
await self.db.guild(guild).mention_here.set(True)
|
||||
await ctx.send(_("When a stream configured for stream alerts "
|
||||
"comes online, {} will be mentioned").format("@\u200bhere"))
|
||||
await ctx.send(
|
||||
_(
|
||||
"When a stream configured for stream alerts "
|
||||
"comes online, {} will be mentioned"
|
||||
).format(
|
||||
"@\u200bhere"
|
||||
)
|
||||
)
|
||||
|
||||
@mention.command()
|
||||
@commands.guild_only()
|
||||
@@ -363,13 +399,22 @@ class Streams:
|
||||
return
|
||||
if current_setting:
|
||||
await self.db.role(role).mention.set(False)
|
||||
await ctx.send(_("{} will no longer be mentioned "
|
||||
"for a stream alert").format("@\u200b{}".format(role.name)))
|
||||
await ctx.send(
|
||||
_("{} will no longer be mentioned " "for a stream alert").format(
|
||||
"@\u200b{}".format(role.name)
|
||||
)
|
||||
)
|
||||
else:
|
||||
await self.db.role(role).mention.set(True)
|
||||
await ctx.send(_("When a stream configured for stream alerts "
|
||||
"comes online, {} will be mentioned"
|
||||
"").format("@\u200b{}".format(role.name)))
|
||||
await ctx.send(
|
||||
_(
|
||||
"When a stream configured for stream alerts "
|
||||
"comes online, {} will be mentioned"
|
||||
""
|
||||
).format(
|
||||
"@\u200b{}".format(role.name)
|
||||
)
|
||||
)
|
||||
|
||||
@streamset.command()
|
||||
@commands.guild_only()
|
||||
@@ -377,8 +422,7 @@ class Streams:
|
||||
"""Toggles automatic deletion of notifications for streams that go offline"""
|
||||
await self.db.guild(ctx.guild).autodelete.set(on_off)
|
||||
if on_off:
|
||||
await ctx.send("The notifications will be deleted once "
|
||||
"streams go offline.")
|
||||
await ctx.send("The notifications will be deleted once " "streams go offline.")
|
||||
else:
|
||||
await ctx.send("Notifications will never be deleted.")
|
||||
|
||||
@@ -387,14 +431,20 @@ class Streams:
|
||||
stream.channels.append(ctx.channel.id)
|
||||
if stream not in self.streams:
|
||||
self.streams.append(stream)
|
||||
await ctx.send(_("I'll send a notification in this channel when {} "
|
||||
"is online.").format(stream.name))
|
||||
await ctx.send(
|
||||
_("I'll send a notification in this channel when {} " "is online.").format(
|
||||
stream.name
|
||||
)
|
||||
)
|
||||
else:
|
||||
stream.channels.remove(ctx.channel.id)
|
||||
if not stream.channels:
|
||||
self.streams.remove(stream)
|
||||
await ctx.send(_("I won't send notifications about {} in this "
|
||||
"channel anymore.").format(stream.name))
|
||||
await ctx.send(
|
||||
_("I won't send notifications about {} in this " "channel anymore.").format(
|
||||
stream.name
|
||||
)
|
||||
)
|
||||
|
||||
await self.save_streams()
|
||||
|
||||
@@ -403,16 +453,28 @@ class Streams:
|
||||
community.channels.append(ctx.channel.id)
|
||||
if community not in self.communities:
|
||||
self.communities.append(community)
|
||||
await ctx.send(_("I'll send a notification in this channel when a "
|
||||
"channel is streaming to the {} community"
|
||||
"").format(community.name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"I'll send a notification in this channel when a "
|
||||
"channel is streaming to the {} community"
|
||||
""
|
||||
).format(
|
||||
community.name
|
||||
)
|
||||
)
|
||||
else:
|
||||
community.channels.remove(ctx.channel.id)
|
||||
if not community.channels:
|
||||
self.communities.remove(community)
|
||||
await ctx.send(_("I won't send notifications about channels streaming "
|
||||
"to the {} community in this channel anymore"
|
||||
"").format(community.name))
|
||||
await ctx.send(
|
||||
_(
|
||||
"I won't send notifications about channels streaming "
|
||||
"to the {} community in this channel anymore"
|
||||
""
|
||||
).format(
|
||||
community.name
|
||||
)
|
||||
)
|
||||
await self.save_communities()
|
||||
|
||||
def get_stream(self, _class, name):
|
||||
@@ -499,13 +561,13 @@ class Streams:
|
||||
settings = self.db.guild(guild)
|
||||
mentions = []
|
||||
if await settings.mention_everyone():
|
||||
mentions.append('@everyone')
|
||||
mentions.append("@everyone")
|
||||
if await settings.mention_here():
|
||||
mentions.append('@here')
|
||||
mentions.append("@here")
|
||||
for role in guild.roles:
|
||||
if await self.db.role(role).mention():
|
||||
mentions.append(role.mention)
|
||||
return ' '.join(mentions)
|
||||
return " ".join(mentions)
|
||||
|
||||
async def check_communities(self):
|
||||
for community in self.communities:
|
||||
@@ -579,8 +641,7 @@ class Streams:
|
||||
# Fast dedupe below
|
||||
seen = set()
|
||||
seen_add = seen.add
|
||||
return [x for x in streams
|
||||
if not (x.name.lower() in seen or seen_add(x.name.lower()))]
|
||||
return [x for x in streams if not (x.name.lower() in seen or seen_add(x.name.lower()))]
|
||||
|
||||
# return streams
|
||||
|
||||
@@ -604,8 +665,7 @@ class Streams:
|
||||
# Fast dedupe below
|
||||
seen = set()
|
||||
seen_add = seen.add
|
||||
return [x for x in communities
|
||||
if not (x.name.lower() in seen or seen_add(x.name.lower()))]
|
||||
return [x for x in communities if not (x.name.lower() in seen or seen_add(x.name.lower()))]
|
||||
# return communities
|
||||
|
||||
async def save_streams(self):
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
from .errors import StreamNotFound, APIError, OfflineStream, CommunityNotFound, OfflineCommunity, \
|
||||
InvalidYoutubeCredentials, InvalidTwitchCredentials
|
||||
from .errors import (
|
||||
StreamNotFound,
|
||||
APIError,
|
||||
OfflineStream,
|
||||
CommunityNotFound,
|
||||
OfflineCommunity,
|
||||
InvalidYoutubeCredentials,
|
||||
InvalidTwitchCredentials,
|
||||
)
|
||||
from random import choice, sample
|
||||
from string import ascii_letters
|
||||
import discord
|
||||
@@ -23,6 +30,7 @@ def rnd(url):
|
||||
|
||||
|
||||
class TwitchCommunity:
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.name = kwargs.pop("name")
|
||||
self.id = kwargs.pop("id", None)
|
||||
@@ -32,15 +40,12 @@ class TwitchCommunity:
|
||||
self.type = self.__class__.__name__
|
||||
|
||||
async def get_community_id(self):
|
||||
headers = {
|
||||
"Accept": "application/vnd.twitchtv.v5+json",
|
||||
"Client-ID": str(self._token)
|
||||
}
|
||||
params = {
|
||||
"name": self.name
|
||||
}
|
||||
headers = {"Accept": "application/vnd.twitchtv.v5+json", "Client-ID": str(self._token)}
|
||||
params = {"name": self.name}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(TWITCH_COMMUNITIES_ENDPOINT, headers=headers, params=params) as r:
|
||||
async with session.get(
|
||||
TWITCH_COMMUNITIES_ENDPOINT, headers=headers, params=params
|
||||
) as r:
|
||||
data = await r.json()
|
||||
if r.status == 200:
|
||||
return data["_id"]
|
||||
@@ -57,14 +62,8 @@ class TwitchCommunity:
|
||||
self.id = await self.get_community_id()
|
||||
except CommunityNotFound:
|
||||
raise
|
||||
headers = {
|
||||
"Accept": "application/vnd.twitchtv.v5+json",
|
||||
"Client-ID": str(self._token)
|
||||
}
|
||||
params = {
|
||||
"community_id": self.id,
|
||||
"limit": 100
|
||||
}
|
||||
headers = {"Accept": "application/vnd.twitchtv.v5+json", "Client-ID": str(self._token)}
|
||||
params = {"community_id": self.id, "limit": 100}
|
||||
url = TWITCH_BASE_URL + "/kraken/streams"
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url, headers=headers, params=params) as r:
|
||||
@@ -82,14 +81,11 @@ class TwitchCommunity:
|
||||
raise APIError()
|
||||
|
||||
async def make_embed(self, streams: list) -> discord.Embed:
|
||||
headers = {
|
||||
"Accept": "application/vnd.twitchtv.v5+json",
|
||||
"Client-ID": str(self._token)
|
||||
}
|
||||
headers = {"Accept": "application/vnd.twitchtv.v5+json", "Client-ID": str(self._token)}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(
|
||||
"{}/{}".format(TWITCH_COMMUNITIES_ENDPOINT, self.id),
|
||||
headers=headers) as r:
|
||||
"{}/{}".format(TWITCH_COMMUNITIES_ENDPOINT, self.id), headers=headers
|
||||
) as r:
|
||||
data = await r.json()
|
||||
|
||||
avatar = data["avatar_image_url"]
|
||||
@@ -102,9 +98,7 @@ class TwitchCommunity:
|
||||
else:
|
||||
stream_list = streams
|
||||
for stream in stream_list:
|
||||
name = "[{}]({})".format(
|
||||
stream["channel"]["display_name"], stream["channel"]["url"]
|
||||
)
|
||||
name = "[{}]({})".format(stream["channel"]["display_name"], stream["channel"]["url"])
|
||||
embed.add_field(name=stream["channel"]["status"], value=name, inline=False)
|
||||
embed.color = 0x6441A4
|
||||
|
||||
@@ -125,10 +119,11 @@ class TwitchCommunity:
|
||||
|
||||
|
||||
class Stream:
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.name = kwargs.pop("name", None)
|
||||
self.channels = kwargs.pop("channels", [])
|
||||
#self.already_online = kwargs.pop("already_online", False)
|
||||
# self.already_online = kwargs.pop("already_online", False)
|
||||
self._messages_cache = kwargs.pop("_messages_cache", [])
|
||||
self.type = self.__class__.__name__
|
||||
|
||||
@@ -153,6 +148,7 @@ class Stream:
|
||||
|
||||
|
||||
class YoutubeStream(Stream):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.id = kwargs.pop("id", None)
|
||||
self._token = kwargs.pop("token", None)
|
||||
@@ -167,7 +163,7 @@ class YoutubeStream(Stream):
|
||||
"part": "snippet",
|
||||
"channelId": self.id,
|
||||
"type": "video",
|
||||
"eventType": "live"
|
||||
"eventType": "live",
|
||||
}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url, params=params) as r:
|
||||
@@ -176,11 +172,7 @@ class YoutubeStream(Stream):
|
||||
raise OfflineStream()
|
||||
elif "items" in data:
|
||||
vid_id = data["items"][0]["id"]["videoId"]
|
||||
params = {
|
||||
"key": self._token,
|
||||
"id": vid_id,
|
||||
"part": "snippet"
|
||||
}
|
||||
params = {"key": self._token, "id": vid_id, "part": "snippet"}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(YOUTUBE_VIDEOS_ENDPOINT, params=params) as r:
|
||||
data = await r.json()
|
||||
@@ -199,17 +191,16 @@ class YoutubeStream(Stream):
|
||||
return embed
|
||||
|
||||
async def fetch_id(self):
|
||||
params = {
|
||||
"key": self._token,
|
||||
"forUsername": self.name,
|
||||
"part": "id"
|
||||
}
|
||||
params = {"key": self._token, "forUsername": self.name, "part": "id"}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(YOUTUBE_CHANNELS_ENDPOINT, params=params) as r:
|
||||
data = await r.json()
|
||||
|
||||
if "error" in data and data["error"]["code"] == 400 and\
|
||||
data["error"]["errors"][0]["reason"] == "keyInvalid":
|
||||
if (
|
||||
"error" in data
|
||||
and data["error"]["code"] == 400
|
||||
and data["error"]["errors"][0]["reason"] == "keyInvalid"
|
||||
):
|
||||
raise InvalidYoutubeCredentials()
|
||||
elif "items" in data and len(data["items"]) == 0:
|
||||
raise StreamNotFound()
|
||||
@@ -222,6 +213,7 @@ class YoutubeStream(Stream):
|
||||
|
||||
|
||||
class TwitchStream(Stream):
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.id = kwargs.pop("id", None)
|
||||
self._token = kwargs.pop("token", None)
|
||||
@@ -232,19 +224,16 @@ class TwitchStream(Stream):
|
||||
self.id = await self.fetch_id()
|
||||
|
||||
url = TWITCH_STREAMS_ENDPOINT + self.id
|
||||
header = {
|
||||
'Client-ID': str(self._token),
|
||||
'Accept': 'application/vnd.twitchtv.v5+json'
|
||||
}
|
||||
header = {"Client-ID": str(self._token), "Accept": "application/vnd.twitchtv.v5+json"}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url, headers=header) as r:
|
||||
data = await r.json(encoding='utf-8')
|
||||
data = await r.json(encoding="utf-8")
|
||||
if r.status == 200:
|
||||
if data["stream"] is None:
|
||||
#self.already_online = False
|
||||
# self.already_online = False
|
||||
raise OfflineStream()
|
||||
#self.already_online = True
|
||||
# self.already_online = True
|
||||
# In case of rename
|
||||
self.name = data["stream"]["channel"]["name"]
|
||||
return self.make_embed(data)
|
||||
@@ -256,10 +245,7 @@ class TwitchStream(Stream):
|
||||
raise APIError()
|
||||
|
||||
async def fetch_id(self):
|
||||
header = {
|
||||
'Client-ID': str(self._token),
|
||||
'Accept': 'application/vnd.twitchtv.v5+json'
|
||||
}
|
||||
header = {"Client-ID": str(self._token), "Accept": "application/vnd.twitchtv.v5+json"}
|
||||
url = TWITCH_ID_ENDPOINT + self.name
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
@@ -280,8 +266,7 @@ class TwitchStream(Stream):
|
||||
url = channel["url"]
|
||||
logo = channel["logo"]
|
||||
if logo is None:
|
||||
logo = ("https://static-cdn.jtvnw.net/"
|
||||
"jtv_user_pictures/xarth/404_user_70x70.png")
|
||||
logo = ("https://static-cdn.jtvnw.net/" "jtv_user_pictures/xarth/404_user_70x70.png")
|
||||
status = channel["status"]
|
||||
if not status:
|
||||
status = "Untitled broadcast"
|
||||
@@ -303,21 +288,22 @@ class TwitchStream(Stream):
|
||||
|
||||
|
||||
class HitboxStream(Stream):
|
||||
|
||||
async def is_online(self):
|
||||
url = "https://api.hitbox.tv/media/live/" + self.name
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as r:
|
||||
#data = await r.json(encoding='utf-8')
|
||||
# data = await r.json(encoding='utf-8')
|
||||
data = await r.text()
|
||||
data = json.loads(data, strict=False)
|
||||
if "livestream" not in data:
|
||||
raise StreamNotFound()
|
||||
elif data["livestream"][0]["media_is_live"] == "0":
|
||||
#self.already_online = False
|
||||
# self.already_online = False
|
||||
raise OfflineStream()
|
||||
elif data["livestream"][0]["media_is_live"] == "1":
|
||||
#self.already_online = True
|
||||
# self.already_online = True
|
||||
return self.make_embed(data)
|
||||
|
||||
raise APIError()
|
||||
@@ -340,20 +326,21 @@ class HitboxStream(Stream):
|
||||
|
||||
|
||||
class MixerStream(Stream):
|
||||
|
||||
async def is_online(self):
|
||||
url = "https://mixer.com/api/v1/channels/" + self.name
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as r:
|
||||
#data = await r.json(encoding='utf-8')
|
||||
data = await r.text(encoding='utf-8')
|
||||
# data = await r.json(encoding='utf-8')
|
||||
data = await r.text(encoding="utf-8")
|
||||
if r.status == 200:
|
||||
data = json.loads(data, strict=False)
|
||||
if data["online"] is True:
|
||||
#self.already_online = True
|
||||
# self.already_online = True
|
||||
return self.make_embed(data)
|
||||
else:
|
||||
#self.already_online = False
|
||||
# self.already_online = False
|
||||
raise OfflineStream()
|
||||
elif r.status == 404:
|
||||
raise StreamNotFound()
|
||||
@@ -361,8 +348,7 @@ class MixerStream(Stream):
|
||||
raise APIError()
|
||||
|
||||
def make_embed(self, data):
|
||||
default_avatar = ("https://mixer.com/_latest/assets/images/main/"
|
||||
"avatars/default.jpg")
|
||||
default_avatar = ("https://mixer.com/_latest/assets/images/main/" "avatars/default.jpg")
|
||||
user = data["user"]
|
||||
url = "https://mixer.com/" + data["token"]
|
||||
embed = discord.Embed(title=data["name"], url=url)
|
||||
@@ -382,19 +368,20 @@ class MixerStream(Stream):
|
||||
|
||||
|
||||
class PicartoStream(Stream):
|
||||
|
||||
async def is_online(self):
|
||||
url = "https://api.picarto.tv/v1/channel/name/" + self.name
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(url) as r:
|
||||
data = await r.text(encoding='utf-8')
|
||||
data = await r.text(encoding="utf-8")
|
||||
if r.status == 200:
|
||||
data = json.loads(data)
|
||||
if data["online"] is True:
|
||||
#self.already_online = True
|
||||
# self.already_online = True
|
||||
return self.make_embed(data)
|
||||
else:
|
||||
#self.already_online = False
|
||||
# self.already_online = False
|
||||
raise OfflineStream()
|
||||
elif r.status == 404:
|
||||
raise StreamNotFound()
|
||||
@@ -402,8 +389,9 @@ class PicartoStream(Stream):
|
||||
raise APIError()
|
||||
|
||||
def make_embed(self, data):
|
||||
avatar = rnd("https://picarto.tv/user_data/usrimg/{}/dsdefault.jpg"
|
||||
"".format(data["name"].lower()))
|
||||
avatar = rnd(
|
||||
"https://picarto.tv/user_data/usrimg/{}/dsdefault.jpg" "".format(data["name"].lower())
|
||||
)
|
||||
url = "https://picarto.tv/" + data["name"]
|
||||
thumbnail = data["thumbnails"]["web"]
|
||||
embed = discord.Embed(title=data["title"], url=url)
|
||||
@@ -424,6 +412,5 @@ class PicartoStream(Stream):
|
||||
data["adult"] = ""
|
||||
|
||||
embed.color = 0x4C90F3
|
||||
embed.set_footer(text="{adult}Category: {category} | Tags: {tags}"
|
||||
"".format(**data))
|
||||
embed.set_footer(text="{adult}Category: {category} | Tags: {tags}" "".format(**data))
|
||||
return embed
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../mod.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../mod.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
regen_messages()
|
||||
regen_messages()
|
||||
|
||||
@@ -10,11 +10,13 @@ from .log import LOG
|
||||
|
||||
__all__ = ["TriviaSession"]
|
||||
|
||||
_REVEAL_MESSAGES = ("I know this one! {}!", "Easy: {}.",
|
||||
"Oh really? It's {} of course.")
|
||||
_FAIL_MESSAGES = ("To the next one I guess...", "Moving on...",
|
||||
"I'm sure you'll know the answer of the next one.",
|
||||
"\N{PENSIVE FACE} Next one.")
|
||||
_REVEAL_MESSAGES = ("I know this one! {}!", "Easy: {}.", "Oh really? It's {} of course.")
|
||||
_FAIL_MESSAGES = (
|
||||
"To the next one I guess...",
|
||||
"Moving on...",
|
||||
"I'm sure you'll know the answer of the next one.",
|
||||
"\N{PENSIVE FACE} Next one.",
|
||||
)
|
||||
|
||||
|
||||
class TriviaSession():
|
||||
@@ -49,10 +51,7 @@ class TriviaSession():
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
ctx,
|
||||
question_list: dict,
|
||||
settings: dict):
|
||||
def __init__(self, ctx, question_list: dict, settings: dict):
|
||||
self.ctx = ctx
|
||||
list_ = list(question_list.items())
|
||||
random.shuffle(list_)
|
||||
@@ -128,9 +127,9 @@ class TriviaSession():
|
||||
num_lists = len(list_names)
|
||||
if num_lists > 2:
|
||||
# at least 3 lists, join all but last with comma
|
||||
msg = ", ".join(list_names[:num_lists-1])
|
||||
msg = ", ".join(list_names[:num_lists - 1])
|
||||
# join onto last with "and"
|
||||
msg = " and ".join((msg, list_names[num_lists-1]))
|
||||
msg = " and ".join((msg, list_names[num_lists - 1]))
|
||||
else:
|
||||
# either 1 or 2 lists, join together with "and"
|
||||
msg = " and ".join(list_names)
|
||||
@@ -150,10 +149,7 @@ class TriviaSession():
|
||||
answers = _parse_answers(answers)
|
||||
yield question, answers
|
||||
|
||||
async def wait_for_answer(self,
|
||||
answers,
|
||||
delay: float,
|
||||
timeout: float):
|
||||
async def wait_for_answer(self, answers, delay: float, timeout: float):
|
||||
"""Wait for a correct answer, and then respond.
|
||||
|
||||
Scores are also updated in this method.
|
||||
@@ -178,7 +174,8 @@ class TriviaSession():
|
||||
"""
|
||||
try:
|
||||
message = await self.ctx.bot.wait_for(
|
||||
"message", check=self.check_answer(answers), timeout=delay)
|
||||
"message", check=self.check_answer(answers), timeout=delay
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
if time.time() - self._last_response >= timeout:
|
||||
await self.ctx.send("Guys...? Well, I guess I'll stop then.")
|
||||
@@ -194,8 +191,7 @@ class TriviaSession():
|
||||
await self.ctx.send(reply)
|
||||
else:
|
||||
self.scores[message.author] += 1
|
||||
reply = "You got it {}! **+1** to you!".format(
|
||||
message.author.display_name)
|
||||
reply = "You got it {}! **+1** to you!".format(message.author.display_name)
|
||||
await self.ctx.send(reply)
|
||||
return True
|
||||
|
||||
@@ -218,9 +214,11 @@ class TriviaSession():
|
||||
|
||||
"""
|
||||
answers = tuple(s.lower() for s in answers)
|
||||
|
||||
def _pred(message: discord.Message):
|
||||
early_exit = (message.channel != self.ctx.channel
|
||||
or message.author == self.ctx.guild.me)
|
||||
early_exit = (
|
||||
message.channel != self.ctx.channel or message.author == self.ctx.guild.me
|
||||
)
|
||||
if early_exit:
|
||||
return False
|
||||
|
||||
@@ -260,8 +258,7 @@ class TriviaSession():
|
||||
"""Cancel whichever tasks this session is running."""
|
||||
self._task.cancel()
|
||||
channel = self.ctx.channel
|
||||
LOG.debug("Force stopping trivia session; #%s in %s", channel,
|
||||
channel.guild.id)
|
||||
LOG.debug("Force stopping trivia session; #%s in %s", channel, channel.guild.id)
|
||||
|
||||
async def pay_winner(self, multiplier: float):
|
||||
"""Pay the winner of this trivia session.
|
||||
@@ -275,8 +272,7 @@ class TriviaSession():
|
||||
paid.
|
||||
|
||||
"""
|
||||
(winner, score) = next((tup for tup in self.scores.most_common(1)),
|
||||
(None, None))
|
||||
(winner, score) = next((tup for tup in self.scores.most_common(1)), (None, None))
|
||||
me_ = self.ctx.guild.me
|
||||
if winner is not None and winner != me_ and score > 0:
|
||||
contestants = list(self.scores.keys())
|
||||
@@ -285,13 +281,12 @@ class TriviaSession():
|
||||
if len(contestants) >= 3:
|
||||
amount = int(multiplier * score)
|
||||
if amount > 0:
|
||||
LOG.debug("Paying trivia winner: %d credits --> %s",
|
||||
amount, str(winner))
|
||||
LOG.debug("Paying trivia winner: %d credits --> %s", amount, str(winner))
|
||||
await deposit_credits(winner, int(multiplier * score))
|
||||
await self.ctx.send(
|
||||
"Congratulations, {0}, you have received {1} credits"
|
||||
" for coming first.".format(winner.display_name,
|
||||
amount))
|
||||
" for coming first.".format(winner.display_name, amount)
|
||||
)
|
||||
|
||||
|
||||
def _parse_answers(answers):
|
||||
|
||||
@@ -26,8 +26,7 @@ class Trivia:
|
||||
|
||||
def __init__(self):
|
||||
self.trivia_sessions = []
|
||||
self.conf = Config.get_conf(
|
||||
self, identifier=UNIQUE_ID, force_registration=True)
|
||||
self.conf = Config.get_conf(self, identifier=UNIQUE_ID, force_registration=True)
|
||||
|
||||
self.conf.register_guild(
|
||||
max_score=10,
|
||||
@@ -36,10 +35,10 @@ class Trivia:
|
||||
bot_plays=False,
|
||||
reveal_answer=True,
|
||||
payout_multiplier=0.0,
|
||||
allow_override=True)
|
||||
allow_override=True,
|
||||
)
|
||||
|
||||
self.conf.register_member(
|
||||
wins=0, games=0, total_score=0)
|
||||
self.conf.register_member(wins=0, games=0, total_score=0)
|
||||
|
||||
@commands.group()
|
||||
@commands.guild_only()
|
||||
@@ -60,7 +59,8 @@ class Trivia:
|
||||
"Payout multiplier: {payout_multiplier}\n"
|
||||
"Allow lists to override settings: {allow_override}"
|
||||
"".format(**settings_dict),
|
||||
lang="py")
|
||||
lang="py",
|
||||
)
|
||||
await ctx.send(msg)
|
||||
|
||||
@triviaset.command(name="maxscore")
|
||||
@@ -81,8 +81,7 @@ class Trivia:
|
||||
return
|
||||
settings = self.conf.guild(ctx.guild)
|
||||
await settings.delay.set(seconds)
|
||||
await ctx.send("Done. Maximum seconds to answer set to {}."
|
||||
"".format(seconds))
|
||||
await ctx.send("Done. Maximum seconds to answer set to {}." "".format(seconds))
|
||||
|
||||
@triviaset.command(name="stopafter")
|
||||
async def triviaset_stopafter(self, ctx: commands.Context, seconds: float):
|
||||
@@ -92,38 +91,41 @@ class Trivia:
|
||||
await ctx.send("Must be larger than the answer time limit.")
|
||||
return
|
||||
await settings.timeout.set(seconds)
|
||||
await ctx.send("Done. Trivia sessions will now time out after {}"
|
||||
" seconds of no responses.".format(seconds))
|
||||
await ctx.send(
|
||||
"Done. Trivia sessions will now time out after {}"
|
||||
" seconds of no responses.".format(seconds)
|
||||
)
|
||||
|
||||
@triviaset.command(name="override")
|
||||
async def triviaset_allowoverride(self,
|
||||
ctx: commands.Context,
|
||||
enabled: bool):
|
||||
async def triviaset_allowoverride(self, ctx: commands.Context, enabled: bool):
|
||||
"""Allow/disallow trivia lists to override settings."""
|
||||
settings = self.conf.guild(ctx.guild)
|
||||
await settings.allow_override.set(enabled)
|
||||
enabled = "now" if enabled else "no longer"
|
||||
await ctx.send("Done. Trivia lists can {} override the trivia settings"
|
||||
" for this server.".format(enabled))
|
||||
await ctx.send(
|
||||
"Done. Trivia lists can {} override the trivia settings"
|
||||
" for this server.".format(enabled)
|
||||
)
|
||||
|
||||
@triviaset.command(name="botplays")
|
||||
async def trivaset_bot_plays(self,
|
||||
ctx: commands.Context,
|
||||
true_or_false: bool):
|
||||
async def trivaset_bot_plays(self, ctx: commands.Context, true_or_false: bool):
|
||||
"""Set whether or not the bot gains points.
|
||||
|
||||
If enabled, the bot will gain a point if no one guesses correctly.
|
||||
"""
|
||||
settings = self.conf.guild(ctx.guild)
|
||||
await settings.bot_plays.set(true_or_false)
|
||||
await ctx.send("Done. " + (
|
||||
"I'll gain a point if users don't answer in time." if true_or_false
|
||||
else "Alright, I won't embarass you at trivia anymore."))
|
||||
await ctx.send(
|
||||
"Done. "
|
||||
+ (
|
||||
"I'll gain a point if users don't answer in time."
|
||||
if true_or_false
|
||||
else "Alright, I won't embarass you at trivia anymore."
|
||||
)
|
||||
)
|
||||
|
||||
@triviaset.command(name="revealanswer")
|
||||
async def trivaset_reveal_answer(self,
|
||||
ctx: commands.Context,
|
||||
true_or_false: bool):
|
||||
async def trivaset_reveal_answer(self, ctx: commands.Context, true_or_false: bool):
|
||||
"""Set whether or not the answer is revealed.
|
||||
|
||||
If enabled, the bot will reveal the answer if no one guesses correctly
|
||||
@@ -131,15 +133,18 @@ class Trivia:
|
||||
"""
|
||||
settings = self.conf.guild(ctx.guild)
|
||||
await settings.reveal_answer.set(true_or_false)
|
||||
await ctx.send("Done. " + (
|
||||
"I'll reveal the answer if no one knows it." if true_or_false else
|
||||
"I won't reveal the answer to the questions anymore."))
|
||||
await ctx.send(
|
||||
"Done. "
|
||||
+ (
|
||||
"I'll reveal the answer if no one knows it."
|
||||
if true_or_false
|
||||
else "I won't reveal the answer to the questions anymore."
|
||||
)
|
||||
)
|
||||
|
||||
@triviaset.command(name="payout")
|
||||
@check_global_setting_admin()
|
||||
async def triviaset_payout_multiplier(self,
|
||||
ctx: commands.Context,
|
||||
multiplier: float):
|
||||
async def triviaset_payout_multiplier(self, ctx: commands.Context, multiplier: float):
|
||||
"""Set the payout multiplier.
|
||||
|
||||
This can be any positive decimal number. If a user wins trivia when at
|
||||
@@ -155,8 +160,7 @@ class Trivia:
|
||||
return
|
||||
await settings.payout_multiplier.set(multiplier)
|
||||
if not multiplier:
|
||||
await ctx.send("Done. I will no longer reward the winner with a"
|
||||
" payout.")
|
||||
await ctx.send("Done. I will no longer reward the winner with a" " payout.")
|
||||
return
|
||||
await ctx.send("Done. Payout multiplier set to {}.".format(multiplier))
|
||||
|
||||
@@ -174,8 +178,7 @@ class Trivia:
|
||||
categories = [c.lower() for c in categories]
|
||||
session = self._get_trivia_session(ctx.channel)
|
||||
if session is not None:
|
||||
await ctx.send(
|
||||
"There is already an ongoing trivia session in this channel.")
|
||||
await ctx.send("There is already an ongoing trivia session in this channel.")
|
||||
return
|
||||
trivia_dict = {}
|
||||
authors = []
|
||||
@@ -185,21 +188,26 @@ class Trivia:
|
||||
try:
|
||||
dict_ = self.get_trivia_list(category)
|
||||
except FileNotFoundError:
|
||||
await ctx.send("Invalid category `{0}`. See `{1}trivia list`"
|
||||
" for a list of trivia categories."
|
||||
"".format(category, ctx.prefix))
|
||||
await ctx.send(
|
||||
"Invalid category `{0}`. See `{1}trivia list`"
|
||||
" for a list of trivia categories."
|
||||
"".format(category, ctx.prefix)
|
||||
)
|
||||
except InvalidListError:
|
||||
await ctx.send("There was an error parsing the trivia list for"
|
||||
" the `{}` category. It may be formatted"
|
||||
" incorrectly.".format(category))
|
||||
await ctx.send(
|
||||
"There was an error parsing the trivia list for"
|
||||
" the `{}` category. It may be formatted"
|
||||
" incorrectly.".format(category)
|
||||
)
|
||||
else:
|
||||
trivia_dict.update(dict_)
|
||||
authors.append(trivia_dict.pop("AUTHOR", None))
|
||||
continue
|
||||
return
|
||||
if not trivia_dict:
|
||||
await ctx.send("The trivia list was parsed successfully, however"
|
||||
" it appears to be empty!")
|
||||
await ctx.send(
|
||||
"The trivia list was parsed successfully, however" " it appears to be empty!"
|
||||
)
|
||||
return
|
||||
settings = await self.conf.guild(ctx.guild).all()
|
||||
config = trivia_dict.pop("CONFIG", None)
|
||||
@@ -215,13 +223,16 @@ class Trivia:
|
||||
"""Stop an ongoing trivia session."""
|
||||
session = self._get_trivia_session(ctx.channel)
|
||||
if session is None:
|
||||
await ctx.send(
|
||||
"There is no ongoing trivia session in this channel.")
|
||||
await ctx.send("There is no ongoing trivia session in this channel.")
|
||||
return
|
||||
author = ctx.author
|
||||
auth_checks = (await ctx.bot.is_owner(author), await
|
||||
ctx.bot.is_mod(author), await ctx.bot.is_admin(author),
|
||||
author == ctx.guild.owner, author == session.ctx.author)
|
||||
auth_checks = (
|
||||
await ctx.bot.is_owner(author),
|
||||
await ctx.bot.is_mod(author),
|
||||
await ctx.bot.is_admin(author),
|
||||
author == ctx.guild.owner,
|
||||
author == session.ctx.author,
|
||||
)
|
||||
if any(auth_checks):
|
||||
await session.end_game()
|
||||
session.force_stop()
|
||||
@@ -234,8 +245,7 @@ class Trivia:
|
||||
"""List available trivia categories."""
|
||||
lists = set(p.stem for p in self._all_lists())
|
||||
|
||||
msg = box("**Available trivia lists**\n\n{}"
|
||||
"".format(", ".join(sorted(lists))))
|
||||
msg = box("**Available trivia lists**\n\n{}" "".format(", ".join(sorted(lists))))
|
||||
if len(msg) > 1000:
|
||||
await ctx.author.send(msg)
|
||||
return
|
||||
@@ -256,10 +266,9 @@ class Trivia:
|
||||
|
||||
@trivia_leaderboard.command(name="server")
|
||||
@commands.guild_only()
|
||||
async def trivia_leaderboard_server(self,
|
||||
ctx: commands.Context,
|
||||
sort_by: str="wins",
|
||||
top: int=10):
|
||||
async def trivia_leaderboard_server(
|
||||
self, ctx: commands.Context, sort_by: str = "wins", top: int = 10
|
||||
):
|
||||
"""Leaderboard for this server.
|
||||
|
||||
<sort_by> can be any of the following fields:
|
||||
@@ -271,9 +280,11 @@ class Trivia:
|
||||
"""
|
||||
key = self._get_sort_key(sort_by)
|
||||
if key is None:
|
||||
await ctx.send("Unknown field `{}`, see `{}help trivia "
|
||||
"leaderboard server` for valid fields to sort by."
|
||||
"".format(sort_by, ctx.prefix))
|
||||
await ctx.send(
|
||||
"Unknown field `{}`, see `{}help trivia "
|
||||
"leaderboard server` for valid fields to sort by."
|
||||
"".format(sort_by, ctx.prefix)
|
||||
)
|
||||
return
|
||||
guild = ctx.guild
|
||||
data = await self.conf.all_members(guild)
|
||||
@@ -282,10 +293,9 @@ class Trivia:
|
||||
await self.send_leaderboard(ctx, data, key, top)
|
||||
|
||||
@trivia_leaderboard.command(name="global")
|
||||
async def trivia_leaderboard_global(self,
|
||||
ctx: commands.Context,
|
||||
sort_by: str="wins",
|
||||
top: int=10):
|
||||
async def trivia_leaderboard_global(
|
||||
self, ctx: commands.Context, sort_by: str = "wins", top: int = 10
|
||||
):
|
||||
"""Global trivia leaderboard.
|
||||
|
||||
<sort_by> can be any of the following fields:
|
||||
@@ -298,9 +308,11 @@ class Trivia:
|
||||
"""
|
||||
key = self._get_sort_key(sort_by)
|
||||
if key is None:
|
||||
await ctx.send("Unknown field `{}`, see `{}help trivia "
|
||||
"leaderboard global` for valid fields to sort by."
|
||||
"".format(sort_by, ctx.prefix))
|
||||
await ctx.send(
|
||||
"Unknown field `{}`, see `{}help trivia "
|
||||
"leaderboard global` for valid fields to sort by."
|
||||
"".format(sort_by, ctx.prefix)
|
||||
)
|
||||
return
|
||||
data = await self.conf.all_members()
|
||||
collated_data = {}
|
||||
@@ -327,11 +339,7 @@ class Trivia:
|
||||
elif key in ("total", "score", "answers", "correct"):
|
||||
return "total_score"
|
||||
|
||||
async def send_leaderboard(self,
|
||||
ctx: commands.Context,
|
||||
data: dict,
|
||||
key: str,
|
||||
top: int):
|
||||
async def send_leaderboard(self, ctx: commands.Context, data: dict, key: str, top: int):
|
||||
"""Send the leaderboard from the given data.
|
||||
|
||||
Parameters
|
||||
@@ -382,23 +390,34 @@ class Trivia:
|
||||
items = sorted(items, key=lambda t: t[1][key], reverse=True)
|
||||
max_name_len = max(map(lambda m: len(str(m)), data.keys()))
|
||||
# Headers
|
||||
headers = ("Rank", "Member{}".format(" " * (max_name_len - 6)), "Wins",
|
||||
"Games Played", "Total Score", "Average Score")
|
||||
headers = (
|
||||
"Rank",
|
||||
"Member{}".format(" " * (max_name_len - 6)),
|
||||
"Wins",
|
||||
"Games Played",
|
||||
"Total Score",
|
||||
"Average Score",
|
||||
)
|
||||
lines = [" | ".join(headers)]
|
||||
# Header underlines
|
||||
lines.append(" | ".join(("-" * len(h) for h in headers)))
|
||||
for rank, tup in enumerate(items, 1):
|
||||
member, m_data = tup
|
||||
# Align fields to header width
|
||||
fields = tuple(map(str, (rank,
|
||||
member,
|
||||
m_data["wins"],
|
||||
m_data["games"],
|
||||
m_data["total_score"],
|
||||
round(m_data["average_score"], 2))))
|
||||
padding = [
|
||||
" " * (len(h) - len(f)) for h, f in zip(headers, fields)
|
||||
]
|
||||
fields = tuple(
|
||||
map(
|
||||
str,
|
||||
(
|
||||
rank,
|
||||
member,
|
||||
m_data["wins"],
|
||||
m_data["games"],
|
||||
m_data["total_score"],
|
||||
round(m_data["average_score"], 2),
|
||||
),
|
||||
)
|
||||
)
|
||||
padding = [" " * (len(h) - len(f)) for h, f in zip(headers, fields)]
|
||||
fields = tuple(f + padding[i] for i, f in enumerate(fields))
|
||||
lines.append(" | ".join(fields).format(member=member, **m_data))
|
||||
if rank == top:
|
||||
@@ -418,8 +437,7 @@ class Trivia:
|
||||
|
||||
"""
|
||||
channel = session.ctx.channel
|
||||
LOG.debug("Ending trivia session; #%s in %s", channel,
|
||||
channel.guild.id)
|
||||
LOG.debug("Ending trivia session; #%s in %s", channel, channel.guild.id)
|
||||
if session in self.trivia_sessions:
|
||||
self.trivia_sessions.remove(session)
|
||||
if session.scores:
|
||||
@@ -462,10 +480,9 @@ class Trivia:
|
||||
try:
|
||||
path = next(p for p in self._all_lists() if p.stem == category)
|
||||
except StopIteration:
|
||||
raise FileNotFoundError("Could not find the `{}` category"
|
||||
"".format(category))
|
||||
raise FileNotFoundError("Could not find the `{}` category" "".format(category))
|
||||
|
||||
with path.open(encoding='utf-8') as file:
|
||||
with path.open(encoding="utf-8") as file:
|
||||
try:
|
||||
dict_ = yaml.load(file)
|
||||
except yaml.error.YAMLError as exc:
|
||||
@@ -473,14 +490,13 @@ class Trivia:
|
||||
else:
|
||||
return dict_
|
||||
|
||||
def _get_trivia_session(self,
|
||||
channel: discord.TextChannel) -> TriviaSession:
|
||||
return next((session for session in self.trivia_sessions
|
||||
if session.ctx.channel == channel), None)
|
||||
def _get_trivia_session(self, channel: discord.TextChannel) -> TriviaSession:
|
||||
return next(
|
||||
(session for session in self.trivia_sessions if session.ctx.channel == channel), None
|
||||
)
|
||||
|
||||
def _all_lists(self):
|
||||
personal_lists = tuple(p.resolve()
|
||||
for p in cog_data_path(self).glob("*.yaml"))
|
||||
personal_lists = tuple(p.resolve() for p in cog_data_path(self).glob("*.yaml"))
|
||||
|
||||
return personal_lists + tuple(ext_trivia.lists())
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ from redbot.core.i18n import Translator
|
||||
_ = Translator("Warnings", __file__)
|
||||
|
||||
|
||||
async def warning_points_add_check(config: Config, ctx: commands.Context, user: discord.Member, points: int):
|
||||
async def warning_points_add_check(
|
||||
config: Config, ctx: commands.Context, user: discord.Member, points: int
|
||||
):
|
||||
"""Handles any action that needs to be taken or not based on the points"""
|
||||
guild = ctx.guild
|
||||
guild_settings = config.guild(guild)
|
||||
@@ -24,7 +26,9 @@ async def warning_points_add_check(config: Config, ctx: commands.Context, user:
|
||||
await create_and_invoke_context(ctx, act["exceed_command"], user)
|
||||
|
||||
|
||||
async def warning_points_remove_check(config: Config, ctx: commands.Context, user: discord.Member, points: int):
|
||||
async def warning_points_remove_check(
|
||||
config: Config, ctx: commands.Context, user: discord.Member, points: int
|
||||
):
|
||||
guild = ctx.guild
|
||||
guild_settings = config.guild(guild)
|
||||
act = {}
|
||||
@@ -38,7 +42,9 @@ async def warning_points_remove_check(config: Config, ctx: commands.Context, use
|
||||
await create_and_invoke_context(ctx, act["drop_command"], user)
|
||||
|
||||
|
||||
async def create_and_invoke_context(realctx: commands.Context, command_str: str, user: discord.Member):
|
||||
async def create_and_invoke_context(
|
||||
realctx: commands.Context, command_str: str, user: discord.Member
|
||||
):
|
||||
m = copy(realctx.message)
|
||||
m.content = command_str.format(user=user.mention, prefix=realctx.prefix)
|
||||
fctx = await realctx.bot.get_context(m, cls=commands.Context)
|
||||
@@ -54,7 +60,7 @@ def get_command_from_input(bot, userinput: str):
|
||||
while com is None:
|
||||
com = bot.get_command(userinput)
|
||||
if com is None:
|
||||
userinput = ' '.join(userinput.split(' ')[:-1])
|
||||
userinput = " ".join(userinput.split(" ")[:-1])
|
||||
if len(userinput) == 0:
|
||||
break
|
||||
if com is None:
|
||||
@@ -63,8 +69,9 @@ def get_command_from_input(bot, userinput: str):
|
||||
check_str = inspect.getsource(checks.is_owner)
|
||||
if any(inspect.getsource(x) in check_str for x in com.checks):
|
||||
# command the user specified has the is_owner check
|
||||
return None, _("That command requires bot owner. I can't "
|
||||
"allow you to use that for an action")
|
||||
return None, _(
|
||||
"That command requires bot owner. I can't " "allow you to use that for an action"
|
||||
)
|
||||
return "{prefix}" + orig, None
|
||||
|
||||
|
||||
@@ -72,13 +79,15 @@ async def get_command_for_exceeded_points(ctx: commands.Context):
|
||||
"""Gets the command to be executed when the user is at or exceeding
|
||||
the points threshold for the action"""
|
||||
await ctx.send(
|
||||
_("Enter the command to be run when the user exceeds the points for "
|
||||
"this action to occur.\nEnter it exactly as you would if you were "
|
||||
"actually trying to run the command, except don't put a prefix and "
|
||||
"use {user} in place of any user/member arguments\n\n"
|
||||
"WARNING: The command entered will be run without regard to checks or cooldowns. "
|
||||
"Commands requiring bot owner are not allowed for security reasons.\n\n"
|
||||
"Please wait 15 seconds before entering your response.")
|
||||
_(
|
||||
"Enter the command to be run when the user exceeds the points for "
|
||||
"this action to occur.\nEnter it exactly as you would if you were "
|
||||
"actually trying to run the command, except don't put a prefix and "
|
||||
"use {user} in place of any user/member arguments\n\n"
|
||||
"WARNING: The command entered will be run without regard to checks or cooldowns. "
|
||||
"Commands requiring bot owner are not allowed for security reasons.\n\n"
|
||||
"Please wait 15 seconds before entering your response."
|
||||
)
|
||||
)
|
||||
await asyncio.sleep(15)
|
||||
|
||||
@@ -110,15 +119,17 @@ async def get_command_for_dropping_points(ctx: commands.Context):
|
||||
when the user exceeded the threshold
|
||||
"""
|
||||
await ctx.send(
|
||||
_("Enter the command to be run when the user returns to a value below "
|
||||
"the points for this action to occur. Please note that this is "
|
||||
"intended to be used for reversal of the action taken when the user "
|
||||
"exceeded the action's point value\nEnter it exactly as you would "
|
||||
"if you were actually trying to run the command, except don't put a prefix "
|
||||
"and use {user} in place of any user/member arguments\n\n"
|
||||
"WARNING: The command entered will be run without regard to checks or cooldowns. "
|
||||
"Commands requiring bot owner are not allowed for security reasons.\n\n"
|
||||
"Please wait 15 seconds before entering your response.")
|
||||
_(
|
||||
"Enter the command to be run when the user returns to a value below "
|
||||
"the points for this action to occur. Please note that this is "
|
||||
"intended to be used for reversal of the action taken when the user "
|
||||
"exceeded the action's point value\nEnter it exactly as you would "
|
||||
"if you were actually trying to run the command, except don't put a prefix "
|
||||
"and use {user} in place of any user/member arguments\n\n"
|
||||
"WARNING: The command entered will be run without regard to checks or cooldowns. "
|
||||
"Commands requiring bot owner are not allowed for security reasons.\n\n"
|
||||
"Please wait 15 seconds before entering your response."
|
||||
)
|
||||
)
|
||||
await asyncio.sleep(15)
|
||||
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import subprocess
|
||||
|
||||
TO_TRANSLATE = [
|
||||
'../warnings.py',
|
||||
'../helpers.py'
|
||||
]
|
||||
TO_TRANSLATE = ["../warnings.py", "../helpers.py"]
|
||||
|
||||
|
||||
def regen_messages():
|
||||
subprocess.run(
|
||||
['pygettext', '-n'] + TO_TRANSLATE
|
||||
)
|
||||
subprocess.run(["pygettext", "-n"] + TO_TRANSLATE)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -3,8 +3,12 @@ from collections import namedtuple
|
||||
import discord
|
||||
import asyncio
|
||||
|
||||
from redbot.cogs.warnings.helpers import warning_points_add_check, get_command_for_exceeded_points, \
|
||||
get_command_for_dropping_points, warning_points_remove_check
|
||||
from redbot.cogs.warnings.helpers import (
|
||||
warning_points_add_check,
|
||||
get_command_for_exceeded_points,
|
||||
get_command_for_dropping_points,
|
||||
warning_points_remove_check,
|
||||
)
|
||||
from redbot.core import Config, modlog, checks, commands
|
||||
from redbot.core.bot import Red
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
@@ -18,17 +22,9 @@ _ = Translator("Warnings", __file__)
|
||||
class Warnings:
|
||||
"""A warning system for Red"""
|
||||
|
||||
default_guild = {
|
||||
"actions": [],
|
||||
"reasons": {},
|
||||
"allow_custom_reasons": False
|
||||
}
|
||||
default_guild = {"actions": [], "reasons": {}, "allow_custom_reasons": False}
|
||||
|
||||
default_member = {
|
||||
"total_points": 0,
|
||||
"status": "",
|
||||
"warnings": {}
|
||||
}
|
||||
default_member = {"total_points": 0, "status": "", "warnings": {}}
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
self.config = Config.get_conf(self, identifier=5757575755)
|
||||
@@ -41,9 +37,7 @@ class Warnings:
|
||||
@staticmethod
|
||||
async def register_warningtype():
|
||||
try:
|
||||
await modlog.register_casetype(
|
||||
"warning", True, "\N{WARNING SIGN}", "Warning", None
|
||||
)
|
||||
await modlog.register_casetype("warning", True, "\N{WARNING SIGN}", "Warning", None)
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
@@ -105,7 +99,7 @@ class Warnings:
|
||||
"action_name": name,
|
||||
"points": points,
|
||||
"exceed_command": exceed_command,
|
||||
"drop_command": drop_command
|
||||
"drop_command": drop_command,
|
||||
}
|
||||
|
||||
# Have all details for the action, now save the action
|
||||
@@ -138,9 +132,7 @@ class Warnings:
|
||||
registered_actions.remove(to_remove)
|
||||
await ctx.tick()
|
||||
else:
|
||||
await ctx.send(
|
||||
_("No action named {} exists!").format(action_name)
|
||||
)
|
||||
await ctx.send(_("No action named {} exists!").format(action_name))
|
||||
|
||||
@commands.group()
|
||||
@commands.guild_only()
|
||||
@@ -159,13 +151,8 @@ class Warnings:
|
||||
if name.lower() == "custom":
|
||||
await ctx.send("That cannot be used as a reason name!")
|
||||
return
|
||||
to_add = {
|
||||
"points": points,
|
||||
"description": description
|
||||
}
|
||||
completed = {
|
||||
name.lower(): to_add
|
||||
}
|
||||
to_add = {"points": points, "description": description}
|
||||
completed = {name.lower(): to_add}
|
||||
|
||||
guild_settings = self.config.guild(guild)
|
||||
|
||||
@@ -219,8 +206,7 @@ class Warnings:
|
||||
msg_list.append(
|
||||
"Name: {}\nPoints: {}\nExceed command: {}\n"
|
||||
"Drop command: {}".format(
|
||||
r["action_name"], r["points"], r["exceed_command"],
|
||||
r["drop_command"]
|
||||
r["action_name"], r["points"], r["exceed_command"], r["drop_command"]
|
||||
)
|
||||
)
|
||||
if msg_list:
|
||||
@@ -262,7 +248,7 @@ class Warnings:
|
||||
str(ctx.message.id): {
|
||||
"points": reason_type["points"],
|
||||
"description": reason_type["description"],
|
||||
"mod": ctx.author.id
|
||||
"mod": ctx.author.id,
|
||||
}
|
||||
}
|
||||
async with member_settings.warnings() as user_warnings:
|
||||
@@ -275,7 +261,7 @@ class Warnings:
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
async def warnings(self, ctx: commands.Context, userid: int=None):
|
||||
async def warnings(self, ctx: commands.Context, userid: int = None):
|
||||
"""Show warnings for the specified user.
|
||||
If userid is None, show warnings for the person running the command
|
||||
Note that showing warnings for users other than yourself requires
|
||||
@@ -285,10 +271,7 @@ class Warnings:
|
||||
else:
|
||||
if not await is_admin_or_superior(self.bot, ctx.author):
|
||||
await ctx.send(
|
||||
warning(
|
||||
_("You are not allowed to check "
|
||||
"warnings for other users!")
|
||||
)
|
||||
warning(_("You are not allowed to check " "warnings for other users!"))
|
||||
)
|
||||
return
|
||||
else:
|
||||
@@ -305,22 +288,14 @@ class Warnings:
|
||||
mod = ctx.guild.get_member(user_warnings[key]["mod"])
|
||||
if mod is None:
|
||||
mod = discord.utils.get(
|
||||
self.bot.get_all_members(),
|
||||
id=user_warnings[key]["mod"]
|
||||
self.bot.get_all_members(), id=user_warnings[key]["mod"]
|
||||
)
|
||||
if mod is None:
|
||||
mod = await self.bot.get_user_info(
|
||||
user_warnings[key]["mod"]
|
||||
)
|
||||
mod = await self.bot.get_user_info(user_warnings[key]["mod"])
|
||||
msg += "{} point warning {} issued by {} for {}\n".format(
|
||||
user_warnings[key]["points"],
|
||||
key,
|
||||
mod,
|
||||
user_warnings[key]["description"]
|
||||
user_warnings[key]["points"], key, mod, user_warnings[key]["description"]
|
||||
)
|
||||
await ctx.send_interactive(
|
||||
pagify(msg), box_lang="Warnings for {}".format(user)
|
||||
)
|
||||
await ctx.send_interactive(pagify(msg), box_lang="Warnings for {}".format(user))
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@@ -348,10 +323,7 @@ class Warnings:
|
||||
@staticmethod
|
||||
async def custom_warning_reason(ctx: commands.Context):
|
||||
"""Handles getting description and points for custom reasons"""
|
||||
to_add = {
|
||||
"points": 0,
|
||||
"description": ""
|
||||
}
|
||||
to_add = {"points": 0, "description": ""}
|
||||
|
||||
def same_author_check(m):
|
||||
return m.author == ctx.author
|
||||
|
||||
Reference in New Issue
Block a user