196 lines
8.2 KiB
Python
196 lines
8.2 KiB
Python
import pluralkit.bot.embeds
|
|
from pluralkit.bot import help
|
|
from pluralkit.bot.commands import *
|
|
from pluralkit.errors import PluralKitError
|
|
|
|
|
|
async def member_root(ctx: CommandContext):
|
|
if ctx.match("new") or ctx.match("create") or ctx.match("add") or ctx.match("register"):
|
|
await new_member(ctx)
|
|
elif ctx.match("help"):
|
|
await ctx.reply(help.member_commands)
|
|
elif ctx.match("set"):
|
|
await member_set(ctx)
|
|
# TODO "pk;member list"
|
|
elif not ctx.has_next():
|
|
raise CommandError("Must pass a subcommand. For a list of subcommands, type `pk;member help`.")
|
|
else:
|
|
await specific_member_root(ctx)
|
|
|
|
|
|
async def specific_member_root(ctx: CommandContext):
|
|
member = await ctx.pop_member(system_only=False)
|
|
|
|
if ctx.has_next():
|
|
# Following commands operate on members only in the caller's own system
|
|
# error if not, to make sure you can't destructively edit someone else's member
|
|
system = await ctx.ensure_system()
|
|
if not member.system == system.id:
|
|
raise CommandError("Member must be in your own system.")
|
|
|
|
if ctx.match("name") or ctx.match("rename"):
|
|
await member_name(ctx, member)
|
|
elif ctx.match("description"):
|
|
await member_description(ctx, member)
|
|
elif ctx.match("avatar") or ctx.match("icon"):
|
|
await member_avatar(ctx, member)
|
|
elif ctx.match("proxy") or ctx.match("tags"):
|
|
await member_proxy(ctx, member)
|
|
elif ctx.match("pronouns") or ctx.match("pronoun"):
|
|
await member_pronouns(ctx, member)
|
|
elif ctx.match("color") or ctx.match("colour"):
|
|
await member_color(ctx, member)
|
|
elif ctx.match("birthday") or ctx.match("birthdate"):
|
|
await member_birthdate(ctx, member)
|
|
elif ctx.match("delete") or ctx.match("remove") or ctx.match("destroy") or ctx.match("erase"):
|
|
await member_delete(ctx, member)
|
|
else:
|
|
raise CommandError(
|
|
"Unknown subcommand {}. For a list of all commands, type `pk;help member`".format(ctx.pop_str()))
|
|
else:
|
|
# Basic lookup
|
|
await member_info(ctx, member)
|
|
|
|
|
|
async def member_info(ctx: CommandContext, member: Member):
|
|
await ctx.reply(embed=await pluralkit.bot.embeds.member_card(ctx.conn, member))
|
|
|
|
|
|
async def new_member(ctx: CommandContext):
|
|
system = await ctx.ensure_system()
|
|
if not ctx.has_next():
|
|
raise CommandError("You must pass a name for the new member.")
|
|
|
|
new_name = ctx.remaining()
|
|
|
|
existing_member = await Member.get_member_by_name(ctx.conn, system.id, new_name)
|
|
if existing_member:
|
|
msg = await ctx.reply_warn(
|
|
"There is already a member with this name, with the ID `{}`. Do you want to create a duplicate member anyway?".format(
|
|
existing_member.hid))
|
|
if not await ctx.confirm_react(ctx.message.author, msg):
|
|
raise CommandError("Member creation cancelled.")
|
|
|
|
try:
|
|
member = await system.create_member(ctx.conn, new_name)
|
|
except PluralKitError as e:
|
|
raise CommandError(e.message)
|
|
|
|
await ctx.reply_ok(
|
|
"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. Type `pk;member help` for a list.")
|
|
|
|
|
|
async def member_name(ctx: CommandContext, member: Member):
|
|
system = await ctx.ensure_system()
|
|
new_name = ctx.pop_str(CommandError("You must pass a new member name."))
|
|
|
|
# Warn if there's a member by the same name already
|
|
existing_member = await Member.get_member_by_name(ctx.conn, system.id, new_name)
|
|
if existing_member and existing_member.id != member.id:
|
|
msg = await ctx.reply_warn(
|
|
"There is already another member with this name, with the ID `{}`. Do you want to rename this member anyway? This will result in two members with the same name.".format(
|
|
existing_member.hid))
|
|
if not await ctx.confirm_react(ctx.message.author, msg):
|
|
raise CommandError("Member renaming cancelled.")
|
|
|
|
await member.set_name(ctx.conn, new_name)
|
|
await ctx.reply_ok("Member name updated.")
|
|
|
|
if len(new_name) < 2 and not system.tag:
|
|
await ctx.reply_warn(
|
|
"This member's new name is under 2 characters, and thus cannot be proxied. To prevent this, use a longer member name, or add a system tag.")
|
|
elif len(new_name) > 32:
|
|
exceeds_by = len(new_name) - 32
|
|
await ctx.reply_warn(
|
|
"This member's new name is longer than 32 characters, and thus cannot be proxied. To prevent this, shorten the member name by {} characters.".format(
|
|
exceeds_by))
|
|
elif len(new_name) > system.get_member_name_limit():
|
|
exceeds_by = len(new_name) - system.get_member_name_limit()
|
|
await ctx.reply_warn(
|
|
"This member's new name, when combined with the system tag `{}`, is longer than 32 characters, and thus cannot be proxied. To prevent this, shorten the name or system tag by at least {} characters.".format(
|
|
system.tag, exceeds_by))
|
|
|
|
|
|
async def member_description(ctx: CommandContext, member: Member):
|
|
new_description = ctx.remaining() or None
|
|
|
|
await member.set_description(ctx.conn, new_description)
|
|
await ctx.reply_ok("Member description {}.".format("updated" if new_description else "cleared"))
|
|
|
|
|
|
async def member_avatar(ctx: CommandContext, member: Member):
|
|
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 member.set_avatar(ctx.conn, new_avatar_url)
|
|
await ctx.reply_ok("Member avatar {}.".format("updated" if new_avatar_url else "cleared"))
|
|
|
|
|
|
async def member_color(ctx: CommandContext, member: Member):
|
|
new_color = ctx.remaining() or None
|
|
|
|
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, member: 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, member: 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, member: Member):
|
|
if not ctx.has_next():
|
|
prefix, suffix = None, None
|
|
else:
|
|
# Sanity checking
|
|
example = ctx.remaining()
|
|
if "text" not in example:
|
|
raise CommandError("Example proxy message must contain the string 'text'. For help, type `pk;help proxy`.")
|
|
|
|
if example.count("text") != 1:
|
|
raise CommandError("Example proxy message must contain the string 'text' exactly once. For help, type `pk;help proxy`.")
|
|
|
|
# Extract prefix and suffix
|
|
prefix = example[:example.index("text")].strip()
|
|
suffix = example[example.index("text") + 4:].strip()
|
|
|
|
# DB stores empty strings as None, make that work
|
|
if not prefix:
|
|
prefix = None
|
|
if not suffix:
|
|
suffix = None
|
|
|
|
async with ctx.conn.transaction():
|
|
await member.set_proxy_tags(ctx.conn, prefix, suffix)
|
|
await ctx.reply_ok(
|
|
"Proxy settings updated." if prefix or suffix else "Proxy settings cleared. If you meant to set your proxy tags, type `pk;help proxy` for help.")
|
|
|
|
|
|
async def member_delete(ctx: CommandContext, member: 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)
|
|
if not await ctx.confirm_text(ctx.message.author, ctx.message.channel, member.hid, delete_confirm_msg):
|
|
raise CommandError("Member deletion cancelled.")
|
|
|
|
await member.delete(ctx.conn)
|
|
await ctx.reply_ok("Member deleted.")
|