Commit bcdaeacd authored by Geovanny's avatar Geovanny

Added helper for commands

parent 5a711f4b
...@@ -7,15 +7,29 @@ from discord.ext import commands ...@@ -7,15 +7,29 @@ from discord.ext import commands
from unit.manager import UnitManager from unit.manager import UnitManager
from user.manager import UserManager from user.manager import UserManager
from util.command_error_handler import CommandErrorHandler from util.command_error_handler import CommandErrorHandler
from util.help import EditedMinimalHelpCommand, PaginatedHelpCommand
from util.api_requests import Api from util.api_requests import Api
from settings import DISCORD_TOKEN from settings import DISCORD_TOKEN
class ConqBot(commands.Bot): class ConqBot(commands.Bot):
def __init__(self, command_prefix='>'): def __init__(self, command_prefix='>'):
super().__init__(command_prefix) super().__init__(
command_prefix=command_prefix,
help_command=EditedMinimalHelpCommand())
self.id_to_session = {} self.id_to_session = {}
self.static_help_command = self.help_command
command_impl = self.help_command._command_impl
self.help_command = PaginatedHelpCommand()
self.static_help_command._command_impl = command_impl
# self.remove_command('help')
# self.add_command(commands.Command(self._help, name='help'))
async def _help(self, ctx, *, command=None):
await ctx.send_help(command)
async def getUserSession(self, user:discord.User): async def getUserSession(self, user:discord.User):
if user.id in self.id_to_session: if user.id in self.id_to_session:
return self.id_to_session[user.id] return self.id_to_session[user.id]
......
...@@ -21,44 +21,11 @@ class UnitManager(commands.Cog): ...@@ -21,44 +21,11 @@ class UnitManager(commands.Cog):
else: else:
return await ctx.send('Unexpected error. Please try again') return await ctx.send('Unexpected error. Please try again')
#@commands.HelpCommand()
async def send_cog_help(self, cog):
print('Hello')
self.add_command_formatting(command)
for name, param in command.clean_params.items():
if isinstance(param.annotation, ArgumentConverter):
arguments = param.annotation.arguments
if not arguments:
continue
self.paginator.add_line("Arguments:")
max_size = max(len(name) for name in arguments)
for name, argument in arguments.items():
entry = "{0}{1:<{width}} {2}".format(self.indent * " ", name, argument.doc, width=max_size)
self.paginator.add_line(self.shorten_text(entry))
self.paginator.close_page()
await self.send_pages()
async def send_bot_help(self, command):
print('There')
self.add_command_formatting(command)
for name, param in command.clean_params.items():
if isinstance(param.annotation, ArgumentConverter):
arguments = param.annotation.arguments
if not arguments:
continue
self.paginator.add_line("Arguments:")
max_size = max(len(name) for name in arguments)
for name, argument in arguments.items():
entry = "{0}{1:<{width}} {2}".format(self.indent * " ", name, argument.doc, width=max_size)
self.paginator.add_line(self.shorten_text(entry))
self.paginator.close_page()
await self.send_pages()
@commands.command() @commands.command()
async def modifyUnit(self, ctx, unit:str, *, params:modify_param_converter=modify_param_converter.defaults()): async def modifyUnit(self, ctx, unit:str, *, params:modify_param_converter=modify_param_converter.defaults()):
"""Modify units by name or id. Specify parameters to be modified""" """Modify units by name or id. Specify parameters to be modified.
Example: modifyUnit 13 name=\"New Name\" type=\"New Type\"
Help for more info"""
if not params: if not params:
return await ctx.send('Some parameters have to be provided') return await ctx.send('Some parameters have to be provided')
...@@ -91,7 +58,9 @@ class UnitManager(commands.Cog): ...@@ -91,7 +58,9 @@ class UnitManager(commands.Cog):
@commands.command() @commands.command()
async def insertUnit(self, ctx, name:str, *, params:insert_param_converter=insert_param_converter.defaults()): async def insertUnit(self, ctx, name:str, *, params:insert_param_converter=insert_param_converter.defaults()):
"""Insert unit by name. Additional parameters can be added""" """Insert unit by name. Additional parameters can be added
Example: insertUnit "New Name" type=\"New Type\"
Help for more info"""
unit_data = params; unit_data = params;
unit_data['name'] = name; unit_data['name'] = name;
...@@ -103,7 +72,7 @@ class UnitManager(commands.Cog): ...@@ -103,7 +72,7 @@ class UnitManager(commands.Cog):
return await self.handleApiError(ctx, error) return await self.handleApiError(ctx, error)
@commands.command() @commands.command()
async def unitinfo(self, ctx, *, unit:str): async def unitInfo(self, ctx, *, unit:str):
"""Get info from unit""" """Get info from unit"""
data = None data = None
...@@ -144,7 +113,7 @@ class UnitManager(commands.Cog): ...@@ -144,7 +113,7 @@ class UnitManager(commands.Cog):
@commands.command() @commands.command()
async def allunits(self, ctx): async def allUnits(self, ctx):
"""Gets all units""" """Gets all units"""
data = await Api.get('/unit/all') data = await Api.get('/unit/all')
......
...@@ -21,6 +21,7 @@ class UserManager(commands.Cog): ...@@ -21,6 +21,7 @@ class UserManager(commands.Cog):
@commands.command() @commands.command()
async def registerUser(self, ctx): async def registerUser(self, ctx):
"""Register user. Only done once per user. Can't access user related content otherwise"""
req_body = {'discordId': ctx.message.author.id} req_body = {'discordId': ctx.message.author.id}
try: try:
await Api.post('/user/discord-register', req_body) await Api.post('/user/discord-register', req_body)
...@@ -30,6 +31,7 @@ class UserManager(commands.Cog): ...@@ -30,6 +31,7 @@ class UserManager(commands.Cog):
@commands.command() @commands.command()
async def removeAssign(self, ctx, term:str): async def removeAssign(self, ctx, term:str):
"""Unassign unit from yourself"""
data = None data = None
try: try:
session = await self.bot.getUserSession(ctx.message.author) session = await self.bot.getUserSession(ctx.message.author)
...@@ -55,6 +57,9 @@ class UserManager(commands.Cog): ...@@ -55,6 +57,9 @@ class UserManager(commands.Cog):
@commands.command() @commands.command()
async def modUnit(self, ctx, term:str, *, params:param_converter=param_converter.defaults()): async def modUnit(self, ctx, term:str, *, params:param_converter=param_converter.defaults()):
"""Modify unit assigned.
Example: modUnit \"Yelmo\" unit_level=20
Help for more info"""
data = None data = None
try: try:
session = await self.bot.getUserSession(ctx.message.author) session = await self.bot.getUserSession(ctx.message.author)
...@@ -81,6 +86,9 @@ class UserManager(commands.Cog): ...@@ -81,6 +86,9 @@ class UserManager(commands.Cog):
@commands.command() @commands.command()
async def assignUnit(self, ctx, term:str, *, params:param_converter=param_converter.defaults()): async def assignUnit(self, ctx, term:str, *, params:param_converter=param_converter.defaults()):
"""Assign unit to yourself.
Example: assignUnit \"Yelmo\" unit_level=1
Help for more info"""
data = None data = None
try: try:
data = await Api.get('/unit/{0}'.format(term)) data = await Api.get('/unit/{0}'.format(term))
...@@ -108,6 +116,7 @@ class UserManager(commands.Cog): ...@@ -108,6 +116,7 @@ class UserManager(commands.Cog):
@commands.command() @commands.command()
async def myUnits(self, ctx): async def myUnits(self, ctx):
"""Get units assigned to user"""
try: try:
session = await self.bot.getUserSession(ctx.message.author) session = await self.bot.getUserSession(ctx.message.author)
data = await Api.getSession('/user/units', session) data = await Api.getSession('/user/units', session)
......
import discord
from discord.ext import commands
from util.pager import Pager
import discord_argparse as da
class HelpPager(Pager):
commands_per_page = 8
indent = 3
def add_page(self, cog_name, cog_desc, cmds):
'''Will split into several pages to accomodate the per_page limit.'''
# will obviously not run if no commands are in the page
for cmds_slice in [cmds[i:i + self.commands_per_page] for i in range(0, len(cmds), self.commands_per_page)]:
self.entries.append((cog_name, cog_desc, cmds_slice))
async def craft_page(self, e, page, entries):
cog_name, cog_desc, commands = entries[0]
name = f'{cog_name} Commands'
self.embed.set_author(name=name, icon_url=self.bot.user.avatar_url)
self.embed.description = cog_desc
for name, value in commands:
self.embed.add_field(name=name, value=value, inline=False)
async def craft_page_detail(self, cog_name, cog_desc, command):
name = f'{cog_name} Commands'
embed = discord.Embed()
embed.set_author(name=name, icon_url=self.bot.user.avatar_url)
embed.description = cog_desc
embed.add_field(name=command.name, value=command.help, inline=False)
for name, param in command.clean_params.items():
if isinstance(param.annotation, da.ArgumentConverter):
arguments = param.annotation.arguments
if not arguments:
continue
max_size = max(len(name) for name in arguments)
params_text = ''
for name, argument in arguments.items():
entry = "{0}{1:<{width}} |\t {2}".format(self.indent * " ", name, argument.doc, width=max_size)
params_text += "{0}\n".format(entry)
embed.add_field(name="Arguments", value=params_text)
return embed
async def help_embed(self, e):
e.set_author(name='How do I use the bot?', icon_url=self.bot.user.avatar_url)
e.description = (
'Invoke a command by sending the prefix followed by a command name.\n\n'
'For example, the command signature `track <query>` can be invoked by doing `track yellow`\n\n'
'The different argument brackets mean:'
)
e.add_field(name='<argument>', value='the argument is required.', inline=False)
e.add_field(name='[argument]', value='the argument is optional.\n\u200b', inline=False)
class PaginatedHelpCommand(commands.HelpCommand):
'''Cog that implements the help command and help pager.'''
async def add_command(self, cmds, command, force=False):
if command.hidden:
return
if force is False:
try:
if not await command.can_run(self.context):
return
except commands.CheckFailure:
return
help_message = command.brief or command.help
if help_message is None:
help_message = 'No description available.'
else:
help_message = help_message.split('\n')[0]
cmds.append((self.context.prefix + get_signature(command), help_message))
async def prepare_help_command(self, ctx, command=None):
self.context = ctx
self.pager = HelpPager(ctx, list(), per_page=1)
async def add_cog(self, cog):
cog_name = cog.__class__.__name__
cog_desc = cog.__doc__
cmds = []
added = []
for command in cog.walk_commands():
if command in added:
continue
await self.add_command(cmds, command)
added.append(command)
self.pager.add_page(cog_name, cog_desc, cmds)
async def send_bot_help(self, mapping):
for cog in mapping:
if cog is not None:
await self.add_cog(cog)
await self.pager.go()
async def send_cog_help(self, cog):
await self.add_cog(cog)
await self.pager.go()
async def send_group_help(self, group):
if group.cog_name.lower() == group.name.lower():
await self.send_cog_help(group.cog)
return
added = []
cmds = []
for command in group.walk_commands():
if command in added:
continue
await self.add_command(cmds, command)
added.append(command)
self.pager.add_page(group.cog_name, group.cog.__doc__, cmds)
await self.pager.go()
async def send_command_help(self, command):
cog_name = command.cog_name
if cog_name is not None and cog_name.lower() == command.name:
await self.send_cog_help(command.cog)
return
cog_desc = command.cog.__doc__
embed = await self.pager.craft_page_detail(cog_name, cog_desc, command)
return await self.context.send(embed=embed)
async def command_not_found(self, string):
return commands.CommandNotFound(string)
async def send_error_message(self, error):
if not isinstance(error, commands.CommandNotFound):
return
cmd = str(error)
for cog in self.context.bot.cogs:
if cmd == cog.lower():
await self.send_cog_help(self.context.bot.get_cog(cog))
return
await self.context.send('Command \'{}\' not found.'.format(cmd))
class EditedMinimalHelpCommand(commands.MinimalHelpCommand):
def get_ending_note(self):
return (
'The interactive help menu did not get sent because the bot is missing '
'the following permissions: ' + ', '.join(self.missing_perms)
)
async def send_error_message(self, error):
return
# rip is just the signature command ripped from the lib, but with alias support removed.
def get_signature(command):
"""Returns a POSIX-like signature useful for help command output."""
result = []
parent = command.full_parent_name
name = command.name if not parent else parent + ' ' + command.name
result.append(name)
if command.usage:
result.append(command.usage)
return ' '.join(result)
params = command.clean_params
if not params:
return ' '.join(result)
for name, param in params.items():
if param.default is not param.empty:
# We don't want None or '' to trigger the [name=value] case and instead it should
# do [name] since [name=None] or [name=] are not exactly useful for the user.
should_print = param.default if isinstance(param.default, str) else param.default is not None
if should_print:
result.append('[%s=%s]' % (name, param.default))
else:
result.append('[%s]' % name)
elif param.kind == param.VAR_POSITIONAL:
result.append('[%s...]' % name)
else:
result.append('<%s>' % name)
return ' '.join(result)
\ No newline at end of file
import discord
import asyncio
from math import ceil
STATIC_PERMS = ('add_reactions', 'manage_messages', 'embed_links')
FIRST_EMOJI = '\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}'
NEXT_EMOJI = '\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE}'
PREV_EMOJI = '\N{BLACK LEFT-POINTING DOUBLE TRIANGLE}'
LAST_EMOJI = '\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}'
STOP_EMOJI = '\N{BLACK SQUARE FOR STOP}'
HELP_EMOJI = '\N{WHITE QUESTION MARK ORNAMENT}'
class Pager:
def __init__(self, ctx, entries=None, page=1, per_page=12, owner=None, timeout=120.0, separator=' '):
self.ctx = ctx
self.bot = ctx.bot
self.author = owner or ctx.author
self.guild = ctx.guild
self.channel = ctx.channel
self.entries = entries or []
self.embed = discord.Embed()
self.page = page
self.timeout = timeout
self.separator = separator
self.per_page = per_page
self.on_help = False
self.static = False
self.missing_perms = []
# overrides to a static view if missing perms!
perms = ctx.guild.me.permissions_in(ctx.channel)
for perm in STATIC_PERMS:
if not getattr(perms, perm):
self.missing_perms.append(perm.replace('_', ' ').title())
self.static = True
async def go(self):
if not len(self.entries):
return
await self.get_page(1)
msg = await self.ctx.send(embed=self.embed)
if self.static:
return
if self.top_page != 1:
emojis = [FIRST_EMOJI, PREV_EMOJI, NEXT_EMOJI, LAST_EMOJI, STOP_EMOJI, HELP_EMOJI]
if self.top_page == 2:
emojis.remove(FIRST_EMOJI)
emojis.remove(LAST_EMOJI)
for emoji in emojis:
try:
await msg.add_reaction(emoji)
except discord.HTTPException:
pass
def pred(reaction, user):
return reaction.message.id == msg.id and user != self.bot.user
while True:
try:
reaction, user = await self.bot.wait_for('reaction_add', check=pred, timeout=self.timeout)
except asyncio.TimeoutError:
break
else:
# just remove if it isn't authors reaction
if user != self.author:
await msg.remove_reaction(reaction.emoji, user)
continue
# if it is, and it's a stop emoji, just stop
if reaction.emoji == STOP_EMOJI:
await msg.clear_reactions()
return
# otherwise, delete the reaction before handling case
await msg.remove_reaction(reaction.emoji, user)
if reaction.emoji == NEXT_EMOJI:
await self.next()
elif reaction.emoji == PREV_EMOJI:
await self.prev()
elif reaction.emoji == FIRST_EMOJI:
await self.first()
elif reaction.emoji == LAST_EMOJI:
await self.last()
elif reaction.emoji == HELP_EMOJI:
await self.help()
else:
continue
await msg.edit(embed=self.embed)
try:
await msg.clear_reactions()
except discord.HTTPException:
pass
@property
def top_page(self):
if self.static:
return 1
return ceil(len(self.entries) / self.per_page)
def clear_embed(self):
e = self.embed
e.title = None
e.description = None
e.set_author(name='', url='')
e.clear_fields()
if self.static:
e.set_footer(text='Non-interactive! I\'m missing: ' + ', '.join(self.missing_perms))
elif self.on_help:
e.set_footer(text='')
elif self.top_page > 1:
e.set_footer(text=f'Page {self.page}/{self.top_page}')
else:
e.set_footer(text='')
async def get_page(self, page):
self.clear_embed()
await self.craft_page(self.embed, page, self.get_page_entries(page))
async def craft_page(self, e, page, entries):
'''Crafts the actual embed.'''
e.description = self.separator.join(str(entry) for entry in entries)
def get_page_entries(self, page):
'''Converts a page number to a range of entries.'''
base = (page - 1) * self.per_page
return self.entries[base:base + self.per_page]
async def try_page(self, page):
if self.top_page >= page >= 1:
self.page = page
await self.get_page(page)
async def next(self):
await self.try_page(self.page + 1)
async def prev(self):
await self.try_page(self.page - 1)
async def first(self):
self.page = 1
await self.get_page(self.page)
async def last(self):
self.page = self.top_page
await self.get_page(self.page)
async def help(self):
if self.on_help:
self.on_help = False
await self.get_page(self.page)
else:
self.on_help = True
self.clear_embed()
await self.help_embed(self.embed)
async def help_embed(self, e):
e.title = 'How to navigate this paginator!'
e.description = 'Below is a description of what each emoji does.'
e.add_field(name=PREV_EMOJI, value='Moves back to the previous page.')
e.add_field(name=NEXT_EMOJI, value='Moves to the next page.')
if self.top_page > 2:
e.add_field(name=FIRST_EMOJI, value='Moves to the first page.')
e.add_field(name=LAST_EMOJI, value='Moves to the last page.')
e.add_field(name=STOP_EMOJI, value='Quits this pagination session.')
e.add_field(name=HELP_EMOJI, value='Toggles this help message.')
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment