Merge remote-tracking branch 'origin/development' into development

release-1.0.0
DustyP 8 years ago
commit 2fdac0b854

91
.gitignore vendored

@ -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

@ -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 '

@ -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:

@ -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"]}')

@ -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

@ -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:

@ -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 (<server>, <ip>, <password>) 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 <server> 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 <server> 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

@ -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):

@ -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 <number> of messages by the given member, it will instead
search the last <number> 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 <number> of messages from the channel
If <contents> is not 'all' then only messages containing <contents>
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

@ -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'],

@ -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())
Loading…
Cancel
Save