diff --git a/.gitignore b/.gitignore index 98e2ace..c48ffda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,93 @@ bot_secrets.json google_client_secret.json -__pycache__/ logs/* *.sh.swp -.idea/ + + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# mypy +.mypy_cache/ + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/dictionaries +.idea/**/shelf + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ +cmake-build-release/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests \ No newline at end of file diff --git a/exts/admin.py b/exts/admin.py index ecafd1a..5ad30b8 100644 --- a/exts/admin.py +++ b/exts/admin.py @@ -84,7 +84,7 @@ class Admin: @commands.command(hidden=True) @commands.check(checks.is_guild_owner) async def get_guild_config(self, ctx): - config = await self.bot.db_con.fetchval('select * from guild_config where guild_id = $1', ctx.guild.id) + config = await self.bot.db_con.fetchrow('select * from guild_config where guild_id = $1', ctx.guild.id) configs = [str(config)[i:i+1990] for i in range(0, len(config), 1990)] await ctx.message.author.send(f'The current config for the {ctx.guild.name} guild is:\n') admin_log.info(configs) @@ -109,8 +109,9 @@ class Admin: @set.command(name='admin_chan', aliases=['ac', 'admin_chat', 'admin chat']) async def _admin_channel(self, ctx, channel: discord.TextChannel=None): + """Sets the channel for admin specific notifications""" if ctx.guild: - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): if channel is not None: await self.bot.db_con.execute('update guild_config set admin_chat = $2 where guild_id = $1', ctx.guild.id, channel.id) @@ -118,11 +119,13 @@ class Admin: @set.command(name='channel_lockdown', aliases=['lockdown', 'restrict_access', 'cl']) async def _channel_lockdown(self, ctx, config='true'): + """Toggles the channel lockdown restricting Geeksbot to only access channels defined in allowed_channels + If you run this before configuring allowed_channels it will tell you to run that command first.""" if ctx.guild: - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): if str(config).lower() == 'true': if await self.bot.db_con.fetchval('select allowed_channels from guild_config ' - 'where guild_id = $1', ctx.guild.id) is []: + 'where guild_id = $1', ctx.guild.id) is []: await ctx.send('Please set at least one allowed channel before running this command.') else: await self.bot.db_con.execute('update guild_config set channel_lockdown = True ' @@ -143,8 +146,10 @@ class Admin: @add.command(name='allowed_channels', aliases=['channel', 'ac']) async def _allowed_channels(self, ctx, *, channels): + """Allows Admin to restrict what channels Geeksbot is allowed to access + This only takes effect if channel_lockdown is enabled.""" if ctx.guild: - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): channels = channels.lower().replace(' ', '').split(',') added = '' for channel in channels: @@ -195,8 +200,12 @@ class Admin: @add.command(aliases=['prefix', 'p']) @commands.cooldown(1, 5, type=commands.BucketType.guild) async def add_prefix(self, ctx, *, prefix=None): + """Adds a guild specific prefix to the guild config + Note: This overwrites the default of g$. If you would + like to keep using g$ you will need to add it to the + Guild config as well.""" if ctx.guild: - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): prefixes = await self.bot.db_con.fetchval('select prefix from guild_config where guild_id = $1', ctx.guild.id) if prefix is None: @@ -224,8 +233,11 @@ class Admin: @remove.command(aliases=['prefix', 'p']) @commands.cooldown(1, 5, type=commands.BucketType.guild) async def remove_prefix(self, ctx, *, prefix=None): + """Removes a guild specific prefix from the guild config + If the last prefix is removed then Geeksbot will default + Back to g$""" if ctx.guild: - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): prefixes = await self.bot.db_con.fetchval('select prefix from guild_config where guild_id = $1', ctx.guild.id) found = 0 @@ -260,6 +272,9 @@ class Admin: @commands.cooldown(1, 5, type=commands.BucketType.guild) @commands.check(checks.is_guild_owner) async def _add_admin_role(self, ctx, role=None): + """The Guild owner can add a role to the admin list + Allowing members of that role to run admin commands + on the current guild.""" role = discord.utils.get(ctx.guild.roles, name=role) if role is not None: roles = json.loads(await self.bot.db_con.fetchval('select admin_roles from guild_config ' @@ -278,6 +293,7 @@ class Admin: @commands.cooldown(1, 5, type=commands.BucketType.guild) @commands.check(checks.is_guild_owner) async def _remove_admin_role(self, ctx, role=None): + """The Guild owner can remove a role from the admin list""" role = discord.utils.get(ctx.guild.roles, name=role) if role is not None: roles = json.loads(await self.bot.db_con.fetchval('select admin_roles from guild_config ' diff --git a/exts/events.py b/exts/events.py index 89d7a9c..395b4ba 100644 --- a/exts/events.py +++ b/exts/events.py @@ -84,7 +84,7 @@ class BotEvents: ctx.webhook_id, [json.dumps({'id': a.id, 'size': a.size, 'height': a.height, 'width': a.width, 'filename': a.filename, 'url': a.url}) for a in ctx.attachments], ctx.pinned, [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions], - ctx.guild.id, ctx.created_at, ctx.system_content, ctx.author.id] + ctx.guild.id if ctx.guild else ctx.author.id, ctx.created_at, ctx.system_content, ctx.author.id] await self.bot.db_con.execute(sql, *msg_data) if ctx.guild: if ctx.author != ctx.guild.me: diff --git a/exts/fun.py b/exts/fun.py index acf8162..7833e1f 100644 --- a/exts/fun.py +++ b/exts/fun.py @@ -65,13 +65,14 @@ class Fun: @commands.command() @commands.cooldown(1, 5, type=commands.BucketType.user) async def slap(self, ctx, member: discord.Member): + trout = await self.bot.db_con.fetchval("select code from geeksbot_emojis where id = 449083238766477312") if member.id == self.bot.user.id and ctx.author.id != owner_id: await ctx.send(f'You rolled a Critical Fail...\nThe trout bounces off and rebounds on the attacker.') await ctx.send(f'{ctx.author.mention} ' - f'You slap yourself in the face with a large trout <:trout:408543365085397013>') + f'You slap yourself in the face with a large trout {trout}') else: await ctx.send(f'{ctx.author.display_name} slaps ' - f'{member.mention} around a bit with a large trout <:trout:408543365085397013>') + f'{member.mention} around a bit with a large trout {trout}') @staticmethod def get_factorial(number): @@ -155,7 +156,7 @@ class Fun: trans = await self.bot.db_con.fetchval('select code from geeksbot_emojis where id = 405943174809255956') msg = await ctx.send(f'{member.mention}{trans*20}{self.bot.unicode_emojis["left_fist"]}') for i in range(4): - await asyncio.sleep(0.1) + await asyncio.sleep(0.5) await msg.edit(content=f'{member.mention}{trans*(20-(i*5))}{self.bot.unicode_emojis["left_fist"]}') await asyncio.sleep(0.1) await msg.edit(content=f'{self.bot.unicode_emojis["boom"]}') diff --git a/exts/imports/utils.py b/exts/imports/utils.py index 582d568..b15a7fc 100644 --- a/exts/imports/utils.py +++ b/exts/imports/utils.py @@ -4,6 +4,8 @@ import asyncio import discord from discord.ext.commands.formatter import Paginator from . import checks +import re +import typing class Capturing(list): @@ -21,7 +23,7 @@ class Capturing(list): async def mute(bot, ctx, admin=0, member_id=None): mute_role = bot.db_con.fetchval(f'select muted_role from guild_config where guild_id = $1', ctx.guild.id) if mute_role: - if admin or checks.is_admin(bot, ctx): + if admin or await checks.is_admin(bot, ctx): if ctx.guild.me.guild_permissions.manage_roles: if member_id: ctx.guild.get_member(member_id).edit(roles=[discord.utils.get(ctx.guild.roles, id=mute_role)]) @@ -64,6 +66,11 @@ def to_list_of_str(items, out: list=list(), level=1, recurse=0): return out +def replace_text_ignorecase(in_str: str, old: str, new: str='') -> str: + re_replace = re.compile(re.escape(old), re.IGNORECASE) + return re_replace.sub(f'{new}', in_str) + + def paginate(text, maxlen=1990): paginator = Paginator(prefix='```py', max_size=maxlen+10) if type(text) == list: @@ -93,4 +100,22 @@ async def run_command(args): # Return stdout return stdout.decode().strip() + # TODO Add Paginator +class Paginator: + def __init__(self, + max_chars: int=1990, + max_lines: int=20, + prefix: str='```md', + suffix: str='```', + page_break: str=''): + assert 0 < max_lines <= max_chars + + self._parts = list() + self._prefix = prefix + self._suffix = suffix + self._max_chars = max_chars if max_chars + len(prefix) + len(suffix) + 2 <= 2000 \ + else 2000 - len(prefix) - len(suffix) - 2 + self._max_lines = max_lines - (prefix + suffix).count('\n') + 1 + self._page_break = page_break + diff --git a/exts/patreon.py b/exts/patreon.py index 2ff603e..da82edb 100644 --- a/exts/patreon.py +++ b/exts/patreon.py @@ -33,7 +33,7 @@ class Patreon: @commands.command(aliases=['patreon_message']) async def set_patreon_message(self, ctx, message): - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): patreon_message = await self.bot.db_con.fetchval('select patreon_message from guild_config ' 'where guild_id = $1', ctx.guild.id) if message == patreon_message: @@ -47,7 +47,7 @@ class Patreon: @commands.command(aliases=['add_patreon', 'set_patreon']) async def add_patreon_info(self, ctx, name, url): - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): patreon_info = await self.bot.db_con.fetchval('select patreon_links from guild_config where guild_id = $1', ctx.guild.id) patreon_links = {} @@ -66,7 +66,7 @@ class Patreon: @commands.command(aliases=['remove_patreon']) async def remove_patreon_info(self, ctx, name): - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): patreon_info = await self.bot.db_con.fetchval('select patreon_links from guild_config where guild_id = $1', ctx.guild.id) if patreon_info: @@ -86,7 +86,7 @@ class Patreon: @commands.command() async def enable_patreon(self, ctx, state: bool=True): - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): patreon_status = await self.bot.db_con.fetchval('select patreon_enabled from guild_config ' 'where guild_id = $1', ctx.guild.id) if patreon_status and state: diff --git a/exts/rcon.py b/exts/rcon.py index b59f877..ccbe593 100644 --- a/exts/rcon.py +++ b/exts/rcon.py @@ -138,7 +138,7 @@ class Rcon: first_last "first last" To view all the valid ARK servers for this guild see list_ark_servers.""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): if server is not None: rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections ' 'from guild_config where guild_id = $1', @@ -200,7 +200,7 @@ class Rcon: async def end_monitor_chat(self, ctx, *, server=None): """Ends chat monitoring on the specified server. Context is the same as monitor_chat""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): if server is not None: rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections ' 'from guild_config where guild_id = $1', @@ -229,7 +229,7 @@ class Rcon: first_last "first last" To view all the valid ARK servers for this guild see list_ark_servers.""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' 'where guild_id = $1', ctx.guild.id)) if server is not None: @@ -267,7 +267,7 @@ class Rcon: async def add_rcon_server(self, ctx, server, ip, port, password): """Adds the specified server to the current guild\'s rcon config. All strings (, , ) must be contained inside double quotes.""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): server = server.title() rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' 'where guild_id = $1', ctx.guild.id)) @@ -296,7 +296,7 @@ class Rcon: async def remove_rcon_server(self, ctx, server): """removes the specified server from the current guild\'s rcon config. All strings must be contained inside double quotes.""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): server = server.title() rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' 'where guild_id = $1', ctx.guild.id)) @@ -316,7 +316,7 @@ class Rcon: """Adds the included Steam 64 IDs to the running whitelist on all the ARK servers in the current guild\'s rcon config. Steam 64 IDs should be a comma seperated list of IDs. Example: 76561198024193239,76561198024193239,76561198024193239""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): if steam_ids is not None: rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections ' 'from guild_config where guild_id = $1', @@ -364,7 +364,7 @@ class Rcon: """Runs SaveWorld on the specified ARK server. If a server is not specified it will default to running saveworld on all servers in the guild\'s config. Will print out "World Saved" for each server when the command completes successfully.""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' 'where guild_id = $1', ctx.guild.id)) success_msg = 'Running saveworld' @@ -406,7 +406,7 @@ class Rcon: """Sends a broadcast message to all servers in the guild config. The message will be prefixed with the Discord name of the person running the command. Will print "Success" for each server once the broadcast is sent.""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' 'where guild_id = $1', ctx.guild.id)) if message is not None: @@ -443,7 +443,7 @@ class Rcon: The message will be prefixed with the Discord name of the person running the command. If has more than one word in it's name it will either need to be surrounded by double quotes or the words separated by _""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' 'where guild_id = $1', ctx.guild.id)) if server is not None: @@ -481,7 +481,7 @@ class Rcon: and these channel's permissions will be synced to the category. These channels will be added to the guild's rcon config and are where the server chat messages will be sent when monitor_chat is run.""" - if checks.is_rcon_admin(self.bot, ctx): + if await checks.is_rcon_admin(self.bot, ctx): rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' 'where guild_id = $1', ctx.guild.id)) edited = 0 diff --git a/exts/repl.py b/exts/repl.py index 7ca306c..464aab1 100644 --- a/exts/repl.py +++ b/exts/repl.py @@ -69,17 +69,9 @@ class Repl: await ctx.message.add_reaction('✅') except Exception: pass - if ret is None: - if value: - for page in paginate(value): - await ctx.send(page) - else: - self._last_result = ret - if value: - for page in paginate(value): - await ctx.send(page) - for page in paginate(ret): - await ctx.send(page) + output = f'{value}\nReturned: {ret}' + for page in paginate(output): + await ctx.send(page) @commands.command(hidden=True) async def repl(self, ctx): diff --git a/exts/utils.py b/exts/utils.py index e89b98f..2369d67 100644 --- a/exts/utils.py +++ b/exts/utils.py @@ -7,7 +7,7 @@ import psutil from datetime import datetime, timedelta import asyncio import async_timeout -from .imports import checks +from .imports import checks, utils import pytz import gspread from oauth2client.service_account import ServiceAccountCredentials @@ -18,6 +18,8 @@ from mpl_toolkits.basemap import Basemap from io import BytesIO from itertools import chain import numpy as np +from dateutil.parser import parse +from copy import copy config_dir = 'config/' admin_id_file = 'admin_ids' @@ -29,6 +31,7 @@ invite_match = '(https?://)?(www.)?discord(app.com/(invite|oauth2)|.gg|.io)/[\w\ utils_log = logging.getLogger('utils') clock_emojis = ['🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚'] +replace_tzs = {'MST': 'US/Mountain', 'HST': 'US/Hawaii', 'EST': 'US/Eastern'} class Utils: @@ -59,6 +62,7 @@ class Utils: @commands.command() @commands.is_owner() async def sysinfo(self, ctx): + """WIP Gets current system status for the server that Geeksbot is running on.""" await ctx.send(f'```ml\n' f'CPU Percentages: {psutil.cpu_percent(percpu=True)}\n' f'Memory Usage: {psutil.virtual_memory().percent}%\n' @@ -292,7 +296,7 @@ class Utils: title=f'Admin Help Requests', color=discord.Colour.green() ) - if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx) or await checks.is_rcon_admin(self.bot, ctx): if assigned_to is None: requests = await self.bot.db_con.fetch(f'select * from admin_requests where guild_orig = $1 ' f'and completed_time is null', ctx.guild.id) @@ -312,8 +316,8 @@ class Utils: else: em.add_field(name='There are no pending requests for this guild.', value='￰', inline=False) else: - if checks.check_admin_role(self.bot, ctx, assigned_to)\ - or checks.check_rcon_role(self.bot, ctx, assigned_to): + if await checks.check_admin_role(self.bot, ctx, assigned_to)\ + or await checks.check_rcon_role(self.bot, ctx, assigned_to): requests = await self.bot.db_con.fetch('select * from admin_requests where assigned_to = $1 ' 'and guild_orig = $2 and completed_time is null', assigned_to.id, ctx.guild.id) @@ -359,7 +363,7 @@ class Utils: """Allows Admin to close admin help tickets. [request_id] must be a valid integer pointing to an open Request ID """ - if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx) or await checks.is_rcon_admin(self.bot, ctx): if request_ids: request_ids = request_ids.replace(' ', '').split(',') for request_id in request_ids: @@ -429,7 +433,11 @@ class Utils: @commands.command(name='localtime', aliases=['time', 'lt']) @commands.cooldown(1, 3, type=commands.BucketType.user) async def get_localtime(self, ctx, timezone: str='Anchorage'): + """Shows the current time localized to the timezone given + This defaults to the Bot's local timezone of Anchorage Alaska USA if none are given.""" + em = discord.Embed() + try: tz = pytz.timezone(timezone) localtime = datetime.now(tz=tz) @@ -450,9 +458,76 @@ class Utils: em.colour = discord.Colour.red() await ctx.send(embed=em) + # noinspection PyUnboundLocalVariable + @commands.command(name='gettimein', aliases=['timein', 'gti']) + @commands.cooldown(1, 3, type=commands.BucketType.user) + async def get_time_in_timezone(self, ctx, timezone: str='US/Eastern', *, time: str=None): + em = discord.Embed() + + if time is None: + em.set_footer(text='Time not given... using current UTC time.') + in_time = datetime.utcnow() + parsed_tz = pytz.timezone('UTC') + else: + try: + orig_time = copy(time) + split_time = time.split() + try: + parsed_tz = pytz.timezone(replace_tzs.get(split_time[-1].upper()) or split_time[-1]) + time = utils.replace_text_ignorecase(time, old=split_time[-1], new='') + except pytz.exceptions.UnknownTimeZoneError: + for tz in pytz.all_timezones: + if split_time[-1].lower() in tz.lower(): + time = utils.replace_text_ignorecase(time, old=split_time[-1], new='') + if tz in replace_tzs: + tz = replace_tzs['tz'] + parsed_tz = pytz.timezone(tz) + break + else: + em.set_footer(text='Valid timezone not found in time string. Using UTC...') + parsed_tz = pytz.timezone('UTC') + if not time.isspace() and not time == '': + in_time = parse(time.upper()) + in_time = parsed_tz.localize(in_time) + else: + em.set_footer(text='Time not given. Using current time.') + in_time = parsed_tz.localize(datetime.utcnow()) + except ValueError: + raise commands.CommandError(f'For some reason I can\'t parse this time string: \n' + f'{orig_time} {time} {parsed_tz}\n' + f'Examples of valid time strings are in my help documentation.\n' + f'Please try again.') + try: + out_tz = pytz.timezone(timezone) + except pytz.exceptions.UnknownTimeZoneError: + for tz in pytz.all_timezones: + if timezone.lower() in tz.lower(): + out_tz = pytz.timezone(tz) + break + else: + out_tz = None + em.title = 'Unknown Timezone.' + em.colour = discord.Colour.red() + finally: + if out_tz: + out_time = in_time.astimezone(out_tz) + em.add_field(name=f'{parsed_tz}', + value=f'{clock_emojis[(in_time.hour % 12)]} {in_time.strftime("%c")}', inline=False) + em.add_field(name=f'{out_tz}', + value=f'{clock_emojis[(out_time.hour % 12)]} {out_time.strftime("%c")}', inline=False) + em.colour = self.bot.embed_color + await ctx.send(embed=em) + @commands.command(name='purge', aliases=['clean', 'erase']) @commands.cooldown(1, 3, type=commands.BucketType.user) async def purge_messages(self, ctx, number: int=20, member: discord.Member=None): + """Gives Admin the ability to quickly clear messages from a channel + By default this will only purge messages sent by Geeksbot and any messages that appear to + have called Geeksbot (aka start with one of the Geeksbot's prefixes for this Guild) + If you want to purge messages from a different user you must provide a number and member + + Note: Geeksbot will not find of messages by the given member, it will instead + search the last messages in the channel and delete any by the given member""" def is_me(message): if message.author == self.bot.user: return True @@ -471,7 +546,7 @@ class Utils: def is_author(message): return message.author == ctx.author - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): if member: deleted = await ctx.channel.purge(limit=number, check=is_member) if member != ctx.author: @@ -487,7 +562,10 @@ class Utils: @commands.command(name='purge_all', aliases=['cls', 'clear']) @commands.cooldown(1, 3, type=commands.BucketType.user) async def purge_all(self, ctx, number: int=20, contents: str='all'): - if checks.is_admin(self.bot, ctx): + """Will delete all of the last of messages from the channel + If is not 'all' then only messages containing + will be deleted.""" + if await checks.is_admin(self.bot, ctx): if contents != 'all': deleted = await ctx.channel.purge(limit=number, check=lambda message: message.content == contents) else: @@ -500,6 +578,7 @@ class Utils: @commands.command(name='google', aliases=['g', 'search']) async def google_search(self, ctx, *, search): + """WIP Search Google for the given string""" res = self.bot.gcs_service.cse().list(q=search, cx=self.bot.bot_secrets['cx']).execute() results = res['items'][:4] em = discord.Embed() @@ -513,7 +592,7 @@ class Utils: @commands.command(hidden=True, name='sheets') async def google_sheets(self, ctx, member: discord.Member): - if checks.is_admin(self.bot, ctx): + if await checks.is_admin(self.bot, ctx): scope = ['https://spreadsheets.google.com/feeds', 'https://www.googleapis.com/auth/drive'] credentials = ServiceAccountCredentials.from_json_keyfile_name('config/google_client_secret.json', scope) @@ -538,6 +617,7 @@ class Utils: @commands.command(name='iss') async def iss_loc(self, ctx): + """WIP Locates the International Space Station and display on a map""" def gen_image(iss_loc): lat = iss_loc['latitude'] lon = iss_loc['longitude'] @@ -561,6 +641,8 @@ class Utils: @commands.command(name='location', aliases=['loc', 'map']) async def map_location(self, ctx, *, location): + """WIP Displays the given location on a map + Note: This is SLOW!!! Be prepared to wait up to a minute for the result""" def draw_map(m, scale=1): # draw a shaded-relief image diff --git a/geeksbot.py b/geeksbot.py index 57b2d4c..b0fa799 100644 --- a/geeksbot.py +++ b/geeksbot.py @@ -1,5 +1,6 @@ from typing import Dict +import discord from discord.ext import commands import logging from datetime import datetime @@ -56,6 +57,7 @@ class Geeksbot(commands.Bot): self.guild_config = {} self.infected = {} self.TOKEN = self.bot_secrets['token'] + self.embed_color = discord.Colour.from_rgb(49, 107, 111) # async def connect_db(): # return await asyncpg.create_pool(host=self.bot_secrets['db_con']['host'], diff --git a/misc.py b/misc.py deleted file mode 100644 index 6960d6c..0000000 --- a/misc.py +++ /dev/null @@ -1,44 +0,0 @@ - @checks.no_bots() - @commands.cooldown(1,5,commands.BucketType.user) - @commands.command() - async def captcha(self, ctx, type, *, text): - type = type.lower() - if type not in "checked unchecked loading".split(): - raise commands.BadArgument(f"Invalid type {type!r}. Available " - "types: `unchecked`, `loading`, `checked`") - font = ImageFont.truetype("Roboto-Regular.ttf", 14) - async with ctx.typing(): - img = Image.open(f"blank-captcha-{type}.png") - img.load() - d = ImageDraw.Draw(img) - fnc = functools.partial(d.text, (53,30), text, fill=(0,0,0,255), - font=font) - await self.bot.loop.run_in_executor(None, fnc) - img.save("captcha.png") - await ctx.send(file=discord.File("captcha.png")) - os.system("rm captcha.png") - img.close() - - -import functools, youtube_dl -#bot.voice_chan = await ctx.author.voice.channel.connect() -bot.voice_chan.stop() -opts = {"format": 'webm[abr>0]/bestaudio/best',"ignoreerrors": True,"default_search": "auto","source_address": "0.0.0.0",'quiet': True} -ydl = youtube_dl.YoutubeDL(opts) -url = 'https://www.youtube.com/watch?v=hjbPszSt5Pc' -func = functools.partial(ydl.extract_info, url, download=False) -info = func() -#bot.voice_chan.play(discord.FFmpegPCMAudio('dead_puppies.mp3')) -bot.voice_chan.play(discord.FFmpegPCMAudio(info['url'])) -#async while bot.voice_chan.is_playing(): -# pass -#await bot.voice_chan.disconnect() - -# Run event in loop after number of seconds -from functools import partial -return bot.loop.call_later(120, partial(bot.loop.create_task, ctx.send(f"{ctx.author.mention} Timer's Up"))) - -# Get the number of tasks currently in the loop -import asyncio -return len(asyncio.Task.all_tasks()) -