bot.load_extension("roxbot.err_handle") | bot.load_extension("roxbot.err_handle") | ||||
bot.load_extension("roxbot.logging") | bot.load_extension("roxbot.logging") | ||||
bot.load_extension("roxbot.system") | bot.load_extension("roxbot.system") | ||||
bot.settings = gs.get_all(bot.guilds) | |||||
print("Discord.py version: " + discord.__version__) | print("Discord.py version: " + discord.__version__) | ||||
print("Client logged in\n") | print("Client logged in\n") |
import roxbot | import roxbot | ||||
from roxbot import guild_settings as gs | from roxbot import guild_settings as gs | ||||
# TODO: Clean up this file. | |||||
def is_owner_or_admin(): | def is_owner_or_admin(): | ||||
def predicate(ctx): | def predicate(ctx): |
class EmojiConverter(commands.EmojiConverter): | class EmojiConverter(commands.EmojiConverter): | ||||
"""Just like the normla EmojiConverter class but with a custom error message and planned extra feature.""" | |||||
"""The Emoji conveter from discord.py but instead it returns the argument if an error is raised | |||||
It's messier than using the EmojiConverter proper but the issue is you can try converters.""" | |||||
async def convert(self, ctx, argument): | async def convert(self, ctx, argument): | ||||
try: | try: | ||||
return await super().convert(ctx, argument) | return await super().convert(ctx, argument) |
def _open_config(): | def _open_config(): | ||||
""" | |||||
Opens the guild settings file | |||||
:return settings file: :type dict: | |||||
"""Opens the guild settings file | |||||
Returns | |||||
======= | |||||
servers.json: dict | |||||
""" | """ | ||||
with open('roxbot/settings/servers.json', 'r') as config_file: | with open('roxbot/settings/servers.json', 'r') as config_file: | ||||
return json.load(config_file) | return json.load(config_file) | ||||
def _write_changes(config): | def _write_changes(config): | ||||
""" | |||||
Writes given config to disk. | |||||
"""Writes given config to disk. MUST BE THE ENTIRE SERVER.JSON FILE. | |||||
:param config: :type dict: | :param config: :type dict: | ||||
:return: | :return: | ||||
""" | """ | ||||
def remove_guild(guild): | def remove_guild(guild): | ||||
"""Removes given guild from settings file and saves changes.""" | |||||
settings = _open_config() | settings = _open_config() | ||||
settings.pop(str(guild.id)) | settings.pop(str(guild.id)) | ||||
_write_changes(settings) | _write_changes(settings) | ||||
def add_guild(guild): | def add_guild(guild): | ||||
"""Adds given guild from settings file and saves changes.""" | |||||
settings = _open_config() | settings = _open_config() | ||||
settings[str(guild.id)] = guild_template["example"] | settings[str(guild.id)] = guild_template["example"] | ||||
_write_changes(settings) | _write_changes(settings) | ||||
def error_check(servers): | def error_check(servers): | ||||
settings = _open_config() | settings = _open_config() | ||||
for server in servers: | for server in servers: | ||||
# Server ID needs to be made a string for this statement because keys have to be strings in JSON. Which is annoying now we use int for ids. | |||||
# Server ID needs to be made a string for this statement because keys have to be strings in JSON. | |||||
server_id = str(server.id) | server_id = str(server.id) | ||||
if str(server_id) not in settings: | if str(server_id) not in settings: | ||||
settings[server_id] = guild_template["example"] | settings[server_id] = guild_template["example"] | ||||
server.name.upper(), setting.upper(), cog_setting.upper())) | server.name.upper(), setting.upper(), cog_setting.upper())) | ||||
def get_all(guilds): | |||||
""" | |||||
Returns a list of GuildSettings for all guilds the bot can see. | |||||
:param guilds: | |||||
:return list of GuildSettings: :type list: | |||||
""" | |||||
error_check(guilds) | |||||
guild_list = [] | |||||
for guild in guilds: | |||||
guild = GuildSettings(guild) | |||||
guild_list.append(guild) | |||||
return guild_list | |||||
def get(guild): | def get(guild): | ||||
""" | """ | ||||
Gets a single GuildSettings Object representing the settings of that guild | Gets a single GuildSettings Object representing the settings of that guild | ||||
:param guild: | |||||
:param guild: :type discord.Guild: | |||||
:return Single GuildSettings Object: :type GuildSettings: | :return Single GuildSettings Object: :type GuildSettings: | ||||
""" | """ | ||||
return GuildSettings(guild) | return GuildSettings(guild) | ||||
def get_guild(guilds, wanted_guild): | |||||
for guild in guilds: | |||||
if guild.id == wanted_guild.id: | |||||
return guild | |||||
return None | |||||
class GuildSettings(object): | class GuildSettings(object): | ||||
""" | """ | ||||
An Object to store all settings for one guild. | An Object to store all settings for one guild. |
async def request(url, *, headers=None, **kwargs): | async def request(url, *, headers=None, **kwargs): | ||||
""" | |||||
Base GET request. | |||||
:param url: | |||||
:param headers: | |||||
:param kwargs: | |||||
:return: | |||||
"""Base GET request in case you need more control over GET requests. | |||||
Params | |||||
======= | |||||
headers: dict (Optional) | |||||
kwarg: kwargs (Optional) | |||||
Returns | |||||
======= | |||||
Response: aiohttp Response object | |||||
""" | """ | ||||
async with aiohttp.ClientSession() as session: | async with aiohttp.ClientSession() as session: | ||||
return await session.get(url, headers=headers, **kwargs) | return await session.get(url, headers=headers, **kwargs) | ||||
async def api_request(url, *, headers=None, **kwargs): | async def api_request(url, *, headers=None, **kwargs): | ||||
""" | |||||
Returns a JSON dict object for most api calls in RoxBot. | |||||
:param url: URL Should be a api endpoint that will return | |||||
:param headers: There is no need to pass the user agent, this is done for you. | |||||
:return: dict of JSON or None if a JSON was not returned from the call. | |||||
"""Returns JSON from an API request. Should be used for all Roxbot API requests. | |||||
Params | |||||
======= | |||||
url: str | |||||
headers: dict (Optional) | |||||
kwargs: kwargs (Optional) | |||||
Returns | |||||
======= | |||||
JSON response: dict | |||||
""" | """ | ||||
if headers is None: | if headers is None: | ||||
headers = {'User-agent': 'RoxBot Discord Bot'} | headers = {'User-agent': 'RoxBot Discord Bot'} | ||||
async def download_file(url, filename=None): | async def download_file(url, filename=None): | ||||
""" | |||||
Downloads the file at the given url and then saves it under the filename given to disk. | |||||
:param filename: | |||||
:param url: | |||||
"""Downloads the file at the given url and then saves it under the filename given to disk. | |||||
Params | |||||
======= | |||||
url: str | |||||
filename: str (Optional) | |||||
if not given, the function will try and determine filename from url. | |||||
Returns | |||||
======= | |||||
filename: str | |||||
Handy if no filename given | |||||
""" | """ | ||||
if filename is None: | if filename is None: | ||||
filename = url.split("/")[-1].split("?")[0] | filename = url.split("/")[-1].split("?")[0] | ||||
async def upload_file(url, file): | async def upload_file(url, file): | ||||
""" | |||||
"""Uploads a file using a POST request. Broke for pomf clones so idk. Might have to revert it to requests. | |||||
:param url: url to POST to. | :param url: url to POST to. | ||||
:param file: Byes-like object to upload. | :param file: Byes-like object to upload. | ||||
async def get_page(url, *, headers=None, **kwargs): | async def get_page(url, *, headers=None, **kwargs): | ||||
""" | |||||
Returns the page at the given url | |||||
:param url: the url of the page you want to get | |||||
:return: the html page | |||||
"""Returns the html of the given url. Will need to run it through BS4 to parse it. | |||||
Params | |||||
======= | |||||
url: str | |||||
Returns | |||||
======= | |||||
HTML Page: str | |||||
""" | """ | ||||
async with aiohttp.ClientSession() as session: | async with aiohttp.ClientSession() as session: | ||||
async with session.get(url, headers=headers, **kwargs) as page: | async with session.get(url, headers=headers, **kwargs) as page: |
async def log(guild, channel, command_name, **kwargs): | async def log(guild, channel, command_name, **kwargs): | ||||
"""Logs activity internally for Roxbot. Will only do anything if the server enables internal logging. | |||||
This is mostly used for logging when certain commands are used that can be an issue for admins. Esp when Roxbot outputs | |||||
something that could break the rules, then deletes their message. | |||||
Params | |||||
======= | |||||
guild: discord.Guild | |||||
Used to check if the guild has logging enabled | |||||
channel: discord.TextChannel | |||||
command_name: str | |||||
kwargs: dict | |||||
All kwargs and two other params will be added to the logging embed as fields, allowing you to customise the output | |||||
""" | |||||
logging = guild_settings.get(guild).logging | logging = guild_settings.get(guild).logging | ||||
if logging["enabled"]: | if logging["enabled"]: | ||||
embed = discord.Embed(title="{} command logging".format(command_name), colour=EmbedColours.pink) | embed = discord.Embed(title="{} command logging".format(command_name), colour=EmbedColours.pink) |
async def delete_option(bot, ctx, message, delete_emoji, timeout=20): | async def delete_option(bot, ctx, message, delete_emoji, timeout=20): | ||||
"""Utility function that allows for you to add a delete option to the end of a command. | """Utility function that allows for you to add a delete option to the end of a command. | ||||
This makes it easier for users to control the output of commands, esp handy for random output ones.""" | |||||
This makes it easier for users to control the output of commands, esp handy for random output ones. | |||||
Params | |||||
======= | |||||
bot: discord.ext.commands.Bot | |||||
The current bot client | |||||
ctx: discord.ext.commands.Context | |||||
The context of the command | |||||
message: discord.Message | |||||
Output message from Roxbot | |||||
delete_emoji: discord.Emoji or str if unicode emoji | |||||
Used as the reaction for the user to click on. | |||||
timeout: int (Optional) | |||||
Amount of time in seconds for the bot to wait for the reaction. Deletes itself after the timer runes out. | |||||
Set to 20 by default | |||||
""" | |||||
await message.add_reaction(delete_emoji) | await message.add_reaction(delete_emoji) | ||||
def check(r, u): | def check(r, u): | ||||
await bot.wait_for("reaction_add", timeout=timeout, check=check) | await bot.wait_for("reaction_add", timeout=timeout, check=check) | ||||
await message.remove_reaction(delete_emoji, bot.user) | await message.remove_reaction(delete_emoji, bot.user) | ||||
await message.remove_reaction(delete_emoji, ctx.author) | await message.remove_reaction(delete_emoji, ctx.author) | ||||
return await message.edit(content="{} requested output be deleted.".format(ctx.author), embed=None) | |||||
await message.edit(content="{} requested output be deleted.".format(ctx.author), embed=None) | |||||
except asyncio.TimeoutError: | except asyncio.TimeoutError: | ||||
await message.remove_reaction(delete_emoji, bot.user) | await message.remove_reaction(delete_emoji, bot.user) | ||||
def blacklisted(user): | def blacklisted(user): | ||||
"""Checks if given user is blacklisted from the bot. | |||||
Params | |||||
======= | |||||
user: discord.User | |||||
Returns | |||||
======= | |||||
If the user is blacklisted: bool""" | |||||
with open("roxbot/settings/blacklist.txt", "r") as fp: | with open("roxbot/settings/blacklist.txt", "r") as fp: | ||||
for line in fp.readlines(): | for line in fp.readlines(): | ||||
if str(user.id)+"\n" == line: | if str(user.id)+"\n" == line: |