Add tag support
This commit is contained in:
parent
78a2ac014d
commit
f7553f5c8f
3 changed files with 304 additions and 3 deletions
276
jarvis/cogs/tags.py
Normal file
276
jarvis/cogs/tags.py
Normal file
|
@ -0,0 +1,276 @@
|
||||||
|
"""JARVIS Tags Cog."""
|
||||||
|
import asyncio
|
||||||
|
import re
|
||||||
|
|
||||||
|
from jarvis_core.db import q
|
||||||
|
from jarvis_core.db.models import Setting, Tag
|
||||||
|
from naff import AutocompleteContext, Client, Extension, InteractionContext
|
||||||
|
from naff.models.discord.embed import EmbedField
|
||||||
|
from naff.models.discord.enums import Permissions
|
||||||
|
from naff.models.discord.modal import InputText, Modal, TextStyles
|
||||||
|
from naff.models.naff.application_commands import (
|
||||||
|
OptionTypes,
|
||||||
|
SlashCommand,
|
||||||
|
slash_option,
|
||||||
|
)
|
||||||
|
from thefuzz import process
|
||||||
|
|
||||||
|
from jarvis.utils import build_embed
|
||||||
|
|
||||||
|
invites = re.compile(
|
||||||
|
r"(?:https?://)?(?:www.)?(?:discord.(?:gg|io|me|li)|discord(?:app)?.com/invite)/([^\s/]+?)(?=\b)", # noqa: E501
|
||||||
|
flags=re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TagCog(Extension):
|
||||||
|
|
||||||
|
tag = SlashCommand(name="tag", description="Create and manage custom tags")
|
||||||
|
|
||||||
|
@tag.subcommand(sub_cmd_name="get", sub_cmd_description="Get a tag")
|
||||||
|
@slash_option(
|
||||||
|
name="name",
|
||||||
|
description="Tag to get",
|
||||||
|
autocomplete=True,
|
||||||
|
opt_type=OptionTypes.STRING,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
async def _get(self, ctx: InteractionContext, name: str) -> None:
|
||||||
|
tag = await Tag.find_one(q(guild=ctx.guild.id, name=name))
|
||||||
|
if not tag:
|
||||||
|
await ctx.send(
|
||||||
|
"Well this is awkward, looks like the tag was deleted just now", ephemeral=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await ctx.send(tag.content)
|
||||||
|
|
||||||
|
@tag.subcommand(sub_cmd_name="create", sub_cmd_description="Create a tag")
|
||||||
|
async def _create(self, ctx: InteractionContext) -> None:
|
||||||
|
modal = Modal(
|
||||||
|
title="Create a new tag!",
|
||||||
|
components=[
|
||||||
|
InputText(
|
||||||
|
label="Tag name",
|
||||||
|
placeholder="name",
|
||||||
|
style=TextStyles.SHORT,
|
||||||
|
custom_id="name",
|
||||||
|
max_length=40,
|
||||||
|
),
|
||||||
|
InputText(
|
||||||
|
label="Content",
|
||||||
|
placeholder="Content to send here",
|
||||||
|
style=TextStyles.PARAGRAPH,
|
||||||
|
custom_id="content",
|
||||||
|
max_length=1000,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
await ctx.send_modal(modal)
|
||||||
|
try:
|
||||||
|
response = await self.bot.wait_for_modal(modal, author=ctx.author.id, timeout=60 * 5)
|
||||||
|
name = response.responses.get("name")
|
||||||
|
content = response.responses.get("content")
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return
|
||||||
|
|
||||||
|
noinvite = await Setting.find_one(q(guild=ctx.guild.id, setting="noinvite"))
|
||||||
|
|
||||||
|
if (invites.search(content) or invites.search(name)) and noinvite.value:
|
||||||
|
await response.send(
|
||||||
|
"Listen, don't use this to try and bypass the rules", ephemeral=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
elif not content.strip() or not name.strip():
|
||||||
|
await response.send("Content and name required", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
tag = await Tag.find_one(q(guild=ctx.guild.id, name=name))
|
||||||
|
if tag:
|
||||||
|
await response.send("That tag already exists", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
content = re.sub(r"\\?([@<])", r"\\\g<1>", content)
|
||||||
|
|
||||||
|
tag = Tag(
|
||||||
|
creator=ctx.author.id,
|
||||||
|
name=name,
|
||||||
|
content=content,
|
||||||
|
guild=ctx.guild.id,
|
||||||
|
)
|
||||||
|
await tag.commit()
|
||||||
|
|
||||||
|
embed = build_embed(
|
||||||
|
title="Tag Created",
|
||||||
|
description=f"{ctx.author.mention} created a new tag",
|
||||||
|
fields=[EmbedField(name="Name", value=name), EmbedField(name="Content", value=content)],
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.set_author(
|
||||||
|
name=ctx.author.username + "#" + ctx.author.discriminator,
|
||||||
|
icon_url=ctx.author.display_avatar.url,
|
||||||
|
)
|
||||||
|
|
||||||
|
await response.send(embeds=embed)
|
||||||
|
|
||||||
|
@tag.subcommand(sub_cmd_name="edit", sub_cmd_description="Edit a tag")
|
||||||
|
@slash_option(
|
||||||
|
name="name",
|
||||||
|
description="Tag name",
|
||||||
|
opt_type=OptionTypes.STRING,
|
||||||
|
autocomplete=True,
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
async def _edit(self, ctx: InteractionContext, name: str) -> None:
|
||||||
|
tag = await Tag.find_one(q(guild=ctx.guild.id, name=name))
|
||||||
|
if not tag:
|
||||||
|
await ctx.send("Tag not found", ephemeral=True)
|
||||||
|
return
|
||||||
|
elif tag.creator != ctx.author.id and not (
|
||||||
|
ctx.author.has_permission(Permissions.ADMINISTRATOR)
|
||||||
|
or ctx.author.has_permission(Permissions.MANAGE_MESSAGES)
|
||||||
|
):
|
||||||
|
await ctx.send("You didn't create this tag, ask the creator to edit it", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
modal = Modal(
|
||||||
|
title="Edit a tag!",
|
||||||
|
components=[
|
||||||
|
InputText(
|
||||||
|
label="Tag name",
|
||||||
|
value=tag.name,
|
||||||
|
style=TextStyles.SHORT,
|
||||||
|
custom_id="name",
|
||||||
|
max_length=40,
|
||||||
|
),
|
||||||
|
InputText(
|
||||||
|
label="Content",
|
||||||
|
value=tag.content,
|
||||||
|
style=TextStyles.PARAGRAPH,
|
||||||
|
custom_id="content",
|
||||||
|
max_length=1000,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
await ctx.send_modal(modal)
|
||||||
|
try:
|
||||||
|
response = await self.bot.wait_for_modal(modal, author=ctx.author.id, timeout=60 * 5)
|
||||||
|
name = response.responses.get("name")
|
||||||
|
content = response.responses.get("content")
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return
|
||||||
|
|
||||||
|
noinvite = await Setting.find_one(q(guild=ctx.guild.id, setting="noinvite"))
|
||||||
|
|
||||||
|
if (invites.search(content) or invites.search(name)) and noinvite.value:
|
||||||
|
await response.send(
|
||||||
|
"Listen, don't use this to try and bypass the rules", ephemeral=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
elif not content.strip() or not name.strip():
|
||||||
|
await response.send("Content and name required", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
tag.content = re.sub(r"\\?([@<])", r"\\\g<1>", content)
|
||||||
|
tag.name = name
|
||||||
|
|
||||||
|
await tag.commit()
|
||||||
|
|
||||||
|
embed = build_embed(
|
||||||
|
title="Tag Updated",
|
||||||
|
description=f"{ctx.author.mention} updated a tag",
|
||||||
|
fields=[
|
||||||
|
EmbedField(name="Name", value=name),
|
||||||
|
EmbedField(name="Content", value=tag.content),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.set_author(
|
||||||
|
name=ctx.author.username + "#" + ctx.author.discriminator,
|
||||||
|
icon_url=ctx.author.display_avatar.url,
|
||||||
|
)
|
||||||
|
|
||||||
|
await response.send(embeds=embed)
|
||||||
|
|
||||||
|
@tag.subcommand(sub_cmd_name="delete", sub_cmd_description="Delete a tag")
|
||||||
|
@slash_option(
|
||||||
|
name="name",
|
||||||
|
description="Tag name",
|
||||||
|
opt_type=OptionTypes.STRING,
|
||||||
|
required=True,
|
||||||
|
autocomplete=True,
|
||||||
|
)
|
||||||
|
async def _delete(self, ctx: InteractionContext, name: str) -> None:
|
||||||
|
tag = await Tag.find_one(q(guild=ctx.guild.id, name=name))
|
||||||
|
if not tag:
|
||||||
|
await ctx.send("Tag not found", ephemeral=True)
|
||||||
|
return
|
||||||
|
elif tag.creator != ctx.author.id and not (
|
||||||
|
ctx.author.has_permission(Permissions.ADMINISTRATOR)
|
||||||
|
or ctx.author.has_permission(Permissions.MANAGE_MESSAGES)
|
||||||
|
):
|
||||||
|
await ctx.send(
|
||||||
|
"You didn't create this tag, ask the creator to delete it", ephemeral=True
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await tag.delete()
|
||||||
|
await ctx.send(f"Tag `{name}` deleted")
|
||||||
|
|
||||||
|
@tag.subcommand(sub_cmd_name="info", sub_cmd_description="Get info on a tag")
|
||||||
|
@slash_option(
|
||||||
|
name="name",
|
||||||
|
description="Tag name",
|
||||||
|
opt_type=OptionTypes.STRING,
|
||||||
|
required=True,
|
||||||
|
autocomplete=True,
|
||||||
|
)
|
||||||
|
async def _info(self, ctx: InteractionContext, name: str) -> None:
|
||||||
|
tag = await Tag.find_one(q(guild=ctx.guild.id, name=name))
|
||||||
|
if not tag:
|
||||||
|
await ctx.send("Tag not found", ephemeral=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
username, discrim, url = None, None, None
|
||||||
|
author = await self.bot.fetch_user(tag.creator)
|
||||||
|
if author:
|
||||||
|
username = author.username
|
||||||
|
discrim = author.discriminator
|
||||||
|
url = author.display_avatar.url
|
||||||
|
|
||||||
|
ts = int(tag.created_at.timestamp())
|
||||||
|
|
||||||
|
embed = build_embed(
|
||||||
|
title="Tag Info",
|
||||||
|
description=f"Here's the info on the tag `{name}`",
|
||||||
|
fields=[
|
||||||
|
EmbedField(name="Name", value=name),
|
||||||
|
EmbedField(name="Content", value=tag.content),
|
||||||
|
EmbedField(name="Created At", value=f"<t:{ts}:F>"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.set_author(
|
||||||
|
name=f"{username}#{discrim}" if username else "Unknown Author",
|
||||||
|
icon_url=url,
|
||||||
|
)
|
||||||
|
|
||||||
|
await ctx.send(embeds=embed)
|
||||||
|
|
||||||
|
@_get.autocomplete("name")
|
||||||
|
@_edit.autocomplete("name")
|
||||||
|
@_delete.autocomplete("name")
|
||||||
|
@_info.autocomplete("name")
|
||||||
|
async def _autocomplete(self, ctx: AutocompleteContext, name: str) -> None:
|
||||||
|
tags = await Tag.find(q(guild=ctx.guild.id)).to_list(None)
|
||||||
|
names = [tag.name for tag in tags]
|
||||||
|
results = process.extract(name, names, limit=25)
|
||||||
|
choices = [{"name": r[0], "value": r[0]} for r in results]
|
||||||
|
await ctx.send(choices=choices)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot: Client) -> None:
|
||||||
|
"""Add TagCog to JARVIS"""
|
||||||
|
TagCog(bot)
|
30
poetry.lock
generated
30
poetry.lock
generated
|
@ -414,7 +414,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jarvis-core"
|
name = "jarvis-core"
|
||||||
version = "0.11.0"
|
version = "0.12.0"
|
||||||
description = "JARVIS core"
|
description = "JARVIS core"
|
||||||
category = "main"
|
category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
|
@ -435,7 +435,7 @@ umongo = "^3.1.0"
|
||||||
type = "git"
|
type = "git"
|
||||||
url = "https://git.zevaryx.com/stark-industries/jarvis/jarvis-core.git"
|
url = "https://git.zevaryx.com/stark-industries/jarvis/jarvis-core.git"
|
||||||
reference = "main"
|
reference = "main"
|
||||||
resolved_reference = "fce3b829a30583abd48b3221825c3ed303610de8"
|
resolved_reference = "4cece14cd9cd1604bf12845339fdb5f66b6c0719"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jinxed"
|
name = "jinxed"
|
||||||
|
@ -825,6 +825,14 @@ requests-toolbelt = ">=0.9.1"
|
||||||
autocompletion = ["argcomplete (>=1.10.0,<3)"]
|
autocompletion = ["argcomplete (>=1.10.0,<3)"]
|
||||||
yaml = ["PyYaml (>=5.2)"]
|
yaml = ["PyYaml (>=5.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-levenshtein"
|
||||||
|
version = "0.12.2"
|
||||||
|
description = "Python extension for computing string edit distances and similarities."
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytz"
|
name = "pytz"
|
||||||
version = "2022.1"
|
version = "2022.1"
|
||||||
|
@ -955,6 +963,20 @@ category = "main"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thefuzz"
|
||||||
|
version = "0.19.0"
|
||||||
|
description = "Fuzzy string matching in python"
|
||||||
|
category = "main"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
python-levenshtein = {version = ">=0.12", optional = true, markers = "extra == \"speedup\""}
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
speedup = ["python-levenshtein (>=0.12)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
|
@ -1168,7 +1190,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-
|
||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "1.1"
|
lock-version = "1.1"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "549486089ef65c69b0932e799efdbb0d22d6631d925de845ec4b6ba98d57c527"
|
content-hash = "d4a3ccd2f79fe0c323784bfba2c5950817257639bbdcdb57a6e71682a8846504"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
aiofile = [
|
aiofile = [
|
||||||
|
@ -1929,6 +1951,7 @@ python-gitlab = [
|
||||||
{file = "python-gitlab-3.5.0.tar.gz", hash = "sha256:29ae7fb9b8c9aeb2e6e19bd2fd04867e93ecd7af719978ce68fac0cf116ab30d"},
|
{file = "python-gitlab-3.5.0.tar.gz", hash = "sha256:29ae7fb9b8c9aeb2e6e19bd2fd04867e93ecd7af719978ce68fac0cf116ab30d"},
|
||||||
{file = "python_gitlab-3.5.0-py3-none-any.whl", hash = "sha256:73b5aa6502efa557ee1a51227cceb0243fac5529627da34f08c5f265bf50417c"},
|
{file = "python_gitlab-3.5.0-py3-none-any.whl", hash = "sha256:73b5aa6502efa557ee1a51227cceb0243fac5529627da34f08c5f265bf50417c"},
|
||||||
]
|
]
|
||||||
|
python-levenshtein = []
|
||||||
pytz = [
|
pytz = [
|
||||||
{file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"},
|
{file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"},
|
||||||
{file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"},
|
{file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"},
|
||||||
|
@ -2094,6 +2117,7 @@ smmap = [
|
||||||
{file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
|
{file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
|
||||||
{file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
|
{file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
|
||||||
]
|
]
|
||||||
|
thefuzz = []
|
||||||
tomli = [
|
tomli = [
|
||||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
|
|
|
@ -29,6 +29,7 @@ naff = { version = "^1.2.0", extras = ["orjson"] }
|
||||||
nafftrack = {git = "https://github.com/artem30801/nafftrack.git", rev = "master"}
|
nafftrack = {git = "https://github.com/artem30801/nafftrack.git", rev = "master"}
|
||||||
ansitoimg = "^2022.1"
|
ansitoimg = "^2022.1"
|
||||||
nest-asyncio = "^1.5.5"
|
nest-asyncio = "^1.5.5"
|
||||||
|
thefuzz = {extras = ["speedup"], version = "^0.19.0"}
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
black = {version = "^22.3.0", allow-prereleases = true}
|
black = {version = "^22.3.0", allow-prereleases = true}
|
||||||
|
|
Loading…
Add table
Reference in a new issue