forked from viveksantayana/geas-bot
15 July Nightly Commit
Split cogs into different files About to change file structuring to move dev file to main file
This commit is contained in:
parent
ef6c49b5f8
commit
b0b417a8d2
2
.gitignore
vendored
2
.gitignore
vendored
@ -143,6 +143,8 @@ cython_debug/
|
|||||||
# Local Dev Env Configs
|
# Local Dev Env Configs
|
||||||
Scripts/
|
Scripts/
|
||||||
pyvenv.cfg
|
pyvenv.cfg
|
||||||
|
app/dev_cogs/template.py.tmp
|
||||||
|
app/dev_cogs/events.py.tmp
|
||||||
|
|
||||||
# ---> VisualStudioCode
|
# ---> VisualStudioCode
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
12
TODO.md
12
TODO.md
@ -2,20 +2,24 @@
|
|||||||
|
|
||||||
## Bot Architecture
|
## Bot Architecture
|
||||||
[] Simplify directory tree
|
[] Simplify directory tree
|
||||||
|
[X] Split event listeners into independent cogs.
|
||||||
|
|
||||||
## Bot Functionality
|
## Bot Functionality
|
||||||
[] Delete Commands Function
|
[] Delete Commands Function
|
||||||
[] Register Commands Function
|
[] Register Commands Function
|
||||||
[] Infer Permissions from Config
|
[] Infer Permissions from Config
|
||||||
[] Dynamic Command Prefixes
|
[X] Dynamic Command Prefixes
|
||||||
[] Infer Games from Server Structure
|
[] Infer Games from Server Structure
|
||||||
|
[] Re-enable logging
|
||||||
|
[X] Delete Dev/Test Functions
|
||||||
|
[] Debug Features
|
||||||
|
|
||||||
## Event Listeners
|
## Event Listeners
|
||||||
|
|
||||||
### Review Configs When
|
### Review Configs When
|
||||||
[] Guild Changing Ownership
|
[X] Guild Changing Ownership
|
||||||
[] Roles Modified
|
[X] Roles Modified
|
||||||
[] Mod Channel Deleted
|
[X] Mod Channel Deleted
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
[] Migrate existing bot commands
|
[] Migrate existing bot commands
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
'864651943820525609':
|
'864651943820525609':
|
||||||
adminroles: []
|
adminroles:
|
||||||
|
- 864661232005939280
|
||||||
|
modchannel: 865348933022515220
|
||||||
name: Test
|
name: Test
|
||||||
owner: 493694762210033664
|
owner: 493694762210033664
|
||||||
|
prefix: '-'
|
||||||
|
1
app/data/data.yml
Normal file
1
app/data/data.yml
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
97
app/debug/debug.py
Normal file
97
app/debug/debug.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
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 dev import clearConfig, configFile, loadCog, loadCogs, setConfig, unloadCog, unloadCogs, yaml_dump, yaml_load
|
||||||
|
|
||||||
|
##### Debug Cog
|
||||||
|
class Debug(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name='reloadcogs',
|
||||||
|
description='Reloads cogs within the specified category, or provide `all` for all cogs. Default: `all`.',
|
||||||
|
brief='Reload multiple cogs by category.'
|
||||||
|
)
|
||||||
|
async def _reload(self, ctx, cog_category: str='all'):
|
||||||
|
unloadCogs(cog_category)
|
||||||
|
loadCogs(cog_category)
|
||||||
|
await ctx.repky(f'`{cog_category}` cogs have been reloaded.')
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name='unloadcogs',
|
||||||
|
description='Unload cogs within the specified category, or provide `all` for all cogs. Default: `all`.',
|
||||||
|
brief='Unload multiple cogs by category.'
|
||||||
|
)
|
||||||
|
async def _unloadcogs(self, ctx, cog_category: str='all'):
|
||||||
|
unloadCogs(cog_category)
|
||||||
|
loadCogs(cog_category)
|
||||||
|
await ctx.repky(f'`{cog_category}` cogs have been unloaded.')
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name='loadcogs',
|
||||||
|
description='Load cogs within the specified category, or provide `all` for all cogs. Default: `all`.',
|
||||||
|
brief='Load multiple cogs by category.'
|
||||||
|
)
|
||||||
|
async def _loadcogs(self, ctx, cog_category: str='all'):
|
||||||
|
unloadCogs(cog_category)
|
||||||
|
loadCogs(cog_category)
|
||||||
|
await ctx.repky(f'`{cog_category}` cogs have been loaded.')
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name='deletecommands',
|
||||||
|
aliases=['delallcommands','deleteslashcommands'],
|
||||||
|
description='Deletes all the public and guild slash commands registered by the bot.',
|
||||||
|
brief='Delets all slash commands'
|
||||||
|
)
|
||||||
|
async def _deleteAll(self, ctx):
|
||||||
|
await utils.manage_commands.remove_all_commands(
|
||||||
|
bot_id=self.client.user.id,
|
||||||
|
bot_token=os.getenv('TEST_3_TOKEN'),
|
||||||
|
guild_ids=None
|
||||||
|
)
|
||||||
|
await utils.manage_commands.remove_all_commands(
|
||||||
|
bot_id=self.client.user.id,
|
||||||
|
bot_token=os.getenv('TEST_3_TOKEN'),
|
||||||
|
guild_ids=[ int(g) for g in yaml_load(configFile)]
|
||||||
|
)
|
||||||
|
await ctx.reply('All slash commands have been deleted.')
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name='retrievecommands',
|
||||||
|
aliases=['slashcommands','retrieveslashcommands']
|
||||||
|
)
|
||||||
|
async def _retrievecommands(self, ctx:commands.Context):
|
||||||
|
c = await utils.manage_commands.get_all_commands(client.user.id,os.getenv('TEST_3_TOKEN'),guild_id=ctx.guild.id)
|
||||||
|
print(c)
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name='clearconfig',
|
||||||
|
aliases=['configclear'],
|
||||||
|
description='Clears any redundant entries in the config file of guilds the bot is not in. Does not require any argument.',
|
||||||
|
brief='Clears redundant entries from config file.'
|
||||||
|
)
|
||||||
|
async def _clearconfig(self, ctx:commands.Context):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
for key in list(conf):
|
||||||
|
clearConfig(key)
|
||||||
|
ctx.reply(f'Config entries have been cleared.')
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name='setconfig',
|
||||||
|
aliases=['configsetup'],
|
||||||
|
description='Creates a config entry for the current guild, and if there are existing entries sets default values. Ignores any values that have already been set. Does not require any argument as it infers the guild from the context in which it was called.',
|
||||||
|
brief='Sets config entry for the current guild.'
|
||||||
|
)
|
||||||
|
async def _setconfig(self, ctx:commands.Context):
|
||||||
|
setConfig(ctx.guild)
|
||||||
|
ctx.reply(f'Config entry has been added for guild `{ctx.guild.name}`.')
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(Debug(client))
|
155
app/dev.py
155
app/dev.py
@ -1,8 +1,8 @@
|
|||||||
import os # OS Locations
|
import os, sys # OS Locations
|
||||||
from dotenv import load_dotenv # Import OS variables from Dotenv file.
|
from dotenv import load_dotenv # Import OS variables from Dotenv file.
|
||||||
load_dotenv() # Load Dotenv. Delete this for production
|
load_dotenv() # Load Dotenv. Delete this for production
|
||||||
import yaml # Parser for yaml files for config settings.
|
import yaml # Parser for yaml files for config settings.
|
||||||
import json # Json Library to manage json Data files
|
import asyncio # Discord Py Dependency
|
||||||
import discord # Main Lib
|
import discord # Main Lib
|
||||||
from discord.ext import commands # Commands module
|
from discord.ext import commands # Commands module
|
||||||
from discord_slash import SlashCommand, SlashContext, cog_ext, utils # Slash Command Library
|
from discord_slash import SlashCommand, SlashContext, cog_ext, utils # Slash Command Library
|
||||||
@ -11,37 +11,49 @@ import logging
|
|||||||
|
|
||||||
## Define YAML functions
|
## Define YAML functions
|
||||||
|
|
||||||
def yaml_load(filepath):
|
def yaml_load(filepath:str):
|
||||||
### Loads a YAML file
|
### Loads a YAML file
|
||||||
with open(filepath, 'r') as file:
|
with open(filepath, 'r') as file:
|
||||||
data = yaml.load(file)
|
data = yaml.load(file, Loader=yaml.FullLoader)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def yaml_dump(data, filepath):
|
def yaml_dump(data:dict, filepath:str):
|
||||||
### Dumps a YAML file
|
### Dumps a YAML file
|
||||||
with open(filepath, 'w') as file:
|
with open(filepath, 'w') as file:
|
||||||
yaml.dump(data, file)
|
yaml.dump(data, file)
|
||||||
|
|
||||||
# Locate or create config File
|
# Locate or create config file
|
||||||
configFile = './data/config.yml'
|
configFile = './data/config.yml'
|
||||||
|
|
||||||
if not os.path.exists(configFile):
|
if not os.path.exists(configFile):
|
||||||
yaml_dump({},configFile)
|
yaml_dump({},configFile)
|
||||||
|
|
||||||
|
# Locate or create data file
|
||||||
|
dataFile = './data/data.yml'
|
||||||
|
|
||||||
|
if not os.path.exists(dataFile):
|
||||||
|
yaml_dump({},dataFile)
|
||||||
|
|
||||||
# Locate Cogs Directory
|
# Locate Cogs Directory
|
||||||
cogsDir = 'dev_cogs'
|
cogsDir = 'dev_cogs'
|
||||||
|
|
||||||
## Logging configuration imported boilerplate from Discord Py Docs
|
# --> Temporary disable logging because of verboseness.
|
||||||
logger = logging.getLogger('discord')
|
# ## Logging configuration imported boilerplate from Discord Py Docs
|
||||||
logger.setLevel(logging.DEBUG)
|
# logger = logging.getLogger('discord')
|
||||||
handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w')
|
# logger.setLevel(logging.DEBUG)
|
||||||
handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
|
# handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w')
|
||||||
logger.addHandler(handler)
|
# handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s'))
|
||||||
|
# logger.addHandler(handler)
|
||||||
|
|
||||||
|
#### Dynamic Prefixes
|
||||||
|
def getPrefix(client, message):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
return conf[str(message.guild.id)]['prefix']
|
||||||
|
|
||||||
# Define Clients
|
# Define Clients
|
||||||
client = commands.Bot(
|
client = commands.Bot(
|
||||||
intents=discord.Intents.all(),
|
intents=discord.Intents.all(),
|
||||||
command_prefix='¬'
|
command_prefix=getPrefix
|
||||||
)
|
)
|
||||||
slash = SlashCommand(
|
slash = SlashCommand(
|
||||||
client,
|
client,
|
||||||
@ -51,67 +63,74 @@ slash = SlashCommand(
|
|||||||
# sync_on_reload is an important parameter that will become relevant when having to reload cogs on changing bot configs.
|
# sync_on_reload is an important parameter that will become relevant when having to reload cogs on changing bot configs.
|
||||||
|
|
||||||
# Define Config keys
|
# Define Config keys
|
||||||
configKeys = ['adminroles', 'botrole', 'modchannel', 'name', 'owner', 'prefix']
|
configKeys = ['adminroles', 'committeerole', 'botrole', 'modchannel', 'name', 'owner', 'prefix']
|
||||||
|
|
||||||
# Create list of guild IDs the bot is currently in
|
def setConfig(guild:discord.Guild):
|
||||||
# guild_ids = [864651943820525609]
|
#### Check if the bot is missing any config entries for the guilds it is in, and if it is then add it in.
|
||||||
guild_ids = []
|
#### N.B.: The way the commands work, the bot will have to list specific guilds in which it will synchronise the commands when it is defining them. So it needs to give a list of all the guilds it is part of when the bot loads, which it draws from the config files.
|
||||||
|
#### Because the bot connects to Discord after it loads, it will not be able to introspect and see what guilds it is part of before the commands are first loaded, and it will only add new guilds to the config files after it has already connected.
|
||||||
|
#### The Bot will first need to set up all of its configurations, and then begin loading all other commands once it is ready.
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
if str(guild.id) not in conf:
|
||||||
|
conf[str(guild.id)] = {}
|
||||||
|
if 'name' not in conf[str(guild.id)] or conf[str(guild.id)]['name'] != guild.name:
|
||||||
|
conf[str(guild.id)]['name'] = guild.name
|
||||||
|
if 'owner' not in conf[str(guild.id)] or conf[str(guild.id)]['owner'] != guild.owner_id:
|
||||||
|
conf[str(guild.id)]['owner'] = guild.owner_id
|
||||||
|
if 'adminroles' not in conf[str(guild.id)] or (type(conf[str(guild.id)]['adminroles']) is not list or len(conf[str(guild.id)]['adminroles']) == 0 or None in conf[str(guild.id)]['adminroles']):
|
||||||
|
conf[str(guild.id)]['adminroles'] = []
|
||||||
|
for role in guild.roles:
|
||||||
|
if not (role.is_bot_managed() or role.is_integration()) and role.permissions.administrator:
|
||||||
|
conf[str(guild.id)]['adminroles'].append(role.id)
|
||||||
|
if 'prefix' not in conf[str(guild.id)]:
|
||||||
|
conf[str(guild.id)]['prefix'] = '-'
|
||||||
|
if 'modchannel' not in conf[str(guild.id)]:
|
||||||
|
if guild.system_channel is None:
|
||||||
|
p = len(guild.channels)
|
||||||
|
c = None
|
||||||
|
for t in guild.text_channels:
|
||||||
|
if t.position < p:
|
||||||
|
p = t.position
|
||||||
|
conf[str(guild.id)]['modchannel'] = t.id
|
||||||
|
else:
|
||||||
|
conf[str(guild.id)]['modchannel'] = guild.system_channel.id
|
||||||
|
yaml_dump(conf, configFile)
|
||||||
|
|
||||||
# Retrieve IDs from config file
|
def clearConfig(guildKey:str):
|
||||||
conf = yaml_load(configFile)
|
#### Delete Configs for Guilds that the Bot is no longer in
|
||||||
for guild in conf:
|
conf = yaml_load(configFile)
|
||||||
if int(guild) not in guild_ids:
|
if discord.utils.find(lambda g: str(g.id) == guildKey, client.guilds) is None:
|
||||||
guild_ids.append(int(guild))
|
del conf[guildKey]
|
||||||
# Will check on bot ready if any other guilds are missing fron configs.
|
yaml_dump(conf, configFile)
|
||||||
|
|
||||||
@client.command(name='foobartest')
|
def loadCog(filepath:str):
|
||||||
async def foobartest(ctx):
|
path = os.path.normpath(filepath).split(os.path.sep)
|
||||||
f = await utils.manage_commands.get_all_commands(client.user.id,os.getenv('TEST_3_TOKEN'),guild_id=ctx.guild.id)
|
if path[-1].endswith('.py'):
|
||||||
print(f)
|
path[-1] = path[-1][:-3]
|
||||||
|
client.load_extension('.'.join(path))
|
||||||
|
|
||||||
def loadCommands():
|
def unloadCog(filepath:str):
|
||||||
for cogfile in os.listdir(f'./{cogsDir}/commands'):
|
path = os.path.normpath(filepath).split(os.path.sep)
|
||||||
logging.info('Loading commands.')
|
if path[-1].endswith('.py'):
|
||||||
if cogfile.endswith('.py'):
|
path[-1] = path[-1][:-3]
|
||||||
c = cogfile[:-3]
|
client.unload_extension('.'.join(path))
|
||||||
client.load_extension(f'{cogsDir}.commands.{c}')
|
|
||||||
print(f'Loaded Cog ./{cogsDir}/{c}')
|
|
||||||
logging.info(f'Loaded Cog ./app/{cogsDir}/commands/{c}')
|
|
||||||
|
|
||||||
def unloadCommands():
|
def loadCogs(cogClass:str = 'all'):
|
||||||
for cogfile in os.listdir(f'./{cogsDir}/commands'):
|
for category in os.listdir(f'./{cogsDir}'):
|
||||||
logging.info('Unloading commands.')
|
if cogClass == 'all' or cogClass == category:
|
||||||
if cogfile.endswith('.py'):
|
for cogfile in os.listdir(f'./{cogsDir}/{category}'):
|
||||||
c = cogfile[:-3]
|
if cogfile.endswith('.py'):
|
||||||
client.unload_extension(f'{cogsDir}.commands.{c}')
|
loadCog(f'./{cogsDir}/{category}/{cogfile}')
|
||||||
print(f'Unloaded Cog ./{cogsDir}/{c}')
|
|
||||||
logging.info(f'Unloaded Cog ./{cogsDir}/commands/{c}')
|
|
||||||
|
|
||||||
def reloadCommands():
|
def unloadCogs(cogClass:str = 'all'):
|
||||||
unloadCommands()
|
for category in os.listdir(f'./{cogsDir}'):
|
||||||
loadCommands()
|
if cogClass == 'all' or cogClass == category:
|
||||||
|
for cogfile in os.listdir(f'./{cogsDir}/{category}'):
|
||||||
|
if cogfile.endswith('.py'):
|
||||||
|
unloadCog(f'./{cogsDir}/{category}/{cogfile}')
|
||||||
|
|
||||||
def loadAllCogs():
|
loadCogs('dev')
|
||||||
for cogfile in os.listdir(f'./{cogsDir}'):
|
loadCogs('events')
|
||||||
logging.info('Loading cogs.')
|
loadCogs('botcommands')
|
||||||
if cogfile.endswith('.py'):
|
|
||||||
c = cogfile[:-3]
|
|
||||||
client.load_extension(f'{cogsDir}.{c}')
|
|
||||||
print(f'Loaded Cog ./{cogsDir}/{c}')
|
|
||||||
logging.info(f'Loaded Cog ./app/{cogsDir}/{c}')
|
|
||||||
|
|
||||||
def unloadAllCogs():
|
|
||||||
for cogfile in os.listdir(f'./{cogsDir}'):
|
|
||||||
logging.info('Unloading cogs.')
|
|
||||||
if cogfile.endswith('.py'):
|
|
||||||
c = cogfile[:-3]
|
|
||||||
client.unload_extension(f'{cogsDir}.{c}')
|
|
||||||
print(f'Unloaded Cog ./{cogsDir}/{c}')
|
|
||||||
logging.info(f'Unloaded Cog ./{cogsDir}/{c}')
|
|
||||||
|
|
||||||
def reloadAllCogs():
|
|
||||||
unloadAllCogs()
|
|
||||||
loadAllCogs()
|
|
||||||
|
|
||||||
loadAllCogs()
|
|
||||||
client.run(os.getenv('TEST_3_TOKEN'))
|
client.run(os.getenv('TEST_3_TOKEN'))
|
26
app/dev_cogs/botcommands/prefix.py
Normal file
26
app/dev_cogs/botcommands/prefix.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
import asyncio # Discord Py Dependency
|
||||||
|
import discord # Main Lib
|
||||||
|
from discord.ext import commands # Commands module
|
||||||
|
from dev import configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
### Cog for handling the non-Slash prefix for native Bot commands.
|
||||||
|
|
||||||
|
class Prefix(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.command(
|
||||||
|
name = 'changeprefix',
|
||||||
|
aliases = ['prefix'],
|
||||||
|
description = 'This command changes the prefix string for the native, non-slash command functionality of the bot. Defaults to `-`. It does not affect the workings of /commands.',
|
||||||
|
brief = 'Changes the bot prefix.'
|
||||||
|
)
|
||||||
|
async def _changePrefix(self, ctx, prefix:str = '-'):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
conf[str(ctx.guild.id)]['prefix'] = prefix.lower()
|
||||||
|
yaml_dump(conf, configFile)
|
||||||
|
await ctx.send(f"`{self.client.user.name}`'s prefix for native bot commands has been changed to `{prefix}` for the guild `{ctx.guild.name}`.\n`Note: This will not affect /commands.`")
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(Prefix(client))
|
@ -1,51 +0,0 @@
|
|||||||
import os
|
|
||||||
from dotenv import load_dotenv # Import OS variables from Dotenv file.
|
|
||||||
load_dotenv() # Load Dotenv. Delete this for production
|
|
||||||
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 dev import guild_ids, unloadAllCogs, loadAllCogs, reloadAllCogs
|
|
||||||
|
|
||||||
##### Debug Cog
|
|
||||||
class Debug(commands.Cog):
|
|
||||||
def __init__(self, client):
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
@cog_ext.cog_slash(
|
|
||||||
name='reload',
|
|
||||||
description='Reloads all cogs',
|
|
||||||
guild_ids=guild_ids
|
|
||||||
)
|
|
||||||
async def _reload(self, ctx:SlashContext):
|
|
||||||
reloadAllCogs()
|
|
||||||
await ctx.send('Reloading Cogs.')
|
|
||||||
|
|
||||||
@cog_ext.cog_slash(
|
|
||||||
name='deleteAll',
|
|
||||||
description='Deletes all Slash Commands',
|
|
||||||
guild_ids=guild_ids
|
|
||||||
)
|
|
||||||
async def _deleteAll(self, ctx:SlashContext):
|
|
||||||
await utils.manage_commands.remove_all_commands(
|
|
||||||
bot_id=self.client.user.id,
|
|
||||||
bot_token=os.getenv('TEST_3_TOKEN'),
|
|
||||||
guild_ids=None
|
|
||||||
)
|
|
||||||
await utils.manage_commands.remove_all_commands(
|
|
||||||
bot_id=self.client.user.id,
|
|
||||||
bot_token=os.getenv('TEST_3_TOKEN'),
|
|
||||||
guild_ids=guild_ids
|
|
||||||
)
|
|
||||||
await ctx.send('Deleted all commands.')
|
|
||||||
|
|
||||||
@commands.command(
|
|
||||||
name='reloadAll'
|
|
||||||
)
|
|
||||||
async def _reloadAll(self, ctx):
|
|
||||||
await ctx.send('Reloading all cogs.')
|
|
||||||
reloadAllCogs()
|
|
||||||
|
|
||||||
def setup(client):
|
|
||||||
client.add_cog(Debug(client))
|
|
31
app/dev_cogs/dev/devcog.py
Normal file
31
app/dev_cogs/dev/devcog.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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 dev import loadCog, unloadCog
|
||||||
|
|
||||||
|
##### Debug Cog
|
||||||
|
class DevCog(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@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, 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.')
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(DevCog(client))
|
@ -1,90 +0,0 @@
|
|||||||
import os # OS Locations
|
|
||||||
import yaml # YAML parser for Bot config files
|
|
||||||
import json # Json Library to manage json Data files
|
|
||||||
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
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from dev import configFile, guild_ids, unloadAllCogs, loadAllCogs, reloadAllCogs, loadCommands, unloadCommands, reloadCommands, logger, handler, yaml_load, yaml_dump
|
|
||||||
|
|
||||||
##### Event Listener Cog
|
|
||||||
class Events(commands.Cog):
|
|
||||||
def __init__(self, client):
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
|
||||||
async def on_connect(self): ## Actions for when bot logs in and enters ready state
|
|
||||||
print('Bot has connected.')
|
|
||||||
logging.info('Bot has connected.')
|
|
||||||
|
|
||||||
#### Check if the bot is missing any config entries for the guilds it is in, and if it is then add it in.
|
|
||||||
#### N.B.: The way the commands work, the bot will have to list specific guilds in which it will synchronise the commands when it is defining them. So it needs to give a list of all the guilds it is part of when the bot loads, which it draws from the config files.
|
|
||||||
#### Because the bot connects to Discord after it loads, it will not be able to introspect and see what guilds it is part of before the commands are first loaded, and it will only add new guilds to the config files after it has already connected.
|
|
||||||
#### So the bot will have to wait until it first connects, then check if any guilds are missing, and if there are any missing guilds it will need to add them to the list of guilds it has configured, then re-load all of the commands and re-sync them.
|
|
||||||
|
|
||||||
gFlag = False
|
|
||||||
print('Checking for guilds with missing configs.') #### The bot will try to update its configs for any missing values. It will check the accuracy of these values if any relevant parameters of the guild change later.
|
|
||||||
conf = yaml_load(configFile)
|
|
||||||
for g in self.client.guilds:
|
|
||||||
if g.id not in guild_ids:
|
|
||||||
gFlag = True
|
|
||||||
guild_ids.append(g.id)
|
|
||||||
conf[str(g.id)] = {}
|
|
||||||
if 'name' not in conf[str(g.id)]:
|
|
||||||
conf[str(g.id)]['name'] = g.name
|
|
||||||
if 'owner' not in conf[str(g.id)]:
|
|
||||||
conf[str(g.id)]['owner'] = await self.client.fetch_guild(g.id).owner.id
|
|
||||||
if 'adminroles' not in conf[str(g.id)]:
|
|
||||||
conf[str(g.id)]['adminroles'] = []
|
|
||||||
for role in g.roles:
|
|
||||||
if not (role.is_bot_managed() or role.is_integration()) and role.permissions.administrator:
|
|
||||||
conf[str(g.id)]['adminroles'].append(role.id)
|
|
||||||
yaml_dump(conf, configFile)
|
|
||||||
if gFlag:
|
|
||||||
reloadAllCogs()
|
|
||||||
## Because the bot will need to re-sync all the commands with an updated list of guilds, it needs to re-load all cogs.
|
|
||||||
## This should hopefully work with the setting declared earlier to sync on cog reload.
|
|
||||||
|
|
||||||
#### Delete Configs for Guilds that the Bot is no longer in
|
|
||||||
conf = yaml_load(configFile)
|
|
||||||
a = []
|
|
||||||
for g in self.client.guilds:
|
|
||||||
a.append(str(g.id))
|
|
||||||
for g in list(conf):
|
|
||||||
if g not in a:
|
|
||||||
del conf[g]
|
|
||||||
yaml_dump(conf, configFile)
|
|
||||||
|
|
||||||
# for g in self.client.guilds:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
|
||||||
async def on_guild_join(self, guild): ## Actions for when bot is added to a new guild for the first time.
|
|
||||||
if guild.id not in guild_ids:
|
|
||||||
guild_ids.append(guild.id)
|
|
||||||
o = f'Adding config entry for guild {g.name}.'
|
|
||||||
print(o)
|
|
||||||
logging.info(o)
|
|
||||||
conf = yaml_load(configFile)
|
|
||||||
conf[str(g.id)] = {}
|
|
||||||
yaml_dump(conf, configFile)
|
|
||||||
reloadAllCogs()
|
|
||||||
## Same reason as above, when the bot is added to a new guild, it will need to re-load all cogs and re-sync commands.
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
|
||||||
async def on_guild_remove(self, guild): ## Actions for when the bot is removed from a guild.
|
|
||||||
if guild.id in guild_ids:
|
|
||||||
guild_ids.remove(guild.id)
|
|
||||||
o = f'Removing config entry for guild {g.name}.'
|
|
||||||
print(o)
|
|
||||||
logging.info(o)
|
|
||||||
conf = yaml_load(configFile)
|
|
||||||
conf.pop(str(guild.id), None)
|
|
||||||
yaml_dump(conf, configFile)
|
|
||||||
reloadAllCogs()
|
|
||||||
|
|
||||||
def setup(client):
|
|
||||||
client.add_cog(Events(client))
|
|
30
app/dev_cogs/events/on_connect.py
Normal file
30
app/dev_cogs/events/on_connect.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
#### Actions for the Bot to take on connecting to Discord.
|
||||||
|
class on_connect(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_connect(self): ## Actions for when bot logs in and enters ready state
|
||||||
|
print('Bot has connected.')
|
||||||
|
# logging.info('Bot has connected.')
|
||||||
|
await self.client.change_presence(
|
||||||
|
status = discord.Status.online,
|
||||||
|
activity = discord.Activity(
|
||||||
|
type = discord.ActivityType.listening,
|
||||||
|
name = f'/commands'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# for g in self.client.guilds:
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_connect(client))
|
33
app/dev_cogs/events/on_guild_channel_delete.py
Normal file
33
app/dev_cogs/events/on_guild_channel_delete.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
##### Actions for the bot to take whenever a channel in a guild is deleted
|
||||||
|
class on_guild_channel_delete(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
#### What to do if a mod channel gets deleted: try and pull default system channel, and if not then top-most channel
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_guild_channel_delete(self, channel):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
if conf[str(channel.guild.id)]['modchannel'] == channel.id:
|
||||||
|
if channel.guild.system_channel is None:
|
||||||
|
p = len(channel.guild.channels)
|
||||||
|
c = None
|
||||||
|
for t in channel.guild.text_channels:
|
||||||
|
if t.position < p:
|
||||||
|
p = t.position
|
||||||
|
conf[str(channel.guild.id)]['modchannel'] = t.id
|
||||||
|
else:
|
||||||
|
conf[str(channel.guild.id)]['modchannel'] = channel.guild.system_channel.id
|
||||||
|
yaml_dump(conf, configFile)
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_guild_channel_delete(client))
|
22
app/dev_cogs/events/on_guild_join.py
Normal file
22
app/dev_cogs/events/on_guild_join.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import configFile, setConfig, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
#### Actions for the bot to take when the Bot joins a guild.
|
||||||
|
|
||||||
|
class on_guild_join(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_guild_join(self, guild):
|
||||||
|
setConfig(guild)
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_guild_join(client))
|
22
app/dev_cogs/events/on_guild_remove.py
Normal file
22
app/dev_cogs/events/on_guild_remove.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import clearConfig, configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
#### Actions for the bot to take when removed from a guild.
|
||||||
|
|
||||||
|
class on_guild_remove(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_guild_remove(self, guild): ## Actions for when the bot is removed from a guild.
|
||||||
|
clearConfig(str(guild.id))
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_guild_remove(client))
|
27
app/dev_cogs/events/on_guild_role_create.py
Normal file
27
app/dev_cogs/events/on_guild_role_create.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
##### Actions for the bot to take whenever there is a new role created.
|
||||||
|
|
||||||
|
class on_guild_role_create(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_guild_role_create(self, role):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
#### Bot will only respond if the role is not a bot-managed role, and the role is an admin role
|
||||||
|
if not (role.is_bot_managed() or role.is_integration()) and role.permissions.administrator:
|
||||||
|
conf[str(role.guild.id)]['adminroles'].append(role.id)
|
||||||
|
yaml_dump(conf, configFile)
|
||||||
|
#### If the role is created with admin privileges, the bot adds the role to its configs.
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_guild_role_create(client))
|
27
app/dev_cogs/events/on_guild_role_delete.py
Normal file
27
app/dev_cogs/events/on_guild_role_delete.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
##### Actions for the bot to take whenever there is a new role deleted.
|
||||||
|
|
||||||
|
class on_guild_role_delete(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_guild_role_delete(self, role):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
#### Bot will only respond if the role is not a bot-managed role, and the role is an admin role
|
||||||
|
if role.id in conf[str(role.guild.id)]['adminroles']:
|
||||||
|
conf[str(role.guild.id)]['adminroles'].remove(role.id)
|
||||||
|
yaml_dump(conf, configFile)
|
||||||
|
#### If the role is one of the Admin roles and is deleted, updates the bot's config to delete that role, preventing unnecessary roles from accumulating.
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_guild_role_delete(client))
|
34
app/dev_cogs/events/on_guild_role_update.py
Normal file
34
app/dev_cogs/events/on_guild_role_update.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
##### Actions for the bot to take whenever there is a new role deleted.
|
||||||
|
|
||||||
|
class on_guild_role_update(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_guild_role_update(self, before, after):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
#### If the original role is in the config as an admin role, and it subsequently is run by a bot or is not an admin, remove it from config
|
||||||
|
if before.id in conf[str(before.guild.id)]['adminroles']:
|
||||||
|
if after.is_bot_managed() or after.is_integration() or not after.permissions.administrator:
|
||||||
|
conf[str(after.guild.id)]['adminroles'].remove(after.id)
|
||||||
|
yaml_dump(conf, configFile)
|
||||||
|
|
||||||
|
#### If the new role is an admin and is not already in the config, add it.
|
||||||
|
if not (after.is_bot_managed() or after.is_integration()) and after.permissions.administrator:
|
||||||
|
if after.id not in conf[str(after.guild.id)]['adminroles']:
|
||||||
|
conf[str(after.guild.id)]['adminroles'].remove(after.id)
|
||||||
|
yaml_dump(conf, configFile)
|
||||||
|
#### If the role is one of the Admin roles and is deleted, updates the bot's config to delete that role, preventing unnecessary roles from accumulating.
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_guild_role_update(client))
|
25
app/dev_cogs/events/on_guild_update.py
Normal file
25
app/dev_cogs/events/on_guild_update.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import configFile, yaml_load, yaml_dump
|
||||||
|
|
||||||
|
##### Actions for the bot to take whenever the guild info or ownership are updated.
|
||||||
|
class on_guild_update(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_guild_update(self, before, after):
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
conf[str(after.id)]['name'] = after.name
|
||||||
|
conf[str(after.id)]['owner'] = after.owner_id
|
||||||
|
# Updates guild name and channel
|
||||||
|
yaml_dump(conf,configFile)
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_guild_update(client))
|
30
app/dev_cogs/events/on_ready.py
Normal file
30
app/dev_cogs/events/on_ready.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import os # OS Locations
|
||||||
|
import yaml # YAML parser for Bot config files
|
||||||
|
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
|
||||||
|
import logging
|
||||||
|
# logger and handler
|
||||||
|
from dev import clearConfig, configFile, setConfig, yaml_dump, yaml_load
|
||||||
|
|
||||||
|
#### Actions for the Bot to take once it is ready to interact with commands.
|
||||||
|
class on_ready(commands.Cog):
|
||||||
|
def __init__(self, client):
|
||||||
|
self.client = client
|
||||||
|
|
||||||
|
@commands.Cog.listener()
|
||||||
|
async def on_ready(self):
|
||||||
|
|
||||||
|
#### Create any missing config entries for guilds
|
||||||
|
for guild in self.client.guilds:
|
||||||
|
setConfig(guild)
|
||||||
|
|
||||||
|
#### Delete any extra config entries for guilds the bot is not in
|
||||||
|
conf = yaml_load(configFile)
|
||||||
|
for key in list(conf):
|
||||||
|
clearConfig(key)
|
||||||
|
|
||||||
|
def setup(client):
|
||||||
|
client.add_cog(on_ready(client))
|
@ -1,13 +1,11 @@
|
|||||||
import os # OS Locations
|
import os # OS Locations
|
||||||
import yaml # YAML parser for Bot config files
|
import yaml # YAML parser for Bot config files
|
||||||
import json # Json Library to manage json Data files
|
import asyncio # Discord Py Dependency
|
||||||
import discord # Main Lib
|
import discord # Main Lib
|
||||||
from discord.ext import commands # Commands module
|
from discord.ext import commands # Commands module
|
||||||
from discord_slash import SlashCommand, SlashContext, cog_ext, utils # Slash Command Library
|
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 discord_slash.utils.manage_commands import create_choice, create_option # Slash Command features
|
||||||
|
|
||||||
from dev import guild_ids
|
|
||||||
|
|
||||||
##### Configuration Cog
|
##### Configuration Cog
|
||||||
class Configuration(commands.Cog):
|
class Configuration(commands.Cog):
|
||||||
def __init__(self, client):
|
def __init__(self, client):
|
Loading…
Reference in New Issue
Block a user