Compare commits

..
Author SHA1 Message Date
Dusty.P c1a19ce2c8 Create LICENSE 2018-05-10 12:47:21 -08:00
36 changed files with 639 additions and 1356 deletions
Binary file not shown.
+1 -90
View File
@@ -1,93 +1,4 @@
bot_secrets.json bot_secrets.json
google_client_secret.json google_client_secret.json
logs/*
*.sh.swp
# Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] logs/*
*$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
+4 -1
View File
@@ -4,8 +4,11 @@
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="R User Library" level="project" />
<orderEntry type="library" name="R Skeletons" level="application" />
</component> </component>
<component name="TestRunnerService"> <component name="TestRunnerService">
<option name="PROJECT_TEST_RUNNER" value="Unittests" /> <option name="projectConfiguration" value="py.test" />
<option name="PROJECT_TEST_RUNNER" value="py.test" />
</component> </component>
</module> </module>
-5
View File
@@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>
+7
View File
@@ -0,0 +1,7 @@
<component name="ProjectDictionaryState">
<dictionary name="Dustin.Pianalto">
<words>
<w>rcon</w>
</words>
</dictionary>
</component>
+1 -1
View File
@@ -3,5 +3,5 @@
<component name="JavaScriptSettings"> <component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" /> <option name="languageLevel" value="ES6" />
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (Geeksbot)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
</project> </project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Dusty.P
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
View File
+78 -103
View File
@@ -4,10 +4,11 @@ import json
import logging import logging
import inspect import inspect
import os import os
from src.imports import checks, utils from .imports import checks
config_dir = 'src/config/' config_dir = 'config/'
admin_id_file = 'admin_ids' admin_id_file = 'admin_ids'
extension_dir = 'extensions'
owner_id = 351794468870946827 owner_id = 351794468870946827
embed_color = discord.Colour.from_rgb(49, 107, 111) embed_color = discord.Colour.from_rgb(49, 107, 111)
bot_config_file = 'bot_config.json' bot_config_file = 'bot_config.json'
@@ -51,7 +52,6 @@ class Admin:
await ctx.send('Geeksbot is restarting.') await ctx.send('Geeksbot is restarting.')
with open(f'{config_dir}reboot', 'w') as f: with open(f'{config_dir}reboot', 'w') as f:
f.write(f'1\n{ctx.channel.id}') f.write(f'1\n{ctx.channel.id}')
# noinspection PyProtectedMember
os._exit(1) os._exit(1)
@commands.command(hidden=True) @commands.command(hidden=True)
@@ -72,19 +72,20 @@ class Admin:
emoji_code = f'<a:{emoji.name}:{emoji.id}>' emoji_code = f'<a:{emoji.name}:{emoji.id}>'
else: else:
emoji_code = f'<:{emoji.name}:{emoji.id}>' emoji_code = f'<:{emoji.name}:{emoji.id}>'
if await self.bot.db_con.fetch('select id from geeksbot_emojis where id = $1', emoji.id): if self.bot.con.all('select id from geeksbot_emojis where id = %(id)s', {'id': emoji.id}):
await self.bot.db_con.execute("update geeksbot_emojis set id = $2, name = $1, code = $3 " self.bot.con.run("update geeksbot_emojis set id = %(id)s, name = %(name)s, code = %(emoji_code)s "
"where name = $1", emoji.name, emoji.id, emoji_code) "where name = %(name)s",
{'name': emoji.name, 'id': emoji.id, 'emoji_code': emoji_code})
else: else:
await self.bot.db_con.execute("insert into geeksbot_emojis(id,name,code) values ($2,$1,$3)", self.bot.con.run("insert into geeksbot_emojis(id,name,code) values (%(id)s,%(name)s,%(emoji_code)s)",
emoji.name, emoji.id, emoji_code) {'name': emoji.name, 'id': emoji.id, 'emoji_code': emoji_code})
await ctx.message.add_reaction('') await ctx.message.add_reaction('')
await ctx.send(f'Emojis have been updated in the database.') await ctx.send(f'Emojis have been updated in the database.')
@commands.command(hidden=True) @commands.command(hidden=True)
@commands.check(checks.is_guild_owner) @commands.check(checks.is_guild_owner)
async def get_guild_config(self, ctx): async def get_guild_config(self, ctx):
config = await self.bot.db_con.fetchrow('select * from guild_config where guild_id = $1', ctx.guild.id) config = self.bot.con.one('select * from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
configs = [str(config)[i:i+1990] for i in range(0, len(config), 1990)] 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') await ctx.message.author.send(f'The current config for the {ctx.guild.name} guild is:\n')
admin_log.info(configs) admin_log.info(configs)
@@ -94,49 +95,45 @@ class Admin:
@commands.group(case_insensitive=True) @commands.group(case_insensitive=True)
async def set(self, ctx): async def set(self, ctx):
"""Group for setting configuration options""" """Run help set for more info"""
pass pass
@commands.group(case_insensitive=True) @commands.group(case_insensitive=True)
async def add(self, ctx): async def add(self, ctx):
"""Group for adding items to guild config""" """Run help set for more info"""
pass pass
@commands.group(case_insensitive=True) @commands.group(case_insensitive=True)
async def remove(self, ctx): async def remove(self, ctx):
"""Group for removing items from guild config""" """Run help set for more info"""
pass pass
@set.command(name='admin_chan', aliases=['ac', 'admin_chat', 'admin chat']) @set.command(name='admin_chan', aliases=['ac', 'admin_chat', 'admin chat'])
async def _admin_channel(self, ctx, channel: discord.TextChannel=None): async def _admin_channel(self, ctx, channel: discord.TextChannel=None):
"""Sets the admin notification channel"""
if ctx.guild: if ctx.guild:
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
if channel is not None: if channel is not None:
await self.bot.db_con.execute('update guild_config set admin_chat = $2 where guild_id = $1', self.bot.con.run('update guild_config set admin_chat = %(chan)s where guild_id = %(id)s',
ctx.guild.id, channel.id) {'id': ctx.guild.id, 'chan': channel.id})
await ctx.send(f'{channel.name} is now set as the Admin Chat channel for this guild.') await ctx.send(f'{channel.name} is now set as the Admin Chat channel for this guild.')
@set.command(name='channel_lockdown', aliases=['lockdown', 'restrict_access', 'cl']) @set.command(name='channel_lockdown', aliases=['lockdown', 'restrict_access', 'cl'])
async def _channel_lockdown(self, ctx, config='true'): async def _channel_lockdown(self, ctx, config='true'):
"""Toggles the channel lockdown restriction
When this is active Geeksbot can only respond in 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 ctx.guild:
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
if str(config).lower() == 'true': if str(config).lower() == 'true':
if await self.bot.db_con.fetchval('select allowed_channels from guild_config ' if self.bot.con.one('select allowed_channels from guild_config where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id) is []: {'id': ctx.guild.id}) is []:
await ctx.send('Please set at least one allowed channel before running this command.') await ctx.send('Please set at least one allowed channel before running this command.')
else: else:
await self.bot.db_con.execute('update guild_config set channel_lockdown = True ' self.bot.con.run('update guild_config set channel_lockdown = True where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id) {'id': ctx.guild.id})
await ctx.send('Channel Lockdown is now active.') await ctx.send('Channel Lockdown is now active.')
elif str(config).lower() == 'false': elif str(config).lower() == 'false':
if await self.bot.db_con.fetchval('select channel_lockdown from guild_config where guild_id = $1', if self.bot.con.one('select channel_lockdown from guild_config where guild_id = %(id)s',
ctx.guild.id): {'id': ctx.guild.id}):
await self.bot.db_con.execute('update guild_config set channel_lockdown = False ' self.bot.con.run('update guild_config set channel_lockdown = False where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id) {'id': ctx.guild.id})
await ctx.send('Channel Lockdown has been deactivated.') await ctx.send('Channel Lockdown has been deactivated.')
else: else:
await ctx.send('Channel Lockdown is already deactivated.') await ctx.send('Channel Lockdown is already deactivated.')
@@ -147,74 +144,59 @@ class Admin:
@add.command(name='allowed_channels', aliases=['channel', 'ac']) @add.command(name='allowed_channels', aliases=['channel', 'ac'])
async def _allowed_channels(self, ctx, *, channels): async def _allowed_channels(self, ctx, *, channels):
"""Defines channels Geeksbot can respond in
This only takes effect if channel_lockdown is enabled.
If one of the channels passed is not found then it is ignored."""
if ctx.guild: if ctx.guild:
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
channels = channels.lower().replace(' ', '').split(',') channels = channels.lower().replace(' ', '').split(',')
existing_channels = list() added = ''
channels_add = list() for channel in channels:
admin_log.info(channels) chnl = discord.utils.get(ctx.guild.channels, name=channel)
allowed_channels = await self.bot.db_con.fetchval('select allowed_channels from guild_config ' if chnl is None:
'where guild_id = $1', ctx.guild.id) await ctx.send(f'{channel} is not a valid text channel in this guild.')
if allowed_channels == 'null': else:
allowed_channels = None admin_log.info('Chan found')
if self.bot.con.one('select allowed_channels from guild_config where guild_id = %(id)s',
channels = [discord.utils.get(ctx.guild.channels, name=channel) {'id': ctx.guild.id}):
for channel in channels if channel is not None] if chnl.id in json.loads(self.bot.con.one('select allowed_channels from guild_config '
'where guild_id = %(id)s',
if allowed_channels and channels: {'id': ctx.guild.id})):
allowed_channels = [int(channel) for channel in json.loads(allowed_channels)] admin_log.info('Chan found in config')
existing_channels = [channel for channel in channels if channel.id in allowed_channels] await ctx.send(f'{channel} is already in the list of allowed channels. Skipping...')
channels_add = [channel for channel in channels if channel.id not in allowed_channels] else:
allowed_channels += [channel.id for channel in channels if channel.id not in allowed_channels] admin_log.info('Chan not found in config')
await self.bot.db_con.execute('update guild_config set allowed_channels = $2 where guild_id = $1', allowed_channels = json.loads(self.bot.con.one('select allowed_channels from '
ctx.guild.id, json.dumps(allowed_channels)) 'guild_config where guild_id = %(id)s',
elif channels: {'id': ctx.guild.id})).append(chnl.id)
admin_log.info('Config is empty') self.bot.con.run('update guild_config set allowed_channels = %(channels)s '
allowed_channels = [channel.id for channel in channels] 'where guild_id = %(id)s',
await self.bot.db_con.execute('update guild_config set allowed_channels = $2 ' {'id': ctx.guild.id, 'channels': allowed_channels})
'where guild_id = $1', ctx.guild.id, added = f'{added}\n{channel}'
json.dumps(allowed_channels)) else:
else: admin_log.info('Chan not found in config')
await ctx.send('None of those are valid text channels for this guild.') allowed_channels = [chnl.id]
return self.bot.con.run('update guild_config set allowed_channels = %(channels)s '
'where guild_id = %(id)s',
if existing_channels: {'id': ctx.guild.id, 'channels': allowed_channels})
channel_str = '\n'.join([str(channel.name) for channel in existing_channels]) added = f'{added}\n{channel}'
await ctx.send(f'The following channels were skipped because they are already in the config:\n' if added != '':
f'{channel_str}\n') await ctx.send(f'The following channels have been added to the allowed channel list: {added}')
if channels_add:
channel_str = '\n'.join([str(channel.name) for channel in channels_add])
await ctx.send('The following channels have been added to the allowed channel list:\n'
f'{channel_str}\n')
await ctx.message.add_reaction('') await ctx.message.add_reaction('')
else: else:
await ctx.send(f'You are not authorized to run this command.') await ctx.send(f'You are not authorized to run this command.')
else: else:
await ctx.send('This command must be run from inside a guild.') await ctx.send('This command must be run from inside a guild.')
# TODO Fix view_code @commands.command()
@commands.command(hidden=True)
@commands.is_owner() @commands.is_owner()
async def view_code(self, ctx, code_name): async def view_code(self, ctx, code_name):
pag = utils.Paginator(self.bot, prefix='```py', suffix='```') await ctx.send(f"```py\n{inspect.getsource(self.bot.get_command(code_name).callback)}\n```")
pag.add(inspect.getsource(self.bot.all_commands[code_name].callback))
for page in pag.pages():
await ctx.send(page)
@add.command(aliases=['prefix', 'p']) @add.command(aliases=['prefix', 'p'])
@commands.cooldown(1, 5, type=commands.BucketType.guild) @commands.cooldown(1, 5, type=commands.BucketType.guild)
async def add_prefix(self, ctx, *, prefix=None): async def add_prefix(self, ctx, *, prefix=None):
"""Adds a custom prefix for the current guild
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 ctx.guild:
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
prefixes = await self.bot.db_con.fetchval('select prefix from guild_config where guild_id = $1', prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s',
ctx.guild.id) {'id': ctx.guild.id})
if prefix is None: if prefix is None:
await ctx.send(prefixes) await ctx.send(prefixes)
return return
@@ -226,8 +208,8 @@ class Admin:
if len(prefixes) > 10: if len(prefixes) > 10:
await ctx.send(f'Only 10 prefixes are allowed per guild.\nPlease remove some before adding more.') await ctx.send(f'Only 10 prefixes are allowed per guild.\nPlease remove some before adding more.')
prefixes = prefixes[:10] prefixes = prefixes[:10]
await self.bot.db_con.execute('update guild_config set prefix = $2 where guild_id = $1', self.bot.con.run('update guild_config set prefix = %(prefixes)s where guild_id = %(id)s',
ctx.guild.id, prefixes) {'id': ctx.guild.id, 'prefixes': prefixes})
await ctx.guild.me.edit(nick=f'[{prefixes[0]}] Geeksbot') await ctx.guild.me.edit(nick=f'[{prefixes[0]}] Geeksbot')
await ctx.send(f"Updated. You currently have {len(prefixes)} " await ctx.send(f"Updated. You currently have {len(prefixes)} "
f"{'prefix' if len(prefixes) == 1 else 'prefixes'} " f"{'prefix' if len(prefixes) == 1 else 'prefixes'} "
@@ -240,13 +222,10 @@ class Admin:
@remove.command(aliases=['prefix', 'p']) @remove.command(aliases=['prefix', 'p'])
@commands.cooldown(1, 5, type=commands.BucketType.guild) @commands.cooldown(1, 5, type=commands.BucketType.guild)
async def remove_prefix(self, ctx, *, prefix=None): async def remove_prefix(self, ctx, *, prefix=None):
"""Removes custom prefix from the current guild
If the last prefix is removed then Geeksbot will default
Back to g$"""
if ctx.guild: if ctx.guild:
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
prefixes = await self.bot.db_con.fetchval('select prefix from guild_config where guild_id = $1', prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s',
ctx.guild.id) {'id': ctx.guild.id})
found = 0 found = 0
if prefix is None: if prefix is None:
await ctx.send(prefixes) await ctx.send(prefixes)
@@ -263,8 +242,8 @@ class Admin:
else: else:
await ctx.send(f'The prefix {p} is not in the config for this guild.') await ctx.send(f'The prefix {p} is not in the config for this guild.')
if found: if found:
await self.bot.db_con.execute('update guild_config set prefix = $2 where guild_id = $1', self.bot.con.run('update guild_config set prefix = %(prefixes)s where guild_id = %(id)s',
ctx.guild.id, prefixes) {'id': ctx.guild.id, 'prefixes': prefixes})
await ctx.guild.me.edit(nick=f'[{prefixes[0] if len(prefixes) != 0 else self.bot.default_prefix}] ' await ctx.guild.me.edit(nick=f'[{prefixes[0] if len(prefixes) != 0 else self.bot.default_prefix}] '
f'Geeksbot') f'Geeksbot')
await ctx.send(f"Updated. You currently have {len(prefixes)} " await ctx.send(f"Updated. You currently have {len(prefixes)} "
@@ -279,19 +258,16 @@ class Admin:
@commands.cooldown(1, 5, type=commands.BucketType.guild) @commands.cooldown(1, 5, type=commands.BucketType.guild)
@commands.check(checks.is_guild_owner) @commands.check(checks.is_guild_owner)
async def _add_admin_role(self, ctx, role=None): async def _add_admin_role(self, ctx, role=None):
"""Adds role to the admin list for current guild
Allowing members of that role to run admin commands
on the current guild."""
role = discord.utils.get(ctx.guild.roles, name=role) role = discord.utils.get(ctx.guild.roles, name=role)
if role is not None: if role is not None:
roles = json.loads(await self.bot.db_con.fetchval('select admin_roles from guild_config ' roles = json.loads(self.bot.con.one('select admin_roles from guild_config where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id)) {'id': ctx.guild.id}))
if role.name in roles: if role.name in roles:
await ctx.send(f'{role.name} is already registered as an admin role in this guild.') await ctx.send(f'{role.name} is already registered as an admin role in this guild.')
else: else:
roles[role.name] = role.id roles[role.name] = role.id
await self.bot.db_con.execute('update guild_config set admin_roles = $2 where guild_id = $1', self.bot.con.run('update guild_config set admin_roles = %(roles)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(roles)) {'id': ctx.guild.id, 'roles': json.dumps(roles)})
await ctx.send(f'{role.name} has been added to the list of admin roles for this guild.') await ctx.send(f'{role.name} has been added to the list of admin roles for this guild.')
else: else:
await ctx.send('You must include a role with this command.') await ctx.send('You must include a role with this command.')
@@ -300,15 +276,14 @@ class Admin:
@commands.cooldown(1, 5, type=commands.BucketType.guild) @commands.cooldown(1, 5, type=commands.BucketType.guild)
@commands.check(checks.is_guild_owner) @commands.check(checks.is_guild_owner)
async def _remove_admin_role(self, ctx, role=None): async def _remove_admin_role(self, ctx, role=None):
"""Removes role from admin list in current guild"""
role = discord.utils.get(ctx.guild.roles, name=role) role = discord.utils.get(ctx.guild.roles, name=role)
if role is not None: if role is not None:
roles = json.loads(await self.bot.db_con.fetchval('select admin_roles from guild_config ' roles = json.loads(self.bot.con.one('select admin_roles from guild_config where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id)) {'id': ctx.guild.id}))
if role.name in roles: if role.name in roles:
del roles[role.name] del roles[role.name]
await self.bot.db_con.execute('update guild_config set admin_roles = $2 where guild_id = $1', self.bot.con.run('update guild_config set admin_roles = %(roles)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(roles)) {'id': ctx.guild.id, 'roles': roles})
await ctx.send(f'{role.name} has been removed from the list of admin roles for this guild.') await ctx.send(f'{role.name} has been removed from the list of admin roles for this guild.')
else: else:
await ctx.send(f'{role.name} is not registered as an admin role in this guild.') await ctx.send(f'{role.name} is not registered as an admin role in this guild.')
+107 -90
View File
@@ -1,10 +1,9 @@
import discord import discord
from discord.ext import commands
import logging import logging
from datetime import datetime from datetime import datetime
import json import json
import re import re
from src.imports import utils from .imports import utils
config_dir = 'config/' config_dir = 'config/'
admin_id_file = 'admin_ids' admin_id_file = 'admin_ids'
@@ -51,20 +50,17 @@ class BotEvents:
config_str = f'{config_str}\n{" "*4}{config}: {guild_config[config]}' config_str = f'{config_str}\n{" "*4}{config}: {guild_config[config]}'
return config_str return config_str
# noinspection PyUnusedLocal
async def on_raw_message_delete(self, msg_id, chan_id): async def on_raw_message_delete(self, msg_id, chan_id):
await self.bot.db_con.execute('update messages set deleted_at = $1 where id = $2', self.bot.con.run('update messages set deleted_at = %(time)s where id = %(id)s',
datetime.utcnow(), msg_id) {'time': datetime.utcnow(), 'id': msg_id})
# noinspection PyUnusedLocal
async def on_raw_bulk_message_delete(self, msg_ids, chan_id): async def on_raw_bulk_message_delete(self, msg_ids, chan_id):
del_time = datetime.utcnow() sql = ''
for msg_id in msg_ids: for msg_id in msg_ids:
await self.bot.db_con.execute('update messages set deleted_at = $1 where id = $2', sql += f';update messages set deleted_at = %(time)s where id = {msg_id}'
del_time, msg_id) self.bot.con.run(sql, {'time': datetime.utcnow()})
async def on_message(self, ctx): async def on_message(self, ctx):
# noinspection PyBroadException
try: try:
if ctx.author in self.bot.infected: if ctx.author in self.bot.infected:
if datetime.now().timestamp() > self.bot.infected[ctx.author][1] + 300: if datetime.now().timestamp() > self.bot.infected[ctx.author][1] + 300:
@@ -77,22 +73,36 @@ class BotEvents:
sql = 'insert into messages (id, tts, type, content, embeds, channel, mention_everyone, mentions,\ sql = 'insert into messages (id, tts, type, content, embeds, channel, mention_everyone, mentions,\
channel_mentions, role_mentions, webhook, attachments, pinned, reactions, guild, created_at,\ channel_mentions, role_mentions, webhook, attachments, pinned, reactions, guild, created_at,\
system_content, author) \ system_content, author) \
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)' values (%(id)s, %(tts)s, %(type)s, %(content)s, %(embeds)s, %(channel)s, %(mention_everyone)s, %(mentions)s,\
msg_data = [ctx.id, ctx.tts, str(ctx.type), ctx.content, [json.dumps(e.to_dict()) for e in ctx.embeds], %(channel_mentions)s, %(role_mentions)s, %(webhook)s, %(attachments)s, %(pinned)s, %(reactions)s, %(guild)s,\
ctx.channel.id, ctx.mention_everyone, [user.id for user in ctx.mentions], %(created_at)s, %(system_content)s, %(author)s)'
[channel.id for channel in ctx.channel_mentions], [role.id for role in ctx.role_mentions], msg_data = dict()
ctx.webhook_id, [json.dumps({'id': a.id, 'size': a.size, 'height': a.height, 'width': a.width, msg_data['id'] = ctx.id
'filename': a.filename, 'url': a.url}) for a in ctx.attachments], msg_data['tts'] = ctx.tts
ctx.pinned, [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions], msg_data['type'] = str(ctx.type)
ctx.guild.id if ctx.guild else ctx.author.id, ctx.created_at, ctx.system_content, ctx.author.id] msg_data['content'] = ctx.content
await self.bot.db_con.execute(sql, *msg_data) msg_data['embeds'] = [json.dumps(e.to_dict()) for e in ctx.embeds]
msg_data['channel'] = ctx.channel.id
msg_data['mention_everyone'] = ctx.mention_everyone
msg_data['mentions'] = [user.id for user in ctx.mentions]
msg_data['channel_mentions'] = [channel.id for channel in ctx.channel_mentions]
msg_data['role_mentions'] = [role.id for role in ctx.role_mentions]
msg_data['webhook'] = ctx.webhook_id
msg_data['attachments'] = [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]
msg_data['pinned'] = ctx.pinned
msg_data['guild'] = ctx.guild.id
msg_data['created_at'] = ctx.created_at
msg_data['system_content'] = ctx.system_content
msg_data['author'] = ctx.author.id
msg_data['reactions'] = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions]
self.bot.con.run(sql, msg_data)
if ctx.guild: if ctx.guild:
if ctx.author != ctx.guild.me: if ctx.author != ctx.guild.me:
if await self.bot.db_con.fetchval("select pg_filter from guild_config where guild_id = $1", if self.bot.con.one(f"select pg_filter from guild_config where guild_id = {ctx.guild.id}"):
ctx.guild.id):
profane = 0 profane = 0
for word in await self.bot.db_con.fetchval('select profane_words from guild_config ' for word in self.bot.con.one('select profane_words from guild_config where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id): {'id': ctx.guild.id}):
word = word.strip() word = word.strip()
if word in ctx.content.lower(): if word in ctx.content.lower():
events_log.info(f'Found non PG word {word}') events_log.info(f'Found non PG word {word}')
@@ -119,44 +129,57 @@ class BotEvents:
await react.message.channel.send(f"You can't Poop on me {user.mention} :P") await react.message.channel.send(f"You can't Poop on me {user.mention} :P")
reactions = react.message.reactions reactions = react.message.reactions
reacts = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in reactions] reacts = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in reactions]
await self.bot.db_con.execute('update messages set reactions = $2 where id = $1', self.bot.con.run('update messages set reactions = %(reacts)s where id = %(id)s',
react.message.id, reacts) {'id': react.message.id, 'reacts': reacts})
async def on_message_edit(self, before, ctx): async def on_message_edit(self, before, ctx):
previous_content = await self.bot.db_con.fetchval('select previous_content from messages where id = $1', ctx.id) previous_content = self.bot.con.one('select previous_content from messages where id = %(id)s', {'id': ctx.id})
if previous_content: if previous_content:
previous_content.append(before.content) previous_content.append(before.content)
else: else:
previous_content = [before.content] previous_content = [before.content]
previous_embeds = await self.bot.db_con.fetchval('select previous_embeds from messages where id = $1', ctx.id) previous_embeds = self.bot.con.one('select previous_embeds from messages where id = %(id)s', {'id': ctx.id})
if previous_embeds: if previous_embeds:
previous_embeds.append([json.dumps(e.to_dict()) for e in before.embeds]) previous_embeds.append([json.dumps(e.to_dict()) for e in before.embeds])
else: else:
previous_embeds = [[json.dumps(e.to_dict()) for e in before.embeds]] previous_embeds = [[json.dumps(e.to_dict()) for e in before.embeds]]
sql = 'update messages set (edited_at, previous_content, previous_embeds, tts, type, content, embeds, ' \ sql = 'update messages set (edited_at, previous_content, previous_embeds, tts, type, content,\
'channel, mention_everyone, mentions, channel_mentions, role_mentions, webhook, attachments, pinned, ' \ embeds, channel, mention_everyone, mentions, channel_mentions, role_mentions, webhook,\
'reactions, guild, created_at, system_content, author) = ' \ attachments, pinned, reactions, guild, created_at, system_content, author) \
'($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20)' \ = (%(edited_at)s, %(previous_content)s, %(previous_embeds)s, %(tts)s, %(type)s, %(content)s,\
'where id = $21' %(embeds)s, %(channel)s, %(mention_everyone)s, %(mentions)s, %(channel_mentions)s, %(role_mentions)s,\
msg_data = [datetime.utcnow(), previous_content, previous_embeds, ctx.tts, str(ctx.type), ctx.content, %(webhook)s, %(attachments)s, %(pinned)s, %(reactions)s, %(guild)s, %(created_at)s, %(system_content)s,\
[json.dumps(e.to_dict()) for e in ctx.embeds], ctx.channel.id, ctx.mention_everyone, %(author)s) where id = %(id)s'
[user.id for user in ctx.mentions], [channel.id for channel in ctx.channel_mentions], msg_data = dict()
[role.id for role in ctx.role_mentions], ctx.webhook_id, msg_data['id'] = ctx.id
[json.dumps({'id': a.id, 'size': a.size, 'height': a.height, 'width': a.width, msg_data['tts'] = ctx.tts
'filename': a.filename, 'url': a.url}) for a in ctx.attachments], ctx.pinned, msg_data['type'] = str(ctx.type)
[json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions], ctx.guild.id, msg_data['content'] = ctx.content
ctx.created_at, ctx.system_content, ctx.author.id, ctx.id] msg_data['embeds'] = [json.dumps(e.to_dict()) for e in ctx.embeds]
await self.bot.db_con.execute(sql, *msg_data) msg_data['channel'] = ctx.channel.id
msg_data['mention_everyone'] = ctx.mention_everyone
msg_data['mentions'] = [user.id for user in ctx.mentions]
msg_data['channel_mentions'] = [channel.id for channel in ctx.channel_mentions]
msg_data['role_mentions'] = [role.id for role in ctx.role_mentions]
msg_data['webhook'] = ctx.webhook_id
msg_data['attachments'] = ctx.attachments
msg_data['pinned'] = ctx.pinned
msg_data['guild'] = ctx.guild.id
msg_data['created_at'] = ctx.created_at
msg_data['system_content'] = ctx.system_content
msg_data['author'] = ctx.author.id
msg_data['reactions'] = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions]
msg_data['previous_content'] = previous_content
msg_data['previous_embeds'] = previous_embeds
msg_data['edited_at'] = datetime.utcnow()
self.bot.con.run(sql, msg_data)
# noinspection PyMethodMayBeStatic @staticmethod
async def on_command_error(self, ctx, error): async def on_command_error(ctx, error):
pag = utils.Paginator(ctx.bot, embed=True, max_line_length=48) if ctx.channel.id == 418452585683484680 and type(error) == discord.ext.commands.errors.CommandNotFound:
pag.set_embed_meta(title=f'Command Error', return
color=self.bot.error_color, for page in utils.paginate(error):
thumbnail=f'{ctx.guild.me.avatar_url}') await ctx.send(page)
pag.add(error)
book = utils.Book(pag, (None, ctx.channel, self.bot, ctx.message))
await book.create_book()
async def on_guild_join(self, guild): async def on_guild_join(self, guild):
with open(f"{config_dir}{default_guild_config_file}", 'r') as file: with open(f"{config_dir}{default_guild_config_file}", 'r') as file:
@@ -166,25 +189,31 @@ class BotEvents:
default_config['name'] = guild.name.replace("'", "\\'") default_config['name'] = guild.name.replace("'", "\\'")
default_config['guild_id'] = guild.id default_config['guild_id'] = guild.id
events_log.info(default_config) events_log.info(default_config)
await self.bot.db_con.execute("insert into guild_config(guild_id, guild_name, admin_roles, rcon_enabled, " self.bot.con.run("insert into guild_config(guild_id, guild_name, admin_roles, rcon_enabled, channel_lockdown,\
"channel_lockdown, raid_status, pg_filter, patreon_enabled, referral_enabled) " raid_status, pg_filter, patreon_enabled, referral_enabled)\
"values ($1, $2, $3, $4, $5, $6, $7, $8, $9)", values (%(guild_id)s, %(name)s, %(admin_roles)s, %(rcon_enabled)s, %(channel_lockdown)s,\
default_config['guild_id'], default_config['name'], %(raid_status)s, %(pg_filter)s, %(patreon_enabled)s, %(referral_enabled)s)",
json.dumps(default_config['admin_roles']), default_config['rcon_enabled'], {'guild_id': default_config['guild_id'],
default_config['channel_lockdown'], default_config['raid_status'], 'name': default_config['name'],
default_config['pg_filter'], default_config['patreon_enabled'], 'admin_roles': json.dumps(default_config['admin_roles']),
default_config['referral_enabled']) 'rcon_enabled': default_config['rcon_enabled'],
'channel_lockdown': default_config['channel_lockdown'],
'raid_status': default_config['raid_status'],
'pg_filter': default_config['pg_filter'],
'patreon_enabled': default_config['patreon_enabled'],
'referral_enabled': default_config['referral_enabled']
})
events_log.info(f'Entry Created for {guild.name}') events_log.info(f'Entry Created for {guild.name}')
await guild.me.edit(nick='[g$] Geeksbot') await guild.me.edit(nick='[g$] Geeksbot')
async def on_guild_remove(self, guild): async def on_guild_remove(self, guild):
await self.bot.db_con.execute(f'delete from guild_config where guild_id = $1', guild.id) self.bot.con.run(f'delete from guild_config where guild_id = %(id)s', {'id': guild.id})
events_log.info(f'Left the {guild.name} guild.') events_log.info(f'Left the {guild.name} guild.')
async def on_member_join(self, member): async def on_member_join(self, member):
events_log.info(f'Member joined: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}') events_log.info(f'Member joined: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}')
join_chan = await self.bot.db_con.fetchval('select join_leave_chat from guild_config where guild_id = $1', join_chan = self.bot.con.one('select join_leave_chat from guild_config where guild_id = %(id)s',
member.guild.id) {'id': member.guild.id})
if join_chan: if join_chan:
em = discord.Embed(style='rich', em = discord.Embed(style='rich',
color=embed_color color=embed_color
@@ -197,30 +226,31 @@ class BotEvents:
em.set_footer(text=f"{member.guild.name} | {member.joined_at.strftime('%Y-%m-%d at %H:%M:%S GMT')}", em.set_footer(text=f"{member.guild.name} | {member.joined_at.strftime('%Y-%m-%d at %H:%M:%S GMT')}",
icon_url=member.guild.icon_url) icon_url=member.guild.icon_url)
await discord.utils.get(member.guild.channels, id=join_chan).send(embed=em) await discord.utils.get(member.guild.channels, id=join_chan).send(embed=em)
mem_data = [member.id, mem_data = {'id': member.id,
member.name, 'name': member.name,
member.discriminator, 'discriminator': member.discriminator,
member.bot 'bot': member.bot
] }
mem = await self.bot.db_con.fetchval('select guilds,nicks from user_data where id = $1', member.id) mem = self.bot.con.one('select guilds,nicks from user_data where id = %(id)s', {'id': member.id})
if mem: if mem:
mem[1].append(json.dumps({member.guild.id: member.display_name})) mem[1].append(json.dumps({member.guild.id: member.display_name}))
mem[0].append(member.guild.id) mem[0].append(member.guild.id)
mem_data.append(mem[1]) mem_data['nicks'] = mem[1]
mem_data.append(mem[0]) mem_data['guilds'] = mem[0]
self.bot.con.run('update user_data set (name, discriminator, bot, nicks, guilds) = ' self.bot.con.run('update user_data set (name, discriminator, bot, nicks, guilds) =\
'($2, $3, $4, $5, $6) where id = $1', *mem_data) (%(name)s, %(discriminator)s, %(bot)s, %(nicks)s, %(guilds)s) where\
id = %(id)s', mem_data)
else: else:
mem_data.append([json.dumps({member.guild.id: member.display_name})]) mem_data['nicks'] = [json.dumps({member.guild.id: member.display_name})]
mem_data.append([member.guild.id]) mem_data['guilds'] = [member.guild.id]
self.bot.con.run('insert into user_data (id, name, discriminator, bot, nicks, guilds) ' self.bot.con.run('insert into user_data (id, name, discriminator, bot, nicks, guilds) values\
'values ($1, $2, $3, $4, $5, $6)', *mem_data) (%(id)s, %(name)s, %(discriminator)s, %(bot)s, %(nicks)s, %(guilds)s)', mem_data)
async def on_member_remove(self, member): async def on_member_remove(self, member):
leave_time = datetime.utcnow() leave_time = datetime.utcnow()
events_log.info(f'Member left: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}') events_log.info(f'Member left: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}')
join_chan = await self.bot.db_con.fetchval('select join_leave_chat from guild_config where guild_id = $1', join_chan = self.bot.con.one('select join_leave_chat from guild_config where guild_id = %(id)s',
member.guild.id) {'id': member.guild.id})
if join_chan: if join_chan:
em = discord.Embed(style='rich', em = discord.Embed(style='rich',
color=red_color color=red_color
@@ -243,19 +273,6 @@ class BotEvents:
em.set_footer(text=f"{member.guild.name} | {datetime.utcnow().strftime('%Y-%m-%d at %H:%M:%S GMT')}", em.set_footer(text=f"{member.guild.name} | {datetime.utcnow().strftime('%Y-%m-%d at %H:%M:%S GMT')}",
icon_url=member.guild.icon_url) icon_url=member.guild.icon_url)
await discord.utils.get(member.guild.channels, id=join_chan).send(embed=em) await discord.utils.get(member.guild.channels, id=join_chan).send(embed=em)
mem_data = [member.id,
member.name,
member.discriminator,
member.bot
]
mem = await self.bot.db_con.fetchrow('select guilds,nicks from user_data where id = $1', member.id)
if mem:
mem[0].remove(member.guild.id)
mem[1].remove(json.dumps({member.guild.id: member.display_name}))
mem_data.append(mem[1])
mem_data.append(mem[0])
self.bot.con.run('update user_data set (name, discriminator, bot, nicks, guilds) = '
'($2, $3, $4, $5, $6) where id = $1', *mem_data)
def setup(bot): def setup(bot):
+19 -43
View File
@@ -33,9 +33,6 @@ class Fun:
@commands.command() @commands.command()
@commands.cooldown(1, 30, type=commands.BucketType.user) @commands.cooldown(1, 30, type=commands.BucketType.user)
async def infect(self, ctx, member: discord.Member, emoji): async def infect(self, ctx, member: discord.Member, emoji):
"""Infects a user with the given emoji
Every time the user sends a message that I am also in I will react to that message with said emoji."""
if member.id == self.bot.user.id and ctx.author.id != owner_id: if member.id == self.bot.user.id and ctx.author.id != owner_id:
await ctx.send(f'You rolled a Critical Fail...\nInfection bounces off and rebounds on the attacker.') await ctx.send(f'You rolled a Critical Fail...\nInfection bounces off and rebounds on the attacker.')
member = ctx.author member = ctx.author
@@ -51,7 +48,6 @@ class Fun:
@commands.command() @commands.command()
@commands.cooldown(1, 5, type=commands.BucketType.user) @commands.cooldown(1, 5, type=commands.BucketType.user)
async def heal(self, ctx, member: discord.Member): async def heal(self, ctx, member: discord.Member):
"""Removes infection from user."""
if ctx.author == member and ctx.author.id != owner_id: if ctx.author == member and ctx.author.id != owner_id:
await ctx.send('You can\'t heal yourself silly...') await ctx.send('You can\'t heal yourself silly...')
else: else:
@@ -61,7 +57,7 @@ class Fun:
else: else:
await ctx.send(f'{member.display_name} is not infected...') await ctx.send(f'{member.display_name} is not infected...')
@commands.command(hidden=True) @commands.command()
@commands.is_owner() @commands.is_owner()
async def print_infections(self, ctx): async def print_infections(self, ctx):
await ctx.author.send(f'```{self.bot.infected}```') await ctx.author.send(f'```{self.bot.infected}```')
@@ -69,15 +65,13 @@ class Fun:
@commands.command() @commands.command()
@commands.cooldown(1, 5, type=commands.BucketType.user) @commands.cooldown(1, 5, type=commands.BucketType.user)
async def slap(self, ctx, member: discord.Member): async def slap(self, ctx, member: discord.Member):
"""IRC Style Trout Slap"""
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: 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'You rolled a Critical Fail...\nThe trout bounces off and rebounds on the attacker.')
await ctx.send(f'{ctx.author.mention} ' await ctx.send(f'{ctx.author.mention} '
f'You slap yourself in the face with a large trout {trout}') f'You slap yourself in the face with a large trout <:trout:408543365085397013>')
else: else:
await ctx.send(f'{ctx.author.display_name} slaps ' await ctx.send(f'{ctx.author.display_name} slaps '
f'{member.mention} around a bit with a large trout {trout}') f'{member.mention} around a bit with a large trout <:trout:408543365085397013>')
@staticmethod @staticmethod
def get_factorial(number): def get_factorial(number):
@@ -86,23 +80,22 @@ class Fun:
a = a * (i + 1) a = a * (i + 1)
return a return a
@commands.command() # @commands.command()
@commands.cooldown(1, 5, type=commands.BucketType.user) # @commands.cooldown(1, 5, type=commands.BucketType.user)
async def fact(self, ctx, number: int): # async def fact(self, ctx, number:int):
"""Returns the given factorial up to 20,000!""" # if number < 20001 and number > 0:
if 0 < number < 20001: # n = 1990
n = 1990 # with ctx.channel.typing():
with ctx.channel.typing(): # a = await self.bot.loop.run_in_executor(None, self.get_factorial, number)
a = await self.bot.loop.run_in_executor(None, self.get_factorial, number) # if len(str(a)) > 6000:
if len(str(a)) > 6000: # for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]:
for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]: # await ctx.author.send(f'```py\n{b}```')
await ctx.author.send(f'```py\n{b}```') # await ctx.send(f"{ctx.author.mention} Check your DMs.")
await ctx.send(f"{ctx.author.mention} Check your DMs.") # else:
else: # for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]:
for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]: # await ctx.send(f'```py\n{b}```')
await ctx.send(f'```py\n{b}```') # else:
else: # await ctx.send("Invalid number. Please enter a number between 0 and 20,000")
await ctx.send("Invalid number. Please enter a number between 0 and 20,000")
@commands.command(hidden=True) @commands.command(hidden=True)
@commands.is_owner() @commands.is_owner()
@@ -148,28 +141,11 @@ class Fun:
else: else:
await ctx.send('Not connected to that voice channel.') await ctx.send('Not connected to that voice channel.')
# noinspection PyUnusedLocal
@commands.command(hidden=True) @commands.command(hidden=True)
@commands.is_owner() @commands.is_owner()
async def volume(self, ctx, volume: float): async def volume(self, ctx, volume: float):
self.bot.player.volume = volume self.bot.player.volume = volume
@commands.command(name='explode', aliases=['splode'])
async def explode_user(self, ctx, member: discord.Member=None):
"""Trolls user by punching them to oblivion."""
if member is None or member.id == 396588996706304010:
member = ctx.author
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.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"]}')
await asyncio.sleep(0.5)
await msg.edit(content=f'{self.bot.unicode_emojis["boom"]} <---- {member.mention} that was you...')
def setup(bot): def setup(bot):
bot.add_cog(Fun(bot)) bot.add_cog(Fun(bot))
+54
View File
@@ -0,0 +1,54 @@
import discord
from discord.ext import commands
import logging
from .imports.utils import paginate, run_command
import asyncio
owner_id = 351794468870946827
embed_color = discord.Colour.from_rgb(49, 107, 111)
git_log = logging.getLogger('git')
class Git:
def __init__(self, bot):
self.bot = bot
@commands.group(case_insensitive=True)
async def git(self, ctx):
"""Run help git for more info"""
pass
@git.command()
@commands.is_owner()
async def pull(self, ctx):
em = discord.Embed(style='rich',
title=f'Git Pull',
color=embed_color)
em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}')
result = await asyncio.wait_for(self.bot.loop.create_task(run_command('git fetch --all')), 120) + '\n'
result += await asyncio.wait_for(self.bot.loop.create_task(run_command('git reset --hard '
'origin/master')), 120) + '\n\n'
result += await asyncio.wait_for(self.bot.loop.create_task(run_command('git show --stat | '
'sed "s/.*@.*[.].*/ /g"')), 10)
results = paginate(result, maxlen=1014)
for page in results[:5]:
em.add_field(name='', value=f'{page}')
await ctx.send(embed=em)
@git.command()
@commands.is_owner()
async def status(self, ctx):
em = discord.Embed(style='rich',
title=f'Git Pull',
color=embed_color)
em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}')
result = await asyncio.wait_for(self.bot.loop.create_task(run_command('git status')), 10)
results = paginate(result, maxlen=1014)
for page in results[:5]:
em.add_field(name='', value=f'{page}')
await ctx.send(embed=em)
def setup(bot):
bot.add_cog(Git(bot))
@@ -1,46 +1,46 @@
import discord import discord
import json import json
from src.imports import utils from . import utils
owner_id = 351794468870946827 owner_id = 351794468870946827
async def check_admin_role(bot, ctx, member): def check_admin_role(bot, ctx, member):
admin_roles = json.loads(await bot.db_con.fetchval(f"select admin_roles from guild_config where guild_id = $1", admin_roles = json.loads(bot.con.one(f"select admin_roles from guild_config where guild_id = %(id)s",
ctx.guild.id)) {'id': ctx.guild.id}))
for role in admin_roles: for role in admin_roles:
if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in member.roles: if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in member.roles:
return True return True
return member.id == ctx.guild.owner.id or member.id == owner_id return member.id == ctx.guild.owner.id or member.id == owner_id
async def check_rcon_role(bot, ctx, member): def check_rcon_role(bot, ctx, member):
rcon_admin_roles = json.loads(await bot.db_con.fetchval("select rcon_admin_roles from guild_config " rcon_admin_roles = json.loads(bot.con.one("select rcon_admin_roles from guild_config where guild_id = %(id)s",
"where guild_id = $1", ctx.guild.id)) {'id': ctx.guild.id}))
for role in rcon_admin_roles: for role in rcon_admin_roles:
if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in member.roles: if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in member.roles:
return True return True
return member.id == ctx.guild.owner.id or member.id == owner_id return member.id == ctx.guild.owner.id or member.id == owner_id
async def is_admin(bot, ctx): def is_admin(bot, ctx):
admin_roles = json.loads(await bot.db_con.fetchval("select admin_roles from guild_config where guild_id = $1", admin_roles = json.loads(bot.con.one("select admin_roles from guild_config where guild_id = %(id)s",
ctx.guild.id)) {'id': ctx.guild.id}))
for role in admin_roles: for role in admin_roles:
if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in ctx.message.author.roles: if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in ctx.message.author.roles:
return True return True
return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id
async def is_guild_owner(ctx): def is_guild_owner(ctx):
if ctx.guild: if ctx.guild:
return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id
return False return False
async def is_rcon_admin(bot, ctx): def is_rcon_admin(bot, ctx):
rcon_admin_roles = json.loads(await bot.db_con.fetchval("select rcon_admin_roles from guild_config " rcon_admin_roles = json.loads(bot.con.one("select rcon_admin_roles from guild_config where guild_id = %(id)s",
"where guild_id = $1", ctx.guild.id)) {'id': ctx.guild.id}))
for role in rcon_admin_roles: for role in rcon_admin_roles:
if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in ctx.message.author.roles: if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in ctx.message.author.roles:
return True return True
+93
View File
@@ -0,0 +1,93 @@
from io import StringIO
import sys
import asyncio
import discord
from discord.ext.commands.formatter import Paginator
from . import checks
class Capturing(list):
def __enter__(self):
self._stdout = sys.stdout
sys.stdout = self._stringio = StringIO()
return self
def __exit__(self, *args):
self.extend(self._stringio.getvalue().splitlines())
del self._stringio # free up some memory
sys.stdout = self._stdout
async def mute(bot, ctx, admin=0, member_id=None):
mute_role = bot.con.one(f'select muted_role from guild_config where guild_id = {ctx.guild.id}')
if mute_role:
if admin or 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)])
def to_list_of_str(items, out: list=list(), level=1, recurse=0):
def rec_loop(item, key, out, level):
quote = '"'
if type(item) == list:
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[')
new_level = level + 1
out = to_list_of_str(item, out, new_level, 1)
out.append(f'{" "*level}]')
elif type(item) == dict:
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{')
new_level = level + 1
out = to_list_of_str(item, out, new_level, 1)
out.append(f'{" "*level}}}')
else:
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},')
if type(items) == list:
if not recurse:
out = list()
out.append('[')
for item in items:
rec_loop(item, None, out, level)
if not recurse:
out.append(']')
elif type(items) == dict:
if not recurse:
out = list()
out.append('{')
for key in items:
rec_loop(items[key], key, out, level)
if not recurse:
out.append('}')
return out
def paginate(text, maxlen=1990):
paginator = Paginator(prefix='```py', max_size=maxlen+10)
if type(text) == list:
data = to_list_of_str(text)
elif type(text) == dict:
data = to_list_of_str(text)
else:
data = str(text).split('\n')
for line in data:
if len(line) > maxlen:
n = maxlen
for l in [line[i:i+n] for i in range(0, len(line), n)]:
paginator.add_line(l)
else:
paginator.add_line(line)
return paginator.pages
async def run_command(args):
# Create subprocess
process = await asyncio.create_subprocess_shell(
args,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE)
# Wait for the subprocess to finish
stdout, stderr = await process.communicate()
# Return stdout
return stdout.decode().strip()
+32 -32
View File
@@ -1,7 +1,7 @@
import discord import discord
from discord.ext import commands from discord.ext import commands
import json import json
from src.imports import checks from .imports import checks
config_dir = 'config' config_dir = 'config'
extension_dir = 'extensions' extension_dir = 'extensions'
@@ -17,11 +17,12 @@ class Patreon:
@commands.cooldown(1, 5, type=commands.BucketType.user) @commands.cooldown(1, 5, type=commands.BucketType.user)
async def get_patreon_links(self, ctx, target: discord.Member=None): async def get_patreon_links(self, ctx, target: discord.Member=None):
"""Prints Patreon information for creators on the server.""" """Prints Patreon information for creators on the server."""
if await self.bot.db_con.fetchval('select patreon_enabled from guild_config where guild_id = $1', ctx.guild.id): if self.bot.con.one('select patreon_enabled from guild_config where guild_id = %(id)s', {'id': ctx.guild.id}):
patreon_info = await self.bot.db_con.fetchrow('select patreon_message,patreon_links from guild_config ' patreon_info = self.bot.con.one('select patreon_message,patreon_links\
'where guild_id = $1', ctx.guild.id) from guild_config where guild_id = %(id)s',
message = patreon_info['patreon_message'].replace('\\n', '\n') {'id': ctx.guild.id})
patreon_links = json.loads(patreon_info['patreon_links']) message = patreon_info[0].replace('\\n', '\n')
patreon_links = json.loads(patreon_info[1])
for key in patreon_links: for key in patreon_links:
message = message + '\n{0}: {1}'.format(key, patreon_links[key]) message = message + '\n{0}: {1}'.format(key, patreon_links[key])
if target is None: if target is None:
@@ -33,23 +34,23 @@ class Patreon:
@commands.command(aliases=['patreon_message']) @commands.command(aliases=['patreon_message'])
async def set_patreon_message(self, ctx, message): async def set_patreon_message(self, ctx, message):
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
patreon_message = await self.bot.db_con.fetchval('select patreon_message from guild_config ' patreon_message = self.bot.con.one('select patreon_message from guild_config where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id) {'id': ctx.guild.id})
if message == patreon_message: if message == patreon_message:
await ctx.send('That is already the current message for this guild.') await ctx.send('That is already the current message for this guild.')
else: else:
await self.bot.db_con.execute('update guild_config set patreon_message = $2 where guild_id = $1', self.bot.con.run('update guild_config set patreon_message = %(message)s where guild_id = %(id)s',
ctx.guild.id, message) {'id': ctx.guild.id, 'message': message})
await ctx.send(f'The patreon message for this guild has been set to:\n{message}') await ctx.send(f'The patreon message for this guild has been set to:\n{message}')
else: else:
await ctx.send(f'You are not authorized to run this command.') await ctx.send(f'You are not authorized to run this command.')
@commands.command(aliases=['add_patreon', 'set_patreon']) @commands.command(aliases=['add_patreon', 'set_patreon'])
async def add_patreon_info(self, ctx, name, url): async def add_patreon_info(self, ctx, name, url):
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
patreon_info = await self.bot.db_con.fetchval('select patreon_links from guild_config where guild_id = $1', patreon_info = self.bot.con.one('select patreon_links from guild_config where guild_id = %(id)s',
ctx.guild.id) {'id': ctx.guild.id})
patreon_links = {} patreon_links = {}
update = 0 update = 0
if patreon_info: if patreon_info:
@@ -57,8 +58,8 @@ class Patreon:
if name in patreon_links: if name in patreon_links:
update = 1 update = 1
patreon_links[name] = url patreon_links[name] = url
await self.bot.db_con.execute('update guild_config set patreon_links = $2 where guild_id = $1', self.bot.con.run('update guild_config set patreon_links = %(links)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(patreon_links)) {'id': ctx.guild.id, 'links': json.dumps(patreon_links)})
await ctx.send(f"The Patreon link for {name} has been " await ctx.send(f"The Patreon link for {name} has been "
f"{'updated to the new url.' if update else'added to the config for this guild.'}") f"{'updated to the new url.' if update else'added to the config for this guild.'}")
else: else:
@@ -66,15 +67,15 @@ class Patreon:
@commands.command(aliases=['remove_patreon']) @commands.command(aliases=['remove_patreon'])
async def remove_patreon_info(self, ctx, name): async def remove_patreon_info(self, ctx, name):
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
patreon_info = await self.bot.db_con.fetchval('select patreon_links from guild_config where guild_id = $1', patreon_info = self.bot.con.one('select patreon_links from guild_config where guild_id = %(id)s',
ctx.guild.id) {'id': ctx.guild.id})
if patreon_info: if patreon_info:
patreon_links = json.loads(patreon_info) patreon_links = json.loads(patreon_info)
if name in patreon_links: if name in patreon_links:
del patreon_links[name] del patreon_links[name]
await self.bot.db_con.execute('update guild_config set patreon_links = $2 where guild_id = $1', self.bot.con.run('update guild_config set patreon_links = %(links)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(patreon_links)) {'id': ctx.guild.id, 'links': json.dumps(patreon_links)})
await ctx.send(f'The Patreon link for {name} has been removed from the config for this guild.') await ctx.send(f'The Patreon link for {name} has been removed from the config for this guild.')
return return
else: else:
@@ -86,18 +87,18 @@ class Patreon:
@commands.command() @commands.command()
async def enable_patreon(self, ctx, state: bool=True): async def enable_patreon(self, ctx, state: bool=True):
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
patreon_status = await self.bot.db_con.fetchval('select patreon_enabled from guild_config ' patreon_status = self.bot.con.one('select patreon_enabled from guild_config where guild_id = %(id)s',
'where guild_id = $1', ctx.guild.id) {'id': ctx.guild.id})
if patreon_status and state: if patreon_status and state:
await ctx.send('Patreon is already enabled for this guild.') await ctx.send('Patreon is already enabled for this guild.')
elif patreon_status and not state: elif patreon_status and not state:
await self.bot.db_con.execute('update guild_config set patreon_enabled = $2 where guild_id = $1', self.bot.con.run('update guild_config set patreon_enabled = %(state)s where guild_id = %(id)s',
ctx.guild.id, state) {'id': ctx.guild.id, 'state': state})
await ctx.send('Patreon has been disabled for this guild.') await ctx.send('Patreon has been disabled for this guild.')
elif not patreon_status and state: elif not patreon_status and state:
await self.bot.db_con.execute('update guild_config set patreon_enabled = $2 where guild_id = $1', self.bot.con.run('update guild_config set patreon_enabled = %(state)s where guild_id = %(id)s',
ctx.guild.id, state) {'id': ctx.guild.id, 'state': state})
await ctx.send('Patreon has been enabled for this guild.') await ctx.send('Patreon has been enabled for this guild.')
elif not patreon_status and not state: elif not patreon_status and not state:
await ctx.send('Patreon is already disabled for this guild.') await ctx.send('Patreon is already disabled for this guild.')
@@ -106,10 +107,9 @@ class Patreon:
@commands.cooldown(1, 5, type=commands.BucketType.user) @commands.cooldown(1, 5, type=commands.BucketType.user)
async def referral_links(self, ctx, target: discord.Member=None): async def referral_links(self, ctx, target: discord.Member=None):
"""Prints G-Portal Referral Links.""" """Prints G-Portal Referral Links."""
if await self.bot.db_con.fetchval('select referral_enabled from guild_config where guild_id = $1', if self.bot.con.one('select referral_enabled from guild_config where guild_id = %(id)s', {'id': ctx.guild.id}):
ctx.guild.id): referral_info = self.bot.con.one('select referral_message,referral_links from guild_config\
referral_info = await self.bot.db_con.fetchval('select referral_message,referral_links from guild_config ' where guild_id = %(id)s', {'id': ctx.guild.id})
'where guild_id = $1', ctx.guild.id)
message = referral_info[0] message = referral_info[0]
referral_links = json.loads(referral_info[1]) referral_links = json.loads(referral_info[1])
for key in referral_links: for key in referral_links:
+48 -56
View File
@@ -7,7 +7,7 @@ import logging
from datetime import datetime from datetime import datetime
import asyncio import asyncio
import traceback import traceback
from src.imports import checks from .imports import checks
config_dir = 'config' config_dir = 'config'
admin_id_file = 'admin_ids' admin_id_file = 'admin_ids'
@@ -120,7 +120,6 @@ class Rcon:
True) True)
con.exec_command('ServerChatToPlayer "{0}" GeeksBot: Admin Geeks have been notified you need assistance. ' con.exec_command('ServerChatToPlayer "{0}" GeeksBot: Admin Geeks have been notified you need assistance. '
'Please be patient.'.format(player)) 'Please be patient.'.format(player))
# noinspection PyProtectedMember
con._sock.close() con._sock.close()
for role in admin_roles: for role in admin_roles:
msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=admin_roles[role]).mention) msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=admin_roles[role]).mention)
@@ -138,16 +137,15 @@ class Rcon:
first_last first_last
"first last" "first last"
To view all the valid ARK servers for this guild see list_ark_servers.""" To view all the valid ARK servers for this guild see list_ark_servers."""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
if server is not None: if server is not None:
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'from guild_config where guild_id = $1', where guild_id = %(id)s', {'id': ctx.guild.id}))
ctx.guild.id))
server = server.replace('_', ' ').title() server = server.replace('_', ' ').title()
if server in rcon_connections: if server in rcon_connections:
rcon_connections[server]["monitoring_chat"] = 1 rcon_connections[server]["monitoring_chat"] = 1
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1', self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(rcon_connections)) {'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
channel = self.bot.get_channel(rcon_connections[server]['game_chat_chan_id']) channel = self.bot.get_channel(rcon_connections[server]['game_chat_chan_id'])
await channel.send('Started monitoring on the {0} server.'.format(server)) await channel.send('Started monitoring on the {0} server.'.format(server))
await ctx.message.add_reaction('') await ctx.message.add_reaction('')
@@ -160,7 +158,6 @@ class Rcon:
True) True)
messages = await self.bot.loop.run_in_executor(None, self.server_chat_background_process, messages = await self.bot.loop.run_in_executor(None, self.server_chat_background_process,
ctx.guild.id, con) ctx.guild.id, con)
# noinspection PyProtectedMember
con._sock.close() con._sock.close()
except TimeoutError: except TimeoutError:
rcon_log.error(traceback.format_exc()) rcon_log.error(traceback.format_exc())
@@ -183,10 +180,8 @@ class Rcon:
message = func(ctx, message, rcon_connections['server']) message = func(ctx, message, rcon_connections['server'])
await channel.send('{0}'.format(message)) await channel.send('{0}'.format(message))
await asyncio.sleep(1) await asyncio.sleep(1)
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'from guild_config ' where guild_id = %(id)s', {'id': ctx.guild.id}))
'where guild_id = $1',
ctx.guild.id))
await channel.send('Monitoring Stopped') await channel.send('Monitoring Stopped')
else: else:
await ctx.send(f'Server not found: {server}') await ctx.send(f'Server not found: {server}')
@@ -200,16 +195,15 @@ class Rcon:
async def end_monitor_chat(self, ctx, *, server=None): async def end_monitor_chat(self, ctx, *, server=None):
"""Ends chat monitoring on the specified server. """Ends chat monitoring on the specified server.
Context is the same as monitor_chat""" Context is the same as monitor_chat"""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
if server is not None: if server is not None:
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'from guild_config where guild_id = $1', where guild_id = %(id)s', {'id': ctx.guild.id}))
ctx.guild.id))
server = server.replace('_', ' ').title() server = server.replace('_', ' ').title()
if server in rcon_connections: if server in rcon_connections:
rcon_connections[server]["monitoring_chat"] = 0 rcon_connections[server]["monitoring_chat"] = 0
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1', self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(rcon_connections)) {'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
else: else:
await ctx.send(f'Server not found: {server}') await ctx.send(f'Server not found: {server}')
else: else:
@@ -229,9 +223,9 @@ class Rcon:
first_last first_last
"first last" "first last"
To view all the valid ARK servers for this guild see list_ark_servers.""" To view all the valid ARK servers for this guild see list_ark_servers."""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
if server is not None: if server is not None:
server = server.replace('_', ' ').title() server = server.replace('_', ' ').title()
if server in rcon_connections: if server in rcon_connections:
@@ -247,10 +241,9 @@ class Rcon:
'"ip" port "password" if you would like to get info from it.'.format(server)) '"ip" port "password" if you would like to get info from it.'.format(server))
else: else:
for server in rcon_connections: for server in rcon_connections:
msg = await ctx.send('Getting Data for the {0} server'.format(server.title()))
# noinspection PyBroadException
try: try:
connection_info = rcon_connections[server] connection_info = rcon_connections[server]
msg = await ctx.send('Getting Data for the {0} server'.format(server.title()))
async with ctx.channel.typing(): async with ctx.channel.typing():
message = self._listplayers(connection_info) message = self._listplayers(connection_info)
except Exception as e: except Exception as e:
@@ -267,10 +260,10 @@ class Rcon:
async def add_rcon_server(self, ctx, server, ip, port, password): async def add_rcon_server(self, ctx, server, ip, port, password):
"""Adds the specified server to the current guild\'s rcon config. """Adds the specified server to the current guild\'s rcon config.
All strings (<server>, <ip>, <password>) must be contained inside double quotes.""" All strings (<server>, <ip>, <password>) must be contained inside double quotes."""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
server = server.title() server = server.title()
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
if server not in rcon_connections: if server not in rcon_connections:
rcon_connections[server] = { rcon_connections[server] = {
'ip': ip, 'ip': ip,
@@ -281,8 +274,8 @@ class Rcon:
'msg_chan_id': 0, 'msg_chan_id': 0,
'monitoring_chat': 0 'monitoring_chat': 0
} }
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1', self.bot.con.run('update guild_config set rcon_connections = %(connections)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(rcon_connections)) {'id': ctx.guild.id, 'connections': json.dumps(rcon_connections)})
await ctx.send('{0} server has been added to my configuration.'.format(server)) await ctx.send('{0} server has been added to my configuration.'.format(server))
else: else:
await ctx.send('This server name is already in my configuration. Please choose another.') await ctx.send('This server name is already in my configuration. Please choose another.')
@@ -296,14 +289,14 @@ class Rcon:
async def remove_rcon_server(self, ctx, server): async def remove_rcon_server(self, ctx, server):
"""removes the specified server from the current guild\'s rcon config. """removes the specified server from the current guild\'s rcon config.
All strings <server> must be contained inside double quotes.""" All strings <server> must be contained inside double quotes."""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
server = server.title() server = server.title()
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
if server in rcon_connections: if server in rcon_connections:
del rcon_connections[server] del rcon_connections[server]
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1', self.bot.con.run('update guild_config set rcon_connections = %(connections)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(rcon_connections)) {'id': ctx.guild.id, 'connections': json.dumps(rcon_connections)})
await ctx.send('{0} has been removed from my configuration.'.format(server)) await ctx.send('{0} has been removed from my configuration.'.format(server))
else: else:
await ctx.send('{0} is not in my configuration.'.format(server)) await ctx.send('{0} is not in my configuration.'.format(server))
@@ -316,11 +309,10 @@ class Rcon:
"""Adds the included Steam 64 IDs to the running whitelist on all the ARK servers in the current guild\'s rcon config. """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. Steam 64 IDs should be a comma seperated list of IDs.
Example: 76561198024193239,76561198024193239,76561198024193239""" Example: 76561198024193239,76561198024193239,76561198024193239"""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
if steam_ids is not None: if steam_ids is not None:
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'from guild_config where guild_id = $1', where guild_id = %(id)s', {'id': ctx.guild.id}))
ctx.guild.id))
error = 0 error = 0
error_msg = '' error_msg = ''
success_msg = 'Adding to the running whitelist on all servers.' success_msg = 'Adding to the running whitelist on all servers.'
@@ -364,9 +356,9 @@ class Rcon:
"""Runs SaveWorld on the specified ARK server. """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. 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.""" Will print out "World Saved" for each server when the command completes successfully."""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
success_msg = 'Running saveworld' success_msg = 'Running saveworld'
if server is None: if server is None:
success_msg += ' on all the servers:' success_msg += ' on all the servers:'
@@ -377,7 +369,7 @@ class Rcon:
await msg.edit(content=success_msg.strip()) await msg.edit(content=success_msg.strip())
message = await self.bot.loop.run_in_executor(None, self._saveworld, rcon_connections[server]) message = await self.bot.loop.run_in_executor(None, self._saveworld, rcon_connections[server])
except Exception as e: except Exception as e:
success_msg = '{0}\n{1}'.format(success_msg, e) success_msg = '{0}\n{1}'.format(success_msg, e.strip())
await msg.edit(content=success_msg.strip()) await msg.edit(content=success_msg.strip())
else: else:
success_msg = '{0}\n{1}'.format(success_msg, message.strip()) success_msg = '{0}\n{1}'.format(success_msg, message.strip())
@@ -406,9 +398,9 @@ class Rcon:
"""Sends a broadcast message to all servers in the guild config. """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. 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.""" Will print "Success" for each server once the broadcast is sent."""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
if message is not None: if message is not None:
message = f'{ctx.author.display_name}: {message}' message = f'{ctx.author.display_name}: {message}'
success_msg = f'Broadcasting "{message}" to all servers.' success_msg = f'Broadcasting "{message}" to all servers.'
@@ -422,7 +414,7 @@ class Rcon:
rcon_connections[server], rcon_connections[server],
message) message)
except Exception as e: except Exception as e:
success_msg = '{0}\n{1}'.format(success_msg, e) success_msg = '{0}\n{1}'.format(success_msg, e.strip())
await msg.edit(content=success_msg.strip()) await msg.edit(content=success_msg.strip())
else: else:
for mesg in messages: for mesg in messages:
@@ -443,9 +435,9 @@ class Rcon:
The message will be prefixed with the Discord name of the person running the command. 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 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 _""" by double quotes or the words separated by _"""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
if server is not None: if server is not None:
server = server.replace('_', ' ').title() server = server.replace('_', ' ').title()
if message is not None: if message is not None:
@@ -481,9 +473,9 @@ class Rcon:
and these channel's permissions will be synced to the category. 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 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.""" server chat messages will be sent when monitor_chat is run."""
if await checks.is_rcon_admin(self.bot, ctx): if checks.is_rcon_admin(self.bot, ctx):
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
edited = 0 edited = 0
category = discord.utils.get(ctx.guild.categories, name='Server Chats') category = discord.utils.get(ctx.guild.categories, name='Server Chats')
if category is None: if category is None:
@@ -506,8 +498,8 @@ class Rcon:
rcon_connections[server]['game_chat_chan_id'] = chan.id rcon_connections[server]['game_chat_chan_id'] = chan.id
edited = 1 edited = 1
if edited == 1: if edited == 1:
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1', self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
ctx.guild.id, json.dumps(rcon_connections)) {'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
await ctx.message.add_reaction('') await ctx.message.add_reaction('')
else: else:
await ctx.send(f'You are not authorized to run this command.') await ctx.send(f'You are not authorized to run this command.')
@@ -517,8 +509,8 @@ class Rcon:
@commands.check(checks.is_restricted_chan) @commands.check(checks.is_restricted_chan)
async def list_ark_servers(self, ctx): async def list_ark_servers(self, ctx):
"""Returns a list of all the ARK servers in the current guild\'s config.""" """Returns a list of all the ARK servers in the current guild\'s config."""
servers = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config ' servers = json.loads(self.bot.con.one('select rcon_connections from guild_config\
'where guild_id = $1', ctx.guild.id)) where guild_id = %(id)s', {'id': ctx.guild.id}))
em = discord.Embed(style='rich', em = discord.Embed(style='rich',
title=f'__**There are currently {len(servers)} ARK servers in my config:**__', title=f'__**There are currently {len(servers)} ARK servers in my config:**__',
color=discord.Colour.green() color=discord.Colour.green()
+27 -22
View File
@@ -7,7 +7,7 @@ import inspect
import textwrap import textwrap
from contextlib import redirect_stdout from contextlib import redirect_stdout
import io import io
from src.imports.utils import run_command, format_output, Paginator from .imports.utils import paginate, run_command
ownerids = [351794468870946827, 275280442884751360] ownerids = [351794468870946827, 275280442884751360]
ownerid = 351794468870946827 ownerid = 351794468870946827
@@ -37,7 +37,6 @@ class Repl:
async def _eval(self, ctx, *, body: str): async def _eval(self, ctx, *, body: str):
if ctx.author.id != ownerid: if ctx.author.id != ownerid:
return return
pag = Paginator(self.bot)
env = { env = {
'bot': self.bot, 'bot': self.bot,
'ctx': ctx, 'ctx': ctx,
@@ -61,10 +60,8 @@ class Repl:
with redirect_stdout(stdout): with redirect_stdout(stdout):
ret = await func() ret = await func()
except Exception: except Exception:
pag.add(stdout.getvalue()) value = stdout.getvalue()
pag.add(traceback.format_exc()) await ctx.send('```py\n{}{}\n```'.format(value, traceback.format_exc()))
for page in pag.pages():
await ctx.send(page)
else: else:
value = stdout.getvalue() value = stdout.getvalue()
# noinspection PyBroadException # noinspection PyBroadException
@@ -72,12 +69,17 @@ class Repl:
await ctx.message.add_reaction('') await ctx.message.add_reaction('')
except Exception: except Exception:
pass pass
value = format_output(value) if ret is None:
pag.add(value) if value:
pag.add(f'\nReturned: {ret}') for page in paginate(value):
self._last_result = ret await ctx.send(page)
for page in pag.pages(): else:
await ctx.send(page) self._last_result = ret
if value:
for page in paginate(value):
await ctx.send(page)
for page in paginate(ret):
await ctx.send(page)
@commands.command(hidden=True) @commands.command(hidden=True)
async def repl(self, ctx): async def repl(self, ctx):
@@ -141,11 +143,12 @@ class Repl:
fmt = '{}'.format(value) fmt = '{}'.format(value)
try: try:
if fmt is not None: if fmt is not None:
pag = Paginator(self.bot) if len(fmt) > 1990:
pag.add(fmt) for page in paginate(fmt):
for page in pag.pages(): await response.channel.send(page)
await response.channel.send(page) await ctx.send(response.channel)
await ctx.send(response.channel) else:
await response.channel.send(f'```py\n{fmt}\n```')
except discord.Forbidden: except discord.Forbidden:
pass pass
except discord.HTTPException as e: except discord.HTTPException as e:
@@ -156,14 +159,16 @@ class Repl:
if ctx.author.id != ownerid: if ctx.author.id != ownerid:
return return
try: try:
body = self.cleanup_code(body) body = self.cleanup_code(body).split(' ')
pag = Paginator(self.bot) result = await asyncio.wait_for(self.bot.loop.create_task(run_command(body)), 10)
pag.add(await asyncio.wait_for(self.bot.loop.create_task(run_command(body)), 10)) value = result
for page in pag.pages(): for page in paginate(value):
await ctx.send(page) await ctx.send(page)
await ctx.message.add_reaction('') await ctx.message.add_reaction('')
except asyncio.TimeoutError: except asyncio.TimeoutError:
await ctx.send(f"Command did not complete in the time allowed.") value = f"Command did not complete in the time allowed."
for page in paginate(value):
await ctx.send(page)
await ctx.message.add_reaction('') await ctx.message.add_reaction('')
+63 -311
View File
@@ -7,19 +7,10 @@ import psutil
from datetime import datetime, timedelta from datetime import datetime, timedelta
import asyncio import asyncio
import async_timeout import async_timeout
from src.imports import checks, utils from .imports import checks
import pytz import pytz
import gspread import gspread
from oauth2client.service_account import ServiceAccountCredentials from oauth2client.service_account import ServiceAccountCredentials
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
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/' config_dir = 'config/'
admin_id_file = 'admin_ids' admin_id_file = 'admin_ids'
@@ -31,7 +22,6 @@ invite_match = '(https?://)?(www.)?discord(app.com/(invite|oauth2)|.gg|.io)/[\w\
utils_log = logging.getLogger('utils') utils_log = logging.getLogger('utils')
clock_emojis = ['🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚'] clock_emojis = ['🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚']
replace_tzs = {'MST': 'US/Mountain', 'HST': 'US/Hawaii', 'EST': 'US/Eastern'}
class Utils: class Utils:
@@ -54,20 +44,20 @@ class Utils:
msg = await self.bot.wait_for('message', timeout=5, check=check) msg = await self.bot.wait_for('message', timeout=5, check=check)
self.bot.ping_times[i]['rec'] = msg self.bot.ping_times[i]['rec'] = msg
@commands.command(hidden=True) @commands.command()
async def channel_ping(self, ctx, wait_time: float=10, message: str='=bump', channel: int=265828729970753537): async def channel_ping(self, ctx, wait_time: float=10, message: str='=bump', channel: int=265828729970753537):
await ctx.send('Starting Background Process.') await ctx.send('Starting Background Process.')
self.bot.loop.create_task(self._4_hour_ping(channel, message, wait_time)) self.bot.loop.create_task(self._4_hour_ping(channel, message, wait_time))
@commands.command(hidden=True) @commands.command()
@commands.is_owner() @commands.is_owner()
async def sysinfo(self, ctx): async def sysinfo(self, ctx):
"""Gets system status for my server.""" await ctx.send(f'''
await ctx.send(f'```ml\n' ```ml
f'CPU Percentages: {psutil.cpu_percent(percpu=True)}\n' CPU Percentages: {psutil.cpu_percent(percpu=True)}
f'Memory Usage: {psutil.virtual_memory().percent}%\n' Memory Usage: {psutil.virtual_memory().percent}%
f'Disc Usage: {psutil.disk_usage("/").percent}%\n' Disc Usage: {psutil.disk_usage("/").percent}%
f'```') ```''')
@commands.command(hidden=True) @commands.command(hidden=True)
async def role(self, ctx, role: str): async def role(self, ctx, role: str):
@@ -178,10 +168,7 @@ class Utils:
@commands.command() @commands.command()
@commands.cooldown(1, 5, type=commands.BucketType.user) @commands.cooldown(1, 5, type=commands.BucketType.user)
async def ping(self, ctx, mode='normal', count: int=2): async def ping(self, ctx, mode='normal', count: int=2):
"""Check the Bot\'s connection to Discord """Check the Bot\'s connection to Discord"""
For more detailed information set the <mode> as comp and it will test the ping
<count> number of times."""
em = discord.Embed(style='rich', em = discord.Embed(style='rich',
title=f'Pong 🏓', title=f'Pong 🏓',
color=discord.Colour.green() color=discord.Colour.green()
@@ -189,8 +176,8 @@ class Utils:
msg = await ctx.send(embed=em) msg = await ctx.send(embed=em)
time1 = ctx.message.created_at time1 = ctx.message.created_at
time = (msg.created_at - time1).total_seconds() * 1000 time = (msg.created_at - time1).total_seconds() * 1000
em.description = f'Response Time: **{math.ceil(time)}ms**\n' \ em.description = f'''Response Time: **{math.ceil(time)}ms**
f'Discord Latency: **{math.ceil(self.bot.latency*1000)}ms**' Discord Latency: **{math.ceil(self.bot.latency*1000)}ms**'''
await msg.edit(embed=em) await msg.edit(embed=em)
if mode == 'comp': if mode == 'comp':
@@ -219,8 +206,8 @@ class Utils:
time = time.total_seconds() time = time.total_seconds()
times.append(time) times.append(time)
value = f"Message Sent:" \ value = f"Message Sent:" \
f"{datetime.strftime(self.bot.ping_times[i]['snd'].created_at, '%H:%M:%S.%f')}\n" \ f"{datetime.strftime(self.bot.ping_times[i]['snd'].created_at, '%H:%M:%S.%f')}" \
f"Response Received: {datetime.strftime(now, '%H:%M:%S.%f')}\n" \ f"Response Received: {datetime.strftime(now, '%H:%M:%S.%f')}" \
f"Total Time: {math.ceil(time * 1000)}ms" f"Total Time: {math.ceil(time * 1000)}ms"
await self.bot.ping_times[i]['rec'].delete() await self.bot.ping_times[i]['rec'].delete()
em.add_field(name=f'Ping Test {i}', value=value, inline=True) em.add_field(name=f'Ping Test {i}', value=value, inline=True)
@@ -233,11 +220,11 @@ class Utils:
em.add_field(value=f'Total Time for Comprehensive test: {math.ceil(total_time)}ms', em.add_field(value=f'Total Time for Comprehensive test: {math.ceil(total_time)}ms',
name=f'Average: **{round(total_time/count,1)}ms**', name=f'Average: **{round(total_time/count,1)}ms**',
inline=False) inline=False)
await msg.edit(embed=em) await msg.edit(embed=em)
@commands.group(case_insensitive=True) @commands.group(case_insensitive=True)
async def admin(self, ctx): async def admin(self, ctx):
"""Group for Admin help requests""" """Run help admin for more info"""
pass pass
@admin.command(name='new', aliases=['nr']) @admin.command(name='new', aliases=['nr'])
@@ -249,20 +236,25 @@ class Utils:
if ctx.guild: if ctx.guild:
if request_msg is not None: if request_msg is not None:
if len(request_msg) < 1000: if len(request_msg) < 1000:
await self.bot.db_con.execute('insert into admin_requests (issuing_member_id, guild_orig, ' self.bot.con.run('insert into admin_requests (issuing_member_id, guild_orig, request_text,'
'request_text, request_time) values ($1, $2, $3, $4)', 'request_time) values (%(member_id)s, %(guild_id)s, %(text)s, %(time)s)',
ctx.author.id, ctx.guild.id, request_msg, ctx.message.created_at) {'member_id': ctx.author.id, 'guild_id': ctx.guild.id, 'text': request_msg,
channel = await self.bot.db_con.fetchval(f'select admin_chat from guild_config where guild_id = $1', 'time': ctx.message.created_at})
ctx.guild.id) channel = self.bot.con.one(f'select admin_chat from guild_config where guild_id = {ctx.guild.id}')
if channel: if channel:
chan = discord.utils.get(ctx.guild.channels, id=channel) chan = discord.utils.get(ctx.guild.channels, id=channel)
msg = '' msg = ''
roles = await self.bot.db_con.fetchval(f'select admin_roles,rcon_admin_roles from guild_config ' admin_roles = []
f'where guild_id = $1', ctx.guild.id) roles = self.bot.con.one(f'select admin_roles,rcon_admin_roles from guild_config where '
request_id = await self.bot.db_con.fetchval(f'select id from admin_requests where ' f'guild_id = %(id)s', {'id': ctx.guild.id})
f'issuing_member_id = $1 and request_time = $2', request_id = self.bot.con.one(f'select id from admin_requests where '
ctx.author.id, ctx.message.created_at) f'issuing_member_id = %(member_id)s and request_time = %(time)s',
admin_roles = json.loads(roles).values() {'member_id': ctx.author.id, 'time': ctx.message.created_at})
for item in roles:
i = json.loads(item)
for j in i:
if i[j] not in admin_roles:
admin_roles.append(i[j])
for role in admin_roles: for role in admin_roles:
msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=role).mention) msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=role).mention)
msg += f"New Request ID: {request_id} " \ msg += f"New Request ID: {request_id} " \
@@ -281,7 +273,7 @@ class Utils:
@admin.command(name='list', aliases=['lr']) @admin.command(name='list', aliases=['lr'])
@commands.cooldown(1, 5, type=commands.BucketType.user) @commands.cooldown(1, 5, type=commands.BucketType.user)
async def list_admin_requests(self, ctx, assigned_to: discord.Member=None): async def list_admin_requests(self, ctx, assigned_to: discord.Member=None):
"""List of all active Admin help requests """Returns a list of all active Admin help requests for this guild
If a user runs this command it will return all the requests that they have submitted and are still open. If a user runs this command it will return all the requests that they have submitted and are still open.
- The [assigned_to] argument is ignored but will still give an error if an incorrect value is entered. - The [assigned_to] argument is ignored but will still give an error if an incorrect value is entered.
@@ -294,10 +286,10 @@ class Utils:
title=f'Admin Help Requests', title=f'Admin Help Requests',
color=discord.Colour.green() color=discord.Colour.green()
) )
if await checks.is_admin(self.bot, ctx) or await checks.is_rcon_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx):
if assigned_to is None: if assigned_to is None:
requests = await self.bot.db_con.fetch(f'select * from admin_requests where guild_orig = $1 ' requests = self.bot.con.all(f'select * from admin_requests where guild_orig = %(guild_id)s '
f'and completed_time is null', ctx.guild.id) f'and completed_time is null', {'guild_id': ctx.guild.id})
em.title = f'Admin help requests for {ctx.guild.name}' em.title = f'Admin help requests for {ctx.guild.name}'
if requests: if requests:
for request in requests: for request in requests:
@@ -314,11 +306,11 @@ class Utils:
else: else:
em.add_field(name='There are no pending requests for this guild.', value='', inline=False) em.add_field(name='There are no pending requests for this guild.', value='', inline=False)
else: else:
if await checks.check_admin_role(self.bot, ctx, assigned_to)\ if checks.check_admin_role(self.bot, ctx, assigned_to)\
or await checks.check_rcon_role(self.bot, ctx, assigned_to): or checks.check_rcon_role(self.bot, ctx, assigned_to):
requests = await self.bot.db_con.fetch('select * from admin_requests where assigned_to = $1 ' requests = self.bot.con.all('select * from admin_requests where assigned_to = %(admin_id)s '
'and guild_orig = $2 and completed_time is null', 'and guild_orig = %(guild_id)s and completed_time is null',
assigned_to.id, ctx.guild.id) {'admin_id': assigned_to.id, 'guild_id': ctx.guild.id})
em.title = f'Admin help requests assigned to {assigned_to.display_name} in {ctx.guild.name}' em.title = f'Admin help requests assigned to {assigned_to.display_name} in {ctx.guild.name}'
if requests: if requests:
for request in requests: for request in requests:
@@ -330,16 +322,15 @@ class Utils:
"", "",
inline=False) inline=False)
else: else:
em.add_field(name=f'There are no pending requests for ' em.add_field(name=f'There are no pending requests for {assigned_to.display_name} on this guild.',
f'{assigned_to.display_name} on this guild.',
value='', value='',
inline=False) inline=False)
else: else:
em.title = f'{assigned_to.display_name} is not an admin in this guild.' em.title = f'{assigned_to.display_name} is not an admin in this guild.'
else: else:
requests = await self.bot.db_con.fetch('select * from admin_requests where issuing_member_id = $1 ' requests = self.bot.con.all('select * from admin_requests where issuing_member_id = %(member_id)s '
'and guild_orig = $2 and completed_time is null', 'and guild_orig = %(guild_id)s and completed_time is null',
ctx.author.id, ctx.guild.id) {'member_id': ctx.author.id, 'guild_id': ctx.guild.id})
em.title = f'Admin help requests for {ctx.author.display_name}' em.title = f'Admin help requests for {ctx.author.display_name}'
if requests: if requests:
for request in requests: for request in requests:
@@ -361,7 +352,7 @@ class Utils:
"""Allows Admin to close admin help tickets. """Allows Admin to close admin help tickets.
[request_id] must be a valid integer pointing to an open Request ID [request_id] must be a valid integer pointing to an open Request ID
""" """
if await checks.is_admin(self.bot, ctx) or await checks.is_rcon_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx):
if request_ids: if request_ids:
request_ids = request_ids.replace(' ', '').split(',') request_ids = request_ids.replace(' ', '').split(',')
for request_id in request_ids: for request_id in request_ids:
@@ -370,13 +361,14 @@ class Utils:
except ValueError: except ValueError:
await ctx.send(f'{request_id} is not a valid request id.') await ctx.send(f'{request_id} is not a valid request id.')
else: else:
request = await self.bot.db_con.fetchrow(f'select * from admin_requests where id = $1', request = self.bot.con.one(f'select * from admin_requests where id = %(request_id)s',
request_id) {'request_id': request_id})
if request: if request:
if request[3] == ctx.guild.id: if request[3] == ctx.guild.id:
if request[6] is None: if request[6] is None:
await self.bot.db_con.execute('update admin_requests set completed_time = $1 where ' self.bot.con.run('update admin_requests set completed_time = %(time_now)s where '
'id = $2', ctx.message.created_at, request_id) 'id = %(request_id)s',
{'time_now': ctx.message.created_at, 'request_id': request_id})
await ctx.send(f'Request {request_id} by ' await ctx.send(f'Request {request_id} by '
f'{ctx.guild.get_member(request[1]).display_name}' f'{ctx.guild.get_member(request[1]).display_name}'
f' has been marked complete.') f' has been marked complete.')
@@ -394,8 +386,7 @@ class Utils:
@commands.command(name='weather', aliases=['wu']) @commands.command(name='weather', aliases=['wu'])
@commands.cooldown(5, 15, type=commands.BucketType.default) @commands.cooldown(5, 15, type=commands.BucketType.default)
async def get_weather(self, ctx, *, location='palmer ak'): async def get_weather(self, ctx, *, location='palmer ak'):
"""Gets the weather data for the location given """Gets the weather data for the location provided,
If no location is included then it will get the weather for the Bot's home location. If no location is included then it will get the weather for the Bot's home location.
""" """
try: try:
@@ -432,12 +423,7 @@ class Utils:
@commands.command(name='localtime', aliases=['time', 'lt']) @commands.command(name='localtime', aliases=['time', 'lt'])
@commands.cooldown(1, 3, type=commands.BucketType.user) @commands.cooldown(1, 3, type=commands.BucketType.user)
async def get_localtime(self, ctx, timezone: str='Anchorage'): async def get_localtime(self, ctx, timezone: str='Anchorage'):
"""Shows the current time in the timezone given
This defaults to the Bot's local timezone of Anchorage Alaska USA if none are given."""
em = discord.Embed() em = discord.Embed()
try: try:
tz = pytz.timezone(timezone) tz = pytz.timezone(timezone)
localtime = datetime.now(tz=tz) localtime = datetime.now(tz=tz)
@@ -458,91 +444,13 @@ class Utils:
em.colour = discord.Colour.red() em.colour = discord.Colour.red()
await ctx.send(embed=em) 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):
"""Convert the time provided to given timezone
Attempts to process the given time and timezone and convert into the given timezone.
Example: g$gti CET Friday June 15 2018 US/Alaska
This will be processed into a datetime with US/Alaska set as the timezone and will
convert it into CET timezone and return both times."""
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 = datetime.now(tz=parsed_tz)
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.command(name='purge', aliases=['clean', 'erase'])
@commands.cooldown(1, 3, type=commands.BucketType.user) @commands.cooldown(1, 3, type=commands.BucketType.user)
async def purge_messages(self, ctx, number: int=20, member: discord.Member=None): async def purge_messages(self, ctx, number: int=20, member: discord.Member=None):
"""Purge messages from the current 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"""
prefixes = await self.bot.db_con.fetchval('select prefix from guild_config '
'where guild_id = $1', ctx.guild.id)
def is_me(message): def is_me(message):
nonlocal prefixes
if message.author == self.bot.user: if message.author == self.bot.user:
return True return True
prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
if prefixes: if prefixes:
for prefix in prefixes: for prefix in prefixes:
if message.content.startswith(prefix): if message.content.startswith(prefix):
@@ -556,7 +464,7 @@ class Utils:
def is_author(message): def is_author(message):
return message.author == ctx.author return message.author == ctx.author
if await checks.is_admin(self.bot, ctx): if checks.is_admin(self.bot, ctx):
if member: if member:
deleted = await ctx.channel.purge(limit=number, check=is_member) deleted = await ctx.channel.purge(limit=number, check=is_member)
if member != ctx.author: if member != ctx.author:
@@ -572,12 +480,7 @@ class Utils:
@commands.command(name='purge_all', aliases=['cls', 'clear']) @commands.command(name='purge_all', aliases=['cls', 'clear'])
@commands.cooldown(1, 3, type=commands.BucketType.user) @commands.cooldown(1, 3, type=commands.BucketType.user)
async def purge_all(self, ctx, number: int=20, contents: str='all'): async def purge_all(self, ctx, number: int=20, contents: str='all'):
"""Purge all messages from the current channel 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': if contents != 'all':
deleted = await ctx.channel.purge(limit=number, check=lambda message: message.content == contents) deleted = await ctx.channel.purge(limit=number, check=lambda message: message.content == contents)
else: else:
@@ -590,24 +493,19 @@ class Utils:
@commands.command(name='google', aliases=['g', 'search']) @commands.command(name='google', aliases=['g', 'search'])
async def google_search(self, ctx, *, 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() res = self.bot.gcs_service.cse().list(q=search, cx=self.bot.bot_secrets['cx']).execute()
results = res['items'] results = res['items'][:4]
pag = utils.Paginator(self.bot, max_line_length=100, embed=True) em = discord.Embed()
pag.set_embed_meta(title='Google Search', description=f'Top results for "{search}"', color=self.bot.embed_color) em.title = f'Google Search'
em.description = f'Top 4 results for "{search}"'
em.colour = embed_color
for result in results: for result in results:
pag.add(f'\uFFF6{result["title"]}\n{result["link"]}', keep_intact=True) em.add_field(name=f'{result["title"]}', value=f'{result["snippet"]}\n{result["link"]}')
pag.add(f'{result["snippet"]}') await ctx.send(embed=em)
pag.add('\uFFF7\n\uFFF8')
msg = await ctx.send('Starting Book')
book = utils.Book(pag, (msg, ctx.channel, self.bot, ctx.message))
await book.create_book()
@commands.command(hidden=True, name='sheets') @commands.command(hidden=True, name='sheets')
async def google_sheets(self, ctx, member: discord.Member): async def google_sheets(self, ctx, member: discord.Member):
"""Access Google Sheets and looks for the member""" if checks.is_admin(self.bot, ctx):
if await checks.is_admin(self.bot, ctx):
scope = ['https://spreadsheets.google.com/feeds', scope = ['https://spreadsheets.google.com/feeds',
'https://www.googleapis.com/auth/drive'] 'https://www.googleapis.com/auth/drive']
credentials = ServiceAccountCredentials.from_json_keyfile_name('config/google_client_secret.json', scope) credentials = ServiceAccountCredentials.from_json_keyfile_name('config/google_client_secret.json', scope)
@@ -630,152 +528,6 @@ class Utils:
value=f'Steam ID: {steam[i]}\nPatreon Level: {tier[i]}\nPatron of: {patron[i]}') value=f'Steam ID: {steam[i]}\nPatreon Level: {tier[i]}\nPatron of: {patron[i]}')
await ctx.send(embed=em) await ctx.send(embed=em)
@commands.command(name='iss')
async def iss_loc(self, ctx):
"""Locate the International Space Station
Gets the location of the ISS and display on a
Blue Marble map."""
def gen_image(iss_loc):
lat = iss_loc['latitude']
lon = iss_loc['longitude']
plt.figure(figsize=(5, 5))
m = Basemap(projection='ortho', resolution=None, lat_0=lat, lon_0=lon)
m.bluemarble(scale=0.5)
x, y = m(lon, lat)
plt.plot(x, y, 'ok', markersize=10, color='red')
plt.text(x, y, ' ISS', fontsize=20, color='red')
plt.tight_layout()
img = BytesIO()
plt.savefig(img, format='png', transparent=True)
img.seek(0)
self.bot.loop.create_task(ctx.send('Current ISS Location', file=discord.File(img, 'output.png')))
async with ctx.typing():
async with self.bot.aio_session.get('https://api.wheretheiss.at/v1/satellites/25544') as response:
loc = await response.json()
await self.bot.loop.run_in_executor(self.bot.tpe, gen_image, loc)
@commands.command(name='location', aliases=['loc', 'map'])
async def map_location(self, ctx, *, location):
"""WIP Displays the given location on a map
Searches for the location provided and plots the Lat Long 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
m.shadedrelief(scale=scale)
m.fillcontinents(color="#FFDDCC", lake_color='#DDEEFF')
m.drawmapboundary(fill_color="#DDEEFF")
m.drawcoastlines(color='gray')
m.drawcountries(color='gray')
m.drawstates(color='gray')
# lats and longs are returned as a dictionary
lats = m.drawparallels(np.linspace(-90, 90, 30))
lons = m.drawmeridians(np.linspace(-180, 180, 90))
# keys contain the plt.Line2D instances
lat_lines = chain(*(tup[1][0] for tup in lats.items()))
lon_lines = chain(*(tup[1][0] for tup in lons.items()))
all_lines = chain(lat_lines, lon_lines)
# cycle through these lines and set the desired style
for line in all_lines:
line.set(linestyle='-', alpha=0.3, color='gray')
def gen_image(loc):
lat = loc['lat']
lon = loc['lng']
plt.figure(figsize=(4, 4))
m = Basemap(projection='lcc', width=2E6, height=2E6, resolution='i', lat_0=lat, lon_0=lon)
draw_map(m)
x, y = m(lon, lat)
plt.plot(x, y, 'ok', markersize=5, color='red')
plt.text(x, y, f' {location.title()}', fontsize=12, color='red')
plt.tight_layout()
img = BytesIO()
plt.savefig(img, format='png', transparent=True)
img.seek(0)
self.bot.loop.create_task(ctx.send(file=discord.File(img, f'{location} map.png')))
self.bot.loop.create_task(ctx.trigger_typing())
msg = await ctx.send(f'Checking on location data for {location.title()}')
async with ctx.typing():
async with self.bot.aio_session.get(
f'https://api.opencagedata.com/geocode/v1/json?q={location}&key={self.bot.geo_api}') as result:
data = await result.json()
if data['total_results'] != 0:
location_data = data['results'][0]['geometry']
await msg.edit(content=f'Got Location. Please wait, Generating the image can take up to a minute.')
async with ctx.typing():
await self.bot.loop.run_in_executor(self.bot.tpe, gen_image, location_data)
await msg.delete()
else:
await msg.edit(content=f'I can\'t find any data for that location.\nPlease try again.')
@commands.command(name='help', aliases=['h'])
@commands.cooldown(1, 5, commands.BucketType.user)
async def custom_help(self, ctx, *, command: str=None):
"""This help message"""
pag = utils.Paginator(self.bot, embed=True, max_line_length=48)
prefixes = await self.bot.get_custom_prefix(self.bot, ctx.message)
if isinstance(prefixes, list):
prefixes = ', '.join(prefixes)
owner = await self.bot.get_user_info(self.bot.owner_id)
if command is None:
pag.set_embed_meta(title='Geeksbot Help',
description=f'For more information about a command please run\n'
f'{prefixes.split(",")[0]}help [group] <command>',
thumbnail=f'{ctx.guild.me.avatar_url}')
pag.add(f"\uFFF6Welcome to Geeksbot's help command.\n"
f"< {self.bot.description} >\n\n"
f"Below you will find some basic information about me.\n\n"
f"Version: <{self.bot.__version__}>\n\n"
f"Owner: <Dusty.P>\n"
f"> Username: {owner.name}#{owner.discriminator}\n"
f"> ID: {owner.id}\n\n"
f"Prefixes available for this guild:\n"
f"> {prefixes}\n\uFFF7\n\uFFF8")
for cog in sorted(self.bot.cogs):
for command in sorted(self.bot.get_cog_commands(cog), key=lambda x: x.name):
if not command.hidden:
pag.add(f'\uFFF6{command.name}')
pag.add(f'> {command.short_doc}', truncate=True)
try:
for com in sorted(command.commands, key=lambda x: x.name):
if not com.hidden:
pag.add(f'# {com.name}')
pag.add(f'> {com.short_doc}', truncate=True)
except AttributeError as e:
pass
pag.add('\uFFF7')
pag.add('\uFFF8')
else:
pag.set_embed_meta(title='Geeksbot Help',
thumbnail=f'{ctx.guild.me.avatar_url}')
command = command.split(maxsplit=1)
if command[0] in self.bot.all_commands:
if len(command) > 1 and self.bot.all_commands[command[0]].group:
command = self.bot.all_commands[command[0]].all_commands.get(command[1], None)
else:
command = self.bot.all_commands[command[0]]
else:
command = None
if command and not command.hidden:
pag.add(f'\uFFF6{command.name}')
pag.add(f'Usage: {prefixes.split()[0]}{command.signature}\n')
pag.add(f'\uFFF0\n{command.help}')
else:
pag.add('\uFFF6There is no command by that name.\n>')
book = utils.Book(pag, (None, ctx.channel, self.bot, ctx.message))
await book.create_book()
def setup(bot): def setup(bot):
bot.add_cog(Utils(bot)) bot.add_cog(Utils(bot))
+28 -51
View File
@@ -1,15 +1,13 @@
from typing import Dict from typing import Dict
import discord
from discord.ext import commands from discord.ext import commands
import logging import logging
from datetime import datetime from datetime import datetime
import json import json
import aiohttp import aiohttp
from postgres import Postgres
from collections import deque
from googleapiclient.discovery import build from googleapiclient.discovery import build
from concurrent import futures
from src.shared_libs import database
log_format = '{asctime}.{msecs:03.0f}|{levelname:<8}|{name}::{message}' log_format = '{asctime}.{msecs:03.0f}|{levelname:<8}|{name}::{message}'
date_format = '%Y.%m.%d %H.%M.%S' date_format = '%Y.%m.%d %H.%M.%S'
@@ -25,7 +23,7 @@ formatter = logging.Formatter(log_format, style='{', datefmt=date_format)
console_handler.setFormatter(formatter) console_handler.setFormatter(formatter)
logging.getLogger('').addHandler(console_handler) logging.getLogger('').addHandler(console_handler)
config_dir = 'src/config/' config_dir = 'config/'
admin_id_file = 'admin_ids' admin_id_file = 'admin_ids'
extension_dir = 'exts' extension_dir = 'exts'
owner_id = 351794468870946827 owner_id = 351794468870946827
@@ -37,70 +35,48 @@ emojis: Dict[str, str] = {
'x': '', 'x': '',
'y': '', 'y': '',
'poop': '💩', 'poop': '💩',
'boom': '💥',
} }
description = 'I am Geeksbot v0.1! Fear me!'
class Geeksbot(commands.Bot): class Geeksbot(commands.Bot):
def __init__(self, **kwargs): def __init__(self, **kwargs):
kwargs["command_prefix"] = self.get_custom_prefix kwargs["command_prefix"] = self.get_custom_prefix
self.description = 'I am Geeksbot! Fear me!'
kwargs['description'] = self.description
super().__init__(**kwargs) super().__init__(**kwargs)
self.aio_session = aiohttp.ClientSession(loop=self.loop) self.aio_session = aiohttp.ClientSession(loop=self.loop)
with open(f'{config_dir}{bot_config_file}') as file: with open(f'{config_dir}{bot_config_file}') as file:
self.bot_config = json.load(file) self.bot_config = json.load(file)
with open(f'{config_dir}{secrets_file}') as file: with open(f'{config_dir}{secrets_file}') as file:
self.bot_secrets = json.load(file) self.bot_secrets = json.load(file)
# with open(f'{config_dir}{profane_words_file}') as file:
# self.profane_words = file.readlines()
self.guild_config = {} self.guild_config = {}
self.infected = {} self.infected = {}
self.TOKEN = self.bot_secrets['token'] self.TOKEN = self.bot_secrets['token']
self.embed_color = discord.Colour.from_rgb(49, 107, 111)
self.error_color = discord.Colour.from_rgb(142, 29, 31)
del self.bot_secrets['token'] del self.bot_secrets['token']
self.db_con = database.DatabaseConnection(**self.bot_secrets['db_con']) self.con = Postgres(f" host={self.bot_secrets['db_con']['host']}\
port={self.bot_secrets['db_con']['port']}\
dbname={self.bot_secrets['db_con']['db_name']}\
connect_timeout=10 user={self.bot_secrets['db_con']['user']}\
password={self.bot_secrets['db_con']['password']}")
del self.bot_secrets['db_con']
self.default_prefix = 'g$' self.default_prefix = 'g$'
self.voice_chans = {} self.voice_chans = {}
self.spam_list = {} self.spam_list = {}
self.owner_id = 351794468870946827
self.__version__ = 'v1.0.0'
self.gcs_service = build('customsearch', 'v1', developerKey=self.bot_secrets['google_search_key']) self.gcs_service = build('customsearch', 'v1', developerKey=self.bot_secrets['google_search_key'])
self.tpe = futures.ThreadPoolExecutor()
self.geo_api = '2d4e419c2be04c8abe91cb5dd1548c72'
self.unicode_emojis: Dict[str, str] = {
'x': '',
'y': '',
'poop': '💩',
'boom': '💥',
'left_fist': '🤛',
'lock': '🔒',
}
self.book_emojis: Dict[str, str] = {
'unlock': '🔓',
'start': '',
'back': '',
'hash': '#\N{COMBINING ENCLOSING KEYCAP}',
'forward': '',
'end': '',
'close': '🇽',
}
async def logout(self): async def get_custom_prefix(self, bot_inst, message):
await self.db_con.close() return self.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': message.guild.id})\
super().logout() or self.default_prefix
@staticmethod
async def get_custom_prefix(bot_inst, message):
return await bot_inst.db_con.fetchval('select prefix from guild_config where guild_id = $1',
message.guild.id) or bot_inst.default_prefix
async def load_ext(self, ctx, mod=None): async def load_ext(self, ctx, mod=None):
self.load_extension('src.{0}.{1}'.format(extension_dir, mod)) self.load_extension('{0}.{1}'.format(extension_dir, mod))
if ctx is not None: if ctx is not None:
await ctx.send('{0} loaded.'.format(mod)) await ctx.send('{0} loaded.'.format(mod))
async def unload_ext(self, ctx, mod=None): async def unload_ext(self, ctx, mod=None):
self.unload_extension('src.{0}.{1}'.format(extension_dir, mod)) self.unload_extension('{0}.{1}'.format(extension_dir, mod))
if ctx is not None: if ctx is not None:
await ctx.send('{0} unloaded.'.format(mod)) await ctx.send('{0} unloaded.'.format(mod))
@@ -109,7 +85,7 @@ class Geeksbot(commands.Bot):
self.aio_session.close() # aiohttp is drunk and can't decide if it's a coro or not self.aio_session.close() # aiohttp is drunk and can't decide if it's a coro or not
bot = Geeksbot(case_insensitive=True) bot = Geeksbot(description=description, case_insensitive=True)
@bot.command(hidden=True) @bot.command(hidden=True)
@@ -144,14 +120,14 @@ async def unload(ctx, mod):
async def on_message(ctx): async def on_message(ctx):
if not ctx.author.bot: if not ctx.author.bot:
if ctx.guild: if ctx.guild:
if int(await bot.db_con.fetchval("select channel_lockdown from guild_config where guild_id = $1", if int(bot.con.one(f"select channel_lockdown from guild_config where guild_id = %(id)s",
ctx.guild.id)): {'id': ctx.guild.id})):
if ctx.channel.id in json.loads(await bot.db_con.fetchval("select allowed_channels from guild_config " if ctx.channel.id in json.loads(bot.con.one(f"select allowed_channels from guild_config "
"where guild_id = $1", f"where guild_id = %(id)s",
ctx.guild.id)): {'id': ctx.guild.id})):
await bot.process_commands(ctx) await bot.process_commands(ctx)
elif ctx.channel.id == 418452585683484680: elif ctx.channel.id == 418452585683484680:
prefix = await bot.db_con.fetchval('select prefix from guild_config where guild_id = $1', ctx.guild.id) prefix = bot.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
prefix = prefix[0] if prefix else bot.default_prefix prefix = prefix[0] if prefix else bot.default_prefix
ctx.content = f'{prefix}{ctx.content}' ctx.content = f'{prefix}{ctx.content}'
await bot.process_commands(ctx) await bot.process_commands(ctx)
@@ -163,19 +139,20 @@ async def on_message(ctx):
@bot.event @bot.event
async def on_ready(): async def on_ready():
bot.remove_command('help')
bot.recent_msgs = {} bot.recent_msgs = {}
for guild in bot.guilds:
bot.recent_msgs[guild.id] = deque(maxlen=50)
logging.info('Logged in as {0.name}|{0.id}'.format(bot.user)) logging.info('Logged in as {0.name}|{0.id}'.format(bot.user))
load_list = bot.bot_config['load_list'] load_list = bot.bot_config['load_list']
for load_item in load_list: for load_item in load_list:
await bot.load_ext(None, f'{load_item}') await bot.load_ext(None, f'{load_item}')
logging.info('Extension Loaded: {0}'.format(load_item)) logging.info('Extension Loaded: {0}'.format(load_item))
logging.info('Done loading, Geeksbot is active.')
with open(f'{config_dir}reboot', 'r') as f: with open(f'{config_dir}reboot', 'r') as f:
reboot = f.readlines() reboot = f.readlines()
if int(reboot[0]) == 1: if int(reboot[0]) == 1:
await bot.get_channel(int(reboot[1])).send('Restart Finished.') await bot.get_channel(int(reboot[1])).send('Restart Finished.')
with open(f'{config_dir}reboot', 'w') as f: with open(f'{config_dir}reboot', 'w') as f:
f.write(f'0') f.write(f'0')
logging.info('Done loading, Geeksbot is active.')
bot.run(bot.TOKEN) bot.run(bot.TOKEN)
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
until python -m src; do until python /home/dusty/bin/geeksbot/geeksbot.py; do
echo "Geeksbot shutdown with error: $?. Restarting..." >&2 echo "Geeksbot shutdown with error: $?. Restarting..." >&2
sleep 1 sleep 1
done done
+35
View File
@@ -0,0 +1,35 @@
@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()
-64
View File
@@ -1,64 +0,0 @@
import discord
from discord.ext import commands
import logging
from src.imports.utils import Paginator, run_command, Book
import asyncio
owner_id = 351794468870946827
embed_color = discord.Colour.from_rgb(49, 107, 111)
git_log = logging.getLogger('git')
class Git:
def __init__(self, bot):
self.bot = bot
@commands.group(case_insensitive=True, invoke_without_command=True)
async def git(self, ctx):
"""Shows my Git link"""
em = discord.Embed(style='rich',
title=f'Here is where you can find my code',
url='https://github.com/dustinpianalto/Geeksbot/tree/development',
description='I am the development branch of Geeksbot. You can find the master branch here:\n'
'https://github.com/dustinpianalto/Geeksbot/',
color=embed_color)
em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}')
await ctx.send(embed=em)
@git.command()
@commands.is_owner()
async def pull(self, ctx):
"""Pulls updates from GitHub rebasing branch."""
pag = Paginator(self.bot, max_line_length=44, embed=True)
pag.set_embed_meta(title='Git Pull',
color=self.bot.embed_color,
thumbnail=f'{ctx.guild.me.avatar_url}')
pag.add('\uFFF6' + await asyncio.wait_for(self.bot.loop.create_task(run_command('git fetch --all')), 120))
pag.add(await asyncio.wait_for(self.bot.loop.create_task(run_command('git reset --hard '
'origin/$(git '
'rev-parse --symbolic-full-name'
' --abbrev-ref HEAD)')), 120))
pag.add('\uFFF7\n\uFFF8')
pag.add(await asyncio.wait_for(self.bot.loop.create_task(run_command('git show --stat | '
'sed "s/.*@.*[.].*/ /g"')), 10))
book = Book(pag, (None, ctx.channel, self.bot, ctx.message))
await book.create_book()
@git.command()
@commands.is_owner()
async def status(self, ctx):
"""Gets status of current branch."""
pag = Paginator(self.bot, max_line_length=44, max_lines=30, embed=True)
pag.set_embed_meta(title='Git Status',
color=self.bot.embed_color,
thumbnail=f'{ctx.guild.me.avatar_url}')
result = await asyncio.wait_for(self.bot.loop.create_task(run_command('git status')), 10)
pag.add(result)
book = Book(pag, (None, ctx.channel, self.bot, ctx.message))
await book.create_book()
def setup(bot):
bot.add_cog(Git(bot))
View File
-449
View File
@@ -1,449 +0,0 @@
from io import StringIO
import sys
import asyncio
import discord
from discord.ext.commands.formatter import Paginator as DannyPag
from src.imports import checks
import re
import typing
from datetime import datetime
class Capturing(list):
def __enter__(self):
self._stdout = sys.stdout
sys.stdout = self._stringio = StringIO()
return self
def __exit__(self, *args):
self.extend(self._stringio.getvalue().splitlines())
del self._stringio # free up some memory
sys.stdout = self._stdout
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 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)])
def to_list_of_str(items, out: list=list(), level=1, recurse=0):
# noinspection PyShadowingNames
def rec_loop(item, key, out, level):
quote = '"'
if type(item) == list:
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[')
new_level = level + 1
out = to_list_of_str(item, out, new_level, 1)
out.append(f'{" "*level}]')
elif type(item) == dict:
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{')
new_level = level + 1
out = to_list_of_str(item, out, new_level, 1)
out.append(f'{" "*level}}}')
else:
out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},')
if type(items) == list:
if not recurse:
out = list()
out.append('[')
for item in items:
rec_loop(item, None, out, level)
if not recurse:
out.append(']')
elif type(items) == dict:
if not recurse:
out = list()
out.append('{')
for key in items:
rec_loop(items[key], key, out, level)
if not recurse:
out.append('}')
return out
def format_output(text):
if type(text) == list:
text = to_list_of_str(text)
elif type(text) == dict:
text = to_list_of_str(text)
return text
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)
async def run_command(args):
# Create subprocess
process = await asyncio.create_subprocess_shell(
args,
# stdout must a pipe to be accessible as process.stdout
stdout=asyncio.subprocess.PIPE)
# Wait for the subprocess to finish
stdout, stderr = await process.communicate()
# Return stdout
return stdout.decode().strip()
class Paginator:
def __init__(self,
bot: discord.ext.commands.Bot,
*,
max_chars: int=1970,
max_lines: int=20,
prefix: str='```md',
suffix: str='```',
page_break: str='\uFFF8',
field_break: str='\uFFF7',
field_name_char: str='\uFFF6',
inline_char: str='\uFFF5',
max_line_length: int=100,
embed=False):
_max_len = 6000 if embed else 1980
assert 0 < max_lines <= max_chars
assert 0 < max_line_length < 120
self._parts = list()
self._prefix = prefix
self._suffix = suffix
self._max_chars = max_chars if max_chars + len(prefix) + len(suffix) + 2 <= _max_len \
else _max_len - len(prefix) - len(suffix) - 2
self._max_lines = max_lines - (prefix + suffix).count('\n') + 1
self._page_break = page_break
self._max_line_length = max_line_length
self._pages = list()
self._max_field_chars = 1014
self._max_field_name = 256
self._max_description = 2048
self._embed = embed
self._field_break = field_break
self._field_name_char = field_name_char
self._inline_char = inline_char
self._embed_title = ''
self._embed_description = ''
self._embed_color = None
self._embed_thumbnail = None
self._embed_url = None
self._bot = bot
def set_embed_meta(self, title: str='\uFFF0',
description: str='\uFFF0',
color: discord.Colour=None,
thumbnail: str=None,
url: str=None):
if len(title) <= self._max_field_name:
self._embed_title = title
else:
raise RuntimeError('Provided Title is too long')
if len(description) <= self._max_description:
self._embed_description = description
else:
raise RuntimeError('Provided Description is too long')
self._embed_color = color
self._embed_thumbnail = thumbnail
self._embed_url = url
def pages(self) -> typing.List[str]:
_pages = list()
_fields = list()
_page = ''
_lines = 0
_field_name = ''
_field_value = ''
_inline = False
def open_page():
nonlocal _page, _lines, _fields
if not self._embed:
_page = self._prefix
_lines = 0
else:
_fields = list()
def close_page():
nonlocal _page, _lines, _fields
if not self._embed:
_page += self._suffix
_pages.append(_page)
else:
if _fields:
_pages.append(_fields)
open_page()
open_page()
if not self._embed:
for part in [str(p) for p in self._parts]:
if part == self._page_break:
close_page()
new_chars = len(_page) + len(part)
if new_chars > self._max_chars:
close_page()
elif (_lines + (part.count('\n') + 1 or 1)) > self._max_lines:
close_page()
_lines += (part.count('\n') + 1 or 1)
_page += '\n' + part
else:
def open_field(name: str):
nonlocal _field_value, _field_name
_field_name = name
_field_value = self._prefix
def close_field(next_name: str=None):
nonlocal _field_name, _field_value, _fields
_field_value += self._suffix
if _field_value != self._prefix + self._suffix:
_fields.append({'name': _field_name, 'value': _field_value, 'inline': _inline})
if next_name:
open_field(next_name)
open_field('\uFFF0')
for part in [str(p) for p in self._parts]:
if part == self._page_break:
close_page()
continue
elif part == self._field_break:
if len(_fields) + 1 < 25:
close_field(next_name='\uFFF0')
else:
close_field()
close_page()
continue
if part.startswith(self._field_name_char):
part = part.replace(self._field_name_char, '')
if part.startswith(self._inline_char):
_inline = True
part = part.replace(self._inline_char, '')
else:
_inline = False
if _field_value and _field_value != self._prefix:
close_field(part)
else:
_field_name = part
continue
_field_value += '\n' + part
close_field()
close_page()
self._pages = _pages
return _pages
def process_pages(self) -> typing.List[str]:
_pages = self._pages or self.pages()
_len_pages = len(_pages)
_len_page_str = len(f'{_len_pages}/{_len_pages}')
if not self._embed:
for i, page in enumerate(_pages):
if len(page) + _len_page_str <= 2000:
_pages[i] = f'{i + 1}/{_len_pages}\n{page}'
else:
for i, page in enumerate(_pages):
em = discord.Embed(title=self._embed_title,
description=self._embed_description,
color=self._bot.embed_color,
)
if self._embed_thumbnail:
em.set_thumbnail(url=self._embed_thumbnail)
if self._embed_url:
em.url = self._embed_url
if self._embed_color:
em.color = self._embed_color
em.set_footer(text=f'{i + 1}/{_len_pages}')
for field in page:
em.add_field(name=field['name'], value=field['value'], inline=field['inline'])
_pages[i] = em
return _pages
def __len__(self):
return sum(len(p) for p in self._parts)
def __eq__(self, other):
# noinspection PyProtectedMember
return self.__class__ == other.__class__ and self._parts == other._parts
def add_page_break(self, *, to_beginning: bool=False) -> None:
self.add(self._page_break, to_beginning=to_beginning)
def add(self, item: typing.Any, *, to_beginning: bool=False, keep_intact: bool=False, truncate=False) -> None:
item = str(item)
i = 0
if not keep_intact and not item == self._page_break:
item_parts = item.strip('\n').split('\n')
for part in item_parts:
if len(part) > self._max_line_length:
if not truncate:
length = 0
out_str = ''
def close_line(line):
nonlocal i, out_str, length
self._parts.insert(i, out_str) if to_beginning else self._parts.append(out_str)
i += 1
out_str = line + ' '
length = len(out_str)
bits = part.split(' ')
for bit in bits:
next_len = length + len(bit) + 1
if next_len <= self._max_line_length:
out_str += bit + ' '
length = next_len
elif len(bit) > self._max_line_length:
if out_str:
close_line(line='')
for out_str in [bit[i:i + self._max_line_length]
for i in range(0, len(bit), self._max_line_length)]:
close_line('')
else:
close_line(bit)
close_line('')
else:
line = f'{part:.{self._max_line_length-3}}...'
self._parts.insert(i, line) if to_beginning else self._parts.append(line)
else:
self._parts.insert(i, part) if to_beginning else self._parts.append(part)
i += 1
elif keep_intact and not item == self._page_break:
if len(item) >= self._max_chars or item.count('\n') > self._max_lines:
raise RuntimeError('{item} is too long to keep on a single page and is marked to keep intact.')
if to_beginning:
self._parts.insert(0, item)
else:
self._parts.append(item)
else:
if to_beginning:
self._parts.insert(0, item)
else:
self._parts.append(item)
class Book:
def __init__(self, pag: Paginator, ctx: typing.Tuple[typing.Optional[discord.Message],
discord.TextChannel,
discord.ext.commands.Bot,
discord.Message]) -> None:
self._pages = pag.process_pages()
self._len_pages = len(self._pages)
self._current_page = 0
self._message, self._channel, self._bot, self._calling_message = ctx
self._locked = True
if pag == Paginator(self._bot):
raise RuntimeError('Cannot create a book out of an empty Paginator.')
def advance_page(self) -> None:
self._current_page += 1
if self._current_page >= self._len_pages:
self._current_page = 0
def reverse_page(self) -> None:
self._current_page += -1
if self._current_page < 0:
self._current_page = self._len_pages - 1
async def display_page(self) -> None:
if isinstance(self._pages[self._current_page], discord.Embed):
if self._message:
await self._message.edit(content=None, embed=self._pages[self._current_page])
else:
self._message = await self._channel.send(embed=self._pages[self._current_page])
else:
if self._message:
await self._message.edit(content=self._pages[self._current_page], embed=None)
else:
self._message = await self._channel.send(self._pages[self._current_page])
async def create_book(self) -> None:
# noinspection PyUnresolvedReferences
async def reaction_checker():
# noinspection PyShadowingNames
def check(reaction, user):
if self._locked:
return str(reaction.emoji) in self._bot.book_emojis.values() \
and user == self._calling_message.author \
and reaction.message.id == self._message.id
else:
return str(reaction.emoji) in self._bot.book_emojis.values() \
and reaction.message.id == self._message.id
await self.display_page()
if len(self._pages) > 1:
for emoji in self._bot.book_emojis.values():
try:
await self._message.add_reaction(emoji)
except (discord.Forbidden, KeyError):
pass
else:
try:
await self._message.add_reaction(self._bot.book_emojis['unlock'])
await self._message.add_reaction(self._bot.book_emojis['close'])
except (discord.Forbidden, KeyError):
pass
while True:
try:
reaction, user = await self._bot.wait_for('reaction_add', timeout=60, check=check)
except asyncio.TimeoutError:
try:
await self._message.clear_reactions()
except discord.Forbidden:
pass
raise asyncio.CancelledError
else:
await self._message.remove_reaction(reaction, user)
if str(reaction.emoji) == self._bot.book_emojis['close']:
await self._calling_message.delete()
await self._message.delete()
raise asyncio.CancelledError
elif str(reaction.emoji) == self._bot.book_emojis['forward']:
self.advance_page()
elif str(reaction.emoji) == self._bot.book_emojis['back']:
self.reverse_page()
elif str(reaction.emoji) == self._bot.book_emojis['end']:
self._current_page = self._len_pages - 1
elif str(reaction.emoji) == self._bot.book_emojis['start']:
self._current_page = 0
elif str(reaction.emoji) == self._bot.book_emojis['hash']:
m = await self._channel.send(f'Please enter a number in range 1 to {self._len_pages}')
def num_check(message):
if self._locked:
return message.content.isdigit() \
and 0 < int(message.content) <= self._len_pages \
and message.author == self._calling_message.author
else:
return message.content.isdigit() \
and 0 < int(message.content) <= self._len_pages
try:
msg = await self._bot.wait_for('message', timeout=30, check=num_check)
except asyncio.TimeoutError:
await m.edit(content='Message Timed out.')
else:
self._current_page = int(msg.content) - 1
try:
await m.delete()
await msg.delete()
except discord.Forbidden:
pass
elif str(reaction.emoji) == self._bot.book_emojis['unlock']:
self._locked = False
await self._message.remove_reaction(reaction, self._channel.guild.me)
continue
await self.display_page()
self._bot.loop.create_task(reaction_checker())
View File
-23
View File
@@ -1,23 +0,0 @@
import asyncpg
import asyncio
class DatabaseConnection:
def __init__(self, host: str='localhost', port: int=5432, database: str='', user: str='', password: str=''):
if user == '' or password == '' or database == '':
raise RuntimeError('Username or Password are blank')
self.kwargs = {'host': host, 'port': port, 'database': database, 'user': user, 'password': password}
self._conn = None
asyncio.get_event_loop().run_until_complete(self.acquire())
self.fetchval = self._conn.fetchval
self.execute = self._conn.execute
self.fetch = self._conn.fetch
self.fetchrow = self._conn.fetchrow
async def acquire(self):
if not self._conn:
self._conn = await asyncpg.create_pool(**self.kwargs)
async def close(self):
await self._conn.close()
self._conn = None