diakonos/autorole/autorole.py

471 lines
18 KiB
Python
Raw Permalink Normal View History

2024-01-17 00:20:44 -05:00
import logging
import random
import string
from typing import Optional, cast
import discord
from redbot import VersionInfo, version_info
from redbot.core import Config, checks, commands
from redbot.core.i18n import Translator, cog_i18n
default_settings = {
"ENABLED": False,
"ROLE": [],
"AGREE_CHANNEL": None,
"AGREE_MSG": None,
"AGREE_KEY": None,
"DELETE_KEY": False,
}
log = logging.getLogger("red.Trusty-cogs.autorole")
_ = Translator("Autorole", __file__)
listener = getattr(commands.Cog, "listener", None) # red 3.0 backwards compatibility support
if listener is None: # thanks Sinbad
def listener(name=None):
return lambda x: x
@cog_i18n(_)
class Autorole(commands.Cog):
"""
Autorole commands. Rewritten for V3 from
https://github.com/Lunar-Dust/Dusty-Cogs/blob/master/autorole/autorole.py
"""
__author__ = ["Lunar-Dust", "TrustyJAID"]
__version__ = "1.3.2"
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, 45463543548)
self.config.register_guild(**default_settings)
self.users = {}
def format_help_for_context(self, ctx: commands.Context) -> str:
"""
Thanks Sinbad!
"""
pre_processed = super().format_help_for_context(ctx)
return f"{pre_processed}\n\nCog Version: {self.__version__}"
async def red_delete_data_for_user(self, **kwargs):
"""
Nothing to delete
"""
return
async def _no_perms(self, channel: Optional[discord.TextChannel] = None) -> None:
m = _(
"It appears that you haven't given this "
"bot enough permissions to use autorole. "
'The bot requires the "Manage Roles" and '
'the "Manage Messages" permissions in'
"order to use autorole. You can change the "
'permissions in the "Roles" tab of the '
"guild settings."
)
if channel is None:
log.info(m)
return
if channel.permissions_for(channel.guild.me).send_messages:
await channel.send(m)
else:
log.info(m + _("\n I also don't have permission to speak in #") + channel.name)
async def get_colour(self, channel: discord.TextChannel) -> discord.Colour:
try:
return await self.bot.get_embed_colour(channel)
except AttributeError:
if await self.bot.db.guild(channel.guild).use_bot_color():
return channel.guild.me.colour
else:
return await self.bot.db.color()
@commands.Cog.listener()
async def on_message(self, message: discord.Message) -> None:
guild = message.guild
if not guild:
return
if version_info >= VersionInfo.from_str("3.4.0"):
if await self.bot.cog_disabled_in_guild(self, guild):
return
user = cast(discord.Member, message.author)
channel = message.channel
agree_channel = cast(
discord.TextChannel, guild.get_channel(await self.config.guild(guild).AGREE_CHANNEL())
)
if guild is None:
return
if agree_channel is None:
return
if channel.id != agree_channel.id:
return
if user.bot:
return
if user.id in self.users:
if not guild.me.guild_permissions.manage_roles:
await self._no_perms(agree_channel)
return
if self.users[user.id]["key"].lower() in message.content.lower():
perms = agree_channel.permissions_for(guild.me)
roles_id = await self.config.guild(guild).ROLE()
roles = [role for role in guild.roles if role.id in roles_id]
for role in roles:
await user.add_roles(role, reason=_("Agreed to the rules"))
if perms.manage_messages and await self.config.guild(guild).DELETE_KEY():
try:
await message.delete()
except Exception:
pass
if self.users[user.id]["message"].guild:
try:
await self.users[user.id]["message"].delete()
except Exception:
pass
elif perms.add_reactions:
await message.add_reaction("")
del self.users[user.id]
async def _agree_maker(self, member: discord.Member) -> None:
guild = member.guild
self.last_guild = guild
# await self._verify_json(None)
key = await self.config.guild(guild).AGREE_KEY()
if key is None:
key = "".join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6))
# <3 Stackoverflow http://stackoverflow.com/questions/2257441/random-string-generation-with-upper-case-letters-and-digits-in-python/23728630#23728630
ch = cast(
discord.TextChannel, guild.get_channel(await self.config.guild(guild).AGREE_CHANNEL())
)
msg = await self.config.guild(guild).AGREE_MSG()
if msg is None:
msg = "{mention} please enter {key} in {channel}"
try:
msg = msg.format(
key=key,
member=member,
name=member.name,
mention=member.mention,
guild=guild.name,
channel=ch.mention,
)
except Exception:
log.error("Error formatting agreement message", exc_info=True)
try:
msg = await member.send(msg)
except discord.Forbidden:
msg = await ch.send(msg)
except discord.HTTPException:
return
self.users[member.id] = {"key": key, "message": msg}
async def _auto_give(self, member: discord.Member) -> None:
guild = member.guild
roles_id = await self.config.guild(guild).ROLE()
roles = [role for role in guild.roles if role.id in roles_id]
if not guild.me.guild_permissions.manage_roles:
await self._no_perms()
return
for role in roles:
await member.add_roles(role, reason=_("Joined the server"))
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member) -> None:
if "h0nde" in member.name or "h0nda" in member.name:
await member.ban(reason="Anti-spam measure.")
guild = member.guild
if await self.config.guild(guild).ENABLED():
if await self.config.guild(guild).AGREE_CHANNEL() is not None:
await self._agree_maker(member)
elif member.guild.name != "BibleBot": # Immediately give the new user the role
await self._auto_give(member)
@commands.Cog.listener()
async def on_member_update(self, before: discord.Member, after: discord.Member) -> None:
if before.guild.name == "BibleBot":
if before.pending == True and after.pending == False:
await after.add_roles(before.guild.get_role(366901317924290562))
@commands.guild_only()
@commands.group(name="autorole")
@commands.bot_has_permissions(manage_roles=True)
async def autorole(self, ctx: commands.Context) -> None:
"""
Change settings for autorole
Requires the manage roles permission
"""
pass
@autorole.command(name="info")
async def autorole_info(self, ctx: commands.Context) -> None:
"""
Display current autorole info
"""
guild = ctx.message.guild
enabled = await self.config.guild(guild).ENABLED()
roles = await self.config.guild(guild).ROLE()
msg = await self.config.guild(guild).AGREE_MSG()
key = await self.config.guild(guild).AGREE_KEY()
ch_id = await self.config.guild(guild).AGREE_CHANNEL()
delete = await self.config.guild(guild).DELETE_KEY()
channel = guild.get_channel(ch_id)
chn_name = channel.name if channel is not None else "None"
chn_mention = channel.mention if channel is not None else "None"
role_name_str = ", ".join(role.mention for role in guild.roles if role.id in roles)
if not role_name_str:
role_name_str = "None"
if ctx.channel.permissions_for(ctx.me).embed_links:
embed = discord.Embed(colour=await self.get_colour(ctx.channel))
embed.set_author(name=_("Autorole settings for ") + guild.name)
embed.add_field(name=_("Current autorole state: "), value=str(enabled))
embed.add_field(name=_("Current Roles: "), value=str(role_name_str))
if msg:
embed.add_field(name=_("Agreement message: "), value=str(msg))
if key:
embed.add_field(name=_("Agreement key: "), value=str(key))
if channel:
embed.add_field(name=_("Agreement channel: "), value=str(chn_mention))
await ctx.send(embed=embed)
else:
send_msg = (
"```"
+ _("Current autorole state: ")
+ f"{enabled}\n"
+ _("Current Roles: ")
+ f"{role_name_str}\n"
+ _("Agreement message: ")
+ f"{msg}\n"
+ _("Agreement key: ")
+ f"{key}\n"
+ _("Delete Agreement: ")
+ f"{delete}\n"
+ _("Agreement channel: ")
+ f"{chn_name}"
+ "```"
)
await ctx.send(send_msg)
@autorole.command()
@checks.admin_or_permissions(manage_roles=True)
async def toggle(self, ctx: commands.Context) -> None:
"""
Enables/Disables autorole
"""
guild = ctx.message.guild
if await self.config.guild(guild).ROLE() is None:
msg = _("You haven't set a " "role to give to new users!")
await ctx.send(msg)
else:
if await self.config.guild(guild).ENABLED():
await self.config.guild(guild).ENABLED.set(False)
await ctx.send(_("Autorole is now disabled."))
else:
await self.config.guild(guild).ENABLED.set(True)
await ctx.send(_("Autorole is now enabled."))
@autorole.command(name="add", aliases=["role"])
@checks.admin_or_permissions(manage_roles=True)
async def role(self, ctx: commands.Context, *, role: discord.Role) -> None:
"""
Add a role for autorole to assign.
You can use this command multiple times to add multiple roles.
"""
guild = ctx.message.guild
roles = await self.config.guild(guild).ROLE()
if ctx.author.top_role < role:
msg = _(
" is higher than your highest role. "
"You can't assign autoroles higher than your own"
)
await ctx.send(role.name + msg)
if role.id in roles:
await ctx.send(role.name + _(" is already in the autorole list."))
return
if guild.me.top_role < role:
msg = _(" is higher than my highest role" " in the Discord hierarchy.")
await ctx.send(role.name + msg)
return
roles.append(role.id)
await self.config.guild(guild).ROLE.set(roles)
await ctx.send(role.name + _(" role added to the autorole."))
@autorole.command()
@checks.admin_or_permissions(manage_roles=True)
async def remove(self, ctx: commands.Context, *, role: discord.Role) -> None:
"""
Remove a role from the autorole.
"""
guild = ctx.message.guild
roles = await self.config.guild(guild).ROLE()
if role.id not in roles:
await ctx.send(role.name + _(" is not in the autorole list."))
return
roles.remove(role.id)
await self.config.guild(guild).ROLE.set(roles)
await ctx.send(role.name + _(" role removed from the autorole."))
@autorole.group()
@checks.admin_or_permissions(manage_roles=True)
async def agreement(self, ctx: commands.Context) -> None:
"""
Set the channel and message that will be used for accepting the rules.
`channel` is the channel they must type the key in to get the role.
`key` is the message they must type to gain access and must be in quotes.
`msg` is the message DM'd to them when they join.
`{key}` must be included in the message so a user knows what to type in the channel.
Optional additions to the message include:
`{channel}` Mentions the channel where they must include the agreement message.
`{mention}` Mentions the user incase they have DM permissions turned off this should be used.
`{name}` Says the member name if you don't want to ping them.
`{guild}` Says the servers current name.
Entering nothing will disable these.
"""
pass
@agreement.command(name="channel")
@checks.admin_or_permissions(manage_roles=True)
async def set_agreement_channel(
self, ctx: commands.Context, channel: discord.TextChannel = None
) -> None:
"""
Set the agreement channel
Entering nothing will clear this.
"""
guild = ctx.message.guild
if await self.config.guild(guild).ROLE() == []:
await ctx.send(_("No roles have been set for autorole."))
return
if not await self.config.guild(guild).ENABLED():
await ctx.send(_("Autorole has been disabled, enable it first."))
return
if channel is None:
await self.config.guild(guild).AGREE_CHANNEL.set(None)
await ctx.send(_("Agreement channel cleared"))
else:
await self.config.guild(guild).AGREE_CHANNEL.set(channel.id)
await ctx.send(_("Agreement channel set to ") + channel.mention)
@agreement.command(name="delete")
@checks.admin_or_permissions(manage_roles=True)
async def set_agreement_delete(self, ctx: commands.Context) -> None:
"""
Toggle automatically deleting the agreement message.
"""
delete_key = await self.config.guild(ctx.guild).DELETE_KEY()
await self.config.guild(ctx.guild).DELETE_KEY.set(not delete_key)
if delete_key:
await ctx.send(_("No longer automatically deleting agreement key."))
else:
await ctx.send(_("Automatically deleting agreement key."))
@agreement.command(name="key")
@checks.admin_or_permissions(manage_roles=True)
async def set_agreement_key(self, ctx: commands.Context, *, key: str = None) -> None:
"""
Set the agreement key
Entering nothing will clear this.
"""
guild = ctx.message.guild
if await self.config.guild(guild).ROLE() == []:
await ctx.send(_("No roles have been set for autorole."))
return
if not await self.config.guild(guild).ENABLED():
await ctx.send(_("Autorole has been disabled, enable it first."))
return
if key is None:
await self.config.guild(guild).AGREE_KEY.set(None)
await ctx.send(_("Agreement key cleared"))
else:
await self.config.guild(guild).AGREE_KEY.set(key)
await ctx.send(_("Agreement key set to ") + key)
@agreement.command(name="message", aliases=["msg"])
@checks.admin_or_permissions(manage_roles=True)
async def set_agreement_msg(self, ctx: commands.Context, *, message: str = None) -> None:
"""
Set the agreement message
`{key}` must be included in the message so a user knows what to type in the channel.
Optional additions to the message include:
`{channel}` Mentions the channel where they must include the agreement message.
`{mention}` Mentions the user incase they have DM permissions turned off this should be used.
`{name}` Says the member name if you don't want to ping them.
`{guild}` Says the servers current name.
Entering nothing will clear this.
"""
guild = ctx.message.guild
if await self.config.guild(guild).ROLE() == []:
await ctx.send(_("No roles have been set for autorole."))
return
if not await self.config.guild(guild).ENABLED():
await ctx.send(_("Autorole has been disabled, enable it first."))
return
if message is None:
await self.config.guild(guild).AGREE_MSG.set(None)
await ctx.send(_("Agreement message cleared"))
else:
await self.config.guild(guild).AGREE_MSG.set(message)
await ctx.send(_("Agreement message set to ") + message)
@agreement.command(name="setup")
@checks.admin_or_permissions(manage_roles=True)
async def agreement_setup(
self,
ctx: commands.Context,
channel: discord.TextChannel = None,
key: str = None,
*,
msg: str = None,
) -> None:
"""
Set the channel and message that will be used for accepting the rules.
`channel` is the channel they must type the key in to get the role.
`key` is the message they must type to gain access and must be in quotes.
`msg` is the message DM'd to them when they join.
`{key}` must be included in the message so a user knows what to type in the channel.
Optional additions to the message include:
`{channel}` Mentions the channel where they must include the agreement message.
`{mention}` Mentions the user incase they have DM permissions turned off this should be used.
`{name}` Says the member name if you don't want to ping them.
`{guild}` Says the servers current name.
Entering nothing will disable this.
"""
guild = ctx.message.guild
if await self.config.guild(guild).ROLE() == []:
await ctx.send(_("No roles have been set for autorole."))
return
if not await self.config.guild(guild).ENABLED():
await ctx.send(_("Autorole has been disabled, enable it first."))
return
if channel is None:
await self.config.guild(guild).AGREE_CHANNEL.set(None)
await self.config.guild(guild).AGREE_MSG.set(None)
await self.config.guild(guild).AGREE_KEY.set(None)
await ctx.send(_("Agreement channel cleared"))
else:
await self.config.guild(guild).AGREE_CHANNEL.set(channel.id)
await self.config.guild(guild).AGREE_MSG.set(msg)
await self.config.guild(guild).AGREE_KEY.set(key)
await ctx.send(_("Agreement channel set to ") + channel.mention)