forked from viveksantayana/geas-bot
Issue present with Purge command
This commit is contained in:
parent
f1d12691c0
commit
51513c89a0
@ -1,6 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## Major Changes in Version 3
|
||||
|
||||
- Discards the database engine in favour of data storage in `.yml` files
|
||||
`The database engine was overkill. It only slowed down the Bot's performance, made it more fragile, and caused unnecessary complexity while not really providing any necessary utility.
|
||||
This also makes server migration much easier.
|
||||
@ -21,4 +22,4 @@ This makes the code a lot more complex to debug and maintain, especially when ha
|
||||
- Refactors the old code for more clarity in the bot's code
|
||||
`Adds some descriptions and explanatory text to the commands themselves so they can be accessed and displayed via the bot help command, making the bot a lot more usable.
|
||||
Also presents the code in a more readable format, with clearer indentation and parameter naming for core functions.
|
||||
The code has a lot more in-line comments explaining how it works so that it can be maintained by other people.`
|
||||
The code has a lot more in-line comments explaining how it works so that it can be maintained by other people.`
|
||||
|
@ -1,4 +1,5 @@
|
||||
# Bot Commands
|
||||
|
||||
A full list of bot commands can be retrieved using the `-help` command in the bot.
|
||||
The commands have full descriptions of their function as well as syntax that can be accessed via the help command.
|
||||
This is a reference file of all of the commands available, as well as the various cogs that control them.
|
@ -11,6 +11,7 @@ The first version I committed to the repository is version 2.1, and I previously
|
||||
Version 3 was the second major upgrade, taking advantage of some of the recent changes to the Discord API.
|
||||
|
||||
## Setup
|
||||
|
||||
The Bot is dockerised and uses docker-compose for deployment, so it is fairly straightforward to deploy an instance of.
|
||||
Clone the repository, install Docker and Docker Compose, navigate to the root directory (that contains the `docker-compose.yml` file), and use `docker-compose up -d` to set up and run the bot.
|
||||
The bot runs on one Docker container with the instance of the app as well as storage for its data and configuration.
|
||||
@ -47,6 +48,7 @@ client.run(os.getenv('BOT_TOKEN'))
|
||||
in order for to authenticate as the correct bot.
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
|-- app
|
||||
| |-- bot.py
|
||||
@ -99,6 +101,7 @@ I was considering merging them into one file, but given how different the two co
|
||||
I had initially condsiders a `.ini` file for the configuration settings and `.json` for the data, but I decided to use `.yml` for both just to avoid unnecessary complexity.
|
||||
|
||||
### `config.yml` Structure
|
||||
|
||||
This tree gives the list of various keys for the `.yml` dictionary as well as the types of different data expected.
|
||||
The entire configuration file is essentially a dictionary with other dictionaries, strings, integers, and lists as values.
|
||||
All values in the dictionary are referenced first by a string of the guild id integer.
|
||||
@ -128,6 +131,7 @@ Remember to convert the guild ID to strings during several operations, and be ca
|
||||
```
|
||||
|
||||
### `data.yml` Structure
|
||||
|
||||
Just like above, the `data.yml` file is also a dictionary of dictionaries that is indexed by a string of the guild id.
|
||||
It stores only the relevant data necessary for the code to function.
|
||||
It only holds, for instance, ID numbers rather than user handles, Discord discriminators, or names.
|
||||
|
33
TODO.md
33
TODO.md
@ -1,15 +1,19 @@
|
||||
# To Do
|
||||
|
||||
## Bot Architecture
|
||||
|
||||
- [x] Simplify directory tree
|
||||
- [x] Split event listeners into individual cogs.
|
||||
- [x] Update with re-organised data and config structure
|
||||
|
||||
> - [x] Correct references to data in existing cogs.
|
||||
|
||||
- [x] Setup minimally functioning configs of guild on startup
|
||||
- [x] Synchronise core configuration `/commands` on startup
|
||||
- [ ] ~~Synchronise secondary `/commands` on complete configuration **(see below)**~~
|
||||
|
||||
## Bot Functionality and Processes
|
||||
|
||||
- [ ] ~~'Delete Commands' Function~~
|
||||
- [ ] ~~'Register Commands' Function~~
|
||||
- [x] Infer Permissions from Config
|
||||
@ -20,37 +24,53 @@
|
||||
- [x] Delete Dev/Test Functions
|
||||
- [x] Error handlers
|
||||
- [x] Debug Features
|
||||
|
||||
> - [ ] ~~Command Installer/Uninstaller~~
|
||||
|
||||
- [x] Help Channel Event Listener
|
||||
|
||||
> - [x] Add Config key for Help Channel
|
||||
|
||||
- [ ] Slash Command Buttons or
|
||||
- [ ] Reaction listener selectors
|
||||
- [ ] Member Verification
|
||||
|
||||
> - [x] Add Config key membership signup channels
|
||||
> - [x] Add config keys: Membership Category Roles
|
||||
> - [ ] Message Receive listener
|
||||
> - [ ] Message React listener or buttons
|
||||
|
||||
- [ ] Membership Restriction
|
||||
|
||||
> - [ ] Message Receive Listener
|
||||
> - [ ] Membership Validation Listener
|
||||
|
||||
- [ ] Re-synchronise commands after any relevant config changes **(see above)**
|
||||
|
||||
> - [ ] Role Delete (member, admin, game)
|
||||
> - [ ] Channel delete (notifications, logs, game text channel)
|
||||
> - [ ] Category delete (games)
|
||||
|
||||
- [x] Flag for checking completeness of configuration for a guild.
|
||||
|
||||
> - [x] Function for checking configs for completeness
|
||||
|
||||
- [ ] Synchronise game channel on role updates
|
||||
|
||||
> - [ ] Exception to event listener to prevent circularity `unsure what this means`
|
||||
|
||||
## Event Listeners
|
||||
|
||||
## Review Configs When
|
||||
|
||||
- [x] Guild Changing Ownership
|
||||
- [x] Roles Modified
|
||||
- [x] Mod Channel Deleted
|
||||
|
||||
## Commands
|
||||
|
||||
- [x] Configure Bot function and sub commands
|
||||
|
||||
> - [x] botrole (role group)
|
||||
> - [x] committeerole (role group)
|
||||
> - [x] modchannel (channel group)
|
||||
@ -61,18 +81,25 @@
|
||||
> - [x] student role (role group)
|
||||
> - [x] help notifications (notification group)
|
||||
> - [x] signup notifications (notification group)
|
||||
|
||||
- [x] Set up timeslots
|
||||
- [x] Delete timeslots
|
||||
|
||||
> - [x] Base command
|
||||
> - [x] ~~Delete all games with the timeslot~~
|
||||
Do the opposite: block deleting timeslots with existing games.
|
||||
|
||||
- [x] List timeslots
|
||||
- [x] Set up command permissions
|
||||
|
||||
> - [x] Slash Commands
|
||||
>> - [x] Admin Commands
|
||||
>> - [x] Game Management Commands
|
||||
|
||||
> - [x] Native Bot Commands
|
||||
|
||||
- [ ] Migrate existing bot commands
|
||||
|
||||
> - [x] setupgame
|
||||
> - [x] ~~definebotrole~~ config
|
||||
> - [x] deletegame
|
||||
@ -82,13 +109,17 @@ Do the opposite: block deleting timeslots with existing games.
|
||||
> - [x] ~~addplayer~~ `/player add`
|
||||
> - [x] ~~leavegame~~ `/player leave`
|
||||
> - [ ] Pitch command and sub-commands
|
||||
|
||||
> > - [ ] run
|
||||
> > - [ ] clear
|
||||
|
||||
## Misc
|
||||
|
||||
- [ ] Review documentation
|
||||
|
||||
> - [ ] Finalise README.md
|
||||
> - [ ] CHANGELOG.md
|
||||
> - [ ] COMMANDS.md
|
||||
> - [ ] resources.md
|
||||
- [ ] Make sure to document **not using discord_components and staying with discord-py-slash-commands library alone**.
|
||||
|
||||
- [ ] Make sure to document **not using discord_components and staying with discord-py-slash-commands library alone**.
|
||||
|
@ -337,7 +337,7 @@ class Configuration(commands.Cog, name='Configuration Commands'):
|
||||
if role.id in conf[guildStr]['membership']:
|
||||
await ctx.send(f'```The role {name} has already been assigned to a membership type for guild `{ctx.guild.name}`. Please use the `remove` sub-command to delete it or assign a different role.```',hidden=True)
|
||||
return
|
||||
if any([ctx.guild.get_role(m).name == name for m in conf[guildStr]['membership']]):
|
||||
if any([ctx.guild.get_role(m).name == name for m in conf[str(ctx.guild.id)]['membership']]):
|
||||
await ctx.send(f'```The membership type {name} has already been assigned a role for guild `{ctx.guild.name}`. Please use the `remove` sub-command to delete the role or assign a different membership type.```',hidden=True)
|
||||
return
|
||||
if not role_exists:
|
||||
|
@ -222,7 +222,7 @@ class GameManagement(commands.Cog, name='Game Management'):
|
||||
if not data[guildStr][old_time]:
|
||||
del data[guildStr][old_time]
|
||||
if game_title and game_title != old_data['game_title']:
|
||||
if game_title in [x['game_title'] for x in lookup[guildStr].values()] and time in [x['time'] for x in lookup[guildStr].values()]:
|
||||
if game_title in [x['game_title'] for x in lookup[str(ctx.guild.id)].values()] and time in [x['time'] for x in lookup[str(ctx.guild.id)].values()]:
|
||||
await ctx.send(f'```The target game `{game_title}` has already been created for the time slot `{conf[guildStr]["timeslots"][time]}`. Please avoud duplicates, or use the `modify` sub-command to edit the existing game.```',hidden=True)
|
||||
return
|
||||
result = ''.join([result,f"The game's title has been updated to {game_title}\n"])
|
||||
@ -418,7 +418,7 @@ class GameManagement(commands.Cog, name='Game Management'):
|
||||
|
||||
if 'timeslots' not in conf[guildStr]:
|
||||
conf[guildStr]['timeslots'] = {}
|
||||
tsDict = {k: conf[guildStr]['timeslots'][k] for k in conf[guildStr]['timeslots'] if data[k]}
|
||||
tsDict = {k: conf[guildStr]['timeslots'][k] for k in conf[guildStr]['timeslots'] if data[guildStr][k]}
|
||||
optionsList = [create_select_option(label=tsDict[x], value=x, description=x) for x in tsDict].insert(0, create_select_option(label='All Timeslots', value='--all', description='--all'))
|
||||
try:
|
||||
m = await ctx.send(
|
||||
|
@ -133,7 +133,7 @@ class GameSetup(commands.Cog, name='Game Setup'):
|
||||
return
|
||||
if guildStr not in lookup:
|
||||
lookup[guildStr] = {}
|
||||
if game_title in [x['game_title'] for x in lookup[guildStr].values()] and time in [x['time'] for x in lookup[guildStr].values()]:
|
||||
if game_title in [x['game_title'] for x in lookup[str(ctx.guild.id)].values()] and time in [x['time'] for x in lookup[str(ctx.guild.id)].values()]:
|
||||
await ctx.send(f'```Game `{game_title}` has already been created for the time slot `{conf[guildStr]["timeslots"][time]}`. Please avoud duplicates, or use the `modify` sub-command to edit the existing game.```',hidden=True)
|
||||
return
|
||||
if guildStr not in data:
|
||||
@ -241,7 +241,7 @@ class GameSetup(commands.Cog, name='Game Setup'):
|
||||
result = ''.join([result,f'There was already a channel category that matched the game, so it has been reconfigured with the appropriate permissions and text and voice channels.\n'])
|
||||
else:
|
||||
result = ''.join([result,f'A channel category with the appropriate text and voice channels has been created.\n'])
|
||||
result = ''.join(['```',result,f'```\n{gm.mention} {r.mention} {t.mention}'])
|
||||
result = ''.join(['```',result,f'```\n{gm.mention} | {r.mention} | {t.mention}'])
|
||||
output = f'```Hello {gm.display_name}! Your game channels for `{game_title}` have been created.\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'])
|
||||
@ -280,7 +280,7 @@ class GameSetup(commands.Cog, name='Game Setup'):
|
||||
if str(guildStr) not in categories: categories[guildStr] = {}
|
||||
gms[guildStr][str(gm.id)].append(r.id)
|
||||
if guildStr not in categories: categories[guildStr] = {}
|
||||
categores[guildStr][str(c.id)] = r.id
|
||||
categories[guildStr][str(c.id)] = r.id
|
||||
yaml_dump(data,dataFile)
|
||||
yaml_dump(lookup,lookupFile)
|
||||
yaml_dump(gms,gmFile)
|
||||
|
@ -59,8 +59,8 @@ class PlayerCommands(commands.Cog, name='Player Commands'):
|
||||
if rStr not in lookup[guildStr]:
|
||||
await ctx.send(f'```Error: This is not a valid game role. Please mention a role that is associated with a game.```',hidden=True)
|
||||
return
|
||||
if not (set(ctx.author.roles) & set([ctx.guild.get_role(x) for x in conf[guildStr]['roles']['admin']]) or ctx.author == ctx.guild.owner):
|
||||
if not set(ctx.author.roles) & set([ctx.guild.get_role(int(x)) for x in gms[guildStr] if gms[guildStr][x]]):
|
||||
if not (set(ctx.author.roles) & set([ctx.guild.get_role(x) for x in conf[str(ctx.guild.id)]['roles']['admin']]) or ctx.author == ctx.guild.owner):
|
||||
if not set(ctx.author.roles) & set([ctx.guild.get_role(int(x)) for x in gms[str(ctx.guild.id)] if gms[str(ctx.guild.id)][x]]):
|
||||
await ctx.send(f'```Error: You are not authorised to issue this command. The command may only be issued by an administrator or by a GM.```',hidden=True)
|
||||
return
|
||||
if ctx.author.id != lookup[guildStr][rStr]['gm']:
|
||||
@ -70,11 +70,11 @@ class PlayerCommands(commands.Cog, name='Player Commands'):
|
||||
await ctx.send(f'```Error: Player `{player.display_name}` is already in the game {lookup[guildStr][rStr]["game_title"]}.```',hidden=True)
|
||||
return
|
||||
await player.add_roles(game, reason=f'`/player add` command issued by {ctx.author.display_name}`.')
|
||||
t = lookup[rStr]['time']
|
||||
t = lookup[guildStr][rStr]['time']
|
||||
if type(data[guildStr][t][rStr]['current_players']) is not int: data[guildStr][t][rStr]['current_players'] = 1
|
||||
else: data[guildStr][t][rStr]['current_players'] += 1
|
||||
hm = lookup[rStr]['header_message']
|
||||
tc = discord.utils.find(lambda x: x.id == lookup[rStr]['text_message'],ctx.guild.channels)
|
||||
hm = data[guildStr][t][rStr]['header_message']
|
||||
tc = discord.utils.find(lambda x: x.id == lookup[guildStr][rStr]['text_channel'],ctx.guild.channels)
|
||||
if tc is not None:
|
||||
p = await tc.pins()
|
||||
if p:
|
||||
@ -133,24 +133,24 @@ class PlayerCommands(commands.Cog, name='Player Commands'):
|
||||
if str(ctx.channel.category.id) not in categories[guildStr]:
|
||||
await ctx.send(f'```Error: This command can only be issued in a text channel associated with a game.```', hidden=True)
|
||||
return
|
||||
game = categories[guildStr][str(ctx.channel.category.id)]
|
||||
game = discord.utils.find(lambda x: x.id == categories[guildStr][str(ctx.channel.category.id)], ctx.guild.roles)
|
||||
rStr = str(game.id)
|
||||
if game not in player.roles:
|
||||
await ctx.send(f'```Error: Player `{player.display_name}` is not in the game {lookup[guildStr][rStr]["game_title"]}.```',hidden=True)
|
||||
return
|
||||
if not (set(ctx.author.roles) & set([ctx.guild.get_role(x) for x in conf[guildStr]['roles']['admin']]) or ctx.author == ctx.guild.owner):
|
||||
if not set(ctx.author.roles) & set([ctx.guild.get_role(int(x)) for x in gms[guildStr] if gms[guildStr][x]]):
|
||||
if not (set(ctx.author.roles) & set([ctx.guild.get_role(x) for x in conf[str(ctx.guild.id)]['roles']['admin']]) or ctx.author == ctx.guild.owner):
|
||||
if not set(ctx.author.roles) & set([ctx.guild.get_role(int(x)) for x in gms[str(ctx.guild.id)] if gms[str(ctx.guild.id)][x]]):
|
||||
await ctx.send(f'```Error: You are not authorised to issue this command. The command may only be issued by an administrator or by a GM.```',hidden=True)
|
||||
return
|
||||
if ctx.author.id != lookup[guildStr][rStr]['gm']:
|
||||
await ctx.send(f'```Error: You are not authorised to issue this command. A player may only be added to a game by the GM or by an administrator.```',hidden=True)
|
||||
return
|
||||
await player.remove_roles(game, reason=f'`/player remove` command issued by {ctx.author.display_name}`.')
|
||||
t = lookup[rStr]['time']
|
||||
if type(data[guildStr][t][rStr]['current_players']) <= 1: data[guildStr][t][rStr]['current_players'] = None
|
||||
t = lookup[guildStr][rStr]['time']
|
||||
if data[guildStr][t][rStr]['current_players'] <= 1: data[guildStr][t][rStr]['current_players'] = None
|
||||
else: data[guildStr][t][rStr]['current_players'] -= 1
|
||||
hm = lookup[rStr]['header_message']
|
||||
tc = discord.utils.find(lambda x: x.id == lookup[rStr]['text_message'],ctx.guild.channels)
|
||||
hm = data[guildStr][t][rStr]['header_message']
|
||||
tc = discord.utils.find(lambda x: x.id == lookup[guildStr][rStr]['text_channel'],ctx.guild.channels)
|
||||
if tc is not None:
|
||||
p = await tc.pins()
|
||||
if p:
|
||||
@ -206,11 +206,11 @@ class PlayerCommands(commands.Cog, name='Player Commands'):
|
||||
await ctx.send(f'```Error: Player `{player.display_name}` is not in the game {lookup[guildStr][rStr]["game_title"]}.```',hidden=True)
|
||||
return
|
||||
await player.remove_roles(game, reason=f'`/player leave` command issued by {ctx.author.display_name}`.')
|
||||
t = lookup[rStr]['time']
|
||||
if type(data[guildStr][t][rStr]['current_players']) <= 1: data[guildStr][t][rStr]['current_players'] = None
|
||||
t = lookup[guildStr][rStr]['time']
|
||||
if data[guildStr][t][rStr]['current_players'] <= 1: data[guildStr][t][rStr]['current_players'] = None
|
||||
else: data[guildStr][t][rStr]['current_players'] -= 1
|
||||
hm = lookup[rStr]['header_message']
|
||||
tc = discord.utils.find(lambda x: x.id == lookup[rStr]['text_message'],ctx.guild.channels)
|
||||
hm = data[guildStr][t][rStr]['header_message']
|
||||
tc = discord.utils.find(lambda x: x.id == lookup[guildStr][rStr]['text_channel'],ctx.guild.channels)
|
||||
if tc is not None:
|
||||
p = await tc.pins()
|
||||
if p:
|
||||
|
@ -1,2 +1,3 @@
|
||||
'864651943820525609':
|
||||
'866788661026488352': 866788659839238164
|
||||
'867516972496060446': 867516971017175070
|
||||
|
@ -12,3 +12,15 @@
|
||||
role: 866788659839238164
|
||||
system: null
|
||||
text_channel: 866788663194812446
|
||||
'867516971017175070':
|
||||
category: 867516972496060446
|
||||
current_players: 1
|
||||
game_title: Masks
|
||||
gm: 493694762210033664
|
||||
header_message: 867517905620303894
|
||||
max_players: 5
|
||||
min_players: null
|
||||
platform: null
|
||||
role: 867516971017175070
|
||||
system: null
|
||||
text_channel: 867516974212055060
|
||||
|
@ -1,4 +1,5 @@
|
||||
'864651943820525609':
|
||||
'493694762210033664':
|
||||
- 866788659839238164
|
||||
- 867516971017175070
|
||||
'864649599671205914': []
|
||||
|
@ -5,3 +5,9 @@
|
||||
gm: 493694762210033664
|
||||
text_channel: 866788663194812446
|
||||
time: suneve
|
||||
'867516971017175070':
|
||||
category: 867516972496060446
|
||||
game_title: Masks
|
||||
gm: 493694762210033664
|
||||
text_channel: 867516974212055060
|
||||
time: suneve
|
||||
|
10
resources.md
10
resources.md
@ -1,21 +1,27 @@
|
||||
# Resources for Maintaining the Bot
|
||||
|
||||
## Documentation
|
||||
|
||||
1. [Discord Py Documentation](https://discordpy.readthedocs.io/en/stable/index.html)
|
||||
|
||||
> 1. [Quickstart Guide](https://discordpy.readthedocs.io/en/stable/quickstart.html)
|
||||
> 2. [Set up of Discord Bot Account](https://discordpy.readthedocs.io/en/stable/discord.html)
|
||||
> 3. [**Important**: Primer to Gateway Intents](https://discordpy.readthedocs.io/en/stable/intents.html)
|
||||
`N.B.: this is an important security feature of Discord that is now mandatory to configure and imposes restructions on some of the Bot's functionality unless appropriately configured. Keep an eye on this.`
|
||||
> 4. [Repository with example code](https://github.com/Rapptz/discord.py/tree/v1.7.3/examples)
|
||||
> 5. [Logging Setup](https://discordpy.readthedocs.io/en/stable/logging.html)
|
||||
|
||||
2. [Discord Py Slash Command Documentation](https://discord-py-slash-command.readthedocs.io/en/latest/index.html)
|
||||
|
||||
> 1. [Discord Py Slash Command Authentication](https://discord-py-slash-command.readthedocs.io/en/latest/quickstart.html)
|
||||
`N.B.: this is an important security feature in Discord's API, and commands will not be configured unless the applications.commands scope is configured correctly.`
|
||||
> 2. [How to add Slash Commands, including sub-commands](https://discord-py-slash-command.readthedocs.io/en/latest/faq.html#:~:text=If%20your%20slash%20commands%20don,commands%20scope%20in%20that%20guild.)
|
||||
> 3. [Slash Command Cogs Module](https://discord-py-slash-command.readthedocs.io/en/latest/discord_slash.cog_ext.html?highlight=cog#discord_slash.cog_ext.cog_subcommand)
|
||||
|
||||
3. [Discord Components Documentation](https://discord-components.readthedocs.io/en/0.5.2.4/)
|
||||
|
||||
## YouTube Tutorials
|
||||
|
||||
1. [Starting from the basics by Lucas](https://www.youtube.com/watch?v=nW8c7vT6Hl4)
|
||||
2. [Introduction to Cogs](https://www.youtube.com/watch?v=vQw8cFfZPx0)
|
||||
3. [Dynamic prefixes for different servers](https://www.youtube.com/watch?v=yrHbGhem6I4)
|
||||
@ -26,10 +32,12 @@
|
||||
## Communities
|
||||
|
||||
### Discord Py
|
||||
|
||||
1. [Discord Py server](https://discord.gg/r3sSKJJ): Discord server to talk to others in the community
|
||||
2. [Discord Py Github issue tracker](https://github.com/Rapptz/discord.py/issues): place to report bugs and issues with the API
|
||||
3. [Discord Py discussion page](https://github.com/Rapptz/discord.py/discussions): wiki for any other discussions
|
||||
|
||||
### Discord Py Slash Commands
|
||||
|
||||
1. [Discord Py Slash Commands Discord Server](https://discord.gg/KkgMBVuEkx): Discord server with a forum to ask questions in
|
||||
2. [Discord Py Slash Commands Issue Tracker on GitHub](https://github.com/discord-py-slash-commands/discord-py-interactions/issues)
|
||||
2. [Discord Py Slash Commands Issue Tracker on GitHub](https://github.com/discord-py-slash-commands/discord-py-interactions/issues)
|
||||
|
Loading…
Reference in New Issue
Block a user