From ef7d015fa56eea6d96ae77ffd5d2d8f27a792707 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 9 Mar 2024 22:54:50 -0700 Subject: [PATCH 01/13] SNIPPETS: Add database revision for snippets table. --- bot/migrations/V5__add_table_for_snippets.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 bot/migrations/V5__add_table_for_snippets.sql diff --git a/bot/migrations/V5__add_table_for_snippets.sql b/bot/migrations/V5__add_table_for_snippets.sql new file mode 100644 index 0000000..8b4255f --- /dev/null +++ b/bot/migrations/V5__add_table_for_snippets.sql @@ -0,0 +1,12 @@ +-- Revision Version: V5 +-- Revises: V4 +-- Creation Date: 2024-03-10 05:51:39.252162 UTC +-- Reason: add table for snippets + +CREATE TABLE IF NOT EXISTS snippets +( + guild_id bigint NOT NULL, + name VARCHAR(100), + content TEXT, + PRIMARY KEY (guild_id, name) +); From 2c72f232760d62a9edec6a30eb2a813ff7a3bfa2 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 16 Mar 2024 18:42:19 -0600 Subject: [PATCH 02/13] SNIPPETS: Add stub python file for snippet cog. --- bot/cogs/snippets.py | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 bot/cogs/snippets.py diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py new file mode 100644 index 0000000..307f494 --- /dev/null +++ b/bot/cogs/snippets.py @@ -0,0 +1,47 @@ +from discord.ext import commands +from libs.utils import GuildContext +from rodhaj import Rodhaj + + +class Snippets(commands.Cog): + """ + Cog for snippet-related commands (#21) + """ + + def __init__(self, bot: Rodhaj): + self._bot = bot + + @commands.guild_only() + @commands.group(name="snippet") + async def snippet_cmd(self, ctx: GuildContext): + if ctx.invoked_subcommand is None: + await ctx.send("placeholder for base command") + + @commands.guild_only() + @snippet_cmd.command() + async def remove(self, ctx, *args): + await ctx.send("placeholder for snippet remove") + + @commands.guild_only() + @snippet_cmd.command() + async def new(self, ctx, *args): + await ctx.send("placeholder for snippet new") + + @commands.guild_only() + @snippet_cmd.command() + async def show(self, ctx, *args): + await ctx.send("placeholder for snippet show") + + @commands.guild_only() + @snippet_cmd.command() + async def list(self, ctx, *args): + await ctx.send("placeholder for snippet list") + + @commands.guild_only() + @snippet_cmd.command() + async def edit(self, ctx, *args): + await ctx.send("placeholder for snippet edit") + + +async def setup(bot: Rodhaj): + await bot.add_cog(Snippets(bot)) From 2a702a7aeff19c1aa3fb8bdf12864a712801270b Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 16 Mar 2024 22:26:34 -0600 Subject: [PATCH 03/13] SNIPPETS: Add base functionality for snippet show. --- bot/cogs/snippets.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index 307f494..c1af66d 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -1,3 +1,4 @@ +import discord from discord.ext import commands from libs.utils import GuildContext from rodhaj import Rodhaj @@ -29,8 +30,28 @@ async def new(self, ctx, *args): @commands.guild_only() @snippet_cmd.command() - async def show(self, ctx, *args): - await ctx.send("placeholder for snippet show") + async def show(self, ctx: GuildContext, name: str): + query = """ + SELECT content FROM snippets + WHERE guild_id = $1 AND name = $2 + """ + data = await self._bot.pool.fetchrow(query, ctx.guild.id, name) + if data is None: + ret_embed = discord.Embed( + title="Oops...", + colour=discord.Colour.red(), + description=f"The snippet `{name}` was not found. " + + "To create a new snippet with this name, " + + f"please run `snippet create {name} `", + ) + await ctx.reply(embed=ret_embed, ephemeral=True) + else: + ret_data = discord.Embed( + title=f"Snippet information for `{name}`", + colour=discord.Colour.green(), + description=data[0], + ) + await ctx.reply(embed=ret_data, ephemeral=True) @commands.guild_only() @snippet_cmd.command() From 685687ec680f288229701f647d9b4f80148abcc8 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 16 Mar 2024 22:43:22 -0600 Subject: [PATCH 04/13] SNIPPETS: Add base functionality for snippet remove. --- bot/cogs/snippets.py | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index c1af66d..61c441e 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -20,8 +20,32 @@ async def snippet_cmd(self, ctx: GuildContext): @commands.guild_only() @snippet_cmd.command() - async def remove(self, ctx, *args): - await ctx.send("placeholder for snippet remove") + async def remove(self, ctx: GuildContext, name: str): + query = """ + DELETE FROM snippets + WHERE guild_id = $1 AND name = $2 + RETURNING name + """ + result = await self._bot.pool.fetchrow(query, ctx.guild.id, name) + if result is None: + await ctx.reply( + embed=discord.Embed( + title="Deletion failed", + colour=discord.Colour.red(), + description=f"Snippet `{name}` was not found and " + + "hence was not deleted.", + ), + ephemeral=True, + ) + else: + await ctx.reply( + embed=discord.Embed( + title="Deletion successful", + colour=discord.Colour.green(), + description=f"Snippet `{name}` was deleted successfully", + ), + ephemeral=True, + ) @commands.guild_only() @snippet_cmd.command() From 878835ba2947852eeb674af50a499df8b674bba5 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sun, 17 Mar 2024 01:36:54 -0600 Subject: [PATCH 05/13] SNIPPETS: Add base functionality for snippet edit. --- bot/cogs/snippets.py | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index 61c441e..7f05df1 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -1,3 +1,5 @@ +from typing import Optional + import discord from discord.ext import commands from libs.utils import GuildContext @@ -82,10 +84,44 @@ async def show(self, ctx: GuildContext, name: str): async def list(self, ctx, *args): await ctx.send("placeholder for snippet list") + async def edit_prompt_user(self, ctx: GuildContext, name: str): + raise NotImplementedError("TODO: Add prompt for editing snippet.") + @commands.guild_only() @snippet_cmd.command() - async def edit(self, ctx, *args): - await ctx.send("placeholder for snippet edit") + async def edit(self, ctx: GuildContext, name: str, content: Optional[str]): + if content is None: + await self.edit_prompt_user(ctx, name) + return + query = """ + UPDATE snippets + SET content = $3 + WHERE guild_id = $1 AND name = $2 + RETURNING name + """ + + result = await self._bot.pool.fetchrow(query, ctx.guild.id, name, content) + if result is None: + await ctx.reply( + embed=discord.Embed( + title="Oops...", + colour=discord.Colour.red(), + description=f"Cannot edit snippet `{name}` as there is no such " + + "snippet. To create a new snippet with the corresponding " + + f"name, please run `snippet new {name} `.", + ), + ephemeral=True, + ) + else: + await ctx.reply( + embed=discord.Embed( + title="Snippet changed", + colour=discord.Colour.green(), + description=f"The contents of snippet {result[0]} has been " + + f"changed to \n\n{content}", + ), + ephemeral=True, + ) async def setup(bot: Rodhaj): From 0466650688821c86f16ac8e3904ef036d0170b20 Mon Sep 17 00:00:00 2001 From: No767 <73260931+No767@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:31:28 -0700 Subject: [PATCH 06/13] Apply better schema --- .../V5__add_table_for_snippets.sql | 0 bot/migrations/V7__snippets.sql | 36 +++++++++++++++++++ docker/pg/init.sh | 1 + docs/dev-guide/intro.rst | 1 + 4 files changed, 38 insertions(+) rename bot/{migrations => }/V5__add_table_for_snippets.sql (100%) create mode 100644 bot/migrations/V7__snippets.sql diff --git a/bot/migrations/V5__add_table_for_snippets.sql b/bot/V5__add_table_for_snippets.sql similarity index 100% rename from bot/migrations/V5__add_table_for_snippets.sql rename to bot/V5__add_table_for_snippets.sql diff --git a/bot/migrations/V7__snippets.sql b/bot/migrations/V7__snippets.sql new file mode 100644 index 0000000..e650afb --- /dev/null +++ b/bot/migrations/V7__snippets.sql @@ -0,0 +1,36 @@ +-- Revision Version: V7 +-- Revises: V6 +-- Creation Date: 2024-06-25 22:08:13.554817 UTC +-- Reason: snippets + +CREATE TABLE IF NOT EXISTS snippets ( + id SERIAL PRIMARY KEY, + name TEXT, + content TEXT, + uses INTEGER DEFAULT (0), + owner_id BIGINT, + location_id BIGINT, + created_at TIMESTAMPTZ DEFAULT (now() at time zone 'utc') +); + +-- Create indices to speed up regular and trigram searches +CREATE INDEX IF NOT EXISTS snippets_name_idx ON snippets (name); +CREATE INDEX IF NOT EXISTS snippets_location_id_idx ON snippets (location_id); +CREATE INDEX IF NOT EXISTS snippets_name_trgm_idx ON snippets USING GIN (name gin_trgm_ops); +CREATE INDEX IF NOT EXISTS snippets_name_lower_idx ON snippets (LOWER(name)); +CREATE UNIQUE INDEX IF NOT EXISTS snippets_uniq_idx ON snippets (LOWER(name), location_id); + +CREATE TABLE IF NOT EXISTS snippets_lookup ( + id SERIAL PRIMARY KEY, + name TEXT, + location_id BIGINT, + owner_id BIGINT, + created_at TIMESTAMPTZ DEFAULT (now() at time zone 'utc'), + snippets_id INTEGER REFERENCES snippets (id) ON DELETE CASCADE ON UPDATE NO ACTION +); + +CREATE INDEX IF NOT EXISTS snippets_lookup_name_idx ON snippets_lookup (name); +CREATE INDEX IF NOT EXISTS snippets_lookup_location_id_idx ON snippets_lookup (location_id); +CREATE INDEX IF NOT EXISTS snippets_lookup_name_trgm_idx ON snippets_lookup USING GIN (name gin_trgm_ops); +CREATE INDEX IF NOT EXISTS snippets_lookup_name_lower_idx ON snippets_lookup (LOWER(name)); +CREATE UNIQUE INDEX IF NOT EXISTS snippets_lookup_uniq_idx ON snippets_lookup (LOWER(name), location_id); \ No newline at end of file diff --git a/docker/pg/init.sh b/docker/pg/init.sh index 94f6a61..bb43f9f 100644 --- a/docker/pg/init.sh +++ b/docker/pg/init.sh @@ -4,4 +4,5 @@ set -e psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL CREATE ROLE rodhaj WITH LOGIN PASSWORD '$RODHAJ_PASSWORD'; CREATE DATABASE rodhaj OWNER rodhaj; + CREATE EXTENSION IF NOT EXISTS pg_trgm; EOSQL diff --git a/docs/dev-guide/intro.rst b/docs/dev-guide/intro.rst index 5c18cc5..8d4f369 100644 --- a/docs/dev-guide/intro.rst +++ b/docs/dev-guide/intro.rst @@ -96,6 +96,7 @@ The following SQL queries can be used to create the user and database: CREATE ROLE rodhaj WITH LOGIN PASSWORD 'somepass'; CREATE DATABASE rodhaj OWNER rodhaj; + CREATE EXTENSION IF NOT EXISTS pg_trgm; .. note:: From 168925f06c14b326c17360286005ed642ca3e638 Mon Sep 17 00:00:00 2001 From: No767 <73260931+No767@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:46:37 -0700 Subject: [PATCH 07/13] rework cog to better align standards and provide better structure --- bot/cogs/snippets.py | 67 ++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index 7f05df1..4cbd671 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -1,34 +1,47 @@ -from typing import Optional +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional import discord from discord.ext import commands -from libs.utils import GuildContext -from rodhaj import Rodhaj + +if TYPE_CHECKING: + from libs.utils.context import GuildContext + + from rodhaj import Rodhaj class Snippets(commands.Cog): - """ - Cog for snippet-related commands (#21) - """ + """Send or display pre-written text to users""" def __init__(self, bot: Rodhaj): - self._bot = bot + self.bot = bot + self.pool = self.bot.pool + + # Editing Utilities + + async def edit_prompt_user(self, ctx: GuildContext, name: str): + raise NotImplementedError("TODO: Add prompt for editing snippet.") @commands.guild_only() - @commands.group(name="snippet") - async def snippet_cmd(self, ctx: GuildContext): - if ctx.invoked_subcommand is None: - await ctx.send("placeholder for base command") + @commands.hybrid_group(name="snippets", alias=["snippet"], fallback="get") + async def snippet(self, ctx: GuildContext, *, name: str): + """Allows for use snippets of text for later retrieval or for quicker responses + + If an subcommand is not called, then this will search + the database for the requested snippet + """ + await ctx.send("Implement getting snippets here") @commands.guild_only() - @snippet_cmd.command() + @snippet.command() async def remove(self, ctx: GuildContext, name: str): query = """ DELETE FROM snippets WHERE guild_id = $1 AND name = $2 RETURNING name """ - result = await self._bot.pool.fetchrow(query, ctx.guild.id, name) + result = await self.pool.fetchrow(query, ctx.guild.id, name) if result is None: await ctx.reply( embed=discord.Embed( @@ -49,19 +62,27 @@ async def remove(self, ctx: GuildContext, name: str): ephemeral=True, ) + # TODO: Run all str inputs through custom converters @commands.guild_only() - @snippet_cmd.command() - async def new(self, ctx, *args): + @snippet.command() + async def new(self, ctx, name: str, *, content: Optional[str] = None): await ctx.send("placeholder for snippet new") @commands.guild_only() - @snippet_cmd.command() + @snippet.command(name="list") + async def snippets_list( + self, ctx: GuildContext, json: Optional[bool] = False + ) -> None: + await ctx.send("list snippets") + + @commands.guild_only() + @snippet.command() async def show(self, ctx: GuildContext, name: str): query = """ SELECT content FROM snippets WHERE guild_id = $1 AND name = $2 """ - data = await self._bot.pool.fetchrow(query, ctx.guild.id, name) + data = await self.pool.fetchrow(query, ctx.guild.id, name) if data is None: ret_embed = discord.Embed( title="Oops...", @@ -80,15 +101,7 @@ async def show(self, ctx: GuildContext, name: str): await ctx.reply(embed=ret_data, ephemeral=True) @commands.guild_only() - @snippet_cmd.command() - async def list(self, ctx, *args): - await ctx.send("placeholder for snippet list") - - async def edit_prompt_user(self, ctx: GuildContext, name: str): - raise NotImplementedError("TODO: Add prompt for editing snippet.") - - @commands.guild_only() - @snippet_cmd.command() + @snippet.command() async def edit(self, ctx: GuildContext, name: str, content: Optional[str]): if content is None: await self.edit_prompt_user(ctx, name) @@ -100,7 +113,7 @@ async def edit(self, ctx: GuildContext, name: str, content: Optional[str]): RETURNING name """ - result = await self._bot.pool.fetchrow(query, ctx.guild.id, name, content) + result = await self.pool.fetchrow(query, ctx.guild.id, name, content) if result is None: await ctx.reply( embed=discord.Embed( From 70a5dd1aeeff5863f2e34df67c0600f1006a444c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 Jun 2024 02:47:08 +0000 Subject: [PATCH 08/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bot/cogs/snippets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index 4cbd671..1a4c801 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -7,7 +7,6 @@ if TYPE_CHECKING: from libs.utils.context import GuildContext - from rodhaj import Rodhaj From 1a5802c5100f96ede58a56ce523bad76492527dc Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Fri, 12 Jul 2024 22:24:16 -0600 Subject: [PATCH 09/13] SNIPPET: Change queries to match the new db schemas. --- bot/cogs/snippets.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index 1a4c801..3f243a0 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -37,8 +37,8 @@ async def snippet(self, ctx: GuildContext, *, name: str): async def remove(self, ctx: GuildContext, name: str): query = """ DELETE FROM snippets - WHERE guild_id = $1 AND name = $2 - RETURNING name + WHERE name = $2 + RETURNING id """ result = await self.pool.fetchrow(query, ctx.guild.id, name) if result is None: @@ -79,9 +79,9 @@ async def snippets_list( async def show(self, ctx: GuildContext, name: str): query = """ SELECT content FROM snippets - WHERE guild_id = $1 AND name = $2 + WHERE name = $1 """ - data = await self.pool.fetchrow(query, ctx.guild.id, name) + data = await self.pool.fetchrow(query, name) if data is None: ret_embed = discord.Embed( title="Oops...", @@ -107,12 +107,12 @@ async def edit(self, ctx: GuildContext, name: str, content: Optional[str]): return query = """ UPDATE snippets - SET content = $3 - WHERE guild_id = $1 AND name = $2 + SET content = $2 + WHERE name = $1 RETURNING name """ - result = await self.pool.fetchrow(query, ctx.guild.id, name, content) + result = await self.pool.fetchrow(query, name, content) if result is None: await ctx.reply( embed=discord.Embed( From ae250ef7d5b3455ced3cd342a9ccc92d7354477c Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 13 Jul 2024 13:30:04 -0600 Subject: [PATCH 10/13] SNIPPET: Add draft model functions for snippets. --- bot/libs/snippets/model.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 bot/libs/snippets/model.py diff --git a/bot/libs/snippets/model.py b/bot/libs/snippets/model.py new file mode 100644 index 0000000..18fcb82 --- /dev/null +++ b/bot/libs/snippets/model.py @@ -0,0 +1,36 @@ +from collections import namedtuple + +import asyncpg.pool + +SnippetHeader = namedtuple( + "SnippetHeader", + ["id", "name", "content", "uses", "owner_id", "location_id", + "created_at"] +) + + +async def get_snippet( + pool: asyncpg.pool.Pool, guild_id: int, owner_id: int, snippet_name: str +): + fields_str = ",".join(SnippetHeader._fields) + query = f""" + SELECT {fields_str} from snippets + WHERE location_id = $1 AND owner_id = $2 AND name = $3 + """ + row = await pool.fetchrow(query, guild_id, owner_id, snippet_name) + if not row: + return None + return SnippetHeader(*row) + + +async def create_snippet( + pool: asyncpg.pool.Pool, guild_id: int, owner_id: int, snippet_name: str, + snippet_text: str +): + query = """ + INSERT INTO snippets (owner_id, location_id, name, content) + VALUES ($1, $2, $3, $4) + """ + await pool.execute( + query, guild_id, owner_id, snippet_name, snippet_text + ) From d633fbc877c6f541ba856421ae4ccd37afd22108 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 13 Jul 2024 13:30:14 -0600 Subject: [PATCH 11/13] SNIPPET: Add basic snippet-releated views. --- bot/libs/snippets/views.py | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 bot/libs/snippets/views.py diff --git a/bot/libs/snippets/views.py b/bot/libs/snippets/views.py new file mode 100644 index 0000000..13e8567 --- /dev/null +++ b/bot/libs/snippets/views.py @@ -0,0 +1,64 @@ +from libs.utils import RoboView, GuildContext +import discord.ui + +from rodhaj import Rodhaj + + +class SnippetCreationModal(discord.ui.Modal, title="Editing Snippet"): + content = discord.ui.TextInput( + label="Snippet message", + placeholder="Call me Ishmael. Some years ago—never mind " + + "how long precisely...", + style=discord.TextStyle.paragraph, + ) + + def __init__(self, bot: Rodhaj, context: GuildContext, name: str): + super().__init__(timeout=12 * 3600) + self._bot = bot + self._ctx = context + self._snippet_name = name + self.title = f"Creating Snippet {name}" + + async def on_submit(self, interaction: discord.Interaction): + await interaction.response.defer() + self._bot.dispatch( + "snippet_create", + self._ctx.guild, + self._ctx.author, + self._snippet_name, + self.content.value, + self._ctx, + ) + self.stop() + + +class SnippetPreCreationConfirmationView(discord.ui.View): + def __init__(self, bot: Rodhaj, ctx: GuildContext, snippet_name: str, timeout=15): + super().__init__(timeout=timeout) + self._bot = bot + self._ctx = ctx + self._snippet_name = snippet_name + + @discord.ui.button(label="Create Snippet", style=discord.ButtonStyle.green) + async def create_snippet( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + if interaction.user.id != self._ctx.author.id: + return + button.disabled = True + modal = SnippetCreationModal(self._bot, self._ctx, self._snippet_name) + await interaction.response.send_modal(modal) + await interaction.edit_original_response( + content="Creating Snippet...", view=None + ) + await modal.wait() + await interaction.delete_original_response() + self.stop() + + async def on_timeout(self): + self.clear_items() + self.stop() + + +class SnippetInfoView(RoboView): + pass From 32f7f2d2103bbe99bb360b1f6258ad7017cdec23 Mon Sep 17 00:00:00 2001 From: Supakorn 'Jamie' Rassameemasmuang Date: Sat, 13 Jul 2024 13:30:48 -0600 Subject: [PATCH 12/13] SNIPPET: Add basic snippet create routine. --- bot/cogs/snippets.py | 65 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index 3f243a0..eed8c3f 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -1,10 +1,14 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Optional +from typing import TYPE_CHECKING, Optional, Union +import asyncpg.exceptions import discord from discord.ext import commands +from libs.snippets.model import create_snippet, get_snippet +from libs.snippets.views import SnippetPreCreationConfirmationView + if TYPE_CHECKING: from libs.utils.context import GuildContext from rodhaj import Rodhaj @@ -64,8 +68,41 @@ async def remove(self, ctx: GuildContext, name: str): # TODO: Run all str inputs through custom converters @commands.guild_only() @snippet.command() - async def new(self, ctx, name: str, *, content: Optional[str] = None): - await ctx.send("placeholder for snippet new") + async def new( + self, + ctx: GuildContext, + name: str, + *, + content: Optional[str] = None, + ): + if ( + await get_snippet(self.pool, ctx.guild.id, ctx.message.author.id, name) + is not None + ): + await ctx.send( + content=f"Snippet `{name}` already exists!", + ) + return + + if not content: + timeout = 15 + confirmation_view = SnippetPreCreationConfirmationView( + self.bot, ctx, name, timeout + ) + await ctx.reply( + content=f"Create snippet with id `{name}`?", + view=confirmation_view, + delete_after=timeout, + ) + else: + self.bot.dispatch( + "snippet_create", + ctx.guild, + ctx.message.author, + name, + content, + ctx, + ) @commands.guild_only() @snippet.command(name="list") @@ -111,7 +148,6 @@ async def edit(self, ctx: GuildContext, name: str, content: Optional[str]): WHERE name = $1 RETURNING name """ - result = await self.pool.fetchrow(query, name, content) if result is None: await ctx.reply( @@ -135,6 +171,27 @@ async def edit(self, ctx: GuildContext, name: str, content: Optional[str]): ephemeral=True, ) + @commands.Cog.listener() + async def on_snippet_create( + self, + guild: discord.Guild, + creator: Union[discord.User, discord.Member], + snippet_name: str, + snippet_text: str, + response_context: GuildContext, + ): + try: + await create_snippet( + self.pool, guild.id, creator.id, snippet_name, snippet_text + ) + if response_context: + await response_context.send( + "Snippet created successfully", delete_after=5 + ) + except asyncpg.exceptions.UniqueViolationError: + if response_context: + await response_context.send("Snippet already exists", delete_after=5) + async def setup(bot: Rodhaj): await bot.add_cog(Snippets(bot)) From 97b55875d12c5e8f746bfb10ed2185e8213823d6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2024 19:31:56 +0000 Subject: [PATCH 13/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- bot/cogs/snippets.py | 27 +++++++++++++-------------- bot/libs/snippets/model.py | 16 ++++++++-------- bot/libs/snippets/views.py | 7 +++---- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/bot/cogs/snippets.py b/bot/cogs/snippets.py index eed8c3f..73216f0 100644 --- a/bot/cogs/snippets.py +++ b/bot/cogs/snippets.py @@ -5,7 +5,6 @@ import asyncpg.exceptions import discord from discord.ext import commands - from libs.snippets.model import create_snippet, get_snippet from libs.snippets.views import SnippetPreCreationConfirmationView @@ -69,15 +68,15 @@ async def remove(self, ctx: GuildContext, name: str): @commands.guild_only() @snippet.command() async def new( - self, - ctx: GuildContext, - name: str, - *, - content: Optional[str] = None, + self, + ctx: GuildContext, + name: str, + *, + content: Optional[str] = None, ): if ( - await get_snippet(self.pool, ctx.guild.id, ctx.message.author.id, name) - is not None + await get_snippet(self.pool, ctx.guild.id, ctx.message.author.id, name) + is not None ): await ctx.send( content=f"Snippet `{name}` already exists!", @@ -173,12 +172,12 @@ async def edit(self, ctx: GuildContext, name: str, content: Optional[str]): @commands.Cog.listener() async def on_snippet_create( - self, - guild: discord.Guild, - creator: Union[discord.User, discord.Member], - snippet_name: str, - snippet_text: str, - response_context: GuildContext, + self, + guild: discord.Guild, + creator: Union[discord.User, discord.Member], + snippet_name: str, + snippet_text: str, + response_context: GuildContext, ): try: await create_snippet( diff --git a/bot/libs/snippets/model.py b/bot/libs/snippets/model.py index 18fcb82..cb2544b 100644 --- a/bot/libs/snippets/model.py +++ b/bot/libs/snippets/model.py @@ -4,13 +4,12 @@ SnippetHeader = namedtuple( "SnippetHeader", - ["id", "name", "content", "uses", "owner_id", "location_id", - "created_at"] + ["id", "name", "content", "uses", "owner_id", "location_id", "created_at"], ) async def get_snippet( - pool: asyncpg.pool.Pool, guild_id: int, owner_id: int, snippet_name: str + pool: asyncpg.pool.Pool, guild_id: int, owner_id: int, snippet_name: str ): fields_str = ",".join(SnippetHeader._fields) query = f""" @@ -24,13 +23,14 @@ async def get_snippet( async def create_snippet( - pool: asyncpg.pool.Pool, guild_id: int, owner_id: int, snippet_name: str, - snippet_text: str + pool: asyncpg.pool.Pool, + guild_id: int, + owner_id: int, + snippet_name: str, + snippet_text: str, ): query = """ INSERT INTO snippets (owner_id, location_id, name, content) VALUES ($1, $2, $3, $4) """ - await pool.execute( - query, guild_id, owner_id, snippet_name, snippet_text - ) + await pool.execute(query, guild_id, owner_id, snippet_name, snippet_text) diff --git a/bot/libs/snippets/views.py b/bot/libs/snippets/views.py index 13e8567..81603bf 100644 --- a/bot/libs/snippets/views.py +++ b/bot/libs/snippets/views.py @@ -1,6 +1,5 @@ -from libs.utils import RoboView, GuildContext import discord.ui - +from libs.utils import GuildContext, RoboView from rodhaj import Rodhaj @@ -8,7 +7,7 @@ class SnippetCreationModal(discord.ui.Modal, title="Editing Snippet"): content = discord.ui.TextInput( label="Snippet message", placeholder="Call me Ishmael. Some years ago—never mind " - + "how long precisely...", + + "how long precisely...", style=discord.TextStyle.paragraph, ) @@ -41,7 +40,7 @@ def __init__(self, bot: Rodhaj, ctx: GuildContext, snippet_name: str, timeout=15 @discord.ui.button(label="Create Snippet", style=discord.ButtonStyle.green) async def create_snippet( - self, interaction: discord.Interaction, button: discord.ui.Button + self, interaction: discord.Interaction, button: discord.ui.Button ): if interaction.user.id != self._ctx.author.id: return