v2 refactor to label-based koans, and add discord
This commit is contained in:
251
koans/discord/cog.py
Normal file
251
koans/discord/cog.py
Normal file
@@ -0,0 +1,251 @@
|
||||
from typing import Optional
|
||||
import asyncio
|
||||
import random
|
||||
|
||||
import discord
|
||||
from discord.enums import TextStyle
|
||||
from discord.ext import commands as cmds
|
||||
from discord import app_commands as appcmds
|
||||
|
||||
from meta import LionBot, LionCog, LionContext
|
||||
from meta.errors import ResponseTimedOut, SafeCancellation, UserInputError
|
||||
from utils.ui import input
|
||||
|
||||
from ..data import KoanInfo, KoansData
|
||||
from ..koans import KoanRegistry
|
||||
from ..lib import minify
|
||||
|
||||
from . import logger
|
||||
from .ui import KoanListUI
|
||||
|
||||
|
||||
class KoanCog(LionCog):
|
||||
def __init__(self, bot: LionBot):
|
||||
self.bot = bot
|
||||
self.data = bot.db.load_registry(KoansData())
|
||||
self.koans = KoanRegistry(self.data)
|
||||
|
||||
async def cog_load(self):
|
||||
await self.data.init()
|
||||
await self.koans.init()
|
||||
|
||||
# ----- API -----
|
||||
async def koan_acmpl(self, interaction: discord.Interaction, partial: str):
|
||||
"""
|
||||
Autocomplete for a community-local koan selection.
|
||||
"""
|
||||
if not interaction.guild:
|
||||
return []
|
||||
|
||||
choices = []
|
||||
community = await self.bot.profiles.fetch_community(
|
||||
interaction.guild, interaction=interaction,
|
||||
touch=False
|
||||
)
|
||||
koans = await self.koans.get_community_koans(community.communityid)
|
||||
if not koans:
|
||||
nullchoice = appcmds.Choice(
|
||||
name="No koans have been created!",
|
||||
value="0",
|
||||
)
|
||||
choices.append(nullchoice)
|
||||
else:
|
||||
for koan in koans:
|
||||
labelstr = f"#{koan.koanlabel}:"
|
||||
if partial.lower() in labelstr + koan.content.lower():
|
||||
minified = minify(koan.content, 100 - len(labelstr) - 1, strip=' >')
|
||||
displayed = f"{labelstr} {minified}"
|
||||
choice = appcmds.Choice(
|
||||
name=displayed,
|
||||
value=str(koan.koanlabel),
|
||||
)
|
||||
choices.append(choice)
|
||||
if not choices:
|
||||
nullchoice = appcmds.Choice(
|
||||
name="No koans matching your input!",
|
||||
value="0",
|
||||
)
|
||||
choices.append(nullchoice)
|
||||
|
||||
return choices
|
||||
|
||||
async def resolve_koan(self, ctx: LionContext, koanstr: str) -> KoanInfo:
|
||||
"""
|
||||
Resolve a koan string provided as an argument.
|
||||
|
||||
Essentially only accepts integer koan labels.
|
||||
"""
|
||||
koanstr = koanstr.strip('# ')
|
||||
if not koanstr.isdigit():
|
||||
raise UserInputError(
|
||||
"Could not parse desired koan! Please enter the number or select from autocomplete options."
|
||||
)
|
||||
elif (label := int(koanstr)) == 0:
|
||||
raise UserInputError(
|
||||
"Invalid option selected!"
|
||||
)
|
||||
else:
|
||||
koan = await self.koans.get_koan_label(ctx.community.communityid, label)
|
||||
if not koan:
|
||||
raise UserInputError(
|
||||
f"Koan #{label} does not exist!"
|
||||
)
|
||||
else:
|
||||
return koan
|
||||
|
||||
# ----- Commands ------
|
||||
@cmds.hybrid_command(
|
||||
name='koan',
|
||||
description="Display a random koan."
|
||||
)
|
||||
@cmds.guild_only()
|
||||
async def koan_cmd(self, ctx: LionContext):
|
||||
koans = await self.koans.get_community_koans(ctx.community.communityid)
|
||||
if koans:
|
||||
# Select a random koan
|
||||
koan = random.choice(koans)
|
||||
if '\n' in koan.content:
|
||||
formatted = f"**#{koan.koanlabel}:**\n{koan.content}"
|
||||
else:
|
||||
formatted = f"**#{koan.koanlabel}:** {koan.content}"
|
||||
await ctx.reply(formatted)
|
||||
else:
|
||||
await ctx.reply("There are no koans to display!")
|
||||
|
||||
@cmds.hybrid_group(
|
||||
name='koans',
|
||||
description="Base command group for koans management.",
|
||||
)
|
||||
@cmds.has_permissions(manage_guild=True)
|
||||
@cmds.guild_only()
|
||||
async def koans_grp(self, ctx: LionContext):
|
||||
await self.koans_list_cmd(ctx)
|
||||
|
||||
@koans_grp.command(
|
||||
name='add',
|
||||
description="Create a new koan. Use without arguments to add a multiline koan."
|
||||
)
|
||||
@appcmds.describe(
|
||||
content="Content of the koan to add"
|
||||
)
|
||||
@cmds.has_permissions(manage_guild=True)
|
||||
async def koans_add_cmd(self, ctx: LionContext, *, content: Optional[str] = None):
|
||||
if content is not None:
|
||||
interaction = ctx.interaction
|
||||
to_create = content
|
||||
elif not ctx.interaction:
|
||||
invoked = ' '.join(
|
||||
(*ctx.invoked_parents, ctx.invoked_with or '')
|
||||
)
|
||||
await ctx.error_reply(
|
||||
"**USAGE:** {prefix}{cmd} <content>".format(
|
||||
prefix=ctx.prefix,
|
||||
cmd=invoked
|
||||
)
|
||||
)
|
||||
raise SafeCancellation
|
||||
else:
|
||||
try:
|
||||
interaction, to_create = await input(
|
||||
interaction=ctx.interaction,
|
||||
title="Create Koan",
|
||||
question="Koan Content",
|
||||
timeout=300,
|
||||
style=TextStyle.long,
|
||||
min_length=1,
|
||||
max_length=2000 - 8
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
raise SafeCancellation
|
||||
|
||||
if interaction:
|
||||
sender = interaction.response.send_message
|
||||
else:
|
||||
sender = ctx.reply
|
||||
|
||||
koan = await self.koans.create_koan(
|
||||
communityid=ctx.community.communityid,
|
||||
content=to_create,
|
||||
created_by=ctx.profile.profileid,
|
||||
)
|
||||
await sender(f"Koan #{koan.koanlabel} created!")
|
||||
|
||||
@koans_grp.command(
|
||||
name='del',
|
||||
description="Delete a saved koan."
|
||||
)
|
||||
@appcmds.describe(
|
||||
koanstr="Select the koan to delete, or write the number."
|
||||
)
|
||||
@cmds.has_permissions(manage_guild=True)
|
||||
@appcmds.rename(koanstr='koan')
|
||||
async def koans_del_cmd(self, ctx: LionContext, koanstr: str):
|
||||
koan = await self.resolve_koan(ctx, koanstr)
|
||||
label = koan.koanlabel
|
||||
await self.koans.delete_koan(koan.koanid)
|
||||
await ctx.reply(f"Koan #{label} was deleted.")
|
||||
|
||||
koans_del_cmd.autocomplete('koanstr')(koan_acmpl)
|
||||
|
||||
@koans_grp.command(
|
||||
name='list',
|
||||
description="Display the community koans. Koans may also be added/edited/deleted here."
|
||||
)
|
||||
@cmds.has_permissions(manage_guild=True)
|
||||
async def koans_list_cmd(self, ctx: LionContext):
|
||||
view = KoanListUI(self.bot, self.koans, ctx.community.communityid, ctx.profile.profileid, ctx.author.id)
|
||||
if ctx.interaction is None:
|
||||
await view.send(ctx.channel)
|
||||
else:
|
||||
await view.run(ctx.interaction)
|
||||
await view.wait()
|
||||
|
||||
@koans_grp.command(
|
||||
name='edit',
|
||||
description="Edit a saved koan."
|
||||
)
|
||||
@appcmds.describe(
|
||||
koanstr="Select the koan to edit, or write the number.",
|
||||
new_content="New content for the koan. Leave unselected to edit in multiline modal."
|
||||
)
|
||||
@appcmds.rename(koanstr='koan')
|
||||
@cmds.has_permissions(manage_guild=True)
|
||||
async def koans_edit_cmd(self, ctx: LionContext, koanstr: str, *, new_content: Optional[str] = None):
|
||||
koan = await self.resolve_koan(ctx, koanstr)
|
||||
if new_content is not None:
|
||||
interaction = ctx.interaction
|
||||
elif not ctx.interaction:
|
||||
invoked = ' '.join(
|
||||
(*ctx.invoked_parents, ctx.invoked_with or '')
|
||||
)
|
||||
await ctx.error_reply(
|
||||
"**USAGE:** {prefix}{cmd} <koanid> <new content>".format(
|
||||
prefix=ctx.prefix,
|
||||
cmd=invoked
|
||||
)
|
||||
)
|
||||
raise SafeCancellation
|
||||
else:
|
||||
try:
|
||||
interaction, new_content = await input(
|
||||
interaction=ctx.interaction,
|
||||
title="Edit Koan",
|
||||
question="Koan Content",
|
||||
timeout=300,
|
||||
style=TextStyle.long,
|
||||
default=koan.content,
|
||||
min_length=1,
|
||||
max_length=2000 - 8
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
raise SafeCancellation
|
||||
|
||||
if interaction:
|
||||
sender = interaction.response.send_message
|
||||
else:
|
||||
sender = ctx.reply
|
||||
|
||||
await self.data.koans.update_where(koanid=koan.koanid).set(content=new_content)
|
||||
await sender(f"Koan #{koan.koanlabel} Updated!")
|
||||
|
||||
koans_edit_cmd.autocomplete('koanstr')(koan_acmpl)
|
||||
Reference in New Issue
Block a user