feat(moderation): updated the main file to use the config object from config.py

This commit is contained in:
Seaswimmer 2023-12-18 15:57:51 -05:00
parent 5fa346bc75
commit c7f7d29be5
Signed by untrusted user: cswimr
GPG key ID: 1EBC234EEDA901AE

View file

@ -9,17 +9,21 @@ import json
import time
import os
from datetime import datetime, timedelta, timezone
import discord
import humanize
import mysql.connector
from discord.ext import tasks
from pytimeparse2 import disable_dateutil, parse
from redbot.core import app_commands, checks, Config, commands, data_manager
from redbot.core import app_commands, checks, commands, data_manager
from redbot.core.app_commands import Choice
from .config import config, register_config
from .database import connect, create_guild_table, fetch_case, mysql_log
from .embed_factory import embed_factory
from .utils import check_conf, check_permissions, check_moddable, fetch_channel_dict, fetch_user_dict, generate_dict, log
from .logger import logger
from .utils import check_conf, check_permissions, check_moddable, fetch_channel_dict, fetch_user_dict, generate_dict, log
class Moderation(commands.Cog):
"""Custom moderation cog.
@ -27,7 +31,7 @@ class Moderation(commands.Cog):
async def red_delete_data_for_user(self, *, requester, user_id: int):
if requester == "discord_deleted_user":
await self.config.user_from_id(user_id).clear()
await config.user_from_id(user_id).clear()
database = await connect()
cursor = database.cursor()
@ -45,41 +49,17 @@ class Moderation(commands.Cog):
cursor.close()
database.close()
if requester == "owner":
await self.config.user_from_id(user_id).clear()
await config.user_from_id(user_id).clear()
if requester == "user":
await self.config.user_from_id(user_id).clear()
await config.user_from_id(user_id).clear()
if requester == "user_strict":
await self.config.user_from_id(user_id).clear()
await config.user_from_id(user_id).clear()
else:
logger.warning("Invalid requester passed to red_delete_data_for_user: %s", requester)
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=481923957134912)
self.config.register_global(
mysql_address= " ",
mysql_database = " ",
mysql_username = " ",
mysql_password = " "
)
self.config.register_guild(
use_discord_permissions = True,
ignore_other_bots = True,
dm_users = True,
log_channel = " ",
immune_roles = [],
history_ephemeral = False,
history_inline = False,
history_pagesize = 5,
history_inline_pagesize = 6,
blacklist_roles = []
)
self.config.register_user(
history_ephemeral = None,
history_inline = None,
history_pagesize = None,
history_inline_pagesize = None
)
register_config(config)
disable_dateutil()
self.handle_expiry.start() # pylint: disable=no-member
@ -134,7 +114,7 @@ class Moderation(commands.Cog):
async def autologger(self, entry: discord.AuditLogEntry):
"""This method automatically logs moderations done by users manually ("right clicks")."""
if not await self.bot.cog_disabled_in_guild(self, entry.guild):
if await self.config.guild(entry.guild).ignore_other_bots() is True:
if await config.guild(entry.guild).ignore_other_bots() is True:
if entry.user.bot or entry.target.bot:
return
else:
@ -194,7 +174,7 @@ class Moderation(commands.Cog):
await interaction.response.send_message(content=f"{target.mention} has recieved a note!\n**Reason** - `{reason}`")
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='note', response=await interaction.original_response())
@ -224,7 +204,7 @@ class Moderation(commands.Cog):
await interaction.response.send_message(content=f"{target.mention} has been warned!\n**Reason** - `{reason}`")
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='warned', response=await interaction.original_response())
@ -238,7 +218,7 @@ class Moderation(commands.Cog):
async def blacklist_autocomplete(self, interaction: discord.Interaction, current: str,) -> list[app_commands.Choice[str]]: # pylint: disable=unused-argument
"""Autocompletes a blacklist role."""
blacklist_roles = await self.config.guild(interaction.guild).blacklist_roles()
blacklist_roles = await config.guild(interaction.guild).blacklist_roles()
if blacklist_roles:
return [app_commands.Choice(name=role.name, value=role.id) for role in interaction.guild.roles if role.id in blacklist_roles]
@ -259,7 +239,7 @@ class Moderation(commands.Cog):
Why are you blacklisting this user?
silent: bool
Should the user be messaged?"""
blacklist_roles = await self.config.guild(interaction.guild).blacklist_roles()
blacklist_roles = await config.guild(interaction.guild).blacklist_roles()
if not blacklist_roles:
await interaction.response.send_message(content="There are no blacklist types set for this server!", ephemeral=True)
@ -284,7 +264,7 @@ class Moderation(commands.Cog):
return
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='blacklisted', response=await interaction.original_response(), duration=matching_role['duration'])
@ -336,7 +316,7 @@ class Moderation(commands.Cog):
await interaction.response.send_message(content=f"{target.mention} has been muted for {humanize.precisedelta(parsed_time)}!\n**Reason** - `{reason}`")
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='muted', response=await interaction.original_response(), duration=parsed_time)
@ -376,7 +356,7 @@ class Moderation(commands.Cog):
await interaction.response.send_message(content=f"{target.mention} has been unmuted!\n**Reason** - `{reason}`")
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='unmuted', response=await interaction.original_response())
@ -406,7 +386,7 @@ class Moderation(commands.Cog):
await interaction.response.send_message(content=f"{target.mention} has been kicked!\n**Reason** - `{reason}`")
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='kicked', response=await interaction.original_response())
@ -478,7 +458,7 @@ class Moderation(commands.Cog):
await interaction.response.send_message(content=f"{target.mention} has been banned!\n**Reason** - `{reason}`")
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='banned', response=await interaction.original_response())
@ -522,7 +502,7 @@ class Moderation(commands.Cog):
await interaction.response.send_message(content=f"{target.mention} has been unbanned!\n**Reason** - `{reason}`")
if silent is None:
silent = not await self.config.guild(interaction.guild).dm_users()
silent = not await config.guild(interaction.guild).dm_users()
if silent is False:
try:
embed = await embed_factory('message', await self.bot.get_embed_color(None), guild=interaction.guild, reason=reason, moderation_type='unbanned', response=await interaction.original_response())
@ -555,23 +535,23 @@ class Moderation(commands.Cog):
export: bool
Exports the server's entire moderation history to a JSON file"""
if ephemeral is None:
ephemeral = (await self.config.user(interaction.user).history_ephemeral()
or await self.config.guild(interaction.guild).history_ephemeral()
ephemeral = (await config.user(interaction.user).history_ephemeral()
or await config.guild(interaction.guild).history_ephemeral()
or False)
if inline is None:
inline = (await self.config.user(interaction.user).history_inline()
or await self.config.guild(interaction.guild).history_inline()
inline = (await config.user(interaction.user).history_inline()
or await config.guild(interaction.guild).history_inline()
or False)
if pagesize is None:
if inline is True:
pagesize = (await self.config.user(interaction.user).history_inline_pagesize()
or await self.config.guild(interaction.guild).history_inline_pagesize()
pagesize = (await config.user(interaction.user).history_inline_pagesize()
or await config.guild(interaction.guild).history_inline_pagesize()
or 6)
else:
pagesize = (await self.config.user(interaction.user).history_pagesize()
or await self.config.guild(interaction.guild).history_pagesize()
pagesize = (await config.user(interaction.user).history_pagesize()
or await config.guild(interaction.guild).history_pagesize()
or 5)
@ -699,7 +679,7 @@ class Moderation(commands.Cog):
database = await connect()
cursor = database.cursor()
db = await self.config.mysql_database()
db = await config.mysql_database()
query_1 = "SELECT * FROM moderation_%s WHERE moderation_id = %s;"
cursor.execute(query_1, (interaction.guild.id, case))
@ -799,8 +779,8 @@ class Moderation(commands.Cog):
return
if ephemeral is None:
ephemeral = (await self.config.user(interaction.user).history_ephemeral()
or await self.config.guild(interaction.guild).history_ephemeral()
ephemeral = (await config.user(interaction.user).history_ephemeral()
or await config.guild(interaction.guild).history_ephemeral()
or False)
if case != 0:
@ -918,7 +898,7 @@ class Moderation(commands.Cog):
database = await connect()
cursor = database.cursor()
db = await self.config.mysql_database()
db = await config.mysql_database()
if parsed_time:
update_query = f"UPDATE `{db}`.`moderation_{interaction.guild.id}` SET changes = %s, reason = %s, duration = %s, end_timestamp = %s WHERE moderation_id = %s"
@ -947,7 +927,7 @@ class Moderation(commands.Cog):
database = await connect()
cursor = database.cursor()
db = await self.config.mysql_database()
db = await config.mysql_database()
guilds: list[discord.Guild] = self.bot.guilds
for guild in guilds:
@ -1018,7 +998,7 @@ class Moderation(commands.Cog):
async def moderationset_list(self, ctx: commands.Context):
"""List all moderation settings."""
if ctx.guild:
guild_settings = await self.config.guild(ctx.guild).all()
guild_settings = await config.guild(ctx.guild).all()
guild_settings_string = ""
for setting in guild_settings:
@ -1030,7 +1010,7 @@ class Moderation(commands.Cog):
else:
guild_settings_string += f"**{setting}**: {guild_settings[setting]}\n"
user_settings = await self.config.user(ctx.author).all()
user_settings = await config.user(ctx.author).all()
user_settings_string = ""
for setting in user_settings:
user_settings_string += f"**{setting}**: {user_settings[setting]}\n"
@ -1050,7 +1030,7 @@ class Moderation(commands.Cog):
@moderationset_history.command(name='ephemeral', aliases=['hidden', 'hide'])
async def moderationset_history_user_ephemeral(self, ctx: commands.Context, enabled: bool):
"""Toggle if the /history command should be ephemeral."""
await self.config.user(ctx.author).history_ephemeral.set(enabled)
await config.user(ctx.author).history_ephemeral.set(enabled)
await ctx.send(f"Ephemeral setting set to {enabled}")
@moderationset_history.command(name='pagesize')
@ -1062,8 +1042,8 @@ class Moderation(commands.Cog):
if pagesize < 1:
await ctx.send("Pagesize cannot be less than 1!")
return
await self.config.user(ctx.author).history_pagesize.set(pagesize)
await ctx.send(f"Pagesize set to {await self.config.user(ctx.author).history_pagesize()}")
await config.user(ctx.author).history_pagesize.set(pagesize)
await ctx.send(f"Pagesize set to {await config.user(ctx.author).history_pagesize()}")
@moderationset_history.group(name='inline')
async def moderationset_history_inline(self, ctx: commands.Context):
@ -1072,7 +1052,7 @@ class Moderation(commands.Cog):
@moderationset_history_inline.command(name='toggle')
async def moderationset_history_user_inline_toggle(self, ctx: commands.Context, enabled: bool):
"""Enable the /history command's inline argument by default."""
await self.config.user(ctx.author).history_inline.set(enabled)
await config.user(ctx.author).history_inline.set(enabled)
await ctx.send(f"Inline setting set to {enabled}")
@moderationset_history_inline.command(name='pagesize')
@ -1084,8 +1064,8 @@ class Moderation(commands.Cog):
if pagesize < 1:
await ctx.send("Pagesize cannot be less than 1!")
return
await self.config.user(ctx.author).history_inline_pagesize.set(pagesize)
await ctx.send(f"Inline pagesize set to {await self.config.user(ctx.author).history_inline_pagesize()}")
await config.user(ctx.author).history_inline_pagesize.set(pagesize)
await ctx.send(f"Inline pagesize set to {await config.user(ctx.author).history_inline_pagesize()}")
@moderationset_history.group(autohelp=True, name='guild')
@checks.admin()
@ -1096,7 +1076,7 @@ class Moderation(commands.Cog):
@checks.admin()
async def moderationset_history_guild_ephemeral(self, ctx: commands.Context, enabled: bool):
"""Toggle if the /history command should be ephemeral."""
await self.config.guild(ctx.guild).history_ephemeral.set(enabled)
await config.guild(ctx.guild).history_ephemeral.set(enabled)
await ctx.send(f"Ephemeral setting set to {enabled}")
@moderationset_history_guild.command(name='pagesize')
@ -1109,8 +1089,8 @@ class Moderation(commands.Cog):
if pagesize < 1:
await ctx.send("Pagesize cannot be less than 1!")
return
await self.config.guild(ctx.guild).history_pagesize.set(pagesize)
await ctx.send(f"Pagesize set to {await self.config.guild(ctx.guild).history_pagesize()}")
await config.guild(ctx.guild).history_pagesize.set(pagesize)
await ctx.send(f"Pagesize set to {await config.guild(ctx.guild).history_pagesize()}")
@moderationset_history_guild.group(name='inline')
@checks.admin()
@ -1121,7 +1101,7 @@ class Moderation(commands.Cog):
@checks.admin()
async def moderationset_history_guild_inline_toggle(self, ctx: commands.Context, enabled: bool):
"""Enable the /history command's inline argument by default."""
await self.config.guild(ctx.guild).history_inline.set(enabled)
await config.guild(ctx.guild).history_inline.set(enabled)
await ctx.send(f"Inline setting set to {enabled}")
@moderationset_history_guild_inline.command(name='pagesize')
@ -1134,8 +1114,8 @@ class Moderation(commands.Cog):
if pagesize < 1:
await ctx.send("Pagesize cannot be less than 1!")
return
await self.config.guild(ctx.guild).history_inline_pagesize.set(pagesize)
await ctx.send(f"Inline pagesize set to {await self.config.guild(ctx.guild).history_inline_pagesize()}")
await config.guild(ctx.guild).history_inline_pagesize.set(pagesize)
await ctx.send(f"Inline pagesize set to {await config.guild(ctx.guild).history_inline_pagesize()}")
@moderationset.group(autohelp=True, name='immunity')
@checks.admin()
@ -1146,31 +1126,31 @@ class Moderation(commands.Cog):
@checks.admin()
async def moderationset_immunity_add(self, ctx: commands.Context, role: discord.Role):
"""Add a role to the immune roles list."""
immune_roles: list = await self.config.guild(ctx.guild).immune_roles()
immune_roles: list = await config.guild(ctx.guild).immune_roles()
if role.id in immune_roles:
await ctx.send("Role is already immune!")
return
immune_roles.append(role.id)
await self.config.guild(ctx.guild).immune_roles.set(immune_roles)
await config.guild(ctx.guild).immune_roles.set(immune_roles)
await ctx.send(f"Role {role.name} added to immune roles.")
@moderationset_immunity.command(name='remove')
@checks.admin()
async def moderationset_immunity_remove(self, ctx: commands.Context, role: discord.Role):
"""Remove a role from the immune roles list."""
immune_roles: list = await self.config.guild(ctx.guild).immune_roles()
immune_roles: list = await config.guild(ctx.guild).immune_roles()
if role.id not in immune_roles:
await ctx.send("Role is not immune!")
return
immune_roles.remove(role.id)
await self.config.guild(ctx.guild).immune_roles.set(immune_roles)
await config.guild(ctx.guild).immune_roles.set(immune_roles)
await ctx.send(f"Role {role.name} removed from immune roles.")
@moderationset_immunity.command(name='list')
@checks.admin()
async def moderationset_immunity_list(self, ctx: commands.Context):
"""List all immune roles."""
immune_roles: list = await self.config.guild(ctx.guild).immune_roles()
immune_roles: list = await config.guild(ctx.guild).immune_roles()
if not immune_roles:
await ctx.send("No immune roles set!")
return
@ -1192,7 +1172,7 @@ class Moderation(commands.Cog):
@checks.admin()
async def moderationset_blacklist_add(self, ctx: commands.Context, role: discord.Role, duration: str):
"""Add a role to the blacklist."""
blacklist_roles: list = await self.config.guild(ctx.guild).blacklist_roles()
blacklist_roles: list = await config.guild(ctx.guild).blacklist_roles()
for blacklist_role in blacklist_roles:
if role.id == blacklist_role['role']:
await ctx.send("Role already has an associated blacklist type!")
@ -1210,18 +1190,18 @@ class Moderation(commands.Cog):
'duration': str(parsed_time)
}
)
await self.config.guild(ctx.guild).blacklist_roles.set(blacklist_roles)
await config.guild(ctx.guild).blacklist_roles.set(blacklist_roles)
await ctx.send(f"Role {role.mention} added as a blacklist type.", allowed_mentions=discord.AllowedMentions.none())
@moderationset_blacklist.command(name='remove')
@checks.admin()
async def moderationset_blacklist_remove(self, ctx: commands.Context, role: discord.Role):
"""Remove a role's blacklist type."""
blacklist_roles: list = await self.config.guild(ctx.guild).blacklist_roles()
blacklist_roles: list = await config.guild(ctx.guild).blacklist_roles()
for blacklist_role in blacklist_roles:
if role.id == blacklist_role['role']:
blacklist_roles.remove(blacklist_role)
await self.config.guild(ctx.guild).blacklist_roles.set(blacklist_roles)
await config.guild(ctx.guild).blacklist_roles.set(blacklist_roles)
await ctx.send(f"Role {role.mention} removed from blacklist types.", allowed_mentions=discord.AllowedMentions.none())
return
await ctx.send("Role does not have an associated blacklist type!")
@ -1230,7 +1210,7 @@ class Moderation(commands.Cog):
@checks.admin()
async def moderationset_blacklist_list(self, ctx: commands.Context):
"""List all blacklist types."""
blacklist_roles: list = await self.config.guild(ctx.guild).blacklist_roles()
blacklist_roles: list = await config.guild(ctx.guild).blacklist_roles()
if not blacklist_roles:
await ctx.send("No blacklist types set!")
return
@ -1247,8 +1227,8 @@ class Moderation(commands.Cog):
@checks.admin()
async def moderationset_ignorebots(self, ctx: commands.Context):
"""Toggle if the cog should ignore other bots' moderations."""
await self.config.guild(ctx.guild).ignore_other_bots.set(not await self.config.guild(ctx.guild).ignore_other_bots())
await ctx.send(f"Ignore bots setting set to {await self.config.guild(ctx.guild).ignore_other_bots()}")
await config.guild(ctx.guild).ignore_other_bots.set(not await config.guild(ctx.guild).ignore_other_bots())
await ctx.send(f"Ignore bots setting set to {await config.guild(ctx.guild).ignore_other_bots()}")
@moderationset.command(name="dm")
@checks.admin()
@ -1256,25 +1236,25 @@ class Moderation(commands.Cog):
"""Toggle automatically messaging moderated users.
This option can be overridden by specifying the `silent` argument in any moderation command."""
await self.config.guild(ctx.guild).dm_users.set(not await self.config.guild(ctx.guild).dm_users())
await ctx.send(f"DM users setting set to {await self.config.guild(ctx.guild).dm_users()}")
await config.guild(ctx.guild).dm_users.set(not await config.guild(ctx.guild).dm_users())
await ctx.send(f"DM users setting set to {await config.guild(ctx.guild).dm_users()}")
@moderationset.command(name="permissions")
@checks.admin()
async def moderationset_permissions(self, ctx: commands.Context):
"""Toggle whether the bot will check for discord permissions."""
await self.config.guild(ctx.guild).use_discord_permissions.set(not await self.config.guild(ctx.guild).use_discord_permissions())
await ctx.send(f"Use Discord Permissions setting set to {await self.config.guild(ctx.guild).use_discord_permissions()}")
await config.guild(ctx.guild).use_discord_permissions.set(not await config.guild(ctx.guild).use_discord_permissions())
await ctx.send(f"Use Discord Permissions setting set to {await config.guild(ctx.guild).use_discord_permissions()}")
@moderationset.command(name="logchannel")
@checks.admin()
async def moderationset_logchannel(self, ctx: commands.Context, channel: discord.TextChannel = None):
"""Set a channel to log infractions to."""
if channel:
await self.config.guild(ctx.guild).log_channel.set(channel.id)
await config.guild(ctx.guild).log_channel.set(channel.id)
await ctx.send(f"Logging channel set to {channel.mention}.")
else:
await self.config.guild(ctx.guild).log_channel.set(" ")
await config.guild(ctx.guild).log_channel.set(" ")
await ctx.send("Logging channel disabled.")
@moderationset.command(name="mysql")
@ -1287,16 +1267,14 @@ class Moderation(commands.Cog):
class ConfigButtons(discord.ui.View):
def __init__(self, timeout):
super().__init__()
self.config = Config.get_conf(None, cog_name='Moderation', identifier=481923957134912)
@discord.ui.button(label="Edit", style=discord.ButtonStyle.success)
async def config_button(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument
await interaction.response.send_modal(Moderation.MySQLConfigModal(self.config))
await interaction.response.send_modal(Moderation.MySQLConfigModal)
class MySQLConfigModal(discord.ui.Modal, title="MySQL Database Configuration"):
def __init__(self, config):
def __init__(self):
super().__init__()
self.config = config
address = discord.ui.TextInput(
label="Address",
placeholder="Input your MySQL address here.",
@ -1330,25 +1308,25 @@ class Moderation(commands.Cog):
message = ""
if self.address.value != "":
await self.config.mysql_address.set(self.address.value)
await config.mysql_address.set(self.address.value)
message += f"- Address set to\n - `{self.address.value}`\n"
if self.database.value != "":
await self.config.mysql_database.set(self.database.value)
await config.mysql_database.set(self.database.value)
message += f"- Database set to\n - `{self.database.value}`\n"
if self.username.value != "":
await self.config.mysql_username.set(self.username.value)
await config.mysql_username.set(self.username.value)
message += f"- Username set to\n - `{self.username.value}`\n"
if self.password.value != "":
await self.config.mysql_password.set(self.password.value)
await config.mysql_password.set(self.password.value)
trimmed_password = self.password.value[:8]
message += f"- Password set to\n - `{trimmed_password}` - Trimmed for security\n"
if message == "":
trimmed_password = str(await self.config.mysql_password())[:8]
send = f"No changes were made.\nCurrent configuration:\n- Address:\n - `{await self.config.mysql_address()}`\n- Database:\n - `{await self.config.mysql_database()}`\n- Username:\n - `{await self.config.mysql_username()}`\n- Password:\n - `{trimmed_password}` - Trimmed for security"
trimmed_password = str(await config.mysql_password())[:8]
send = f"No changes were made.\nCurrent configuration:\n- Address:\n - `{await config.mysql_address()}`\n- Database:\n - `{await config.mysql_database()}`\n- Username:\n - `{await config.mysql_username()}`\n- Password:\n - `{trimmed_password}` - Trimmed for security"
else:
send = f"Configuration changed:\n{message}"
@ -1375,7 +1353,6 @@ class Moderation(commands.Cog):
super().__init__()
self.ctx: commands.Context = ctx
self.message: discord.Message = message
self.config = Config.get_conf(None, cog_name='Moderation', identifier=481923957134912)
@discord.ui.button(label="Yes", style=discord.ButtonStyle.success)
async def import_button_y(self, interaction: discord.Interaction, button: discord.ui.Button): # pylint: disable=unused-argument