Added bot messages. Updated polls and self-roles.

Added bot messages, intended for moderators to be able to post as the bot.
These messages are also editable through a context menu.
Intended for rules/info messages etc.

Polls:
Added some error handling, as the bot only supports Unicode emojis.

Self-roles:
Edited the name of the context menu for updating a role-group message.
This commit is contained in:
Vegard Berg 2022-08-06 02:01:29 +02:00
parent fed475d8c7
commit f1987d2788
4 changed files with 229 additions and 23 deletions

View File

@ -134,4 +134,5 @@ if __name__ == "__main__":
bot.load_extension("commands.infractions")
bot.load_extension("commands.self_roles")
bot.load_extension("commands.polls")
bot.load_extension("commands.bot_messages")
bot.start(getenv("DISCORD_TOKEN"))

190
commands/bot_messages.py Normal file
View File

@ -0,0 +1,190 @@
from copy import deepcopy
import json
from json.decoder import JSONDecodeError
import logging
from naff import (
Client,
Extension,
slash_command,
slash_option,
InteractionContext,
Modal,
ParagraphText,
ModalContext,
context_menu,
Permissions,
CommandTypes,
Message,
)
from database import BotMessages as BotMessagesModel
message_creation_modal = Modal(
custom_id="bot-message-create",
title=f"Create a message as the bot",
components=[
ParagraphText(
custom_id="embeds",
label="Embeds",
placeholder="Embeds as JSON",
required=False,
),
ParagraphText(
custom_id="content",
label="Text",
placeholder="Lorem ipsum dolor sit amet and so on",
required=False,
),
]
)
class BotMessages(Extension):
def __init__(self, client: Client) -> None:
self.client = client
@slash_command(
name="bot-message-create",
description="Create a message as the bot.",
dm_permission=False,
default_member_permissions=Permissions.MANAGE_GUILD,
)
async def bot_message_create_command(self, ctx: InteractionContext):
await ctx.send_modal(message_creation_modal)
modal_ctx: ModalContext = await self.client.wait_for_modal(message_creation_modal, author=ctx.author)
if modal_ctx.custom_id != "bot-message-create":
return
embeds_string: str = modal_ctx.responses["embeds"]
content_string: str = modal_ctx.responses["content"]
if (
(embeds_string is None or embeds_string == "")
and (content_string is None or content_string == "")
):
await modal_ctx.send(
"You must provide either an embed or text.",
ephemeral=True,
)
return
embed: dict | None = None
embeds: list | None = None
try:
if embeds_string:
embeds_temp = json.loads(embeds_string)
if isinstance(embeds_temp, list):
embeds = embeds_temp
elif isinstance(embeds_temp, dict):
embed = embeds_temp
except JSONDecodeError:
await modal_ctx.send(
"The embeds were not valid JSON.",
ephemeral=True,
)
return
msg = await ctx.channel.send(
content=content_string if content_string else None,
embed=embed,
embeds=embeds,
)
BotMessagesModel.create(
guild_id=msg.guild.id,
channel_id=msg.channel.id,
message_id=msg.id,
)
await modal_ctx.send(
"Message created!",
ephemeral=True,
)
@context_menu(
name="Edit bot message",
context_type=CommandTypes.MESSAGE,
dm_permission=False,
default_member_permissions=Permissions.MANAGE_GUILD,
)
async def edit_bot_message_context_menu(self, ctx: InteractionContext):
message: Message = ctx.target
if message.author.id != self.client.user.id:
await ctx.send(
"This is not a bot message.",
ephemeral=True,
)
return
bot_message: BotMessagesModel | None = BotMessagesModel.get(
BotMessagesModel.guild_id == message.channel.guild.id,
BotMessagesModel.channel_id == message.channel.id,
BotMessagesModel.message_id == message.id,
)
if bot_message is None:
await ctx.send(
"This is not an editable bot message.",
ephemeral=True,
)
return
modal = deepcopy(message_creation_modal)
modal.title = "Edit bot message"
modal.components[0].value = json.dumps(
[e.to_dict() for e in message.embeds] if message.embeds else "",
indent=4,
)
modal.components[1].value = message.content
await ctx.send_modal(modal)
modal_ctx: ModalContext = await self.client.wait_for_modal(modal, author=ctx.author)
if modal_ctx.custom_id != "bot-message-create":
return
embeds_string: str = modal_ctx.responses["embeds"]
content_string: str = modal_ctx.responses["content"]
if (
(embeds_string is None or embeds_string == "")
and (content_string is None or content_string == "")
):
await modal_ctx.send(
"You must provide either an embed or text.",
ephemeral=True,
)
return
embed: dict | None = None
embeds: list | None = None
try:
if embeds_string:
embeds_temp = json.loads(embeds_string)
if isinstance(embeds_temp, list):
embeds = embeds_temp
elif isinstance(embeds_temp, dict):
embed = embeds_temp
except JSONDecodeError:
await modal_ctx.send(
"The embeds were not valid JSON.",
ephemeral=True,
)
return
await message.edit(
content=content_string if content_string else None,
embed=embed,
embeds=embeds,
)
await modal_ctx.send(
"Message edited!",
ephemeral=True,
)
def setup(client: Client):
BotMessagesModel.create_table()
BotMessages(client)
logging.info("BotMessages extension loaded")

View File

@ -23,8 +23,10 @@ from naff import (
Task,
IntervalTrigger,
GuildText,
PartialEmoji,
)
from naff.api import events
from naff.client.errors import HTTPException
from database import Polls as PollsModel, PollVotes as PollVotesModel
from peewee import fn
@ -70,7 +72,7 @@ def generate_poll_embed(
embed = Embed(
title=title,
description=("\n".join(data) if data else None),
)
)
sum_votes = sum(votes)
for i, (emoji, option) in enumerate(options):
@ -217,9 +219,13 @@ class Polls(Extension):
buttons: List[Button] = []
for i, option in enumerate(options):
try:
emoji = PartialEmoji.from_str((option[0] or num_to_emoji(i + 1)))
except ValueError:
emoji = num_to_emoji(i + 1)
buttons.append(
Button(
emoji=(option[0] or num_to_emoji(i + 1)),
emoji=emoji,
style=ButtonStyles.PRIMARY,
custom_id=f"poll-vote:{poll_entry.id}:{i}",
)
@ -227,9 +233,10 @@ class Polls(Extension):
buttons.append(
Button(
label="Delete",
emoji="🔒",
label="Lock",
style=ButtonStyles.DANGER,
custom_id=f"poll-delete:{poll_entry.id}",
custom_id=f"poll-lock:{poll_entry.id}",
)
)
embed = generate_poll_embed(
@ -239,10 +246,18 @@ class Polls(Extension):
multiple_choice=multiple_choice,
expires=duration,
)
poll_message: Message = await modal_ctx.send(
embed=embed,
components=spread_to_rows(*buttons),
)
try:
poll_message: Message = await modal_ctx.send(
embed=embed,
components=spread_to_rows(*buttons),
)
except HTTPException as e:
logging.error(f"Error sending poll: {e}")
await modal_ctx.send(
"Error during poll creation. NB: You can not use server-specific emojis",
ephemeral=True,
)
return
poll_entry.message_id = poll_message.id
poll_entry.channel_id = poll_message.channel.id
@ -346,8 +361,8 @@ class Polls(Extension):
)
await ctx.message.edit(embed=embed)
elif ctx.custom_id.startswith("poll-delete:"):
elif ctx.custom_id.startswith("poll-lock:"):
poll_id = ctx.custom_id.split(":", 1)[1]
poll_entry: PollsModel | None = PollsModel.get_or_none(
@ -357,17 +372,17 @@ class Polls(Extension):
await ctx.send("That poll doesn't exist.")
return
if (
not ctx.author.id == int(poll_entry.author_id)
or not ctx.author.has_permission(Permissions.MANAGE_MESSAGES)
):
await ctx.send("You don't have permission to delete that poll.")
if not ctx.author.id == int(
poll_entry.author_id
) or not ctx.author.has_permission(Permissions.MANAGE_MESSAGES):
await ctx.send("You don't have permission to lock that poll.")
return
poll_entry.delete_instance()
PollVotesModel.delete().where(PollVotesModel.poll_id == poll_id).execute()
await ctx.message.delete()
await ctx.send("Poll deleted.")
poll_entry.expires = datetime.now() - timedelta(minutes=1)
poll_entry.save()
await self.poll_expiry_check()
await ctx.send("Poll locked.")
@Task.create(IntervalTrigger(minutes=1))
async def poll_expiry_check(self):
@ -376,7 +391,9 @@ class Polls(Extension):
polls_q: List[PollsModel] = PollsModel.select().where(PollsModel.expires < now)
for poll_entry in polls_q:
channel: GuildText = await self.client.fetch_channel(int(poll_entry.channel_id))
channel: GuildText = await self.client.fetch_channel(
int(poll_entry.channel_id)
)
if not channel:
continue
@ -386,8 +403,6 @@ class Polls(Extension):
await message.edit(components=[])
def setup(client: Client):
PollsModel.create_table()

View File

@ -536,7 +536,7 @@ class SelfRoles(Extension):
)
@context_menu(
name="Regenerate group",
name="Regenerate role group message",
context_type=CommandTypes.MESSAGE,
dm_permission=False,
default_member_permissions=Permissions.MANAGE_ROLES,