forked from viveksantayana/geas-bot
Updated bot.
Bugfix: player count on GM leaving Added /tcard command with audio Updated debug commands
This commit is contained in:
parent
40daa58326
commit
9014bdaac4
16
COMMANDS.md
16
COMMANDS.md
@ -14,7 +14,7 @@ The default prefix for the bot is `-`.
|
||||
|
||||
In the syntax description below, mandatory arguments are given in `<angled brackets>`, and optional arguments are given in `[square brackets]`.
|
||||
|
||||
For most purposes, the bot will primarily rely on the new `/command` framework, and as such those commadns will be listed first. The native bot commands will be provided below.
|
||||
For most purposes, the bot will primarily rely on the new `/command` framework, and as such those commands will be listed first. The native bot commands will be provided below.
|
||||
|
||||
## `/commands`
|
||||
|
||||
@ -60,7 +60,7 @@ The sub-commands have additional restrictions.
|
||||
|
||||
### Player Commands
|
||||
|
||||
These commands are for anaging players and their membership of games.
|
||||
These commands are for managing players and their membership of games.
|
||||
The base command is `/player`.
|
||||
Permissions for this command are open to all users (i.e. all users who have the `use slash command` permission enabled on the Guild settings).
|
||||
These commands are locked until there is at least one game configured.
|
||||
@ -96,16 +96,12 @@ These commands are default to the `Discord.py` library and are automatically ena
|
||||
|---|---|---| --- |
|
||||
| help | All Users | `-help [command]`| Default Discord helper command. Gives a list of all of the commands enabled in the Guild. And when used with an optional argument that gives a command name, it provides the help text, description, and syntax of the command.|
|
||||
|
||||
### Control Commands
|
||||
### Debug Commands
|
||||
|
||||
These commands are found in the file `./cogs/controlcommands/control.py` and provide meta-controls for how the bot and its functionality work.
|
||||
The permissions for these commands has been set up at the Cog level.
|
||||
These commands are found in the file `./cogs/controlcommands/debug.py` and provide meta-controls for how the bot and its functionality work.
|
||||
The permissions for these commands has been set up at the Cog level for those with the Bot Maintainer role only or for the owner of the Guild. If no bot maintainer role is defined, the command will fail.
|
||||
|
||||
| Command | Permissions | Syntax and Aliases | Description |
|
||||
|---|---|---| --- |
|
||||
| debug | Admin Only | `-debug on/off`| Enables debug features for the bot. Debug features can be used by the Bot's maintainer, as set up during configuration. Activating debug features will need to be authorised by an Admin.|
|
||||
| migrate | Bot Maintainer Only | `-migrate`| Migrates the guild data from the old bot to the new bot by inferring information from the roles and categories on the server. `After data migration has been completed, please uninstall the migrate.py cog to prevent future data clashes`.|
|
||||
| testconfig | Admin Only | `-testconfig` or `-configtest` | This command checks the completeness of the configurations of the Guild against the blueprint. |
|
||||
Information about debug commands will be displayed to users authorised to use them via the `-help` command.
|
||||
|
||||
### Prefix Command
|
||||
|
||||
|
@ -33,7 +33,6 @@ GM=(Path to the GM lookup file. The bot defaults to './data/gm.yml' if not provi
|
||||
CATEGORIES=(Path to the channel category lookup file. The bot defaults to './data/categories.yml' if not provided.)
|
||||
PITCHES=(Path to the pitches data file. The bot defaults to './data/pitches.yml' if not provided.)
|
||||
BOT_VERSION=(verson string)
|
||||
BOT_MAINTAINER_ID=(Discord user ID of the person maintaining the bot to enable debug features.)
|
||||
```
|
||||
|
||||
The correct API keys need to be entered in the environment variables in the `.env` file, and for a copy of this file to be placed in the root and the `app` directories.
|
||||
@ -63,7 +62,7 @@ in order for to authenticate as the correct bot.
|
||||
| | |-- botcommands
|
||||
| | | `-- prefix.py
|
||||
| | |-- controlcommands
|
||||
| | | `-- control.py
|
||||
| | | `-- debug.py
|
||||
| | |-- events
|
||||
| | | |-- on_command_error.py
|
||||
| | | |-- on_connect.py
|
||||
@ -98,8 +97,6 @@ in order for to authenticate as the correct bot.
|
||||
| | |-- data.yml
|
||||
| | |-- gm.yml
|
||||
| | `-- lookup.yml
|
||||
| |-- debug
|
||||
| | `-- debug.py
|
||||
| |-- Dockerfile
|
||||
| `-- requirements.txt
|
||||
|-- CHANGELOG.md
|
||||
|
BIN
app/assets/tcard.wav
Normal file
BIN
app/assets/tcard.wav
Normal file
Binary file not shown.
@ -1,58 +0,0 @@
|
||||
import os
|
||||
from dotenv import load_dotenv # Import OS variables from Dotenv file.
|
||||
load_dotenv() # Load Dotenv. Delete this for production
|
||||
import asyncio # Discord Py Dependency
|
||||
import discord # Main Lib
|
||||
from discord.ext import commands # Commands module
|
||||
from discord_slash import SlashCommand, SlashContext, cog_ext, utils # Slash Command Library
|
||||
from discord_slash.utils.manage_commands import create_choice, create_option # Slash Command features
|
||||
from deepdiff import DeepDiff
|
||||
from pprint import pprint
|
||||
|
||||
from bot import loadCog, unloadCog, cogsDir, checkConfig, parseConfigCheck, yaml_load, configFile
|
||||
|
||||
##### Control Cog
|
||||
class Control(commands.Cog, name='Cog Control Commands'):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
#### Check if user is an administrator
|
||||
async def cog_check(self, ctx:commands.Context):
|
||||
for role in ctx.author.roles:
|
||||
if role.permissions.administrator:
|
||||
return True
|
||||
return ctx.author.guild_permissions.administrator
|
||||
|
||||
@commands.command(
|
||||
name='debug',
|
||||
description='Toggles debug feature for the guild. Enter either `on` or `off`.',
|
||||
brief='Toggle debug features.'
|
||||
)
|
||||
async def _debug(self, ctx:commands.Context, toggle:str):
|
||||
if toggle.lower() == 'on':
|
||||
loadCog(f'./debug/debug.py')
|
||||
await ctx.reply(f'```Debug commands enabled. Use them carefully.```')
|
||||
elif toggle.lower() == 'off':
|
||||
unloadCog(f'./debug/debug.py')
|
||||
await ctx.reply(f'```Debug commands disabled.```')
|
||||
else:
|
||||
raise commands.CommandError(message='Invalid argument.')
|
||||
# await ctx.reply(f'```Invalid argument.```')
|
||||
|
||||
@commands.command(
|
||||
name='testconfig',
|
||||
description='Tests the completeness of the configuration values of the current guild by comparing it to a configuration blueprint.',
|
||||
brief='Tests config values for current guild.',
|
||||
aliases=['configtest']
|
||||
)
|
||||
async def _testconfig(self, ctx:commands.Context):
|
||||
checkConfig(ctx.guild)
|
||||
status, output = checkConfig(ctx.guild)
|
||||
conf = yaml_load(configFile)
|
||||
if not status:
|
||||
await ctx.reply(f"```The Bot's configurations are incomplete for the guild {ctx.guild.name}. Some limited functions will still be available, but most features cannot be used until the configurations are complete.\n{parseConfigCheck(output)}\nYou can set these configuration values using the `/config` command.```")
|
||||
elif status:
|
||||
await ctx.reply(f"```The Bot's configurations for the guild {ctx.guild.name} are in order. The Bot is ready to interact with the guild.```")
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(Control(client))
|
@ -8,7 +8,7 @@ from discord_slash import SlashCommand, SlashContext, cog_ext, utils # Slash C
|
||||
from discord_slash.utils.manage_commands import create_choice, create_option # Slash Command features
|
||||
from pprint import pprint
|
||||
|
||||
from bot import clearConfig, configFile, loadCog, loadCogs, setConfig, unloadCog, unloadCogs, yaml_dump, yaml_load, reloadCog, reloadCogs, pitchesFile, cogsDir
|
||||
from bot import clearConfig, configFile, checkConfig, loadCog, loadCogs, setConfig, unloadCog, unloadCogs, yaml_dump, yaml_load, reloadCog, reloadCogs, pitchesFile, cogsDir, parseConfigCheck
|
||||
|
||||
##### Debug Cog
|
||||
class Debug(commands.Cog, name='Debug Commands'):
|
||||
@ -16,9 +16,32 @@ class Debug(commands.Cog, name='Debug Commands'):
|
||||
self.client = client
|
||||
|
||||
#### Permission Check: Only available to the bot's maintainer.
|
||||
|
||||
async def cog_check(self, ctx:commands.Context):
|
||||
conf = yaml_load(configFile)
|
||||
for role in ctx.author.roles:
|
||||
if 'maintainer' in conf[str(ctx.guild.id)]['roles']:
|
||||
if role.id == conf[str(ctx.guild.id)]['roles']['maintainer']: return True
|
||||
return ctx.author.id == ctx.guild.owner_id
|
||||
|
||||
async def cog_check(self, ctx):
|
||||
return ctx.author.id == int(os.getenv('BOT_MAINTAINER_ID'))
|
||||
|
||||
@commands.command(
|
||||
name='testconfig',
|
||||
description='Tests the completeness of the configuration values of the current guild by comparing it to a configuration blueprint.',
|
||||
brief='Tests config values for current guild.',
|
||||
aliases=['configtest']
|
||||
)
|
||||
async def _testconfig(self, ctx:commands.Context):
|
||||
checkConfig(ctx.guild)
|
||||
status, output = checkConfig(ctx.guild)
|
||||
conf = yaml_load(configFile)
|
||||
if not status:
|
||||
await ctx.reply(f"```The Bot's configurations are incomplete for the guild {ctx.guild.name}. Some limited functions will still be available, but most features cannot be used until the configurations are complete.\n{parseConfigCheck(output)}\nYou can set these configuration values using the `/config` command.```")
|
||||
elif status:
|
||||
await ctx.reply(f"```The Bot's configurations for the guild {ctx.guild.name} are in order. The Bot is ready to interact with the guild.```")
|
||||
|
||||
@commands.command(
|
||||
name='reloadcogs',
|
||||
description='Reloads cogs within the specified category, or provide `all` for all cogs. Default: `all`.',
|
@ -71,8 +71,9 @@ class PitchListener(commands.Cog, name='Pitch Listener'):
|
||||
i = pitches[guildStr][timeslot]['indices'][role.id]
|
||||
element = pitches[guildStr][timeslot]['entries'][i]
|
||||
gm = await self.client.fetch_user(element['gm'])
|
||||
data[guildStr][timeslot][str(role.id)]['current_players'] -= 1
|
||||
element['current_players'] -= 1
|
||||
if ctx.author.id != lookup[guildStr][str(role.id)]['gm']:
|
||||
data[guildStr][timeslot][str(role.id)]['current_players'] -= 1
|
||||
element['current_players'] -= 1
|
||||
o = f'_ _\n***{element["game_title"]}*** (GM: {gm.mention})\n```\n'
|
||||
if element['system'] is not None: o = ''.join([o,f'System: {element["system"]}\n'])
|
||||
if element['min_players'] is not None: o = ''.join([o,f'Minimum Players: {str(element["min_players"])} '])
|
||||
@ -131,8 +132,10 @@ class PitchListener(commands.Cog, name='Pitch Listener'):
|
||||
await ctx.send(f'```Error: You are not in the game `{lookup[guildStr][str(role.id)]["game_title"]}`.```', hidden=True)
|
||||
else:
|
||||
await ctx.author.remove_roles(role,reason=f'/pitch interaction by {ctx.author.display_name}')
|
||||
data[guildStr][timeslot][str(role.id)]['current_players'] -= 1
|
||||
element = pitches[guildStr][timeslot]['entries'][index]
|
||||
if ctx.author.id != lookup[guildStr][str(role.id)]['gm']:
|
||||
data[guildStr][timeslot][str(role.id)]['current_players'] -= 1
|
||||
element['current_players'] -= 1
|
||||
gm = await self.client.fetch_user(element['gm'])
|
||||
o = f'_ _\n***{element["game_title"]}*** (GM: {gm.mention})\n```\n'
|
||||
if element['system'] is not None: o = ''.join([o,f'System: {element["system"]}\n'])
|
||||
|
@ -59,6 +59,10 @@ class Configuration(commands.Cog, name='Configuration Commands'):
|
||||
create_choice(
|
||||
name='`Student` role',
|
||||
value='student'
|
||||
),
|
||||
create_choice(
|
||||
name='`Bot Maintainer` role',
|
||||
value='maintainer'
|
||||
)
|
||||
]
|
||||
),
|
||||
|
@ -345,7 +345,7 @@ class GameManagement(commands.Cog, name='Game Management'):
|
||||
|
||||
result = ''.join(['```',result,f"The game has been configured with space for {max_players} players (with {current_players if current_players else '0'} currently occupied).",'```'])
|
||||
|
||||
output = f'```Hello {gm.display_name}! Your game channels for `{game_title}` has been updated.\nYou can ping your players or edit the game settings by interacting with the game role through the Bot commands. Have fun!```\n'
|
||||
output = f'```Hello {gm.display_name}! Your game channels for `{game_title}` have been updated.\nYou can ping your players or edit the game settings by interacting with the game role through the Bot commands. Have fun!```\n'
|
||||
output = ''.join([output,f'```Game Title: {game_title}\n'])
|
||||
output = ''.join([output,f'GM: {gm.display_name}\n'])
|
||||
output = ''.join([output,f'Time: {conf[guildStr]["timeslots"][time]}\n'])
|
||||
|
@ -200,6 +200,9 @@ class PlayerCommands(commands.Cog, name='Player Commands'):
|
||||
if game not in player.roles:
|
||||
await ctx.send(f'```Error: You are not in the game {lookup[guildStr][rStr]["game_title"]}.```',hidden=True)
|
||||
return
|
||||
if player.id == lookup[guildStr][rStr]['gm']:
|
||||
await ctx.send(f'```Error: You are the GM of the game {lookup[guildStr][rStr]["game_title"]} and cannot leave until a new GM is assigned.```',hidden=True)
|
||||
return
|
||||
await player.remove_roles(game, reason=f'`/player leave` command issued by {ctx.author.display_name}`.')
|
||||
t = lookup[guildStr][rStr]['time']
|
||||
if data[guildStr][t][rStr]['current_players'] <= 1: data[guildStr][t][rStr]['current_players'] = None
|
||||
|
84
app/cogs/slashcommands/secondary/tcard.py
Normal file
84
app/cogs/slashcommands/secondary/tcard.py
Normal file
@ -0,0 +1,84 @@
|
||||
import os # OS Locations
|
||||
import yaml # YAML parser for Bot config files
|
||||
import asyncio # Discord Py Dependency
|
||||
import discord # Main Lib
|
||||
from datetime import datetime
|
||||
import time
|
||||
from discord.ext import commands # Commands module
|
||||
from discord_slash import SlashCommand, SlashContext, cog_ext, utils # Slash Command Library
|
||||
from discord_slash.utils.manage_commands import create_choice, create_option, create_permission # Slash Command features
|
||||
from discord_slash.model import SlashCommandPermissionType
|
||||
|
||||
from bot import configFile, yaml_load, yaml_dump, lookupFile, dataFile, gmFile, categoriesFile
|
||||
|
||||
#### T Card Command
|
||||
|
||||
class TCardCommand(commands.Cog, name='T-Card Command'):
|
||||
def __init__(self, client):
|
||||
self.client = client
|
||||
|
||||
conf = yaml_load(configFile)
|
||||
lookup = yaml_load(lookupFile)
|
||||
data = yaml_load(dataFile)
|
||||
gms = yaml_load(gmFile)
|
||||
categories = yaml_load(categoriesFile)
|
||||
guild_ids= [ int(x) for x in list(lookup)]
|
||||
|
||||
@cog_ext.cog_slash(
|
||||
name='tcard',
|
||||
description='Invokes a T-Card in the current game.',
|
||||
default_permission=True,
|
||||
guild_ids=guild_ids,
|
||||
)
|
||||
async def _tcard(self, ctx:SlashContext):
|
||||
conf = yaml_load(configFile)
|
||||
lookup = yaml_load(lookupFile)
|
||||
data = yaml_load(dataFile)
|
||||
gms = yaml_load(gmFile)
|
||||
categories = yaml_load(categoriesFile)
|
||||
guildStr = str(ctx.guild.id)
|
||||
# rStr = str(game.id)
|
||||
embed = discord.Embed(
|
||||
title='T-Card',
|
||||
description='A T-Card Has Been Played',
|
||||
colour=discord.Color.dark_red,
|
||||
)
|
||||
embed.set_footer(text=datetime.now().strftime('%a %-d %b %y, %-I:%M %p'))
|
||||
embed.set_image(url='http://geas.org.uk/wp-content/uploads/2020/08/tcard-1-e1597167776966.png')
|
||||
|
||||
"""If this was called in a game channel"""
|
||||
if str(ctx.channel.category.id) in categories[guildStr]:
|
||||
|
||||
"""Send a T-Card graphic to the channel, tagging the role."""
|
||||
role = ctx.guild.get_role(categories[guildStr][str(ctx.channel.category.id)])
|
||||
await ctx.channel.send(f'{role.mention}', embed=embed)
|
||||
|
||||
if ctx.author_id == lookup[guildStr][str(role.id)]["gm"]:
|
||||
|
||||
"""Behaviour for when the GM issues T-Card command."""
|
||||
await ctx.send(content=f'```You have invoked the T-Card in the game {lookup[guildStr][str(role.id)]["game_title"]} as the GM.```', hidden=True)
|
||||
else:
|
||||
"""Default behaviour for when someone who is not the GM issues the command."""
|
||||
|
||||
"""Privately message the GM."""
|
||||
gm = await ctx.guild.fetch_member(lookup[guildStr][str(role.id)]["gm"])
|
||||
await gm.send(f'**Important**\n```Player {ctx.author.display_name} has invoked a T-Card in your game {lookup[guildStr][str(role.id)]["game_title"]}. Please check in with them and make sure everything is okay. If you need any further help, please feel free to contact a member of the Committee.```')
|
||||
|
||||
"""Notify the issuer of the command privately via hidden reply."""
|
||||
await ctx.send(content=f'```You have invoked the T-Card in the game {lookup[guildStr][str(role.id)]["game_title"]}. The GM has been notified privately.```', hidden=True)
|
||||
|
||||
"""Do the audio thing."""
|
||||
for vc in ctx.channel.category.voice_channels:
|
||||
v = await vc.connect()
|
||||
tcardaudio = discord.PCMAudio(open("./assets/tcard.wav", "rb"))
|
||||
v.play(tcardaudio)
|
||||
while v.is_playing():
|
||||
time.sleep(1)
|
||||
await v.disconnect()
|
||||
else:
|
||||
"""Send a T-Card to the immediate channel if this is a generic channel."""
|
||||
await ctx.channel.send(embed=embed)
|
||||
await ctx.send(content=f'```You have invoked the T-Card in the channel {ctx.channel.name}.```', hidden=True)
|
||||
|
||||
def setup(client):
|
||||
client.add_cog(TCardCommand(client))
|
Loading…
Reference in New Issue
Block a user