Add system member list command

This commit is contained in:
Ske 2019-03-08 14:48:33 +01:00
parent aad5a8b417
commit 942f7cd0ab
6 changed files with 77 additions and 33 deletions

4
.vscode/launch.json vendored
View File

@ -9,8 +9,8 @@
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"program": "${workspaceRoot}/src/bot_main.py", "program": "${workspaceRoot}/src/bot_main.py",
"envFile": "${workspaceFolder}/.env", "args": ["${workspaceRoot}/pluralkit.conf"],
"console": "integratedTerminal" "console": "integratedTerminal",
} }
] ]
} }

View File

@ -42,6 +42,9 @@ class CommandError(Exception):
class CommandContext: class CommandContext:
client: discord.Client
message: discord.Message
def __init__(self, client: discord.Client, message: discord.Message, conn, args: str, system: Optional[System]): def __init__(self, client: discord.Client, message: discord.Message, conn, args: str, system: Optional[System]):
self.client = client self.client = client
self.message = message self.message = message

View File

@ -39,6 +39,8 @@ async def system_root(ctx: CommandContext):
await system_timezone(ctx) await system_timezone(ctx)
elif ctx.match("set"): elif ctx.match("set"):
await system_set(ctx) await system_set(ctx)
elif ctx.match("list") or ctx.match("members"):
await system_list(ctx, await ctx.ensure_system())
elif not ctx.has_next(): elif not ctx.has_next():
# (no argument, command ends here, default to showing own system) # (no argument, command ends here, default to showing own system)
await system_info(ctx, await ctx.ensure_system()) await system_info(ctx, await ctx.ensure_system())
@ -63,12 +65,15 @@ async def specified_system_root(ctx: CommandContext):
await system_fronthistory(ctx, system) await system_fronthistory(ctx, system)
elif ctx.match("frontpercent") or ctx.match("frontbreakdown") or ctx.match("frontpercentage"): elif ctx.match("frontpercent") or ctx.match("frontbreakdown") or ctx.match("frontpercentage"):
await system_frontpercent(ctx, system) await system_frontpercent(ctx, system)
elif ctx.match("list") or ctx.match("members"):
await system_list(ctx, system)
else: else:
await system_info(ctx, system) await system_info(ctx, system)
async def system_info(ctx: CommandContext, system: System): async def system_info(ctx: CommandContext, system: System):
await ctx.reply(embed=await pluralkit.bot.embeds.system_card(ctx.conn, ctx.client, system)) this_system = await ctx.get_system()
await ctx.reply(embed=await pluralkit.bot.embeds.system_card(ctx.conn, ctx.client, system, this_system and this_system.id == system.id))
async def system_new(ctx: CommandContext): async def system_new(ctx: CommandContext):
@ -124,6 +129,10 @@ async def system_timezone(ctx: CommandContext):
# Take the lat/long given by Overpass and put it into timezonefinder # Take the lat/long given by Overpass and put it into timezonefinder
lat, lng = (float(data[0]["lat"]), float(data[0]["lon"])) lat, lng = (float(data[0]["lat"]), float(data[0]["lon"]))
timezone_name = tzf.timezone_at(lng=lng, lat=lat) timezone_name = tzf.timezone_at(lng=lng, lat=lat)
# Also delete the original searching message
await msg.delete()
if not timezone_name: if not timezone_name:
raise CommandError("Time zone for city '{}' not found. This should never happen.".format(data[0]["display_name"])) raise CommandError("Time zone for city '{}' not found. This should never happen.".format(data[0]["display_name"]))
@ -132,6 +141,7 @@ async def system_timezone(ctx: CommandContext):
tz = await system.set_time_zone(ctx.conn, timezone_name) tz = await system.set_time_zone(ctx.conn, timezone_name)
offset = tz.utcoffset(datetime.utcnow()) offset = tz.utcoffset(datetime.utcnow())
offset_str = "UTC{:+02d}:{:02d}".format(int(offset.total_seconds() // 3600), int(offset.total_seconds() // 60 % 60)) offset_str = "UTC{:+02d}:{:02d}".format(int(offset.total_seconds() // 3600), int(offset.total_seconds() // 60 % 60))
await ctx.reply_ok("System time zone set to {} ({}, {}).\n*Data from OpenStreetMap, queried using Nominatim.*".format(tz.tzname(datetime.utcnow()), offset_str, tz.zone)) await ctx.reply_ok("System time zone set to {} ({}, {}).\n*Data from OpenStreetMap, queried using Nominatim.*".format(tz.tzname(datetime.utcnow()), offset_str, tz.zone))
@ -349,3 +359,41 @@ async def system_frontpercent(ctx: CommandContext, system: System):
embed.set_footer(text="Since {} ({} ago)".format(ctx.format_time(span_start), embed.set_footer(text="Since {} ({} ago)".format(ctx.format_time(span_start),
display_relative(span_start))) display_relative(span_start)))
await ctx.reply(embed=embed) await ctx.reply(embed=embed)
async def system_list(ctx: CommandContext, system: System):
all_members = sorted(await system.get_members(ctx.conn), key=lambda m: m.name)
page_size = 10
if len(all_members) <= page_size:
# If we have less than 10 members, don't bother paginating
await ctx.reply(embed=embeds.member_list(await ctx.get_system(), all_members, 0, page_size = page_size))
else:
current_page = 0
msg: discord.Message = None
while True:
page_count = len(all_members) // page_size
embed = embeds.member_list(await ctx.get_system(), all_members, current_page)
# Add reactions for moving back and forth
if not msg:
msg = await ctx.reply(embed=embed)
await msg.add_reaction("\u2B05")
await msg.add_reaction("\u27A1")
else:
await msg.edit(embed=embed)
def check(reaction, user):
return user.id == ctx.message.author.id and reaction.emoji in ["\u2B05", "\u27A1"]
try:
reaction, _ = await ctx.client.wait_for("reaction_add", timeout=5*60, check=check)
except asyncio.TimeoutError:
return
if reaction.emoji == "\u2B05":
current_page = (current_page - 1) % page_count
elif reaction.emoji == "\u27A1":
current_page = (current_page + 1) % page_count
# If we can, remove the original reaction from the member
if ctx.message.channel.permissions_for(ctx.message.guild.get_member(ctx.client.user.id)).manage_messages:
await reaction.remove(ctx.message.author)

View File

@ -1,6 +1,6 @@
import discord import discord
import humanize import humanize
from typing import Tuple from typing import Tuple, List
from pluralkit import db from pluralkit import db
from pluralkit.bot.utils import escape from pluralkit.bot.utils import escape
@ -66,7 +66,7 @@ def exception_log(message_content, author_name, author_discriminator, author_id,
return embed return embed
async def system_card(conn, client: discord.Client, system: System) -> discord.Embed: async def system_card(conn, client: discord.Client, system: System, is_own_system: bool = True) -> discord.Embed:
card = discord.Embed() card = discord.Embed()
card.colour = discord.Colour.blue() card.colour = discord.Colour.blue()
@ -97,33 +97,7 @@ async def system_card(conn, client: discord.Client, system: System) -> discord.E
card.add_field(name="Description", card.add_field(name="Description",
value=truncate_field_body(system.description), inline=False) value=truncate_field_body(system.description), inline=False)
# Get names of all members card.add_field(name="Members", value="*See `pk;system {} list`".format(system.hid) if not is_own_system else "*See `pk;system list`*")
all_members = await system.get_members(conn)
if all_members:
member_texts = []
for member in all_members:
member_texts.append("{} (`{}`)".format(escape(member.name), member.hid))
# Interim solution for pagination of large systems
# Previously a lot of systems would hit the 1024 character limit and thus break the message
# This splits large system lists into multiple embed fields
# The 6000 character total limit will still apply here but this sort of pushes the problem until I find a better fix
pages = [""]
for member in member_texts:
last_page = pages[-1]
new_page = last_page + "\n" + member if last_page else member
if len(new_page) >= 1024:
pages.append(member)
else:
pages[-1] = new_page
for index, page in enumerate(pages):
field_name = "Members"
if index >= 1:
field_name = "Members (part {})".format(index + 1)
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)) card.set_footer(text="System ID: {}".format(system.hid))
return card return card
@ -243,3 +217,21 @@ def help_footer_embed() -> discord.Embed:
embed = discord.Embed() embed = discord.Embed()
embed.set_footer(text="By @Ske#6201 | GitHub: https://github.com/xSke/PluralKit/") embed.set_footer(text="By @Ske#6201 | GitHub: https://github.com/xSke/PluralKit/")
return embed return embed
def member_list(system: System, all_members: List[Member], current_page: int = 0, page_size: int = 10):
page_count = len(all_members) // page_size
title = ""
if len(all_members) > page_size:
title += "[{}/{}] ".format(current_page + 1, page_count)
if system.name:
title += "Members of {} (`{}`)".format(system.name, system.hid)
else:
title += "Members of `{}`".format(system.hid)
embed = discord.Embed()
embed.title = title
for member in all_members[current_page*page_size:current_page*page_size+page_size]:
embed.add_field(name=member.name, value=(member.description or "") + "\n*ID: `{}`*".format(member.hid), inline=False)
return embed

View File

@ -13,6 +13,7 @@ pk;system delete
pk;system [system] fronter pk;system [system] fronter
pk;system [system] fronthistory pk;system [system] fronthistory
pk;system [system] frontpercent pk;system [system] frontpercent
pk;system [system] list
pk;link <other account> pk;link <other account>
pk;unlink pk;unlink
``` ```

View File

@ -2,7 +2,7 @@ aiodns
aiohttp==3.3.0 aiohttp==3.3.0
asyncpg asyncpg
dateparser dateparser
https://github.com/Rapptz/discord.py/archive/860d6a9ace8248dfeec18b8b159e7b757d9f56bb.zip#egg=discord.py https://github.com/Rapptz/discord.py/archive/aceec2009a7c819d2236884fa9ccc5ce58a92bea.zip#egg=discord.py
humanize humanize
uvloop; sys.platform != 'win32' and sys.platform != 'cygwin' and sys.platform != 'cli' uvloop; sys.platform != 'win32' and sys.platform != 'cygwin' and sys.platform != 'cli'
ciso8601 ciso8601