Quotes data and cog skeleton.
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
INSERT INTO version_history (component, from_version, to_version, author)
|
||||||
|
VALUES ('QUOTES', 0, 1, 'Initial Creation');
|
||||||
|
|
||||||
|
CREATE TABLE quotes(
|
||||||
|
quoteid INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
||||||
|
communityid INTEGER NOT NULL REFERENCES communities(communityid) ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
created_by INTEGER REFERENCES user_profiles(profileid) ON UPDATE CASCADE ON DELETE NO ACTION,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
_timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
CREATE TRIGGER quotes_timestamp BEFORE UPDATE ON quotes
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION update_timestamp_column();
|
||||||
|
|
||||||
|
CREATE VIEW
|
||||||
|
quotes_info
|
||||||
|
AS
|
||||||
|
SELECT
|
||||||
|
*,
|
||||||
|
row_number() + 1 OVER (PARITION BY communityid ORDER BY created_at ASC)
|
||||||
|
FROM
|
||||||
|
quotes
|
||||||
|
ORDER BY (communityid, created_at);
|
||||||
|
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|||||||
5
quotes/__init__.py
Normal file
5
quotes/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
from .discord import setup
|
||||||
28
quotes/data.py
Normal file
28
quotes/data.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from data import Registry, RowModel, Table
|
||||||
|
from data.columns import Integer, Timestamp, String
|
||||||
|
|
||||||
|
from weakref import WeakValueDictionary
|
||||||
|
|
||||||
|
class Quote(RowModel):
|
||||||
|
_tablename_ = 'quotes'
|
||||||
|
_cache_ = WeakValueDictionary()
|
||||||
|
|
||||||
|
quoteid = Integer(primary=True)
|
||||||
|
communityid = Integer()
|
||||||
|
content = String()
|
||||||
|
created_by = Integer()
|
||||||
|
created_at = Timestamp()
|
||||||
|
_timestamp = Timestamp()
|
||||||
|
|
||||||
|
|
||||||
|
class QuoteInfo(Quote):
|
||||||
|
_tablename_ = 'quotes_info'
|
||||||
|
_readonly_ = True
|
||||||
|
_cache_ = WeakValueDictionary()
|
||||||
|
|
||||||
|
quotelabel = Integer()
|
||||||
|
|
||||||
|
|
||||||
|
class QuotesData(Registry):
|
||||||
|
quotes = Quote.table
|
||||||
|
quotes_info = QuoteInfo.table
|
||||||
5
quotes/discord/__init__.py
Normal file
5
quotes/discord/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .. import logger
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
from .cog import QuoteCog
|
||||||
|
await bot.add_cog(QuoteCog(bot))
|
||||||
92
quotes/discord/cog.py
Normal file
92
quotes/discord/cog.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
from typing import Optional
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
import discord
|
||||||
|
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 . import logger
|
||||||
|
from ..data import QuotesData
|
||||||
|
from ..quotes import QuoteRegistry
|
||||||
|
|
||||||
|
|
||||||
|
class QuoteCog(LionCog):
|
||||||
|
def __init__(self, bot: LionBot):
|
||||||
|
self.data = bot.db.load_registry(QuotesData())
|
||||||
|
self.quotes = QuoteRegistry(self.data)
|
||||||
|
|
||||||
|
async def cog_load(self):
|
||||||
|
await self.data.init()
|
||||||
|
await self.quotes.init()
|
||||||
|
|
||||||
|
# ----- API -----
|
||||||
|
async def quote_acmpl(self, interaction: discord.Interaction, partial: str):
|
||||||
|
# TODO
|
||||||
|
...
|
||||||
|
|
||||||
|
# ----- Commands ------
|
||||||
|
@cmds.hybrid_command(
|
||||||
|
name='quote',
|
||||||
|
description="Display a random quote."
|
||||||
|
)
|
||||||
|
async def quote_cmd(self, ctx: LionContext):
|
||||||
|
# TODO
|
||||||
|
...
|
||||||
|
|
||||||
|
@cmds.hybrid_group(
|
||||||
|
name='quotes',
|
||||||
|
description="Base command group for quotes management.",
|
||||||
|
)
|
||||||
|
async def quotes_grp(self, ctx: LionContext):
|
||||||
|
# TODO
|
||||||
|
# Call the quotes list command
|
||||||
|
...
|
||||||
|
|
||||||
|
@cmds.hybrid_command(
|
||||||
|
name='addquote',
|
||||||
|
description="Create a new quote. Use without arguments to add a multiline quote."
|
||||||
|
)
|
||||||
|
@quotes_grp.command(
|
||||||
|
name='add',
|
||||||
|
description="Create a new quote. Use without arguments to add a multiline quote."
|
||||||
|
)
|
||||||
|
@appcmds.describe(
|
||||||
|
content="Content of the quote to add"
|
||||||
|
)
|
||||||
|
async def quotes_add_cmd(self, ctx: LionContext, content: Optional[str]):
|
||||||
|
# TODO
|
||||||
|
...
|
||||||
|
|
||||||
|
@quotes_grp.command(
|
||||||
|
name='del',
|
||||||
|
description="Delete a saved quote (WARNING: This will change all quote numbers.)"
|
||||||
|
)
|
||||||
|
@appcmds.describe(
|
||||||
|
quote="Select the quote to delete, or write the number."
|
||||||
|
)
|
||||||
|
async def quotes_del_cmd(self, ctx: LionContext, quote: str):
|
||||||
|
# TODO
|
||||||
|
...
|
||||||
|
|
||||||
|
@quotes_grp.command(
|
||||||
|
name='list',
|
||||||
|
description="Display the community quotes. Quotes may also be added/edited/deleted here."
|
||||||
|
)
|
||||||
|
async def quotes_list_cmd(self, ctx: LionContext):
|
||||||
|
# TODO
|
||||||
|
...
|
||||||
|
|
||||||
|
@quotes_grp.command(
|
||||||
|
name='edit',
|
||||||
|
description="Edit a saved quote."
|
||||||
|
)
|
||||||
|
@appcmds.describe(
|
||||||
|
quote="Select the quote to delete, or write the number."
|
||||||
|
)
|
||||||
|
async def quotes_edit_cmd(self, ctx: LionContext, quote: str):
|
||||||
|
# TODO: Move quote to QuoteConverter?
|
||||||
|
# TODO
|
||||||
|
...
|
||||||
41
quotes/quotes.py
Normal file
41
quotes/quotes.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from typing import Optional
|
||||||
|
from .data import Quote, QuoteInfo, QuotesData
|
||||||
|
|
||||||
|
|
||||||
|
class QuoteRegistry:
|
||||||
|
def __init__(self, data: QuotesData):
|
||||||
|
self.data = data
|
||||||
|
# TODO: For efficiency we could have a cache here
|
||||||
|
# Caching quotesinfo by community.
|
||||||
|
# Particularly efficient for autocomplete.
|
||||||
|
|
||||||
|
async def init(self):
|
||||||
|
await self.data.init()
|
||||||
|
|
||||||
|
async def get_community_quotes(self, communityid: int) -> list[QuoteInfo]:
|
||||||
|
return await QuoteInfo.fetch_where(communityid=communityid)
|
||||||
|
|
||||||
|
async def get_quoteinfo(self, quoteid: int) -> Optional[QuoteInfo]:
|
||||||
|
return await QuoteInfo.fetch(quoteid)
|
||||||
|
|
||||||
|
async def get_quote(self, quoteid: int) -> Optional[Quote]:
|
||||||
|
return await Quote.fetch(quoteid)
|
||||||
|
|
||||||
|
async def get_quote_label(self, communityid: int, label: int) -> Optional[QuoteInfo]:
|
||||||
|
results = await QuoteInfo.fetch_where(communityid=communityid, quotelabel=label)
|
||||||
|
return results[0] if results else None
|
||||||
|
|
||||||
|
async def create_quote(
|
||||||
|
self,
|
||||||
|
communityid: int,
|
||||||
|
content: str,
|
||||||
|
created_by: Optional[int] = None
|
||||||
|
) -> QuoteInfo:
|
||||||
|
quote = await Quote.create(
|
||||||
|
content=content,
|
||||||
|
communityid=communityid,
|
||||||
|
created_by=created_by,
|
||||||
|
)
|
||||||
|
info = await QuoteInfo.fetch(quote.quoteid)
|
||||||
|
assert info is not None
|
||||||
|
return info
|
||||||
Reference in New Issue
Block a user