v2 refactor to label-based koans, and add discord

This commit is contained in:
2025-09-04 02:29:48 +10:00
parent fda6847671
commit 89173f1676
10 changed files with 679 additions and 76 deletions

View File

@@ -1,20 +1,30 @@
BEGIN; BEGIN;
-- Koan data {{{
INSERT INTO version_history (component, from_version, to_version, author)
VALUES ('KOANS', 0, 1, 'Initial Creation');
---- !koans lists koans. !koan gives a random koan. !koans add name ... !koans del name ... INSERT INTO version_history (component, from_version, to_version, author)
VALUES ('KOANS', 0, 2, 'Initial Creation');
CREATE TABLE koans( CREATE TABLE koans(
koanid INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, koanid INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
communityid INTEGER NOT NULL REFERENCES communities ON UPDATE CASCADE ON DELETE CASCADE, communityid INTEGER NOT NULL REFERENCES communities(communityid) ON DELETE CASCADE ON UPDATE CASCADE,
name TEXT NOT NULL, content TEXT NOT NULL,
message TEXT NOT NULL, created_by INTEGER REFERENCES user_profiles(profileid) ON UPDATE CASCADE ON DELETE NO ACTION,
deleted_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW() _timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW()
); );
CREATE TRIGGER koans_timestamp BEFORE UPDATE ON koans CREATE TRIGGER koans_timestamp BEFORE UPDATE ON koans
FOR EACH ROW EXECUTE FUNCTION update_timestamp_column(); FOR EACH ROW EXECUTE FUNCTION update_timestamp_column();
-- }}} CREATE VIEW
koans_info
AS
SELECT
*,
(deleted_at is not NULL) AS is_deleted,
(row_number() OVER (PARTITION BY communityid ORDER BY created_at ASC)) as koanlabel
FROM
koans
ORDER BY (communityid, created_at);
COMMIT; COMMIT;

View File

@@ -3,3 +3,4 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
from .twitch import setup as twitch_setup from .twitch import setup as twitch_setup
from .discord import setup

View File

@@ -1,32 +1,31 @@
from data import Registry, RowModel from data import Registry, RowModel, Table
from data.columns import String, Timestamp, Integer from data.columns import Bool, Integer, Timestamp, String
from weakref import WeakValueDictionary
class Koan(RowModel): class Koan(RowModel):
"""
Schema
======
CREATE TABLE koans(
koanid INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
communityid INTEGER NOT NULL REFERENCES communities ON UPDATE CASCADE ON DELETE CASCADE,
name TEXT NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
"""
_tablename_ = 'koans' _tablename_ = 'koans'
_cache_ = {} _cache_ = WeakValueDictionary()
koanid = Integer(primary=True) koanid = Integer(primary=True)
communityid = Integer() communityid = Integer()
name = String() content = String()
message = String() deleted_at = Timestamp()
created_by = Integer()
created_at = Timestamp() created_at = Timestamp()
_timestamp = Timestamp() _timestamp = Timestamp()
class KoanData(Registry): class KoanInfo(Koan):
VERSION = ('KOANS', 1) _tablename_ = 'koans_info'
_readonly_ = True
_cache_ = WeakValueDictionary()
koanlabel = Integer()
is_deleted = Bool()
class KoansData(Registry):
VERSION = ('KOANS', 2)
koans = Koan.table koans = Koan.table
koans_info = KoanInfo.table

View File

@@ -0,0 +1,5 @@
from .. import logger
async def setup(bot):
from .cog import KoanCog
await bot.add_cog(KoanCog(bot))

251
koans/discord/cog.py Normal file
View 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)

View File

@@ -0,0 +1 @@
from .koanlist import KoanListUI

View File

@@ -0,0 +1,271 @@
import asyncio
import discord
from discord.ui.select import select, Select
from discord.components import SelectOption
from discord.enums import ButtonStyle, TextStyle
from discord.ui.button import button, Button
from discord.ui.text_input import TextInput
from meta import LionBot, conf
from meta.errors import SafeCancellation
from utils.ui import MessageUI, input
from utils.lib import MessageArgs
from .. import logger
from ...koans import KoanRegistry
from ...lib import minify
class KoanListUI(MessageUI):
block_len = 10
def __init__(self, bot: LionBot, koans: KoanRegistry, communityid: int, profileid: int, callerid: int, **kwargs):
super().__init__(callerid=callerid, **kwargs)
self.bot = bot
self.koans = koans
self.communityid = communityid
self.caller_profileid = profileid
# Paging state
self._pagen = 0
self.blocks = [[]]
@property
def page_count(self):
return len(self.blocks)
@property
def pagen(self):
self._pagen = self._pagen % self.page_count
return self._pagen
@pagen.setter
def pagen(self, value):
self._pagen = value % self.page_count
@property
def current_page(self):
return self.blocks[self.pagen]
# ----- API -----
# ----- UI Components -----
@button(
label="New Koan",
style=ButtonStyle.green
)
async def new_koan_button(self, press: discord.Interaction, pressed: Button):
# Show the create koan modal
# Create koan flow.
try:
interaction, to_create = await input(
interaction=press,
title="Create Koan",
question="Koan Content",
timeout=300,
style=TextStyle.long,
min_length=1,
max_length=2000 - 8
)
except asyncio.TimeoutError:
raise SafeCancellation
await interaction.response.defer(thinking=True, ephemeral=True)
koan = await self.koans.create_koan(
communityid=self.communityid,
content=to_create,
created_by=self.caller_profileid
)
self.pagen = -1
await self.refresh(thinking=interaction)
# Koan edit and delete selectors
async def make_page_options(self):
options = []
for koan in self.current_page:
labelstr = f"#{koan.koanlabel}: "
minified = minify(koan.content, 100 - len(labelstr) - 1, strip=' >')
option = SelectOption(
label=f"{labelstr} {minified}",
value=str(koan.koanid)
)
options.append(option)
return options
@select(
cls=Select,
placeholder="Select to delete",
min_values=1, max_values=1
)
async def delete_menu(self, selection: discord.Interaction, selected: Select):
await selection.response.defer(thinking=True, ephemeral=True)
if selected.values:
koanid = int(selected.values[0])
await self.koans.delete_koan(koanid)
await self.refresh(thinking=selection)
async def refresh_delete_menu(self):
self.delete_menu.options = await self.make_page_options()
@select(
cls=Select,
placeholder="Select to edit",
min_values=1, max_values=1
)
async def edit_menu(self, selection: discord.Interaction, selected: Select):
if selected.values:
koanid = int(selected.values[0])
koan = await self.koans.get_koan(koanid)
assert koan is not None
try:
interaction, new_content = await input(
interaction=selection,
title="Edit Koan",
question="Koan Content",
timeout=300,
style=TextStyle.long,
default=koan.content,
min_length=1,
max_length=2000 - 8
)
await interaction.response.defer(thinking=True, ephemeral=True)
except asyncio.TimeoutError:
raise SafeCancellation
await koan.update(content=new_content)
await self.refresh(thinking=interaction)
async def refresh_edit_menu(self):
self.edit_menu.options = await self.make_page_options()
# Backwards
@button(emoji=conf.emojis.backward, style=ButtonStyle.grey)
async def prev_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True, ephemeral=True)
self.pagen -= 1
await self.refresh(thinking=press)
# Jump to page
@button(label="JUMP_PLACEHOLDER", style=ButtonStyle.blurple)
async def jump_button(self, press: discord.Interaction, pressed: Button):
"""
Jump-to-page button.
Loads a page-switch dialogue.
"""
t = self.bot.translator.t
try:
interaction, value = await input(
press,
title="Jump to page",
question="Page number to jump to"
)
value = value.strip()
except asyncio.TimeoutError:
return
if not value.lstrip('- ').isdigit():
error_embed = discord.Embed(
title="Invalid page number, please try again!",
colour=discord.Colour.brand_red()
)
await interaction.response.send_message(embed=error_embed, ephemeral=True)
else:
await interaction.response.defer(thinking=True)
pagen = int(value.lstrip('- '))
if value.startswith('-'):
pagen = -1 * pagen
elif pagen > 0:
pagen = pagen - 1
self.pagen = pagen
await self.refresh(thinking=interaction)
async def jump_button_refresh(self):
component = self.jump_button
component.label = f"{self.pagen + 1}/{self.page_count}"
component.disabled = (self.page_count <= 1)
# Forward
@button(emoji=conf.emojis.forward, style=ButtonStyle.grey)
async def next_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True)
self.pagen += 1
await self.refresh(thinking=press)
# Quit
@button(emoji=conf.emojis.cancel, style=ButtonStyle.red)
async def quit_button(self, press: discord.Interaction, pressed: Button):
"""
Quit the UI.
"""
await press.response.defer()
await self.quit()
# ----- UI Flow -----
async def make_message(self) -> MessageArgs:
if self.current_page:
lines = []
for koan in self.current_page:
labelstr = f"**#{koan.koanlabel}:** "
minified = minify(koan.content, 100 - len(labelstr) - 1, strip=' >')
lines.append(f"{labelstr} {minified}")
embed = discord.Embed(
title="Community Koans",
description='\n'.join(lines)
)
else:
embed = discord.Embed(
title="Community Koans",
description="No koans have been created! Use `/koans add` or the 'New Koan' button below to make a koan!"
)
return MessageArgs(embed=embed)
async def refresh_layout(self):
# Refresh menus
to_refresh = (
self.refresh_edit_menu(),
self.refresh_delete_menu(),
self.jump_button_refresh(),
)
await asyncio.gather(*to_refresh)
if self.page_count > 1:
# Multiple page case
# < Add Jump Close >
# edit
# delete
self.set_layout(
(self.prev_button,
self.new_koan_button,
self.jump_button,
self.quit_button,
self.next_button),
(self.edit_menu,),
(self.delete_menu,),
)
elif self.current_page:
# Single page case
# Add Close
# Edit
# Delete
self.set_layout(
(self.new_koan_button,
self.quit_button),
(self.edit_menu,),
(self.delete_menu,),
)
else:
# Add Close
self.set_layout(
(self.new_koan_button,
self.quit_button),
)
async def reload(self):
koans = await self.koans.get_community_koans(self.communityid)
blocks = [
koans[i:i+self.block_len]
for i in range(0, len(koans), self.block_len)
]
self.blocks = blocks or [[]]

View File

@@ -1,34 +1,42 @@
from typing import Optional from typing import Optional
from .data import Koan, KoanData from .data import Koan, KoanInfo, KoansData
from .lib import utc_now
class KoanRegistry: class KoanRegistry:
def __init__(self, data: KoanData): def __init__(self, data: KoansData):
self.data = data self.data = data
async def init(self): async def init(self):
await self.data.init() await self.data.init()
async def get_community_koans(self, communityid: int) -> list[Koan]: async def get_community_koans(self, communityid: int) -> list[KoanInfo]:
return await Koan.fetch_where(communityid=communityid) return await KoanInfo.fetch_where(communityid=communityid, is_deleted=False)
async def get_koaninfo(self, koanid: int) -> Optional[KoanInfo]:
return await KoanInfo.fetch(koanid)
async def get_koan(self, koanid: int) -> Optional[Koan]: async def get_koan(self, koanid: int) -> Optional[Koan]:
return await Koan.fetch(koanid) return await Koan.fetch(koanid)
async def get_koan_named(self, communityid: int, name: str) -> Optional[Koan]: async def get_koan_label(self, communityid: int, label: int) -> Optional[KoanInfo]:
name = name.lower() results = await KoanInfo.fetch_where(communityid=communityid, koanlabel=label, is_deleted=False)
koans = await Koan.fetch_where(communityid=communityid, name=name) return results[0] if results else None
if koans:
return koans[0]
else:
return None
async def create_koan(self, communityid: int, name: str, message: str) -> Koan: async def create_koan(
name = name.lower() self,
await Koan.table.delete_where(communityid=communityid, name=name) communityid: int,
content: str,
created_by: Optional[int] = None
) -> KoanInfo:
koan = await Koan.create( koan = await Koan.create(
content=content,
communityid=communityid, communityid=communityid,
name=name, created_by=created_by,
message=message
) )
return koan info = await KoanInfo.fetch(koan.koanid)
assert info is not None
return info
async def delete_koan(self, koanid: int):
await self.data.koans.update_where(koanid=koanid).set(deleted_at=utc_now())

19
koans/lib.py Normal file
View File

@@ -0,0 +1,19 @@
from typing import Optional
import datetime as dt
from datetime import datetime
def minify(content: str, maxlength: int, strip: Optional[str] = ' ', newlines: str = ' '):
content.replace('\n', newlines)
if strip:
content = content.strip(strip)
if len(content) > maxlength:
new_content = content[maxlength-3] + '...'
else:
new_content = content
return content
def utc_now():
return datetime.now(dt.UTC)

View File

@@ -7,13 +7,14 @@ from meta import Bot, Context
from . import logger from . import logger
from ..koans import KoanRegistry from ..koans import KoanRegistry
from ..data import KoanData from ..data import KoansData
from ..lib import minify
class KoanComponent(cmds.Component): class KoanComponent(cmds.Component):
def __init__(self, bot: Bot): def __init__(self, bot: Bot):
self.bot = bot self.bot = bot
self.data = bot.dbconn.load_registry(KoanData()) self.data = bot.dbconn.load_registry(KoansData())
self.koans = KoanRegistry(self.data) self.koans = KoanRegistry(self.data)
async def component_load(self): async def component_load(self):
@@ -25,6 +26,17 @@ class KoanComponent(cmds.Component):
async def event_message(self, payload: twitchio.ChatMessage) -> None: async def event_message(self, payload: twitchio.ChatMessage) -> None:
print(f"[{payload.broadcaster.name}] - {payload.chatter.name}: {payload.text}") print(f"[{payload.broadcaster.name}] - {payload.chatter.name}: {payload.text}")
async def component_command_error(self, payload):
try:
raise payload.exception
except cmds.ArgumentError:
if cmd := payload.context.command:
usage = f"{cmd.qualified_name} {cmd.signature}"
await payload.context.reply(f"USAGE: {usage}")
return False
except Exception:
pass
@cmds.group(invoke_fallback=True) @cmds.group(invoke_fallback=True)
async def koans(self, ctx: cmds.Context) -> None: async def koans(self, ctx: cmds.Context) -> None:
""" """
@@ -38,74 +50,100 @@ class KoanComponent(cmds.Component):
koans = await self.koans.get_community_koans(cid) koans = await self.koans.get_community_koans(cid)
if koans: if koans:
names = ', '.join(koan.name for koan in koans) count = len(koans)
await ctx.reply( parts = []
f"Koans: {names}" for koan in koans[-10:]:
) minified = minify(koan.content, 20)
formatted = f"#{koan.koanlabel}: {minified}"
parts.append(formatted)
partstr = '; '.join(parts)
laststr = "Last 10: " if count > 10 else ""
message = f"We have {count} koans! {laststr}{partstr}"
await ctx.reply(message)
else: else:
await ctx.reply("No koans have been made in this channel!") await ctx.reply("No koans have been made in this channel!")
@koans.command(name='add', aliases=['new', 'create']) @koans.command(name='add', aliases=['new', 'create'])
@cmds.is_moderator() @cmds.is_moderator()
async def koans_add(self, ctx: cmds.Context, name: str, *, text: str): async def koans_add(self, ctx: cmds.Context, *, content: str):
""" """
Add or overwrite a koan to this channel. Add or overwrite a koan to this channel.
!koans add wind This is a wind koan !koans add This is a wind koan
""" """
community = await self.bot.profiles.fetch_community(ctx.broadcaster) community = await self.bot.profiles.fetch_community(ctx.broadcaster)
cid = community.communityid cid = community.communityid
profile = await self.bot.profiles.fetch_profile(ctx.chatter)
pid = profile.profileid
name = name.lower() koan = await self.koans.create_koan(
existing = await self.koans.get_koan_named(cid, name) cid,
await self.koans.create_koan(cid, name, text) content,
created_by=pid,
)
if existing: await ctx.reply(f"Koan #{koan.koanlabel} created!")
await ctx.reply(f"Updated the koan '{name}'")
# TODO: Error message on signature failure
@koans.command(name='edit', aliases=['update',])
@cmds.is_moderator()
async def koans_edit(self, ctx: cmds.Context, label: int, *, new_content: str):
community = await self.bot.profiles.fetch_community(ctx.broadcaster)
cid = community.communityid
koan = await self.koans.get_koan_label(cid, label)
if koan:
await self.data.koans.update_where(koanid=koan.koanid).set(content=new_content)
await ctx.reply(f"Updated koan #{label}")
else: else:
await ctx.reply(f"Created the new koan '{name}'") await ctx.reply(f"Koan #{label}' does not exist to delete!")
@koans.command(name='del', aliases=['delete', 'rm', 'remove']) @koans.command(name='del', aliases=['delete', 'rm', 'remove'])
@cmds.is_moderator() @cmds.is_moderator()
async def koans_del(self, ctx: cmds.Context, name: str): async def koans_del(self, ctx: cmds.Context, label: int):
""" """
Remove a koan from this channel by name. Remove a koan from this channel by number.
!koans del wind !koans del 5
""" """
community = await self.bot.profiles.fetch_community(ctx.broadcaster) community = await self.bot.profiles.fetch_community(ctx.broadcaster)
cid = community.communityid cid = community.communityid
name = name.lower() koan = await self.koans.get_koan_label(cid, label)
koan = await self.koans.get_koan_named(cid, name)
if koan: if koan:
await koan.delete() await koan.delete()
await ctx.reply(f"Deleted the koan '{name}'") await ctx.reply(f"Deleted koan #{label}")
else: else:
await ctx.reply(f"The koan '{name}' does not exist to delete!") await ctx.reply(f"Koan #{label}' does not exist to delete!")
@cmds.command(name='koan') @cmds.command(name='koan')
async def koan(self, ctx: cmds.Context, name: Optional[str] = None): async def koan(self, ctx: cmds.Context, label: Optional[int] = None):
""" """
Show a koan from this channel. Optionally by name. Show a koan from this channel. Optionally by number.
!koan !koan
!koan wind !koan 5
""" """
community = await self.bot.profiles.fetch_community(ctx.broadcaster) community = await self.bot.profiles.fetch_community(ctx.broadcaster)
cid = community.communityid cid = community.communityid
if name is not None: if label is not None:
name = name.lower() koan = await self.koans.get_koan_label(cid, label)
koan = await self.koans.get_koan_named(cid, name)
if koan: if koan:
await ctx.reply(koan.message) await ctx.reply(koan.message)
else: else:
await ctx.reply(f"The requested koan '{name}' does not exist! Use '{ctx.prefix}koans' to see all the koans.") await ctx.reply(f"Koan #{label} does not exist!")
else: else:
koans = await self.koan.get_community_koans(communityid=cid) koans = await self.koan.get_community_koans(communityid=cid)
if koans: if koans:
koan = random.choice(koans) koan = random.choice(koans)
await ctx.reply(koan.message) formatted = f"Koan #{koan.koanlabel}: {koan.message}"
parts = [formatted[i: i:500] for i in range(0, len(formatted), 500)]
for part in parts:
await ctx.reply(part)
else: else:
await ctx.reply("This channel doesn't have any koans!") await ctx.reply("This channel doesn't have any koans!")