System/member set command rework, should be more user friendly now
This commit is contained in:
parent
8e504fa879
commit
10746ae807
@ -6,6 +6,7 @@ from typing import Tuple, Optional, Union
|
||||
|
||||
from pluralkit import db
|
||||
from pluralkit.bot import embeds, utils
|
||||
from pluralkit.errors import PluralKitError
|
||||
from pluralkit.member import Member
|
||||
from pluralkit.system import System
|
||||
|
||||
@ -13,6 +14,7 @@ logger = logging.getLogger("pluralkit.bot.commands")
|
||||
|
||||
|
||||
def next_arg(arg_string: str) -> Tuple[str, Optional[str]]:
|
||||
# A basic quoted-arg parser
|
||||
if arg_string.startswith("\""):
|
||||
end_quote = arg_string[1:].find("\"") + 1
|
||||
if end_quote > 0:
|
||||
@ -135,18 +137,27 @@ import pluralkit.bot.commands.system_commands
|
||||
|
||||
|
||||
async def run_command(ctx: CommandContext, func):
|
||||
# lol nested try
|
||||
try:
|
||||
result = await func(ctx)
|
||||
try:
|
||||
await func(ctx)
|
||||
except PluralKitError as e:
|
||||
raise CommandError(e.message, e.help_page)
|
||||
except CommandError as e:
|
||||
content, embed = e.format()
|
||||
await ctx.reply(content=content, embed=embed)
|
||||
|
||||
|
||||
|
||||
async def command_dispatch(client: discord.Client, message: discord.Message, conn) -> bool:
|
||||
prefix = "^(pk(;|!)|<@{}> )".format(client.user.id)
|
||||
commands = [
|
||||
(r"system (new|register|create|init)", system_commands.new_system),
|
||||
(r"system set", system_commands.system_set),
|
||||
(r"system (name|rename)", system_commands.system_name),
|
||||
(r"system description", system_commands.system_description),
|
||||
(r"system avatar", system_commands.system_avatar),
|
||||
(r"system tag", system_commands.system_tag),
|
||||
(r"system link", system_commands.system_link),
|
||||
(r"system unlink", system_commands.system_unlink),
|
||||
(r"system fronter", system_commands.system_fronter),
|
||||
@ -159,6 +170,12 @@ async def command_dispatch(client: discord.Client, message: discord.Message, con
|
||||
|
||||
(r"member (new|create|add|register)", member_commands.new_member),
|
||||
(r"member set", member_commands.member_set),
|
||||
(r"member (name|rename)", member_commands.member_name),
|
||||
(r"member description", member_commands.member_description),
|
||||
(r"member avatar", member_commands.member_avatar),
|
||||
(r"member color", member_commands.member_color),
|
||||
(r"member (pronouns|pronoun)", member_commands.member_pronouns),
|
||||
(r"member (birthday|birthdate)", member_commands.member_birthdate),
|
||||
(r"member proxy", member_commands.member_proxy),
|
||||
(r"member (delete|remove|destroy|erase)", member_commands.member_delete),
|
||||
(r"member", member_commands.member_info),
|
||||
|
@ -1,5 +1,3 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pluralkit.bot.embeds
|
||||
from pluralkit.bot import help
|
||||
from pluralkit.bot.commands import *
|
||||
@ -27,73 +25,72 @@ async def new_member(ctx: CommandContext):
|
||||
raise CommandError(e.message)
|
||||
|
||||
await ctx.reply_ok(
|
||||
"Member \"{}\" (`{}`) registered! To register their proxy tags, use `pk;member proxy`.".format(new_name, member.hid))
|
||||
"Member \"{}\" (`{}`) registered! To register their proxy tags, use `pk;member proxy`.".format(new_name,
|
||||
member.hid))
|
||||
|
||||
|
||||
async def member_set(ctx: CommandContext):
|
||||
raise CommandError(
|
||||
"`pk;member set` has been retired. Please use the new member modifying commands: `pk;member [name|description|avatar|color|pronouns|birthdate]`.")
|
||||
|
||||
|
||||
async def member_name(ctx: CommandContext):
|
||||
system = await ctx.ensure_system()
|
||||
member = await ctx.pop_member(CommandError("You must pass a member name.", help=help.edit_member))
|
||||
new_name = ctx.pop_str(CommandError("You must pass a new member name.", help=help.edit_member))
|
||||
|
||||
property_name = ctx.pop_str(CommandError("You must pass a property name to set.", help=help.edit_member))
|
||||
await member.set_name(ctx.conn, system, new_name)
|
||||
await ctx.reply_ok("Member name updated.")
|
||||
|
||||
async def name_setter(conn, new_name):
|
||||
if not new_name:
|
||||
raise CommandError("You can't clear the member name.")
|
||||
await member.set_name(conn, system, new_name)
|
||||
|
||||
async def avatar_setter(conn, url):
|
||||
if url:
|
||||
user = await utils.parse_mention(ctx.client, url)
|
||||
if user:
|
||||
# Set the avatar to the mentioned user's avatar
|
||||
# Discord pushes webp by default, which isn't supported by webhooks, but also hosts png alternatives
|
||||
url = user.avatar_url.replace(".webp", ".png")
|
||||
async def member_description(ctx: CommandContext):
|
||||
await ctx.ensure_system()
|
||||
member = await ctx.pop_member(CommandError("You must pass a member name.", help=help.edit_member))
|
||||
new_description = ctx.remaining() or None
|
||||
|
||||
await member.set_avatar(conn, url)
|
||||
await member.set_description(ctx.conn, new_description)
|
||||
await ctx.reply_ok("Member description {}.".format("updated" if new_description else "cleared"))
|
||||
|
||||
async def birthdate_setter(conn, date_str):
|
||||
if date_str:
|
||||
try:
|
||||
date = datetime.strptime(date_str, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
try:
|
||||
# Try again, adding 0001 as a placeholder year
|
||||
# This is considered a "null year" and will be omitted from the info card
|
||||
# Useful if you want your birthday to be displayed yearless.
|
||||
date = datetime.strptime("0001-" + date_str, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
raise CommandError("Invalid date. Date must be in ISO-8601 format (YYYY-MM-DD, eg. 1999-07-25).")
|
||||
else:
|
||||
date = None
|
||||
|
||||
await member.set_birthdate(conn, date)
|
||||
async def member_avatar(ctx: CommandContext):
|
||||
await ctx.ensure_system()
|
||||
member = await ctx.pop_member(CommandError("You must pass a member name.", help=help.edit_member))
|
||||
new_avatar_url = ctx.remaining() or None
|
||||
|
||||
properties = {
|
||||
"name": name_setter,
|
||||
"description": member.set_description,
|
||||
"avatar": avatar_setter,
|
||||
"color": member.set_color,
|
||||
"pronouns": member.set_pronouns,
|
||||
"birthdate": birthdate_setter,
|
||||
}
|
||||
if new_avatar_url:
|
||||
user = await utils.parse_mention(ctx.client, new_avatar_url)
|
||||
if user:
|
||||
new_avatar_url = user.avatar_url_as(format="png")
|
||||
|
||||
if property_name not in properties:
|
||||
raise CommandError(
|
||||
"Unknown property {}. Allowed properties are {}.".format(property_name, ", ".join(properties.keys())),
|
||||
help=help.edit_system)
|
||||
await member.set_avatar(ctx.conn, new_avatar_url)
|
||||
await ctx.reply_ok("Member avatar {}.".format("updated" if new_avatar_url else "cleared"))
|
||||
|
||||
value = ctx.remaining() or None
|
||||
|
||||
try:
|
||||
await properties[property_name](ctx.conn, value)
|
||||
except PluralKitError as e:
|
||||
raise CommandError(e.message)
|
||||
async def member_color(ctx: CommandContext):
|
||||
await ctx.ensure_system()
|
||||
member = await ctx.pop_member(CommandError("You must pass a member name.", help=help.edit_member))
|
||||
new_color = ctx.remaining() or None
|
||||
|
||||
# if prop == "avatar" and value:
|
||||
# response.set_image(url=value)
|
||||
# if prop == "color" and value:
|
||||
# response.colour = int(value, 16)
|
||||
await ctx.reply_ok("{} member {}.".format("Updated" if value else "Cleared", property_name))
|
||||
await member.set_color(ctx.conn, new_color)
|
||||
await ctx.reply_ok("Member color {}.".format("updated" if new_color else "cleared"))
|
||||
|
||||
|
||||
async def member_pronouns(ctx: CommandContext):
|
||||
await ctx.ensure_system()
|
||||
member = await ctx.pop_member(CommandError("You must pass a member name.", help=help.edit_member))
|
||||
new_pronouns = ctx.remaining() or None
|
||||
|
||||
await member.set_pronouns(ctx.conn, new_pronouns)
|
||||
await ctx.reply_ok("Member pronouns {}.".format("updated" if new_pronouns else "cleared"))
|
||||
|
||||
|
||||
async def member_birthdate(ctx: CommandContext):
|
||||
await ctx.ensure_system()
|
||||
member = await ctx.pop_member(CommandError("You must pass a member name.", help=help.edit_member))
|
||||
new_birthdate = ctx.remaining() or None
|
||||
|
||||
await member.set_birthdate(ctx.conn, new_birthdate)
|
||||
await ctx.reply_ok("Member birthdate {}.".format("updated" if new_birthdate else "cleared"))
|
||||
|
||||
|
||||
async def member_proxy(ctx: CommandContext):
|
||||
@ -110,7 +107,7 @@ async def member_proxy(ctx: CommandContext):
|
||||
|
||||
if example.count("text") != 1:
|
||||
raise CommandError("Example proxy message must contain the string 'text' exactly once.",
|
||||
help=help.member_proxy)
|
||||
help=help.member_proxy)
|
||||
|
||||
# Extract prefix and suffix
|
||||
prefix = example[:example.index("text")].strip()
|
||||
@ -132,7 +129,8 @@ async def member_delete(ctx: CommandContext):
|
||||
await ctx.ensure_system()
|
||||
member = await ctx.pop_member(CommandError("You must pass a member name.", help=help.remove_member))
|
||||
|
||||
delete_confirm_msg = "Are you sure you want to delete {}? If so, reply to this message with the member's ID (`{}`).".format(member.name, member.hid)
|
||||
delete_confirm_msg = "Are you sure you want to delete {}? If so, reply to this message with the member's ID (`{}`).".format(
|
||||
member.name, member.hid)
|
||||
if not await ctx.confirm_text(ctx.message.author, ctx.message.channel, member.hid, delete_confirm_msg):
|
||||
raise CommandError("Member deletion cancelled.")
|
||||
|
||||
|
@ -32,48 +32,53 @@ async def new_system(ctx: CommandContext):
|
||||
|
||||
|
||||
async def system_set(ctx: CommandContext):
|
||||
raise CommandError("`pk;system set` has been retired. Please use the new member modifying commands: `pk;system [name|description|avatar|tag]`.")
|
||||
|
||||
|
||||
async def system_name(ctx: CommandContext):
|
||||
system = await ctx.ensure_system()
|
||||
new_name = ctx.remaining() or None
|
||||
|
||||
property_name = ctx.pop_str(CommandError("You must pass a property name to set.", help=help.edit_system))
|
||||
await system.set_name(ctx.conn, new_name)
|
||||
await ctx.reply_ok("System name {}.".format("updated" if new_name else "cleared"))
|
||||
|
||||
async def avatar_setter(conn, url):
|
||||
if url:
|
||||
user = await utils.parse_mention(ctx.client, url)
|
||||
if user:
|
||||
# Set the avatar to the mentioned user's avatar
|
||||
# Discord pushes webp by default, which isn't supported by webhooks, but also hosts png alternatives
|
||||
url = user.avatar_url.replace(".webp", ".png")
|
||||
|
||||
await system.set_avatar(conn, url)
|
||||
async def system_description(ctx: CommandContext):
|
||||
system = await ctx.ensure_system()
|
||||
new_description = ctx.remaining() or None
|
||||
|
||||
properties = {
|
||||
"name": system.set_name,
|
||||
"description": system.set_description,
|
||||
"tag": system.set_tag,
|
||||
"avatar": avatar_setter
|
||||
}
|
||||
await system.set_description(ctx.conn, new_description)
|
||||
await ctx.reply_ok("System description {}.".format("updated" if new_description else "cleared"))
|
||||
|
||||
if property_name not in properties:
|
||||
raise CommandError(
|
||||
"Unknown property {}. Allowed properties are {}.".format(property_name, ", ".join(properties.keys())),
|
||||
help=help.edit_system)
|
||||
|
||||
value = ctx.remaining() or None
|
||||
async def system_tag(ctx: CommandContext):
|
||||
system = await ctx.ensure_system()
|
||||
new_tag = ctx.remaining() or None
|
||||
|
||||
try:
|
||||
await properties[property_name](ctx.conn, value)
|
||||
except PluralKitError as e:
|
||||
raise CommandError(e.message)
|
||||
await system.set_tag(ctx.conn, new_tag)
|
||||
await ctx.reply_ok("System tag {}.".format("updated" if new_tag else "cleared"))
|
||||
|
||||
await ctx.reply_ok("{} system {}.".format("Updated" if value else "Cleared", property_name))
|
||||
# if prop == "avatar" and value:
|
||||
# response.set_image(url=value)
|
||||
|
||||
async def system_avatar(ctx: CommandContext):
|
||||
system = await ctx.ensure_system()
|
||||
new_avatar_url = ctx.remaining() or None
|
||||
|
||||
if new_avatar_url:
|
||||
user = await utils.parse_mention(ctx.client, new_avatar_url)
|
||||
if user:
|
||||
new_avatar_url = user.avatar_url_as(format="png")
|
||||
|
||||
await system.set_avatar(ctx.conn, new_avatar_url)
|
||||
await ctx.reply_ok("System avatar {}.".format("updated" if new_avatar_url else "cleared"))
|
||||
|
||||
|
||||
async def system_link(ctx: CommandContext):
|
||||
system = await ctx.ensure_system()
|
||||
account_name = ctx.pop_str(CommandError("You must pass an account to link this system to.", help=help.link_account))
|
||||
|
||||
# Do the sanity checking here too (despite it being done in System.link_account)
|
||||
# Because we want it to be done before the confirmation dialog is shown
|
||||
|
||||
# Find account to link
|
||||
linkee = await utils.parse_mention(ctx.client, account_name)
|
||||
if not linkee:
|
||||
|
@ -11,29 +11,41 @@ from pluralkit.switch import Switch
|
||||
from pluralkit.system import System
|
||||
from pluralkit.utils import get_fronters
|
||||
|
||||
def truncate_field_name(s: str) -> str:
|
||||
return s[:256]
|
||||
|
||||
def truncate_field_body(s: str) -> str:
|
||||
return s[:1024]
|
||||
|
||||
def truncate_description(s: str) -> str:
|
||||
return s[:2048]
|
||||
|
||||
def truncate_title(s: str) -> str:
|
||||
return s[:256]
|
||||
|
||||
|
||||
def success(text: str) -> discord.Embed:
|
||||
embed = discord.Embed()
|
||||
embed.description = text
|
||||
embed.description = truncate_description(text)
|
||||
embed.colour = discord.Colour.green()
|
||||
return embed
|
||||
|
||||
|
||||
def error(text: str, help: Tuple[str, str] = None) -> discord.Embed:
|
||||
embed = discord.Embed()
|
||||
embed.description = text
|
||||
embed.description = truncate_description(s)
|
||||
embed.colour = discord.Colour.dark_red()
|
||||
|
||||
if help:
|
||||
help_title, help_text = help
|
||||
embed.add_field(name=help_title, value=help_text)
|
||||
embed.add_field(name=truncate_field_name(help_title), value=truncate_field_body(help_text))
|
||||
|
||||
return embed
|
||||
|
||||
|
||||
def status(text: str) -> discord.Embed:
|
||||
embed = discord.Embed()
|
||||
embed.description = text
|
||||
embed.description = truncate_description(text)
|
||||
embed.colour = discord.Colour.blue()
|
||||
return embed
|
||||
|
||||
@ -41,7 +53,7 @@ def status(text: str) -> discord.Embed:
|
||||
def exception_log(message_content, author_name, author_discriminator, author_id, server_id, channel_id) -> discord.Embed:
|
||||
embed = discord.Embed()
|
||||
embed.colour = discord.Colour.dark_red()
|
||||
embed.title = message_content
|
||||
embed.title = truncate_title(message_content)
|
||||
|
||||
embed.set_footer(text="Sender: {}#{} ({}) | Server: {} | Channel: {}".format(
|
||||
author_name, author_discriminator, author_id,
|
||||
@ -56,30 +68,30 @@ async def system_card(conn, client: discord.Client, system: System) -> discord.E
|
||||
card.colour = discord.Colour.blue()
|
||||
|
||||
if system.name:
|
||||
card.title = system.name
|
||||
card.title = truncate_title(system.name)
|
||||
|
||||
if system.avatar_url:
|
||||
card.set_thumbnail(url=system.avatar_url)
|
||||
|
||||
if system.tag:
|
||||
card.add_field(name="Tag", value=system.tag)
|
||||
card.add_field(name="Tag", value=truncate_field_body(system.tag))
|
||||
|
||||
fronters, switch_time = await get_fronters(conn, system.id)
|
||||
if fronters:
|
||||
names = ", ".join([member.name for member in fronters])
|
||||
fronter_val = "{} (for {})".format(names, humanize.naturaldelta(switch_time))
|
||||
card.add_field(name="Current fronter" if len(fronters) == 1 else "Current fronters", value=fronter_val)
|
||||
card.add_field(name="Current fronter" if len(fronters) == 1 else "Current fronters", value=truncate_field_body(fronter_val))
|
||||
|
||||
account_names = []
|
||||
for account_id in await system.get_linked_account_ids(conn):
|
||||
account = await client.get_user_info(account_id)
|
||||
account_names.append("{}#{}".format(account.name, account.discriminator))
|
||||
|
||||
card.add_field(name="Linked accounts", value="\n".join(account_names))
|
||||
card.add_field(name="Linked accounts", value=truncate_field_body("\n".join(account_names)))
|
||||
|
||||
if system.description:
|
||||
card.add_field(name="Description",
|
||||
value=system.description, inline=False)
|
||||
value=truncate_field_body(system.description), inline=False)
|
||||
|
||||
# Get names of all members
|
||||
all_members = await system.get_members(conn)
|
||||
@ -106,7 +118,7 @@ async def system_card(conn, client: discord.Client, system: System) -> discord.E
|
||||
field_name = "Members"
|
||||
if index >= 1:
|
||||
field_name = "Members (part {})".format(index + 1)
|
||||
card.add_field(name=field_name, value=page, inline=False)
|
||||
card.add_field(name=truncate_field_name(field_name), value=truncate_field_body(page), inline=False)
|
||||
|
||||
card.set_footer(text="System ID: {}".format(system.hid))
|
||||
return card
|
||||
@ -122,7 +134,7 @@ async def member_card(conn, member: Member) -> discord.Embed:
|
||||
if system.name:
|
||||
name_and_system += " ({})".format(system.name)
|
||||
|
||||
card.set_author(name=name_and_system, icon_url=member.avatar_url or discord.Embed.Empty)
|
||||
card.set_author(name=truncate_field_name(name_and_system), icon_url=member.avatar_url or discord.Embed.Empty)
|
||||
if member.avatar_url:
|
||||
card.set_thumbnail(url=member.avatar_url)
|
||||
|
||||
@ -136,7 +148,7 @@ async def member_card(conn, member: Member) -> discord.Embed:
|
||||
card.add_field(name="Birthdate", value=bday_val)
|
||||
|
||||
if member.pronouns:
|
||||
card.add_field(name="Pronouns", value=member.pronouns)
|
||||
card.add_field(name="Pronouns", value=truncate_field_body(member.pronouns))
|
||||
|
||||
message_count = await member.message_count(conn)
|
||||
if message_count > 0:
|
||||
@ -146,11 +158,11 @@ async def member_card(conn, member: Member) -> discord.Embed:
|
||||
prefix = member.prefix or ""
|
||||
suffix = member.suffix or ""
|
||||
card.add_field(name="Proxy Tags",
|
||||
value="{}text{}".format(prefix, suffix))
|
||||
value=truncate_field_body("{}text{}".format(prefix, suffix)))
|
||||
|
||||
if member.description:
|
||||
card.add_field(name="Description",
|
||||
value=member.description, inline=False)
|
||||
value=truncate_field_body(member.description), inline=False)
|
||||
|
||||
card.set_footer(text="System ID: {} | Member ID: {}".format(system.hid, member.hid))
|
||||
return card
|
||||
@ -164,9 +176,9 @@ async def front_status(switch: Switch, conn) -> discord.Embed:
|
||||
if len(fronter_names) == 0:
|
||||
embed.add_field(name="Current fronter", value="(no fronter)")
|
||||
elif len(fronter_names) == 1:
|
||||
embed.add_field(name="Current fronter", value=fronter_names[0])
|
||||
embed.add_field(name="Current fronter", value=truncate_field_body(fronter_names[0]))
|
||||
else:
|
||||
embed.add_field(name="Current fronters", value=", ".join(fronter_names))
|
||||
embed.add_field(name="Current fronters", value=truncate_field_body(", ".join(fronter_names)))
|
||||
|
||||
if switch.timestamp:
|
||||
embed.add_field(name="Since",
|
||||
|
@ -20,13 +20,13 @@ For example:
|
||||
`pk;system` - Shows details of your own system.
|
||||
`pk;system abcde` - Shows details of the system with the ID `abcde`.
|
||||
`pk;system @JohnsAccount` - Shows details of the system linked to @JohnsAccount.""")
|
||||
edit_system = ("Editing system properties", """You can use the `pk;system set` command to change your system properties. The properties you can change are name, description, and tag.
|
||||
edit_system = ("Editing system properties", """You can use the `pk;system` commands to change your system properties. The properties you can change are name, description, and tag.
|
||||
|
||||
For example:
|
||||
`pk;system set name My System` - sets your system name to "My System".
|
||||
`pk;system set description A really cool system.` - sets your system description.
|
||||
`pk;system set tag [MS]` - Sets the tag (which will be displayed after member names in messages) to "[MS]".
|
||||
`pk;system set avatar https://placekitten.com/400/400` - Changes your system's avatar to a linked image.
|
||||
`pk;system name My System` - sets your system name to "My System".
|
||||
`pk;system description A really cool system.` - sets your system description.
|
||||
`pk;system tag [MS]` - Sets the tag (which will be displayed after member names in messages) to "[MS]".
|
||||
`pk;system avatar https://placekitten.com/400/400` - Changes your system's avatar to a linked image.
|
||||
|
||||
If you don't specify any value, the property will be cleared.""")
|
||||
link_account = ("Linking accounts", """If your system has multiple accounts, you can link all of them to your system, and you can use the bot from all of those accounts.
|
||||
@ -48,17 +48,17 @@ For example:
|
||||
`pk;member abcde` - Shows details of the member with the ID `abcde`.
|
||||
|
||||
You can use member IDs to look up members in other systems.""")
|
||||
edit_member = ("Editing member properties", """You can use the `pk;member set` command to change a member's properties. The properties you can change are name, description, color, pronouns, birthdate and avatar.
|
||||
edit_member = ("Editing member properties", """You can use the `pk;member` commands to change a member's properties. The properties you can change are name, description, color, pronouns, birthdate and avatar.
|
||||
|
||||
For example:
|
||||
`pk;member set John name Joe` - Changes John's name to Joe.
|
||||
`pk;member set John description Pretty cool dude.` - Changes John's description.
|
||||
`pk;member set John color #ff0000` - Changes John's color to red.
|
||||
`pk;member set John pronouns he/him` - Changes John's pronouns.
|
||||
`pk;member set John birthdate 1996-02-27` - Changes John's birthdate to Feb 27, 1996. (Must be YYYY-MM-DD format).
|
||||
`pk;member set John birthdate 02-27` - Changes John's birthdate to February 27th, with no year.
|
||||
`pk;member set John avatar https://placekitten.com/400/400` - Changes John's avatar to a linked image.
|
||||
`pk;member set John avatar @JohnsAccount` - Changes John's avatar to the avatar of the mentioned account.
|
||||
`pk;member name John Joe` - Changes John's name to Joe.
|
||||
`pk;member description John Pretty cool dude.` - Changes John's description.
|
||||
`pk;member color John #ff0000` - Changes John's color to red.
|
||||
`pk;member pronouns John he/him` - Changes John's pronouns.
|
||||
`pk;member birthdate John 1996-02-27` - Changes John's birthdate to Feb 27, 1996. (Must be YYYY-MM-DD format).
|
||||
`pk;member birthdate John 02-27` - Changes John's birthdate to February 27th, with no year.
|
||||
`pk;member avatar John https://placekitten.com/400/400` - Changes John's avatar to a linked image.
|
||||
`pk;member avatar John @JohnsAccount` - Changes John's avatar to the avatar of the mentioned account.
|
||||
|
||||
If you don't specify any value, the property will be cleared.""")
|
||||
remove_member = ("Removing a member", """If you want to delete a member, you can use the `pk;member delete` command.
|
||||
|
@ -67,4 +67,8 @@ class MemberNameTooLongError(PluralKitError):
|
||||
|
||||
class InvalidColorError(PluralKitError):
|
||||
def __init__(self):
|
||||
super().__init__("Color must be a valid hex color. (eg. #ff0000)")
|
||||
super().__init__("Color must be a valid hex color. (eg. #ff0000)")
|
||||
|
||||
class InvalidDateStringError(PluralKitError):
|
||||
def __init__(self):
|
||||
super().__init__("Invalid date string. Date must be in ISO-8601 format (YYYY-MM-DD, eg. 1999-07-25).")
|
@ -2,7 +2,7 @@ import re
|
||||
from datetime import date, datetime
|
||||
|
||||
from collections.__init__ import namedtuple
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
from pluralkit import db, errors
|
||||
from pluralkit.utils import validate_avatar_url_or_raise, contains_custom_emoji
|
||||
@ -59,9 +59,16 @@ class Member(namedtuple("Member",
|
||||
Set the name of a member. Requires the system to be passed in order to bounds check with the system tag.
|
||||
:raises: MemberNameTooLongError, CustomEmojiError
|
||||
"""
|
||||
# Custom emojis can't go in the member name
|
||||
# Technically they *could* but they wouldn't render properly
|
||||
# so I'd rather explicitly ban them to in order to avoid confusion
|
||||
|
||||
# The textual form is longer than the length limit in most cases
|
||||
# so we check this *before* the length check for better errors
|
||||
if contains_custom_emoji(new_name):
|
||||
raise errors.CustomEmojiError()
|
||||
|
||||
# Explicit name length checking
|
||||
if len(new_name) > system.get_member_name_limit():
|
||||
raise errors.MemberNameTooLongError(tag_present=bool(system.tag))
|
||||
|
||||
@ -72,6 +79,7 @@ class Member(namedtuple("Member",
|
||||
Set or clear the description of a member.
|
||||
:raises: DescriptionTooLongError
|
||||
"""
|
||||
# Explicit length checking
|
||||
if new_description and len(new_description) > 1024:
|
||||
raise errors.DescriptionTooLongError()
|
||||
|
||||
@ -96,14 +104,31 @@ class Member(namedtuple("Member",
|
||||
if new_color:
|
||||
match = re.fullmatch("#?([0-9A-Fa-f]{6})", new_color)
|
||||
if not match:
|
||||
return errors.InvalidColorError()
|
||||
raise errors.InvalidColorError()
|
||||
|
||||
cleaned_color = match.group(1).lower()
|
||||
|
||||
await db.update_member_field(conn, self.id, "color", cleaned_color)
|
||||
|
||||
async def set_birthdate(self, conn, new_date: date):
|
||||
"""Set or clear the birthdate of a member. To hide the birth year, pass a year of 0001."""
|
||||
async def set_birthdate(self, conn, new_date: Union[date, str]):
|
||||
"""
|
||||
Set or clear the birthdate of a member. To hide the birth year, pass a year of 0001.
|
||||
:raises: InvalidDateStringError
|
||||
"""
|
||||
|
||||
if isinstance(new_date, str):
|
||||
date_str = new_date
|
||||
try:
|
||||
new_date = datetime.strptime(date_str, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
try:
|
||||
# Try again, adding 0001 as a placeholder year
|
||||
# This is considered a "null year" and will be omitted from the info card
|
||||
# Useful if you want your birthday to be displayed yearless.
|
||||
new_date = datetime.strptime("0001-" + date_str, "%Y-%m-%d").date()
|
||||
except ValueError:
|
||||
raise errors.InvalidDateStringError()
|
||||
|
||||
await db.update_member_field(conn, self.id, "birthday", new_date)
|
||||
|
||||
async def set_pronouns(self, conn, new_pronouns: str):
|
||||
|
@ -48,6 +48,7 @@ class System(namedtuple("System", ["id", "hid", "name", "description", "tag", "a
|
||||
await db.update_system_field(conn, self.id, "name", new_name)
|
||||
|
||||
async def set_description(self, conn, new_description: Optional[str]):
|
||||
# Explicit length error
|
||||
if new_description and len(new_description) > 1024:
|
||||
raise errors.DescriptionTooLongError()
|
||||
|
||||
@ -55,12 +56,14 @@ class System(namedtuple("System", ["id", "hid", "name", "description", "tag", "a
|
||||
|
||||
async def set_tag(self, conn, new_tag: Optional[str]):
|
||||
if new_tag:
|
||||
# Explicit length error
|
||||
if len(new_tag) > 32:
|
||||
raise errors.TagTooLongError()
|
||||
|
||||
if contains_custom_emoji(new_tag):
|
||||
raise errors.CustomEmojiError()
|
||||
|
||||
# Check name+tag length for all members
|
||||
members_exceeding = await db.get_members_exceeding(conn, system_id=self.id, length=32 - len(new_tag) - 1)
|
||||
if len(members_exceeding) > 0:
|
||||
raise errors.TagTooLongWithMembersError([member.name for member in members_exceeding])
|
||||
|
Loading…
Reference in New Issue
Block a user