Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,10 @@ async def wait_for_connected(self) -> None:
def snippets(self) -> typing.Dict[str, str]:
return self.config["snippets"]

@property
def args(self) -> typing.Dict[str, str]:
return self.config["args"]

@property
def aliases(self) -> typing.Dict[str, str]:
return self.config["aliases"]
Expand Down
277 changes: 275 additions & 2 deletions cogs/modmail.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from dateutil import parser

from core import checks
from core.models import DMDisabled, PermissionLevel, SimilarCategoryConverter, getLogger
from core.models import DMDisabled, PermissionLevel, SimilarCategoryConverter, UnseenFormatter, getLogger
from core.paginator import EmbedPaginatorSession
from core.thread import Thread
from core.time import UserFriendlyTime, human_timedelta
Expand All @@ -25,6 +25,10 @@
logger = getLogger(__name__)


# Arg names reserved by formatreply commands (channel, recipient, author).
RESERVED_ARG_NAMES = {"channel", "recipient", "author"}


class Modmail(commands.Cog):
"""Commands directly related to Modmail functionality."""

Expand Down Expand Up @@ -209,7 +213,7 @@ async def snippet(self, ctx, *, name: str.lower = None):

When `{prefix}snippet` is used by itself, this will retrieve
a list of snippets that are currently set. `{prefix}snippet-name` will show what the
snippet point to.
snippet points to.

To create a snippet:
- `{prefix}snippet add snippet-name A pre-defined text.`
Expand Down Expand Up @@ -535,6 +539,214 @@ async def snippet_rename(self, ctx, name: str.lower, *, value):
embed = create_not_found_embed(name, self.bot.snippets.keys(), "Snippet")
await ctx.send(embed=embed)

@commands.group(invoke_without_command=True)
@checks.has_permissions(PermissionLevel.SUPPORTER)
async def args(self, ctx, *, name: str.lower = None):
"""
Create dynamic args for use in replies.

When `{prefix}args` is used by itself, this will retrieve
a list of args that are currently set. `{prefix}args name` will show what the
arg points to.

To create an arg:
- `{prefix}args add arg-name A value.`

You can use your arg in a reply with `{arg-name}`.
Comment thread
lorenzo132 marked this conversation as resolved.
"""
Comment on lines +552 to +556

if name is not None:
if name == "compact":
embeds = []

for i, names in enumerate(zip_longest(*(iter(sorted(self.bot.args)),) * 15)):
description = format_description(i, names)
embed = discord.Embed(color=self.bot.main_color, description=description)
embed.set_author(name="Args", icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128))
embeds.append(embed)

session = EmbedPaginatorSession(ctx, *embeds)
await session.run()
return

if name not in self.bot.args:
embed = create_not_found_embed(name, self.bot.args.keys(), "Arg")
Comment thread
lorenzo132 marked this conversation as resolved.
else:
val = self.bot.args[name]
embed = discord.Embed(
title=f'Arg - "{name}":',
description=val,
color=self.bot.main_color,
)
return await ctx.send(embed=embed)

if not self.bot.args:
embed = discord.Embed(
color=self.bot.error_color,
description="You don't have any args at the moment.",
)
embed.set_footer(text=f'See "{self.bot.prefix}help args add" for how to add an arg.')
embed.set_author(
name="Args",
icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128),
)
return await ctx.send(embed=embed)

embeds = [discord.Embed(color=self.bot.main_color) for _ in range((len(self.bot.args) // 10) + 1)]
for embed in embeds:
embed.set_author(name="Args", icon_url=self.bot.get_guild_icon(guild=ctx.guild, size=128))

for i, arg in enumerate(sorted(self.bot.args.items())):
embeds[i // 10].add_field(name=arg[0], value=return_or_truncate(arg[1], 350), inline=False)

session = EmbedPaginatorSession(ctx, *embeds)
await session.run()

@args.command(name="raw")
@checks.has_permissions(PermissionLevel.SUPPORTER)
async def args_raw(self, ctx, *, name: str.lower):
"""
View the raw content of an arg.
"""
if name not in self.bot.args:
embed = create_not_found_embed(name, self.bot.args.keys(), "Arg")
else:
val = truncate(escape_code_block(self.bot.args[name]), 2048 - 7)
embed = discord.Embed(
title=f'Raw arg - "{name}":',
description=f"```\n{val}```",
color=self.bot.main_color,
)

return await ctx.send(embed=embed)

@args.command(name="add", aliases=["create", "make"])
Comment thread
lorenzo132 marked this conversation as resolved.
@checks.has_permissions(PermissionLevel.SUPPORTER)
async def args_add(self, ctx, name: str.lower, *, value: commands.clean_content):
"""
Add an arg.

To add an arg, simply do: ```
{prefix}args add name value
```
"""
if name in self.bot.args:
Comment thread
lorenzo132 marked this conversation as resolved.
embed = discord.Embed(
title="Error",
color=self.bot.error_color,
description=f"Arg `{name}` already exists.",
)
return await ctx.send(embed=embed)

if name in RESERVED_ARG_NAMES:
embed = discord.Embed(
title="Error",
color=self.bot.error_color,
description=f"Arg name `{name}` is reserved (used by formatreply commands). "
f"Reserved names: {', '.join(f'`{n}`' for n in sorted(RESERVED_ARG_NAMES))}.",
)
return await ctx.send(embed=embed)

if len(name) > 120:
embed = discord.Embed(
title="Error",
color=self.bot.error_color,
description="Arg names cannot be longer than 120 characters.",
)
return await ctx.send(embed=embed)
Comment on lines +650 to +656

self.bot.args[name] = value
await self.bot.config.update()

embed = discord.Embed(
title="Added arg",
color=self.bot.main_color,
description="Successfully created arg.",
)
return await ctx.send(embed=embed)
Comment thread
sebkuip marked this conversation as resolved.
Comment thread
sebkuip marked this conversation as resolved.
Comment thread
sebkuip marked this conversation as resolved.

@args.command(name="remove", aliases=["del", "delete"])
@checks.has_permissions(PermissionLevel.SUPPORTER)
async def args_remove(self, ctx, *, name: str.lower):
"""Remove an arg."""
Comment thread
lorenzo132 marked this conversation as resolved.
if name in self.bot.args:
self.bot.args.pop(name)
await self.bot.config.update()
embed = discord.Embed(
title="Removed arg",
color=self.bot.main_color,
description=f"Arg `{name}` is now deleted.",
)
else:
embed = create_not_found_embed(name, self.bot.args.keys(), "Arg")
await ctx.send(embed=embed)

@args.command(name="edit")
@checks.has_permissions(PermissionLevel.SUPPORTER)
async def args_edit(self, ctx, name: str.lower, *, value):
"""
Edit an arg.
"""
if name in self.bot.args:
self.bot.args[name] = value
await self.bot.config.update()

embed = discord.Embed(
title="Edited arg",
color=self.bot.main_color,
description=f'`{name}` will now be replaced with "{value}".',
)
Comment thread
lorenzo132 marked this conversation as resolved.
else:
embed = create_not_found_embed(name, self.bot.args.keys(), "Arg")
await ctx.send(embed=embed)

@args.command(name="rename")
@checks.has_permissions(PermissionLevel.SUPPORTER)
async def args_rename(self, ctx, name: str.lower, *, value):
"""
Rename an arg.
"""
if name not in self.bot.args:
embed = create_not_found_embed(name, self.bot.args.keys(), "Arg")
return await ctx.send(embed=embed)

if value in self.bot.args:
embed = discord.Embed(
title="Error",
color=self.bot.error_color,
description=f"Arg `{value}` already exists.",
)
return await ctx.send(embed=embed)

if value in RESERVED_ARG_NAMES:
embed = discord.Embed(
title="Error",
color=self.bot.error_color,
description=f"Arg name `{value}` is reserved (used by formatreply commands). "
f"Reserved names: {', '.join(f'`{n}`' for n in sorted(RESERVED_ARG_NAMES))}.",
)
return await ctx.send(embed=embed)

if len(value) > 120:
embed = discord.Embed(
title="Error",
color=self.bot.error_color,
description="Arg names cannot be longer than 120 characters.",
)
return await ctx.send(embed=embed)
Comment on lines +730 to +736

old_arg_value = self.bot.args[name]
self.bot.args.pop(name)
self.bot.args[value] = old_arg_value
await self.bot.config.update()

embed = discord.Embed(
title="Renamed arg",
color=self.bot.main_color,
description=f'`{name}` has been renamed to "{value}".',
)
await ctx.send(embed=embed)
Comment thread
sebkuip marked this conversation as resolved.

@commands.command(usage="<category> [options]")
@checks.has_permissions(PermissionLevel.MODERATOR)
@checks.thread_only()
Expand Down Expand Up @@ -1510,6 +1722,19 @@ async def reply(self, ctx, *, msg: str = ""):
automatically embedding image URLs.
"""

if self.bot.args:
msg = UnseenFormatter().format(msg, **self.bot.args)

Comment on lines +1725 to +1727
if len(msg) > 4096:
return await ctx.send(
embed=discord.Embed(
title="Error",
color=self.bot.error_color,
description="The resulting message is too long to fit in an embed description "
f"({len(msg)}/4096 characters). Please shorten your message or args.",
)
)

# Ensure logs record only the reply text, not the command.
ctx.message.content = msg
async with safe_typing(ctx):
Expand All @@ -1532,10 +1757,22 @@ async def freply(self, ctx, *, msg: str = ""):
"""
msg = self.bot.formatter.format(
msg,
**self.bot.args,
channel=ctx.channel,
recipient=ctx.thread.recipient,
author=ctx.message.author,
Comment thread
lorenzo132 marked this conversation as resolved.
)

if len(msg) > 4096:
return await ctx.send(
embed=discord.Embed(
title="Error",
color=self.bot.error_color,
description="The resulting message is too long to fit in an embed description "
f"({len(msg)}/4096 characters). Please shorten your message or args.",
)
)

# Ensure logs record only the reply text, not the command.
ctx.message.content = msg
async with safe_typing(ctx):
Expand All @@ -1558,10 +1795,22 @@ async def fareply(self, ctx, *, msg: str = ""):
"""
msg = self.bot.formatter.format(
msg,
**self.bot.args,
channel=ctx.channel,
recipient=ctx.thread.recipient,
author=ctx.message.author,
Comment thread
sebkuip marked this conversation as resolved.
)

if len(msg) > 4096:
return await ctx.send(
embed=discord.Embed(
title="Error",
color=self.bot.error_color,
description="The resulting message is too long to fit in an embed description "
f"({len(msg)}/4096 characters). Please shorten your message or args.",
)
)

# Ensure logs record only the reply text, not the command.
ctx.message.content = msg
async with safe_typing(ctx):
Expand All @@ -1584,10 +1833,22 @@ async def fpreply(self, ctx, *, msg: str = ""):
"""
msg = self.bot.formatter.format(
msg,
**self.bot.args,
channel=ctx.channel,
recipient=ctx.thread.recipient,
author=ctx.message.author,
Comment thread
sebkuip marked this conversation as resolved.
)

if len(msg) > 4096:
return await ctx.send(
embed=discord.Embed(
title="Error",
color=self.bot.error_color,
description="The resulting message is too long to fit in an embed description "
f"({len(msg)}/4096 characters). Please shorten your message or args.",
)
)

# Ensure logs record only the reply text, not the command.
ctx.message.content = msg
async with safe_typing(ctx):
Expand All @@ -1610,10 +1871,22 @@ async def fpareply(self, ctx, *, msg: str = ""):
"""
msg = self.bot.formatter.format(
msg,
**self.bot.args,
channel=ctx.channel,
recipient=ctx.thread.recipient,
author=ctx.message.author,
Comment thread
sebkuip marked this conversation as resolved.
)

if len(msg) > 4096:
return await ctx.send(
embed=discord.Embed(
title="Error",
color=self.bot.error_color,
description="The resulting message is too long to fit in an embed description "
f"({len(msg)}/4096 characters). Please shorten your message or args.",
)
)

# Ensure logs record only the reply text, not the command.
ctx.message.content = msg
async with safe_typing(ctx):
Expand Down
2 changes: 1 addition & 1 deletion cogs/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ async def alias(self, ctx, *, name: str.lower = None):

When `{prefix}alias` is used by itself, this will retrieve
a list of alias that are currently set. `{prefix}alias-name` will show what the
alias point to.
alias points to.

To use alias:

Expand Down
1 change: 1 addition & 0 deletions core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class ConfigManager:
"override_command_level": {},
# threads
"snippets": {},
"args": {},
"notification_squad": {},
"subscriptions": {},
"closures": {},
Expand Down
Loading
Loading