Merge remote-tracking branch 'origin/development' into development
This commit is contained in:
commit
2fdac0b854
91
.gitignore
vendored
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,8 +119,10 @@ 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 []:
|
||||
@ -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:
|
||||
|
||||
20
exts/rcon.py
20
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 (<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
|
||||
|
||||
12
exts/repl.py
12
exts/repl.py
@ -69,16 +69,8 @@ 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):
|
||||
output = f'{value}\nReturned: {ret}'
|
||||
for page in paginate(output):
|
||||
await ctx.send(page)
|
||||
|
||||
@commands.command(hidden=True)
|
||||
|
||||
@ -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'],
|
||||
|
||||
44
misc.py
44
misc.py
@ -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…
x
Reference in New Issue
Block a user