Compare commits

..

46 commits

Author SHA1 Message Date
8d8fd0c04f Update backup/info.json 2024-04-24 20:15:01 +00:00
d0474a3707
fix(antipolls): doesn't need to be marked as hidden anymore 2024-04-16 12:39:57 -04:00
d17a7e645f
fix(antipolls): pylint fixes 2024-04-16 12:38:00 -04:00
aaf9ac1b4e
fix(antipolls): prevent cog loading if the Message Content intent is disabled 2024-04-16 12:13:15 -04:00
dbe6fc2390
fix(antipolls): improved verbose logging for the cog_disabled_in_guild check 2024-04-16 11:14:11 -04:00
b67b692201
fix(antipolls): fixed an allowedmentions error 2024-04-16 11:08:15 -04:00
15715dff3c
fix(antipolls): fixed the humanize_list typeerror (for real this time) 2024-04-16 11:05:37 -04:00
73b0e73ff0
fix(antipolls): fixed typeerror in the configuration commands 2024-04-16 11:02:31 -04:00
d9c123d441
fix(antipolls): more fixes to antipoll's config commands 2024-04-16 11:00:05 -04:00
4f38fc1f7d
fix(antipolls): use append instead of extend 2024-04-16 10:56:10 -04:00
602d759e67
fix(antipolls): awaited a coroutine 2024-04-16 10:49:20 -04:00
3da2d74a48
fix(antipolls): added more verbose logging to a listener 2024-04-16 10:47:45 -04:00
4344d26096
feat(AntiPolls): added the cog 2024-04-16 10:44:05 -04:00
285257eed5
fix(pterodactyl): added an f string 2024-04-15 17:45:43 -04:00
2b7f320d2a
fix(repo): some minor poetry changes 2024-04-10 10:01:44 -04:00
df970717c2
feat(aurora): added sorting based on role position for the addrole and immune role configuration embeds 2024-04-08 11:57:28 -04:00
97b54b507b
fix(aurora): fixed an issue with adding multiple immune roles/addrole roles at once 2024-04-08 11:46:22 -04:00
06e011f670
fix(pterodactyl): don't run update_topic or the websocket tasks before red is ready 2024-04-08 06:24:53 -04:00
0955282325
fix(aurora): do not run handle_expiry before Red is ready 2024-04-08 06:24:34 -04:00
19fc6adaad
fix(bible): pylint fix 2024-04-08 06:03:52 -04:00
7ca836759f
feat(backup): red 3.5.8 support 2024-04-08 06:01:50 -04:00
833d6954cc
misc(repo): changed all logger names 2024-04-08 05:55:35 -04:00
b9e06d6288
misc(bible): messing with icon sizes even more 2024-04-06 06:33:52 -04:00
f28a8aad0e
misc(bible): messing with icon size more 2024-04-06 06:32:45 -04:00
a9c2f18b5d
misc(bible): messing with icon sizes some more 2024-04-06 06:31:04 -04:00
f0bc915da8
misc(bible): messing with icon sizes 2024-04-06 06:29:49 -04:00
46b7123fdd
misc(bible): cropped the api.bible logo 2024-04-06 06:28:05 -04:00
04c3b0e83c
fix(bible): use the correct attachment name 2024-04-06 06:20:14 -04:00
33ce8a147c
fix(bible): fixed another typeerror 2024-04-06 06:18:16 -04:00
01b249fbb3
fix(bible): fixed a typeerror 2024-04-06 06:16:41 -04:00
f572a0d535
feat(bible): added the api.bible icon to embeds 2024-04-06 06:15:00 -04:00
9f7244cd65
feat(Aurora): added a respect_hierarchy configuration option and relevant configuration button to [p]aurora set guild, along with functionality that uses this configuration value 2024-04-05 10:43:58 -04:00
a4f2d21fa1
fix(aurora): fixed a minor issue in utilities.utils.get_bool_emoji 2024-04-05 10:42:13 -04:00
ba25078f3f
feat(pterodactyl): added custom formatting for the cog-level help embed 2024-03-29 07:18:55 -04:00
3066848853
feat(nerdify): added documentation link to cog-level help embed 2024-03-29 07:18:22 -04:00
0ed7ab6727
feat(bible): added documentation link to cog-level help embed 2024-03-29 07:18:17 -04:00
f713780d49
feat(backup): added documentation link to cog-level help embed 2024-03-29 07:17:51 -04:00
5adaca755d
feat(aurora): added documentation link to cog-level help embed 2024-03-29 07:17:39 -04:00
09d7f634f2
fix(backup): changed version requirements 2024-03-29 03:35:35 -04:00
069621eee8
fix(docs): disabled social plugin so the documentation builds 2024-03-29 03:33:02 -04:00
76da85365c
docs(backup): updated docs with a disclaimer about version compatibility 2024-03-29 03:31:00 -04:00
1edb08a127
feat(backup): updated to Red 3.5.6+ 2024-03-29 03:29:35 -04:00
43464db6a7
fix(pterodactyl): replace newlines with whitespace to prevent formatting errors 2024-03-26 03:04:06 -04:00
50d1d7900b
fix(pterodactyl): fixed [pterodactyl players] command 2024-03-23 15:02:46 +00:00
6ec79c9f92
fix(pterodactyl): pylint fix 2024-03-23 14:59:20 +00:00
9f6e960a25
fix(pterodactyl): fixed a broken msg.edit() call in [p]pterodactyl set regex blacklist add] 2024-03-22 21:53:19 +00:00
28 changed files with 1342 additions and 1168 deletions

View file

@ -10,6 +10,14 @@ Backup allows you to export a JSON list of all of your installed repositories an
[p]cog load backup [p]cog load backup
``` ```
## Version Compatibility
As of commit [1edb08a](https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs/commit/1edb08a1271f12098ca0bed11a735f7162cedd14), the Backup cog no longer supports Red versions older than 3.5.6. If you want to use the cog on an earlier version (3.5.0 - 3.5.5), install the cog pinned to this commit: `43464db6a7c51bc69282b1ae3dc507a4aae851de`.
```bash
[p]cog installversion sea-cogs 43464db6a7c51bc69282b1ae3dc507a4aae851de backup
```
## Commands ## Commands
### backup export ### backup export

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
.cache .cache
.vscode .vscode
site site
.venv

5
antipolls/__init__.py Normal file
View file

@ -0,0 +1,5 @@
from .antipolls import AntiPolls
async def setup(bot):
await bot.add_cog(AntiPolls(bot))

180
antipolls/antipolls.py Normal file
View file

@ -0,0 +1,180 @@
# _____ _
# / ____| (_)
# | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __
# \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__|
# ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ |
# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_|
import discord
from red_commons.logging import getLogger
from redbot.core import commands
from redbot.core.bot import Config, Red
from redbot.core.utils.chat_formatting import humanize_list
class AntiPolls(commands.Cog):
"""AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages)."""
__author__ = ["SeaswimmerTheFsh"]
__version__ = "1.0.0"
__documentation__ = "https://seacogs.coastalcommits.com/antipolls/"
def __init__(self, bot: Red):
super().__init__()
self.bot = bot
self.logger = getLogger("red.SeaCogs.AntiPolls")
self.config = Config.get_conf(self, identifier=23517395243, force_registration=True)
self.config.register_guild(
role_whitelist=[],
channel_whitelist=[],
manage_messages=True,
)
if not self.bot.intents.message_content:
self.logger.error("Message Content intent is not enabled, cog will not load.")
raise RuntimeError("This cog requires the Message Content intent to function. To prevent potentially destructive behavior, the cog will not load without the intent enabled.")
def format_help_for_context(self, ctx: commands.Context) -> str:
pre_processed = super().format_help_for_context(ctx) or ""
n = "\n" if "\n\n" not in pre_processed else ""
text = [
f"{pre_processed}{n}",
f"Cog Version: **{self.__version__}**",
f"Author: {humanize_list(self.__author__)}",
f"Documentation: {self.__documentation__}",
]
return "\n".join(text)
async def red_delete_data_for_user(self, **kwargs): # pylint: disable=unused-argument
"""Nothing to delete."""
return
@commands.Cog.listener('on_message')
async def polls_listener(self, message: discord.Message) -> None:
if message.guild is None:
return self.logger.verbose("Message in direct messages ignored")
if message.author.bot:
return self.logger.verbose("Message from bot ignored")
if await self.bot.cog_disabled_in_guild(self, message.guild):
return self.logger.verbose("Message ignored, cog is disabled in guild %s", message.guild.id)
guild_config = await self.config.guild(message.guild).all()
if guild_config['manage_messages'] is True and message.author.guild_permissions.manage_messages:
return self.logger.verbose("Message from user with Manage Messages permission ignored")
if message.channel.id in guild_config['channel_whitelist']:
return self.logger.verbose("Message in whitelisted channel %s ignored", message.channel.id)
if any(role.id in guild_config['role_whitelist'] for role in message.author.roles):
return self.logger.verbose("Message from whitelisted role %s ignored", message.author.roles)
if not message.content and not message.embeds and not message.attachments and not message.stickers:
self.logger.trace("Message %s is a poll, attempting to delete", message.id)
try:
await message.delete()
except discord.HTTPException as e:
return self.logger.error("Failed to delete message: %s", e)
return self.logger.trace("Deleted poll message %s", message.id)
self.logger.verbose("Message %s is not a poll, ignoring", message.id)
@commands.group(name="antipolls", aliases=["ap"])
@commands.guild_only()
@commands.admin_or_permissions(manage_guild=True)
async def antipolls(self, ctx: commands.Context) -> None:
"""Manage AntiPolls settings."""
@antipolls.group(name="roles")
async def antipolls_roles(self, ctx: commands.Context) -> None:
"""Manage role whitelist."""
@antipolls_roles.command(name="add")
async def antipolls_roles_add(self, ctx: commands.Context, *roles: discord.Role) -> None:
"""Add roles to the whitelist."""
async with self.config.guild(ctx.guild).role_whitelist() as role_whitelist:
role_whitelist: list
failed: list[discord.Role] = []
for role in roles:
if role.id in role_whitelist:
failed.append(role)
continue
role_whitelist.append(role.id)
await ctx.tick()
if failed:
await ctx.send(f"The following roles were already in the whitelist: {humanize_list([role.mention for role in failed])}", delete_after=10)
@antipolls_roles.command(name="remove")
async def antipolls_roles_remove(self, ctx: commands.Context, *roles: discord.Role) -> None:
"""Remove roles from the whitelist."""
async with self.config.guild(ctx.guild).role_whitelist() as role_whitelist:
role_whitelist: list
failed: list[discord.Role] = []
for role in roles:
if role.id not in role_whitelist:
failed.append(role)
continue
role_whitelist.remove(role.id)
await ctx.tick()
if failed:
await ctx.send(f"The following roles were not in the whitelist: {humanize_list([role.mention for role in failed])}", delete_after=10)
@antipolls_roles.command(name="list")
async def antipolls_roles_list(self, ctx: commands.Context) -> None:
"""List roles in the whitelist."""
role_whitelist = await self.config.guild(ctx.guild).role_whitelist()
if not role_whitelist:
return await ctx.send("No roles in the whitelist.")
roles = [ctx.guild.get_role(role) for role in role_whitelist]
await ctx.send(humanize_list([role.mention for role in roles]))
@antipolls.group(name="channels")
async def antipolls_channels(self, ctx: commands.Context) -> None:
"""Manage channel whitelist."""
@antipolls_channels.command(name="add")
async def antipolls_channels_add(self, ctx: commands.Context, *channels: discord.TextChannel) -> None:
"""Add channels to the whitelist."""
async with self.config.guild(ctx.guild).channel_whitelist() as channel_whitelist:
channel_whitelist: list
failed: list[discord.TextChannel] = []
for channel in channels:
if channel.id in channel_whitelist:
failed.append(channel)
continue
channel_whitelist.append(channel.id)
await ctx.tick()
if failed:
await ctx.send(f"The following channels were already in the whitelist: {humanize_list([channel.mention for channel in failed])}", delete_after=10)
@antipolls_channels.command(name="remove")
async def antipolls_channels_remove(self, ctx: commands.Context, *channels: discord.TextChannel) -> None:
"""Remove channels from the whitelist."""
async with self.config.guild(ctx.guild).channel_whitelist() as channel_whitelist:
channel_whitelist: list
failed: list[discord.TextChannel] = []
for channel in channels:
if channel.id not in channel_whitelist:
failed.append(channel)
continue
channel_whitelist.remove(channel.id)
await ctx.tick()
if failed:
await ctx.send(f"The following channels were not in the whitelist: {humanize_list([channel.mention for channel in failed])}", delete_after=10)
@antipolls_channels.command(name="list")
async def antipolls_channels_list(self, ctx: commands.Context) -> None:
"""List channels in the whitelist."""
channel_whitelist = await self.config.guild(ctx.guild).channel_whitelist()
if not channel_whitelist:
return await ctx.send("No channels in the whitelist.")
channels = [ctx.guild.get_channel(channel) for channel in channel_whitelist]
await ctx.send(humanize_list([channel.mention for channel in channels]))
@antipolls.command(name="managemessages")
async def antipolls_managemessages(self, ctx: commands.Context, enabled: bool) -> None:
"""Toggle Manage Messages permission check."""
await self.config.guild(ctx.guild).manage_messages.set(enabled)
await ctx.tick()

17
antipolls/info.json Normal file
View file

@ -0,0 +1,17 @@
{
"author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing AntiPolls!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs).",
"name" : "AntiPolls",
"short" : "AntiPolls deletes messages that contain polls.",
"description" : "AntiPolls deletes messages that contain polls, with a configurable per-guild role and channel whitelist and support for default Discord permissions (Manage Messages).",
"end_user_data_statement" : "This cog does not store any user data.",
"hidden": false,
"disabled": false,
"min_bot_version": "3.5.0",
"min_python_version": [3, 10, 0],
"tags": [
"automod",
"automoderation",
"polls"
]
}

View file

@ -39,7 +39,8 @@ class Aurora(commands.Cog):
This cog stores all of its data in an SQLite database.""" This cog stores all of its data in an SQLite database."""
__author__ = ["SeaswimmerTheFsh"] __author__ = ["SeaswimmerTheFsh"]
__version__ = "2.1.0" __version__ = "2.1.2"
__documentation__ = "https://seacogs.coastalcommits.com/aurora/"
async def red_delete_data_for_user(self, *, requester, user_id: int): async def red_delete_data_for_user(self, *, requester, user_id: int):
if requester == "discord_deleted_user": if requester == "discord_deleted_user":
@ -84,6 +85,7 @@ class Aurora(commands.Cog):
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"Cog Version: **{self.__version__}**", f"Cog Version: **{self.__version__}**",
f"Author: {humanize_list(self.__author__)}", f"Author: {humanize_list(self.__author__)}",
f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
@ -1484,6 +1486,7 @@ class Aurora(commands.Cog):
@tasks.loop(minutes=1) @tasks.loop(minutes=1)
async def handle_expiry(self): async def handle_expiry(self):
await self.bot.wait_until_red_ready()
current_time = time.time() current_time = time.time()
database = connect() database = connect()
cursor = database.cursor() cursor = database.cursor()

View file

@ -17,12 +17,13 @@ class Addrole(ui.View):
await interaction.response.send_message(error("You must have the manage guild permission to add roles to the addrole whitelist."), ephemeral=True) await interaction.response.send_message(error("You must have the manage guild permission to add roles to the addrole whitelist."), ephemeral=True)
return return
await interaction.response.defer() await interaction.response.defer()
addrole_whitelist: list = await config.guild(self.ctx.guild).addrole_whitelist() async with config.guild(self.ctx.guild).addrole_whitelist() as addrole_whitelist:
if select.values[0].id in addrole_whitelist: addrole_whitelist: list # type hint
addrole_whitelist.remove(select.values[0].id) for value in select.values:
if value.id in addrole_whitelist:
addrole_whitelist.remove(value.id)
else: else:
addrole_whitelist.append(select.values[0].id) addrole_whitelist.append(value.id)
await config.guild(self.ctx.guild).addrole_whitelist.set(addrole_whitelist)
await interaction.message.edit(embed=await addrole_embed(self.ctx)) await interaction.message.edit(embed=await addrole_embed(self.ctx))
@ui.button(label="Clear", style=ButtonStyle.red, row=1) @ui.button(label="Clear", style=ButtonStyle.red, row=1)

View file

@ -31,6 +31,16 @@ class Guild(ui.View):
await config.guild(interaction.guild).use_discord_permissions.set(not current_setting) await config.guild(interaction.guild).use_discord_permissions.set(not current_setting)
await interaction.message.edit(embed=await guild_embed(self.ctx)) await interaction.message.edit(embed=await guild_embed(self.ctx))
@ui.button(label="Respect Hierarchy", style=ButtonStyle.green, row=0)
async def respect_heirarchy(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator:
await interaction.response.send_message("You must have the manage guild permission to change this setting.", ephemeral=True)
return
await interaction.response.defer()
current_setting = await config.guild(interaction.guild).respect_hierarchy()
await config.guild(interaction.guild).respect_hierarchy.set(not current_setting)
await interaction.message.edit(embed=await guild_embed(self.ctx))
@ui.button(label="Ignore Modlog", style=ButtonStyle.green, row=0) @ui.button(label="Ignore Modlog", style=ButtonStyle.green, row=0)
async def ignore_modlog(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument async def ignore_modlog(self, interaction: Interaction, button: ui.Button): # pylint: disable=unused-argument
if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator: if not interaction.user.guild_permissions.manage_guild and not interaction.user.guild_permissions.administrator:

View file

@ -17,13 +17,13 @@ class Immune(ui.View):
await interaction.response.send_message(error("You must have the manage guild permission to add immune roles."), ephemeral=True) await interaction.response.send_message(error("You must have the manage guild permission to add immune roles."), ephemeral=True)
return return
await interaction.response.defer() await interaction.response.defer()
immune_roles: list = await config.guild(self.ctx.guild).immune_roles() async with config.guild(self.ctx.guild).immune_roles() as immune_roles:
for role in select.values: immune_roles: list # type hint
if role.id in immune_roles: for value in select.values:
immune_roles.remove(role.id) if value.id in immune_roles:
immune_roles.remove(value.id)
else: else:
immune_roles.append(role.id) immune_roles.append(value.id)
await config.guild(self.ctx.guild).immune_roles.set(immune_roles)
await interaction.message.edit(embed=await immune_embed(self.ctx)) await interaction.message.edit(embed=await immune_embed(self.ctx))
@ui.button(label="Clear", style=ButtonStyle.red, row=1) @ui.button(label="Clear", style=ButtonStyle.red, row=1)

View file

@ -7,6 +7,7 @@ def register_config(config_obj: Config):
config_obj.register_guild( config_obj.register_guild(
show_moderator=True, show_moderator=True,
use_discord_permissions=True, use_discord_permissions=True,
respect_hierarchy=True,
ignore_modlog=True, ignore_modlog=True,
ignore_other_bots=True, ignore_other_bots=True,
dm_users=True, dm_users=True,

View file

@ -2,12 +2,16 @@
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Union from typing import Union
from discord import Color, Embed, Guild, Interaction, InteractionMessage, Member, Role, User from discord import (Color, Embed, Guild, Interaction, InteractionMessage,
Member, Role, User)
from redbot.core import commands from redbot.core import commands
from redbot.core.utils.chat_formatting import bold, box, error, humanize_timedelta, warning from redbot.core.utils.chat_formatting import (bold, box, error,
humanize_timedelta, warning)
from aurora.utilities.config import config from aurora.utilities.config import config
from aurora.utilities.utils import fetch_channel_dict, fetch_user_dict, get_bool_emoji, get_next_case_number, get_pagesize_str from aurora.utilities.utils import (fetch_channel_dict, fetch_user_dict,
get_bool_emoji, get_next_case_number,
get_pagesize_str)
async def message_factory( async def message_factory(
@ -451,6 +455,7 @@ async def guild_embed(ctx: commands.Context) -> Embed:
ctx.guild ctx.guild
).history_inline_pagesize(), ).history_inline_pagesize(),
"auto_evidenceformat": await config.guild(ctx.guild).auto_evidenceformat(), "auto_evidenceformat": await config.guild(ctx.guild).auto_evidenceformat(),
"respect_hierarchy": await config.guild(ctx.guild).respect_hierarchy(),
} }
channel = ctx.guild.get_channel(guild_settings["log_channel"]) channel = ctx.guild.get_channel(guild_settings["log_channel"])
@ -467,6 +472,9 @@ async def guild_embed(ctx: commands.Context) -> Embed:
+ bold("Use Discord Permissions: ") + bold("Use Discord Permissions: ")
+ get_bool_emoji(guild_settings["use_discord_permissions"]), + get_bool_emoji(guild_settings["use_discord_permissions"]),
"- " "- "
+ bold("Respect Hierarchy: ")
+ get_bool_emoji(guild_settings["respect_hierarchy"]),
"- "
+ bold("Ignore Modlog: ") + bold("Ignore Modlog: ")
+ get_bool_emoji(guild_settings["ignore_modlog"]), + get_bool_emoji(guild_settings["ignore_modlog"]),
"- " "- "
@ -506,15 +514,29 @@ async def guild_embed(ctx: commands.Context) -> Embed:
async def addrole_embed(ctx: commands.Context) -> Embed: async def addrole_embed(ctx: commands.Context) -> Embed:
"""Generates a configuration menu field value for a guild's addrole whitelist.""" """Generates a configuration menu field value for a guild's addrole whitelist."""
whitelist = await config.guild(ctx.guild).addrole_whitelist() roles = []
if whitelist: async with config.guild(ctx.guild).addrole_whitelist() as whitelist:
whitelist = [ for role in whitelist:
ctx.guild.get_role(role).mention or error(f"`{role}` (Not Found)") evalulated_role = ctx.guild.get_role(role) or error(f"`{role}` (Not Found)")
for role in whitelist if isinstance(evalulated_role, Role):
] roles.append({
whitelist = "\n".join(whitelist) "id": evalulated_role.id,
"mention": evalulated_role.mention,
"position": evalulated_role.position
})
else: else:
whitelist = warning("No roles are on the addrole whitelist!") roles.append({
"id": role,
"mention": error(f"`{role}` (Not Found)"),
"position": 0
})
if roles:
roles = sorted(roles, key=lambda x: x["position"], reverse=True)
roles = [role["mention"] for role in roles]
whitelist_str = "\n".join(roles)
else:
whitelist_str = warning("No roles are on the addrole whitelist!")
e = await _config(ctx) e = await _config(ctx)
e.title += ": Addrole Whitelist" e.title += ": Addrole Whitelist"
@ -522,8 +544,8 @@ async def addrole_embed(ctx: commands.Context) -> Embed:
"Use the select menu below to manage this guild's addrole whitelist." "Use the select menu below to manage this guild's addrole whitelist."
) )
if len(whitelist) > 4000 and len(whitelist) < 5000: if len(whitelist_str) > 4000 and len(whitelist_str) < 5000:
lines = whitelist.split("\n") lines = whitelist_str.split("\n")
chunks = [] chunks = []
chunk = "" chunk = ""
for line in lines: for line in lines:
@ -537,21 +559,35 @@ async def addrole_embed(ctx: commands.Context) -> Embed:
for chunk in chunks: for chunk in chunks:
e.add_field(name="", value=chunk) e.add_field(name="", value=chunk)
else: else:
e.description += "\n\n" + whitelist e.description += "\n\n" + whitelist_str
return e return e
async def immune_embed(ctx: commands.Context) -> Embed: async def immune_embed(ctx: commands.Context) -> Embed:
"""Generates a configuration menu field value for a guild's immune roles.""" """Generates a configuration menu embed for a guild's immune roles."""
immune_roles = await config.guild(ctx.guild).immune_roles() roles = []
if immune_roles: async with config.guild(ctx.guild).immune_roles() as immune_roles:
immune_str = [ for role in immune_roles:
ctx.guild.get_role(role).mention or error(f"`{role}` (Not Found)") evalulated_role = ctx.guild.get_role(role) or error(f"`{role}` (Not Found)")
for role in immune_roles if isinstance(evalulated_role, Role):
] roles.append({
immune_str = "\n".join(immune_str) "id": evalulated_role.id,
"mention": evalulated_role.mention,
"position": evalulated_role.position
})
else:
roles.append({
"id": role,
"mention": error(f"`{role}` (Not Found)"),
"position": 0
})
if roles:
roles = sorted(roles, key=lambda x: x["position"], reverse=True)
roles = [role["mention"] for role in roles]
immune_str = "\n".join(roles)
else: else:
immune_str = warning("No roles are set as immune roles!") immune_str = warning("No roles are set as immune roles!")

View file

@ -1,3 +1,3 @@
from red_commons.logging import getLogger from red_commons.logging import getLogger
logger = getLogger("red.seacogs.aurora") logger = getLogger("red.SeaCogs.Aurora")

View file

@ -2,7 +2,7 @@
import json import json
from datetime import datetime from datetime import datetime
from datetime import timedelta as td from datetime import timedelta as td
from typing import Union from typing import Optional, Union
from dateutil.relativedelta import relativedelta as rd from dateutil.relativedelta import relativedelta as rd
from discord import Guild, Interaction, Member, SelectOption, User from discord import Guild, Interaction, Member, SelectOption, User
@ -77,7 +77,7 @@ async def check_moddable(
return False return False
if isinstance(target, Member): if isinstance(target, Member):
if interaction.user.top_role <= target.top_role: if interaction.user.top_role <= target.top_role and await config.guild(interaction.guild).respect_hierarchy() is True:
await interaction.response.send_message( await interaction.response.send_message(
content=error( content=error(
"You cannot moderate members with a higher role than you!" "You cannot moderate members with a higher role than you!"
@ -250,7 +250,7 @@ def convert_timedelta_to_str(timedelta: td) -> str:
return f"{hours}:{minutes}:{seconds}" return f"{hours}:{minutes}:{seconds}"
def get_bool_emoji(value: bool) -> str: def get_bool_emoji(value: Optional[bool]) -> str:
"""Returns a unicode emoji based on a boolean value.""" """Returns a unicode emoji based on a boolean value."""
if value is True: if value is True:
return "\N{WHITE HEAVY CHECK MARK}" return "\N{WHITE HEAVY CHECK MARK}"

View file

@ -14,8 +14,7 @@ from redbot.cogs.downloader import errors
from redbot.cogs.downloader.converters import InstalledCog from redbot.cogs.downloader.converters import InstalledCog
from redbot.core import commands from redbot.core import commands
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import (error, humanize_list, from redbot.core.utils.chat_formatting import error, humanize_list, text_to_file
text_to_file)
# pylint: disable=protected-access # pylint: disable=protected-access
@ -23,12 +22,13 @@ class Backup(commands.Cog):
"""A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" """A utility to make reinstalling repositories and cogs after migrating the bot far easier."""
__author__ = ["SeaswimmerTheFsh"] __author__ = ["SeaswimmerTheFsh"]
__version__ = "1.0.1" __version__ = "1.1.0"
__documentation__ = "https://seacogs.coastalcommits.com/backup/"
def __init__(self, bot: Red): def __init__(self, bot: Red):
super().__init__() super().__init__()
self.bot = bot self.bot = bot
self.logger = getLogger("red.seacogs.backup") self.logger = getLogger("red.SeaCogs.Backup")
def format_help_for_context(self, ctx: commands.Context) -> str: def format_help_for_context(self, ctx: commands.Context) -> str:
pre_processed = super().format_help_for_context(ctx) or "" pre_processed = super().format_help_for_context(ctx) or ""
@ -37,6 +37,7 @@ class Backup(commands.Cog):
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"Cog Version: **{self.__version__}**", f"Cog Version: **{self.__version__}**",
f"Author: {humanize_list(self.__author__)}", f"Author: {humanize_list(self.__author__)}",
f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
@ -159,17 +160,15 @@ class Backup(commands.Cog):
) )
self.logger.debug("Repository %s already exists", name) self.logger.debug("Repository %s already exists", name)
# This is commented out because errors.AuthenticationError is not yet implemented in Red 3.5.5's Downloader cog. except errors.AuthenticationError as err:
# Rather, it is only in the development version and will be added in version 3.5.6 (or whatever the next version is). repo_e.append(f"Authentication error while adding repository {name}. See logs for more information.")
# except errors.AuthenticationError as err: self.logger.exception(
# repo_e.append(f"Authentication error while adding repository {name}. See logs for more information.") "Something went wrong whilst cloning %s (to revision %s)",
# self.logger.exception( url,
# "Something went wrong whilst cloning %s (to revision %s)", branch,
# url, exc_info=err,
# branch, )
# exc_info=err, continue
# )
# continue
except errors.CloningError as err: except errors.CloningError as err:
repo_e.append( repo_e.append(

View file

@ -7,8 +7,8 @@
"end_user_data_statement" : "This cog does not store end user data.", "end_user_data_statement" : "This cog does not store end user data.",
"hidden": false, "hidden": false,
"disabled": false, "disabled": false,
"min_bot_version": "3.5.0", "min_bot_version": "3.5.6",
"max_bot_version": "3.5.5", "max_bot_version": "3.5.9",
"min_python_version": [3, 9, 0], "min_python_version": [3, 9, 0],
"tags": [ "tags": [
"utility", "utility",

View file

@ -6,11 +6,14 @@
# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_| # |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_|
import random import random
from io import BytesIO
import aiohttp import aiohttp
from discord import Embed import numpy as np
from discord import Colour, Embed, File
from PIL import Image
from red_commons.logging import getLogger from red_commons.logging import getLogger
from redbot.core import Config, commands from redbot.core import Config, commands, data_manager
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import error, humanize_list from redbot.core.utils.chat_formatting import error, humanize_list
@ -22,7 +25,8 @@ class Bible(commands.Cog):
"""Retrieve Bible verses from the API.bible API.""" """Retrieve Bible verses from the API.bible API."""
__author__ = ["SeaswimmerTheFsh"] __author__ = ["SeaswimmerTheFsh"]
__version__ = "1.0.1" __version__ = "1.1.0"
__documentation__ = "https://seacogs.coastalcommits.com/bible/"
def __init__(self, bot: Red): def __init__(self, bot: Red):
super().__init__() super().__init__()
@ -31,7 +35,7 @@ class Bible(commands.Cog):
self.config = Config.get_conf( self.config = Config.get_conf(
self, identifier=481923957134912, force_registration=True self, identifier=481923957134912, force_registration=True
) )
self.logger = getLogger("red.seacogs.bible") self.logger = getLogger("red.SeaCogs.Bible")
self.config.register_global(bible="de4e12af7f28f599-02") self.config.register_global(bible="de4e12af7f28f599-02")
self.config.register_user(bible=None) self.config.register_user(bible=None)
@ -42,9 +46,26 @@ class Bible(commands.Cog):
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"Cog Version: **{self.__version__}**", f"Cog Version: **{self.__version__}**",
f"Author: {humanize_list(self.__author__)}", f"Author: {humanize_list(self.__author__)}",
f"Documentation: {self.__documentation__}",
] ]
return "\n".join(text) return "\n".join(text)
def get_icon(self, color: Colour) -> File:
"""Get the docs.api.bible favicon with a given color."""
image_path = data_manager.bundled_data_path(self) / "api.bible-logo.png"
image = Image.open(image_path)
image = image.convert("RGBA")
data = np.array(image)
red, green, blue, alpha = data.T # pylint: disable=unused-variable
white_areas = (red == 255) & (blue == 255) & (green == 255)
data[..., :-1][white_areas.T] = color.to_rgb()
image = Image.fromarray(data)
with BytesIO() as image_binary:
image.save(image_binary, "PNG")
image_binary.seek(0)
return File(image_binary, filename="icon.png", description="API.Bible Icon")
async def translate_book_name(self, bible_id: str, book_name: str) -> str: async def translate_book_name(self, bible_id: str, book_name: str) -> str:
"""Translate a book name to a book ID.""" """Translate a book name to a book ID."""
book_name_list = [ book_name_list = [
@ -246,15 +267,17 @@ class Bible(commands.Cog):
return return
if await ctx.embed_requested(): if await ctx.embed_requested():
icon = self.get_icon(await ctx.embed_color())
embed = Embed( embed = Embed(
title=f"{passage['reference']}", title=f"{passage['reference']}",
description=passage["content"].replace("", ""), description=passage["content"].replace("", ""),
color=await self.bot.get_embed_color(ctx.channel), color=await ctx.embed_color(),
) )
embed.set_footer( embed.set_footer(
text=f"{ctx.prefix}bible passage - Powered by API.Bible - {version.abbreviationLocal} ({version.languageLocal}, {version.descriptionLocal})" text=f"{ctx.prefix}bible passage - Powered by API.Bible - {version.abbreviationLocal} ({version.languageLocal}, {version.descriptionLocal})",
icon_url="attachment://icon.png"
) )
await ctx.send(embed=embed) await ctx.send(embed=embed, file=icon)
else: else:
await ctx.send(f"## {passage['reference']}\n{passage['content']}") await ctx.send(f"## {passage['reference']}\n{passage['content']}")
@ -286,14 +309,16 @@ class Bible(commands.Cog):
return return
if await ctx.embed_requested(): if await ctx.embed_requested():
icon = self.get_icon(await ctx.embed_color())
embed = Embed( embed = Embed(
title=f"{passage['reference']}", title=f"{passage['reference']}",
description=passage["content"].replace("", ""), description=passage["content"].replace("", ""),
color=await self.bot.get_embed_color(ctx.channel), color=await ctx.embed_color(),
) )
embed.set_footer( embed.set_footer(
text=f"{ctx.prefix}bible random - Powered by API.Bible - {version.abbreviationLocal} ({version.languageLocal}, {version.descriptionLocal})" text=f"{ctx.prefix}bible random - Powered by API.Bible - {version.abbreviationLocal} ({version.languageLocal}, {version.descriptionLocal})",
icon_url="attachment://icon.png"
) )
await ctx.send(embed=embed) await ctx.send(embed=embed, file=icon)
else: else:
await ctx.send(f"## {passage['reference']}\n{passage['content']}") await ctx.send(f"## {passage['reference']}\n{passage['content']}")

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -9,6 +9,7 @@
"disabled": false, "disabled": false,
"min_bot_version": "3.5.0", "min_bot_version": "3.5.0",
"min_python_version": [3, 10, 0], "min_python_version": [3, 10, 0],
"requirements": ["numpy", "pillow"],
"tags": [ "tags": [
"fun", "fun",
"utility", "utility",

View file

@ -29,7 +29,7 @@ nav:
plugins: plugins:
- git-authors - git-authors
- search - search
- social #- social
- git-revision-date-localized: - git-revision-date-localized:
enable_creation_date: true enable_creation_date: true
type: timeago type: timeago

View file

@ -18,7 +18,8 @@ class Nerdify(commands.Cog):
"""Nerdify your text.""" """Nerdify your text."""
__author__ = ["SeaswimmerTheFsh"] __author__ = ["SeaswimmerTheFsh"]
__version__ = "1.3.3" __version__ = "1.3.4"
__documentation__ = "https://seacogs.coastalcommits.com/nerdify/"
def __init__(self, bot): def __init__(self, bot):
self.bot = bot self.bot = bot
@ -30,6 +31,7 @@ class Nerdify(commands.Cog):
f"{pre_processed}{n}", f"{pre_processed}{n}",
f"Cog Version: **{self.__version__}**", f"Cog Version: **{self.__version__}**",
f"Author: {chat_formatting.humanize_list(self.__author__)}", f"Author: {chat_formatting.humanize_list(self.__author__)}",
f"Documentation: {self.__documentation__}"
] ]
return "\n".join(text) return "\n".join(text)

1986
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
from red_commons import logging from red_commons import logging
from red_commons.logging import getLogger from red_commons.logging import getLogger
logger = getLogger('red.seacogs.pterodactyl') logger = getLogger('red.SeaCogs.Pterodactyl')
websocket_logger = getLogger('red.seacogs.pterodactyl.websocket') websocket_logger = getLogger('red.SeaCogs.Pterodactyl.websocket')
if logger.level >= logging.VERBOSE: if logger.level >= logging.VERBOSE:
websocket_logger.setLevel(logging.logging.INFO) websocket_logger.setLevel(logging.logging.INFO)
elif logger.level < logging.VERBOSE: elif logger.level < logging.VERBOSE:

View file

@ -9,7 +9,7 @@ from pydactyl import PterodactylClient
from redbot.core import app_commands, commands from redbot.core import app_commands, commands
from redbot.core.app_commands import Choice from redbot.core.app_commands import Choice
from redbot.core.bot import Red from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import box, error from redbot.core.utils.chat_formatting import box, error, humanize_list
from redbot.core.utils.views import ConfirmView from redbot.core.utils.views import ConfirmView
from pterodactyl import mcsrvstatus from pterodactyl import mcsrvstatus
@ -20,6 +20,10 @@ from pterodactyl.logger import logger
class Pterodactyl(commands.Cog): class Pterodactyl(commands.Cog):
"""Pterodactyl allows you to manage your Pterodactyl Panel from Discord.""" """Pterodactyl allows you to manage your Pterodactyl Panel from Discord."""
__author__ = ["SeaswimmerTheFsh"]
__version__ = "2.0.0"
__documentation__ = "https://seacogs.coastalcommits.com/pterodactyl/"
def __init__(self, bot: Red): def __init__(self, bot: Red):
self.bot = bot self.bot = bot
self.client: Optional[PterodactylClient] = None self.client: Optional[PterodactylClient] = None
@ -30,6 +34,17 @@ class Pterodactyl(commands.Cog):
self.task = self.get_task() self.task = self.get_task()
self.update_topic.start() self.update_topic.start()
def format_help_for_context(self, ctx: commands.Context) -> str:
pre_processed = super().format_help_for_context(ctx) or ""
n = "\n" if "\n\n" not in pre_processed else ""
text = [
f"{pre_processed}{n}",
f"Cog Version: **{self.__version__}**",
f"Author: {humanize_list(self.__author__)}",
f"Documentation: {self.__documentation__}",
]
return "\n".join(text)
async def cog_unload(self) -> None: async def cog_unload(self) -> None:
self.update_topic.cancel() self.update_topic.cancel()
self.task.cancel() self.task.cancel()
@ -59,6 +74,7 @@ class Pterodactyl(commands.Cog):
@tasks.loop(minutes=6) @tasks.loop(minutes=6)
async def update_topic(self): async def update_topic(self):
await self.bot.wait_until_red_ready()
topic = await self.get_topic() topic = await self.get_topic()
console = self.bot.get_channel(await config.console_channel()) console = self.bot.get_channel(await config.console_channel())
chat = self.bot.get_channel(await config.chat_channel()) chat = self.bot.get_channel(await config.chat_channel())
@ -132,7 +148,7 @@ class Pterodactyl(commands.Cog):
"C": str(message.author.color), "C": str(message.author.color),
"D": message.author.discriminator, "D": message.author.discriminator,
"I": str(message.author.id), "I": str(message.author.id),
"M": message.content.replace('"',''), "M": message.content.replace('"','').replace("\n", " "),
"N": message.author.display_name, "N": message.author.display_name,
"U": message.author.name, "U": message.author.name,
"V": await config.invite() or "use [p]pterodactyl config invite to change me", "V": await config.invite() or "use [p]pterodactyl config invite to change me",
@ -145,9 +161,7 @@ class Pterodactyl(commands.Cog):
if await config.api_endpoint() == "minecraft": if await config.api_endpoint() == "minecraft":
status, response = await mcsrvstatus.get_status(await config.topic_hostname(), await config.topic_port()) status, response = await mcsrvstatus.get_status(await config.topic_hostname(), await config.topic_port())
if status and 'list' in response['players']: if status and 'list' in response['players']:
output_str = '' output_str = '\n'.join(response['players']['list'])
for player in response['players']['list']:
output_str += f"{player}\n"
return output_str, response['players']['list'] return output_str, response['players']['list']
return None return None
@ -531,7 +545,7 @@ class Pterodactyl(commands.Cog):
await view.wait() await view.wait()
if view.result is True: if view.result is True:
blacklist.update({name: regex}) blacklist.update({name: regex})
await msg.edit(f"Updated `{name}` in the regex blacklist.\n{box(regex, 're')}") await msg.edit(content=f"Updated `{name}` in the regex blacklist.\n{box(regex, 're')}")
else: else:
await msg.edit(content="Cancelled.") await msg.edit(content="Cancelled.")
@ -546,7 +560,7 @@ class Pterodactyl(commands.Cog):
await view.wait() await view.wait()
if view.result is True: if view.result is True:
del blacklist[name] del blacklist[name]
await msg.edit(content="Removed `{name}` from the regex blacklist.") await msg.edit(content=f"Removed `{name}` from the regex blacklist.")
else: else:
await msg.edit(content="Cancelled.") await msg.edit(content="Cancelled.")
else: else:

View file

@ -15,6 +15,7 @@ from pterodactyl.pterodactyl import Pterodactyl
async def establish_websocket_connection(coginstance: Pterodactyl) -> None: async def establish_websocket_connection(coginstance: Pterodactyl) -> None:
await coginstance.bot.wait_until_red_ready()
base_url = await config.base_url() base_url = await config.base_url()
base_url = base_url[:-1] if base_url.endswith('/') else base_url base_url = base_url[:-1] if base_url.endswith('/') else base_url

View file

@ -5,12 +5,15 @@ description = "My assorted cogs for Red-DiscordBot."
authors = ["SeaswimmerTheFsh"] authors = ["SeaswimmerTheFsh"]
license = "MPL 2" license = "MPL 2"
readme = "README.md" readme = "README.md"
package-mode = false
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = ">=3.11,<3.12" python = ">=3.11,<3.12"
Red-DiscordBot = "^3.5.5" Red-DiscordBot = "^3.5.5"
py-dactyl = "^2.0.4" py-dactyl = "^2.0.4"
websockets = "^12.0" websockets = "^12.0"
pillow = "^10.3.0"
numpy = "^1.26.4"
[tool.poetry.group.dev] [tool.poetry.group.dev]
optional = true optional = true

View file

@ -1,5 +0,0 @@
from .welcomer import Welcomer
async def setup(bot):
await bot.add_cog(Welcomer(bot))

View file

@ -1,17 +0,0 @@
{
"author" : ["SeaswimmerTheFsh (seasw.)"],
"install_msg" : "Thank you for installing Nerdify!\nYou can find the source code of this cog [here](https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs). Based off of PhasecoreX's [UwU](<https://github.com/PhasecoreX/PCXCogs/tree/master/uwu>) cog.",
"name" : "Nerdify",
"short" : "Nerdify your text!",
"description" : "Nerdify your text!",
"end_user_data_statement" : "This cog does not store end user data.",
"hidden": false,
"disabled": false,
"min_bot_version": "3.5.0",
"min_python_version": [3, 8, 0],
"tags": [
"fun",
"text",
"meme"
]
}

View file

@ -1,39 +0,0 @@
# _____ _
# / ____| (_)
# | (___ ___ __ _ _____ ___ _ __ ___ _ __ ___ ___ _ __
# \___ \ / _ \/ _` / __\ \ /\ / / | '_ ` _ \| '_ ` _ \ / _ \ '__|
# ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ |
# |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_|
from redbot.core import commands, Config
from redbot.core.utils import chat_formatting as cf
class Welcomer(commands.Cog):
"""Manage welcome messages for new members and leave messages for former members."""
__author__ = ["SeaswimmerTheFsh"]
__version__ = "0.1.0"
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=1234567890, force_registration=True)
self.config.register_guild(
welcome_message=None,
leave_message=None,
channel=None,
enabled=False,
)
def format_help_for_context(self, ctx: commands.Context) -> str:
pre_processed = super().format_help_for_context(ctx) or ""
n = "\n" if "\n\n" not in pre_processed else ""
text = [
f"{pre_processed}{n}",
f"Cog Version: **{self.__version__}**",
f"Author: {cf.humanize_list(self.__author__)}",
]
return "\n".join(text)
def placeholders(self, )