jarvis-bot/jarvis/cogs/starboard.py

310 lines
9.4 KiB
Python

"""J.A.R.V.I.S. Starboard Cog."""
from discord import TextChannel
from discord.ext import commands
from discord.utils import find
from discord_slash import SlashContext, cog_ext
from discord_slash.context import MenuContext
from discord_slash.model import ContextMenuType, SlashMessage
from discord_slash.utils.manage_commands import create_option
from discord_slash.utils.manage_components import (
create_actionrow,
create_select,
create_select_option,
wait_for_component,
)
from jarvis.db.models import Star, Starboard
from jarvis.utils import build_embed
from jarvis.utils.permissions import admin_or_permissions
supported_images = [
"image/png",
"image/gif",
"image/jpeg",
"image/webp",
"image/svg",
]
class StarboardCog(commands.Cog):
"""J.A.R.V.I.S. Starboard Cog."""
def __init__(self, bot: commands.Bot):
self.bot = bot
@cog_ext.cog_subcommand(
base="starboard",
name="list",
description="Lists all Starboards",
)
@admin_or_permissions(manage_guild=True)
async def _list(self, ctx: SlashContext) -> None:
starboards = Starboard.objects(guild=ctx.guild.id)
if starboards != []:
message = "Available Starboards:\n"
for s in starboards:
message += f"<#{s.channel}>\n"
await ctx.send(message)
else:
await ctx.send("No Starboards available.")
@cog_ext.cog_subcommand(
base="starboard",
name="create",
description="Create a starboard",
options=[
create_option(
name="channel",
description="Starboard channel",
option_type=7,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _create(self, ctx: SlashContext, channel: TextChannel) -> None:
if channel not in ctx.guild.channels:
await ctx.send(
"Channel not in guild. Choose an existing channel.",
hidden=True,
)
return
if not isinstance(channel, TextChannel):
await ctx.send("Channel must be a TextChannel", hidden=True)
return
exists = Starboard.objects(channel=channel.id, guild=ctx.guild.id).first()
if exists:
await ctx.send(f"Starboard already exists at {channel.mention}.", hidden=True)
return
count = Starboard.objects(guild=ctx.guild.id).count()
if count >= 25:
await ctx.send("25 starboard limit reached", hidden=True)
return
_ = Starboard(
guild=ctx.guild.id,
channel=channel.id,
admin=ctx.author.id,
).save()
await ctx.send(f"Starboard created. Check it out at {channel.mention}.")
@cog_ext.cog_subcommand(
base="starboard",
name="delete",
description="Delete a starboard",
options=[
create_option(
name="channel",
description="Starboard channel",
option_type=7,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _delete(self, ctx: SlashContext, channel: TextChannel) -> None:
deleted = Starboard.objects(channel=channel.id, guild=ctx.guild.id).delete()
if deleted:
_ = Star.objects(starboard=channel.id).delete()
await ctx.send(f"Starboard deleted from {channel.mention}.", hidden=True)
else:
await ctx.send(f"Starboard not found in {channel.mention}.", hidden=True)
@cog_ext.cog_context_menu(name="Star Message", target=ContextMenuType.MESSAGE)
async def _star_message(self, ctx: MenuContext) -> None:
await self._star_add.invoke(ctx, ctx.target_message)
@cog_ext.cog_subcommand(
base="star",
name="add",
description="Star a message",
options=[
create_option(
name="message",
description="Message to star",
option_type=3,
required=True,
),
create_option(
name="channel",
description=(
"Channel that has the message, " "required if different than command message"
),
option_type=7,
required=False,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _star_add(
self,
ctx: SlashContext,
message: str,
channel: TextChannel = None,
) -> None:
if not channel:
channel = ctx.channel
starboards = Starboard.objects(guild=ctx.guild.id)
if not starboards:
await ctx.send("No starboards exist.", hidden=True)
return
await ctx.defer()
channel_list = []
for starboard in starboards:
channel_list.append(find(lambda x: x.id == starboard.channel, ctx.guild.channels))
select_channels = [
create_select_option(label=x.name, value=str(idx)) for idx, x in enumerate(channel_list)
]
select = create_select(
options=select_channels,
min_values=1,
max_values=1,
)
components = [create_actionrow(select)]
msg = await ctx.send(content="Choose a starboard", components=components)
com_ctx = await wait_for_component(
self.bot,
messages=msg,
components=components,
check=lambda x: x.author.id == ctx.author.id,
)
starboard = channel_list[int(com_ctx.selected_options[0])]
if not isinstance(message, SlashMessage):
if message.startswith("https://"):
message = message.split("/")[-1]
message = await channel.fetch_message(message)
exists = Star.objects(
message=message.id,
channel=message.channel.id,
guild=message.guild.id,
starboard=starboard.id,
).first()
if exists:
await ctx.send(
f"Message already sent to Starboard {starboard.mention}",
hidden=True,
)
return
count = Star.objects(guild=message.guild.id, starboard=starboard.id).count()
content = message.content
attachments = message.attachments
image_url = None
if attachments:
for attachment in attachments:
if attachment.content_type in supported_images:
image_url = attachment.url
break
if not content and image_url:
content = "\u200b"
embed = build_embed(
title=f"[#{count}] Click Here to view context",
description=content,
fields=[],
url=message.jump_url,
timestamp=message.created_at,
)
embed.set_author(
name=message.author.name,
url=message.jump_url,
icon_url=message.author.display_avatar,
)
embed.set_footer(text=message.guild.name + " | " + message.channel.name)
if image_url:
embed.set_image(url=image_url)
star = await starboard.send(embed=embed)
_ = Star(
index=count,
message=message.id,
channel=message.channel.id,
guild=message.guild.id,
starboard=starboard.id,
admin=ctx.author.id,
star=star.id,
active=True,
).save()
components[0]["components"][0]["disabled"] = True
await com_ctx.edit_origin(
content=f"Message saved to Starboard.\nSee it in {starboard.mention}",
components=components,
)
@cog_ext.cog_subcommand(
base="star",
name="delete",
description="Delete a starred message",
options=[
create_option(
name="id",
description="Star to delete",
option_type=4,
required=True,
),
create_option(
name="starboard",
description="Starboard to delete star from",
option_type=7,
required=True,
),
],
)
@admin_or_permissions(manage_guild=True)
async def _star_delete(
self,
ctx: SlashContext,
id: int,
starboard: TextChannel,
) -> None:
if not isinstance(starboard, TextChannel):
await ctx.send("Channel must be a TextChannel", hidden=True)
return
exists = Starboard.objects(channel=starboard.id, guild=ctx.guild.id).first()
if not exists:
await ctx.send(
f"Starboard does not exist in {starboard.mention}. Please create it first",
hidden=True,
)
return
star = Star.objects(
starboard=starboard.id,
index=id,
guild=ctx.guild.id,
active=True,
).first()
if not star:
await ctx.send(f"No star exists with id {id}", hidden=True)
return
message = await starboard.fetch_message(star.star)
if message:
await message.delete()
star.active = False
star.save()
await ctx.send(f"Star {id} deleted")
def setup(bot: commands.Bot) -> None:
"""Add StarboardCog to J.A.R.V.I.S."""
bot.add_cog(StarboardCog(bot))
bot.add_cog(StarboardCog(bot))