Compare commits
300
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
897c77fee6 | ||
|
|
9c340116eb | ||
|
|
52174175ef | ||
|
|
0858fbc7d5 | ||
|
|
3e75b20c54 | ||
|
|
3dc75dabe6 | ||
|
|
db525d0048 | ||
|
|
35ab455a7c | ||
|
|
66cbaffc7a | ||
|
|
71686b4cc2 | ||
|
|
7ac58a435c | ||
|
|
d596db870b | ||
|
|
69109dc3a9 | ||
|
|
c4a0398a60 | ||
|
|
90ca2d0fac | ||
|
|
4bf8649950 | ||
|
|
70545be638 | ||
|
|
d56fb072bb | ||
|
|
49894b2dd7 | ||
|
|
b4a5f83689 | ||
|
|
63b87bf1c7 | ||
|
|
dd96a9cf90 | ||
|
|
9c281c6d7f | ||
|
|
0c4fd467ff | ||
|
|
f11f53ac81 | ||
|
|
dc51b4e6b6 | ||
|
|
3531459b03 | ||
|
|
31a245a7db | ||
|
|
8c7f33c166 | ||
|
|
512588a34c | ||
|
|
7c3e9726f9 | ||
|
|
95c7bcb928 | ||
|
|
44c8ca1eb5 | ||
|
|
9d894a4438 | ||
|
|
2bdbdb9749 | ||
|
|
9b47aff4b0 | ||
|
|
54d5be24e8 | ||
|
|
d616a94709 | ||
|
|
f8fe207721 | ||
|
|
2259900dcd | ||
|
|
ec6cd95c9e | ||
|
|
d14de7a964 | ||
|
|
a77f976562 | ||
|
|
687c62dce7 | ||
|
|
778e9890ba | ||
|
|
b46d8dd366 | ||
|
|
46c9075397 | ||
|
|
677fc9a60e | ||
|
|
4c70c67f31 | ||
|
|
240d606f2b | ||
|
|
47815c7119 | ||
|
|
b672768ef4 | ||
|
|
576f070cf2 | ||
|
|
ca25f6e07a | ||
|
|
bf89d6f1ba | ||
|
|
f9b2fe0a25 | ||
|
|
5c60a9ec7a | ||
|
|
916e605a6e | ||
|
|
b9689b502d | ||
|
|
ee258bc62f | ||
|
|
eaa8511455 | ||
|
|
2537b8331a | ||
|
|
3791d205de | ||
|
|
011daf3063 | ||
|
|
4963d773e2 | ||
|
|
87566fffcd | ||
|
|
97d7dc7e25 | ||
|
|
b5c0e002ab | ||
|
|
d989591431 | ||
|
|
eab32f3b84 | ||
|
|
1dca5d41bb | ||
|
|
8b5af80bad | ||
|
|
340ab4b140 | ||
|
|
d9f4858485 | ||
|
|
499adbfced | ||
|
|
fc1a6c63d7 | ||
|
|
641cdb7150 | ||
|
|
643a7776df | ||
|
|
7797e8c771 | ||
|
|
d5c5801e3a | ||
|
|
7f37194574 | ||
|
|
98c84c8442 | ||
|
|
2773de1503 | ||
|
|
34183adcfb | ||
|
|
73e5a227a5 | ||
|
|
c2e6495be3 | ||
|
|
5447601cec | ||
|
|
19155577fa | ||
|
|
3f33154606 | ||
|
|
bbee079d2c | ||
|
|
4cd62e1e05 | ||
|
|
f7fab22d81 | ||
|
|
51ac2ba474 | ||
|
|
7d901f55f9 | ||
|
|
1c022ad214 | ||
|
|
f174b0cbfa | ||
|
|
71cf7a44a7 | ||
|
|
103f715969 | ||
|
|
b1574a96d7 | ||
|
|
feefff0a2b | ||
|
|
3d8c3b0d88 | ||
|
|
fc655767f7 | ||
|
|
222159f059 | ||
|
|
cb7290be00 | ||
|
|
88d0af40c5 | ||
|
|
681936b211 | ||
|
|
e83f283e60 | ||
|
|
377201f5ca | ||
|
|
83787333e5 | ||
|
|
34174cb148 | ||
|
|
35d383420f | ||
|
|
c52d708af9 | ||
|
|
d6b53bb07c | ||
|
|
a24749dd63 | ||
|
|
63006cd200 | ||
|
|
910686d80b | ||
|
|
6312d21b00 | ||
|
|
4c52d9eafc | ||
|
|
12db8a520d | ||
|
|
95fce3f636 | ||
|
|
8746cd0bcf | ||
|
|
59a7bb37c6 | ||
|
|
ab14f9da3c | ||
|
|
ee2ebe4274 | ||
|
|
714d5cd229 | ||
|
|
05536052e9 | ||
|
|
ab83f968b9 | ||
|
|
08d8f424be | ||
|
|
82cf5d9856 | ||
|
|
dc98e5eea3 | ||
|
|
4e8bee6785 | ||
|
|
61e774bc5a | ||
|
|
7344187eb1 | ||
|
|
a6273e6856 | ||
|
|
9d6216e344 | ||
|
|
b031adce62 | ||
|
|
38469d38b2 | ||
|
|
077a9e66b3 | ||
|
|
5edc0c98ef | ||
|
|
9799265c0a | ||
|
|
994e257026 | ||
|
|
4801daeafe | ||
|
|
8104302e43 | ||
|
|
0600b883fe | ||
|
|
3700ab2344 | ||
|
|
762984592b | ||
|
|
1c778e4a49 | ||
|
|
08c3d8a79c | ||
|
|
061c39a8eb | ||
|
|
0d45b8b827 | ||
|
|
8a68823f82 | ||
|
|
914ee90f70 | ||
|
|
39bf9c71fb | ||
|
|
b782bf681a | ||
|
|
789eee5b2e | ||
|
|
42388f4b63 | ||
|
|
6c4de0fb58 | ||
|
|
54d631df67 | ||
|
|
1102b54972 | ||
|
|
80a6c0c924 | ||
|
|
96ca3208e8 | ||
|
|
bfa9d7c1ef | ||
|
|
4945ac630b | ||
|
|
5ed3152dc9 | ||
|
|
b56e57d939 | ||
|
|
2b41489bd6 | ||
|
|
1434ac36c4 | ||
|
|
dd1e0d8812 | ||
|
|
679a49d9e4 | ||
|
|
c7532b6ed7 | ||
|
|
84a7734f39 | ||
|
|
cedf030345 | ||
|
|
cd79629340 | ||
|
|
999ebae413 | ||
|
|
127bd483b2 | ||
|
|
bb46a28a1a | ||
|
|
2fdac0b854 | ||
|
|
73ce5de59f | ||
|
|
0fc8d6644d | ||
|
|
d5e4a5dbd7 | ||
|
|
55fb9ae3e7 | ||
|
|
70dbc350c2 | ||
|
|
cf729b094b | ||
|
|
43477595b2 | ||
|
|
f188a145eb | ||
|
|
ca5b862852 | ||
|
|
e2252f7140 | ||
|
|
ea177a3d71 | ||
|
|
73503c9199 | ||
|
|
02f189781f | ||
|
|
352470e364 | ||
|
|
cc9a934fe6 | ||
|
|
70535d0399 | ||
|
|
ec5d1bd1da | ||
|
|
62a58bc1ec | ||
|
|
6e5ad99253 | ||
|
|
fecff7925c | ||
|
|
bb7240689b | ||
|
|
c924412e67 | ||
|
|
c0cb5568d7 | ||
|
|
9dbcff12b4 | ||
|
|
efc73a0119 | ||
|
|
a4dca2de21 | ||
|
|
51d3f32055 | ||
|
|
7949707a3c | ||
|
|
5e5f50bcc0 | ||
|
|
996d7a3ea3 | ||
|
|
7b3b953fea | ||
|
|
8c702a854d | ||
|
|
47ca65da91 | ||
|
|
249e5d94eb | ||
|
|
f992d85d26 | ||
|
|
469cd5a4b1 | ||
|
|
960fbfbcd2 | ||
|
|
fac236ed04 | ||
|
|
b42d0ef35a | ||
|
|
09f3aa538d | ||
|
|
d8e28af901 | ||
|
|
69a0475edd | ||
|
|
b6f3125d30 | ||
|
|
336c8a6906 | ||
|
|
5146c01ba9 | ||
|
|
5b6c94b16f | ||
|
|
fa1c789b40 | ||
|
|
1c04e9fe1a | ||
|
|
e9874bd060 | ||
|
|
05b4bd184f | ||
|
|
b4fd2e5d54 | ||
|
|
5df671a4b1 | ||
|
|
c53fd11789 | ||
|
|
9a82390682 | ||
|
|
3e1f2290cb | ||
|
|
d413f52a5f | ||
|
|
f4962cef77 | ||
|
|
48cdea96ee | ||
|
|
bd34de573c | ||
|
|
c7e0fe017c | ||
|
|
d55b889a3a | ||
|
|
c0834b3ab4 | ||
|
|
f7c4a9f04b | ||
|
|
09b87e8278 | ||
|
|
d37fb816bd | ||
|
|
5924e0f39d | ||
|
|
3da8b579a4 | ||
|
|
b73a2a5016 | ||
|
|
2d345e6891 | ||
|
|
5a2fb878f3 | ||
|
|
7b3a130c91 | ||
|
|
e435b0cbb9 | ||
|
|
dcc61f6546 | ||
|
|
5916f3470c | ||
|
|
ab760af838 | ||
|
|
3d7e32b2d1 | ||
|
|
7fa35bf478 | ||
|
|
c052248cfe | ||
|
|
081b62d267 | ||
|
|
bfe7f5835d | ||
|
|
e62e2c252f | ||
|
|
3dde1eabd4 | ||
|
|
450783b785 | ||
|
|
618c3acf84 | ||
|
|
baaa252c7e | ||
|
|
774f95a70e | ||
|
|
c4148f2cc0 | ||
|
|
d797203a7b | ||
|
|
c93043bf91 | ||
|
|
0c48f345b7 | ||
|
|
9bde33b3c3 | ||
|
|
3bff3e8276 | ||
|
|
d27d85df19 | ||
|
|
2ba7f09065 | ||
|
|
9f0997c492 | ||
|
|
f6864ddc0a | ||
|
|
9b9a4e8227 | ||
|
|
c4fee255dc | ||
|
|
a004c9c70c | ||
|
|
f939df6bc0 | ||
|
|
54eb8abfd6 | ||
|
|
5495aa213e | ||
|
|
dd5dab317d | ||
|
|
fc4529bcba | ||
|
|
9cc1004f38 | ||
|
|
11ac36d5fd | ||
|
|
13d4dc564c | ||
|
|
4958e6ea02 | ||
|
|
6244f8cb77 | ||
|
|
34ed2717d4 | ||
|
|
ab49f3a469 | ||
|
|
da615a54ea | ||
|
|
a38cdfb6e1 | ||
|
|
9f552d76eb | ||
|
|
9f849bdf29 | ||
|
|
005f4ca25d | ||
|
|
6d8f3531a7 | ||
|
|
9ef06824a7 | ||
|
|
0654bd51eb | ||
|
|
3a43e18c61 | ||
|
|
6d49bcea36 | ||
|
|
61f632a359 | ||
|
|
76574f94a7 |
Binary file not shown.
+90
-1
@@ -1,4 +1,93 @@
|
||||
bot_secrets.json
|
||||
google_client_secret.json
|
||||
__pycache__/
|
||||
logs/*
|
||||
*.sh.swp
|
||||
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
cmake-build-release/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
Generated
+1
-4
@@ -4,11 +4,8 @@
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="R User Library" level="project" />
|
||||
<orderEntry type="library" name="R Skeletons" level="application" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="projectConfiguration" value="py.test" />
|
||||
<option name="PROJECT_TEST_RUNNER" value="py.test" />
|
||||
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+5
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
Generated
-7
@@ -1,7 +0,0 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="Dustin.Pianalto">
|
||||
<words>
|
||||
<w>rcon</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
Generated
+1
-1
@@ -3,5 +3,5 @@
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (Geeksbot)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
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,93 +0,0 @@
|
||||
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()
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
until python /home/dusty/bin/geeksbot/geeksbot.py; do
|
||||
until python -m src; do
|
||||
echo "Geeksbot shutdown with error: $?. Restarting..." >&2
|
||||
sleep 1
|
||||
done
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
@checks.no_bots()
|
||||
@commands.cooldown(1,5,commands.BucketType.user)
|
||||
@commands.command()
|
||||
async def captcha(self, ctx, type, *, text):
|
||||
type = type.lower()
|
||||
if type not in "checked unchecked loading".split():
|
||||
raise commands.BadArgument(f"Invalid type {type!r}. Available "
|
||||
"types: `unchecked`, `loading`, `checked`")
|
||||
font = ImageFont.truetype("Roboto-Regular.ttf", 14)
|
||||
async with ctx.typing():
|
||||
img = Image.open(f"blank-captcha-{type}.png")
|
||||
img.load()
|
||||
d = ImageDraw.Draw(img)
|
||||
fnc = functools.partial(d.text, (53,30), text, fill=(0,0,0,255),
|
||||
font=font)
|
||||
await self.bot.loop.run_in_executor(None, fnc)
|
||||
img.save("captcha.png")
|
||||
await ctx.send(file=discord.File("captcha.png"))
|
||||
os.system("rm captcha.png")
|
||||
img.close()
|
||||
|
||||
|
||||
import functools, youtube_dl
|
||||
#bot.voice_chan = await ctx.author.voice.channel.connect()
|
||||
bot.voice_chan.stop()
|
||||
opts = {"format": 'webm[abr>0]/bestaudio/best',"ignoreerrors": True,"default_search": "auto","source_address": "0.0.0.0",'quiet': True}
|
||||
ydl = youtube_dl.YoutubeDL(opts)
|
||||
url = 'https://www.youtube.com/watch?v=hjbPszSt5Pc'
|
||||
func = functools.partial(ydl.extract_info, url, download=False)
|
||||
info = func()
|
||||
#bot.voice_chan.play(discord.FFmpegPCMAudio('dead_puppies.mp3'))
|
||||
bot.voice_chan.play(discord.FFmpegPCMAudio(info['url']))
|
||||
#async while bot.voice_chan.is_playing():
|
||||
# pass
|
||||
#await bot.voice_chan.disconnect()
|
||||
@@ -1,13 +1,15 @@
|
||||
from typing import Dict
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import json
|
||||
import aiohttp
|
||||
from postgres import Postgres
|
||||
from collections import deque
|
||||
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}'
|
||||
date_format = '%Y.%m.%d %H.%M.%S'
|
||||
@@ -23,7 +25,7 @@ formatter = logging.Formatter(log_format, style='{', datefmt=date_format)
|
||||
console_handler.setFormatter(formatter)
|
||||
logging.getLogger('').addHandler(console_handler)
|
||||
|
||||
config_dir = 'config/'
|
||||
config_dir = 'src/config/'
|
||||
admin_id_file = 'admin_ids'
|
||||
extension_dir = 'exts'
|
||||
owner_id = 351794468870946827
|
||||
@@ -35,48 +37,70 @@ emojis: Dict[str, str] = {
|
||||
'x': '❌',
|
||||
'y': '✅',
|
||||
'poop': '💩',
|
||||
'boom': '💥',
|
||||
}
|
||||
|
||||
description = 'I am Geeksbot v0.1! Fear me!'
|
||||
|
||||
|
||||
class Geeksbot(commands.Bot):
|
||||
def __init__(self, **kwargs):
|
||||
kwargs["command_prefix"] = self.get_custom_prefix
|
||||
self.description = 'I am Geeksbot! Fear me!'
|
||||
kwargs['description'] = self.description
|
||||
super().__init__(**kwargs)
|
||||
self.aio_session = aiohttp.ClientSession(loop=self.loop)
|
||||
with open(f'{config_dir}{bot_config_file}') as file:
|
||||
self.bot_config = json.load(file)
|
||||
with open(f'{config_dir}{secrets_file}') as 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.infected = {}
|
||||
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']
|
||||
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.db_con = database.DatabaseConnection(**self.bot_secrets['db_con'])
|
||||
self.default_prefix = 'g$'
|
||||
self.voice_chans = {}
|
||||
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.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 get_custom_prefix(self, bot_inst, message):
|
||||
return self.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': message.guild.id})\
|
||||
or self.default_prefix
|
||||
async def logout(self):
|
||||
await self.db_con.close()
|
||||
super().logout()
|
||||
|
||||
@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):
|
||||
self.load_extension('{0}.{1}'.format(extension_dir, mod))
|
||||
self.load_extension('src.{0}.{1}'.format(extension_dir, mod))
|
||||
if ctx is not None:
|
||||
await ctx.send('{0} loaded.'.format(mod))
|
||||
|
||||
async def unload_ext(self, ctx, mod=None):
|
||||
self.unload_extension('{0}.{1}'.format(extension_dir, mod))
|
||||
self.unload_extension('src.{0}.{1}'.format(extension_dir, mod))
|
||||
if ctx is not None:
|
||||
await ctx.send('{0} unloaded.'.format(mod))
|
||||
|
||||
@@ -85,7 +109,7 @@ class Geeksbot(commands.Bot):
|
||||
self.aio_session.close() # aiohttp is drunk and can't decide if it's a coro or not
|
||||
|
||||
|
||||
bot = Geeksbot(description=description, case_insensitive=True)
|
||||
bot = Geeksbot(case_insensitive=True)
|
||||
|
||||
|
||||
@bot.command(hidden=True)
|
||||
@@ -120,14 +144,14 @@ async def unload(ctx, mod):
|
||||
async def on_message(ctx):
|
||||
if not ctx.author.bot:
|
||||
if ctx.guild:
|
||||
if int(bot.con.one(f"select channel_lockdown from guild_config where guild_id = %(id)s",
|
||||
{'id': ctx.guild.id})):
|
||||
if ctx.channel.id in json.loads(bot.con.one(f"select allowed_channels from guild_config "
|
||||
f"where guild_id = %(id)s",
|
||||
{'id': ctx.guild.id})):
|
||||
if int(await bot.db_con.fetchval("select channel_lockdown from guild_config where guild_id = $1",
|
||||
ctx.guild.id)):
|
||||
if ctx.channel.id in json.loads(await bot.db_con.fetchval("select allowed_channels from guild_config "
|
||||
"where guild_id = $1",
|
||||
ctx.guild.id)):
|
||||
await bot.process_commands(ctx)
|
||||
elif ctx.channel.id == 418452585683484680:
|
||||
prefix = bot.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||
prefix = await bot.db_con.fetchval('select prefix from guild_config where guild_id = $1', ctx.guild.id)
|
||||
prefix = prefix[0] if prefix else bot.default_prefix
|
||||
ctx.content = f'{prefix}{ctx.content}'
|
||||
await bot.process_commands(ctx)
|
||||
@@ -139,20 +163,19 @@ async def on_message(ctx):
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
bot.remove_command('help')
|
||||
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))
|
||||
load_list = bot.bot_config['load_list']
|
||||
for load_item in load_list:
|
||||
await bot.load_ext(None, f'{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:
|
||||
reboot = f.readlines()
|
||||
if int(reboot[0]) == 1:
|
||||
await bot.get_channel(int(reboot[1])).send('Restart Finished.')
|
||||
with open(f'{config_dir}reboot', 'w') as f:
|
||||
f.write(f'0')
|
||||
logging.info('Done loading, Geeksbot is active.')
|
||||
|
||||
bot.run(bot.TOKEN)
|
||||
@@ -4,11 +4,10 @@ import json
|
||||
import logging
|
||||
import inspect
|
||||
import os
|
||||
from .imports import checks
|
||||
from src.imports import checks, utils
|
||||
|
||||
config_dir = 'config/'
|
||||
config_dir = 'src/config/'
|
||||
admin_id_file = 'admin_ids'
|
||||
extension_dir = 'extensions'
|
||||
owner_id = 351794468870946827
|
||||
embed_color = discord.Colour.from_rgb(49, 107, 111)
|
||||
bot_config_file = 'bot_config.json'
|
||||
@@ -52,6 +51,7 @@ class Admin:
|
||||
await ctx.send('Geeksbot is restarting.')
|
||||
with open(f'{config_dir}reboot', 'w') as f:
|
||||
f.write(f'1\n{ctx.channel.id}')
|
||||
# noinspection PyProtectedMember
|
||||
os._exit(1)
|
||||
|
||||
@commands.command(hidden=True)
|
||||
@@ -72,20 +72,19 @@ class Admin:
|
||||
emoji_code = f'<a:{emoji.name}:{emoji.id}>'
|
||||
else:
|
||||
emoji_code = f'<:{emoji.name}:{emoji.id}>'
|
||||
if self.bot.con.all('select id from geeksbot_emojis where id = %(id)s', {'id': emoji.id}):
|
||||
self.bot.con.run("update geeksbot_emojis set id = %(id)s, name = %(name)s, code = %(emoji_code)s "
|
||||
"where name = %(name)s",
|
||||
{'name': emoji.name, 'id': emoji.id, 'emoji_code': emoji_code})
|
||||
if await self.bot.db_con.fetch('select id from geeksbot_emojis where id = $1', emoji.id):
|
||||
await self.bot.db_con.execute("update geeksbot_emojis set id = $2, name = $1, code = $3 "
|
||||
"where name = $1", emoji.name, emoji.id, emoji_code)
|
||||
else:
|
||||
self.bot.con.run("insert into geeksbot_emojis(id,name,code) values (%(id)s,%(name)s,%(emoji_code)s)",
|
||||
{'name': emoji.name, 'id': emoji.id, 'emoji_code': emoji_code})
|
||||
await self.bot.db_con.execute("insert into geeksbot_emojis(id,name,code) values ($2,$1,$3)",
|
||||
emoji.name, emoji.id, emoji_code)
|
||||
await ctx.message.add_reaction('✅')
|
||||
await ctx.send(f'Emojis have been updated in the database.')
|
||||
|
||||
@commands.command(hidden=True)
|
||||
@commands.check(checks.is_guild_owner)
|
||||
async def get_guild_config(self, ctx):
|
||||
config = self.bot.con.one('select * from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||
config = await self.bot.db_con.fetchrow('select * from guild_config where guild_id = $1', ctx.guild.id)
|
||||
configs = [str(config)[i:i+1990] for i in range(0, len(config), 1990)]
|
||||
await ctx.message.author.send(f'The current config for the {ctx.guild.name} guild is:\n')
|
||||
admin_log.info(configs)
|
||||
@@ -95,45 +94,49 @@ class Admin:
|
||||
|
||||
@commands.group(case_insensitive=True)
|
||||
async def set(self, ctx):
|
||||
"""Run help set for more info"""
|
||||
"""Group for setting configuration options"""
|
||||
pass
|
||||
|
||||
@commands.group(case_insensitive=True)
|
||||
async def add(self, ctx):
|
||||
"""Run help set for more info"""
|
||||
"""Group for adding items to guild config"""
|
||||
pass
|
||||
|
||||
@commands.group(case_insensitive=True)
|
||||
async def remove(self, ctx):
|
||||
"""Run help set for more info"""
|
||||
"""Group for removing items from guild config"""
|
||||
pass
|
||||
|
||||
@set.command(name='admin_chan', aliases=['ac', 'admin_chat', 'admin chat'])
|
||||
async def _admin_channel(self, ctx, channel: discord.TextChannel=None):
|
||||
"""Sets the admin notification channel"""
|
||||
if ctx.guild:
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
if channel is not None:
|
||||
self.bot.con.run('update guild_config set admin_chat = %(chan)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'chan': channel.id})
|
||||
await self.bot.db_con.execute('update guild_config set admin_chat = $2 where guild_id = $1',
|
||||
ctx.guild.id, channel.id)
|
||||
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'])
|
||||
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 checks.is_admin(self.bot, ctx):
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
if str(config).lower() == 'true':
|
||||
if self.bot.con.one('select allowed_channels from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id}) is []:
|
||||
if await self.bot.db_con.fetchval('select allowed_channels from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id) is []:
|
||||
await ctx.send('Please set at least one allowed channel before running this command.')
|
||||
else:
|
||||
self.bot.con.run('update guild_config set channel_lockdown = True where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
await self.bot.db_con.execute('update guild_config set channel_lockdown = True '
|
||||
'where guild_id = $1', ctx.guild.id)
|
||||
await ctx.send('Channel Lockdown is now active.')
|
||||
elif str(config).lower() == 'false':
|
||||
if self.bot.con.one('select channel_lockdown from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id}):
|
||||
self.bot.con.run('update guild_config set channel_lockdown = False where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
if await self.bot.db_con.fetchval('select channel_lockdown from guild_config where guild_id = $1',
|
||||
ctx.guild.id):
|
||||
await self.bot.db_con.execute('update guild_config set channel_lockdown = False '
|
||||
'where guild_id = $1', ctx.guild.id)
|
||||
await ctx.send('Channel Lockdown has been deactivated.')
|
||||
else:
|
||||
await ctx.send('Channel Lockdown is already deactivated.')
|
||||
@@ -144,59 +147,74 @@ class Admin:
|
||||
|
||||
@add.command(name='allowed_channels', aliases=['channel', 'ac'])
|
||||
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 checks.is_admin(self.bot, ctx):
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
channels = channels.lower().replace(' ', '').split(',')
|
||||
added = ''
|
||||
for channel in channels:
|
||||
chnl = discord.utils.get(ctx.guild.channels, name=channel)
|
||||
if chnl is None:
|
||||
await ctx.send(f'{channel} is not a valid text channel in this guild.')
|
||||
else:
|
||||
admin_log.info('Chan found')
|
||||
if self.bot.con.one('select allowed_channels from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id}):
|
||||
if chnl.id in json.loads(self.bot.con.one('select allowed_channels from guild_config '
|
||||
'where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})):
|
||||
admin_log.info('Chan found in config')
|
||||
await ctx.send(f'{channel} is already in the list of allowed channels. Skipping...')
|
||||
else:
|
||||
admin_log.info('Chan not found in config')
|
||||
allowed_channels = json.loads(self.bot.con.one('select allowed_channels from '
|
||||
'guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})).append(chnl.id)
|
||||
self.bot.con.run('update guild_config set allowed_channels = %(channels)s '
|
||||
'where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'channels': allowed_channels})
|
||||
added = f'{added}\n{channel}'
|
||||
else:
|
||||
admin_log.info('Chan not found in config')
|
||||
allowed_channels = [chnl.id]
|
||||
self.bot.con.run('update guild_config set allowed_channels = %(channels)s '
|
||||
'where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'channels': allowed_channels})
|
||||
added = f'{added}\n{channel}'
|
||||
if added != '':
|
||||
await ctx.send(f'The following channels have been added to the allowed channel list: {added}')
|
||||
existing_channels = list()
|
||||
channels_add = list()
|
||||
admin_log.info(channels)
|
||||
allowed_channels = await self.bot.db_con.fetchval('select allowed_channels from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id)
|
||||
if allowed_channels == 'null':
|
||||
allowed_channels = None
|
||||
|
||||
channels = [discord.utils.get(ctx.guild.channels, name=channel)
|
||||
for channel in channels if channel is not None]
|
||||
|
||||
if allowed_channels and channels:
|
||||
allowed_channels = [int(channel) for channel in json.loads(allowed_channels)]
|
||||
existing_channels = [channel for channel in channels if channel.id in allowed_channels]
|
||||
channels_add = [channel for channel in channels if channel.id not in allowed_channels]
|
||||
allowed_channels += [channel.id for channel in channels if channel.id not in allowed_channels]
|
||||
await self.bot.db_con.execute('update guild_config set allowed_channels = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(allowed_channels))
|
||||
elif channels:
|
||||
admin_log.info('Config is empty')
|
||||
allowed_channels = [channel.id for channel in channels]
|
||||
await self.bot.db_con.execute('update guild_config set allowed_channels = $2 '
|
||||
'where guild_id = $1', ctx.guild.id,
|
||||
json.dumps(allowed_channels))
|
||||
else:
|
||||
await ctx.send('None of those are valid text channels for this guild.')
|
||||
return
|
||||
|
||||
if existing_channels:
|
||||
channel_str = '\n'.join([str(channel.name) for channel in existing_channels])
|
||||
await ctx.send(f'The following channels were skipped because they are already in the config:\n'
|
||||
f'{channel_str}\n')
|
||||
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('✅')
|
||||
else:
|
||||
await ctx.send(f'You are not authorized to run this command.')
|
||||
else:
|
||||
await ctx.send('This command must be run from inside a guild.')
|
||||
|
||||
@commands.command()
|
||||
# TODO Fix view_code
|
||||
@commands.command(hidden=True)
|
||||
@commands.is_owner()
|
||||
async def view_code(self, ctx, code_name):
|
||||
await ctx.send(f"```py\n{inspect.getsource(self.bot.get_command(code_name).callback)}\n```")
|
||||
pag = utils.Paginator(self.bot, prefix='```py', suffix='```')
|
||||
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'])
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||
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 checks.is_admin(self.bot, ctx):
|
||||
prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
prefixes = await self.bot.db_con.fetchval('select prefix from guild_config where guild_id = $1',
|
||||
ctx.guild.id)
|
||||
if prefix is None:
|
||||
await ctx.send(prefixes)
|
||||
return
|
||||
@@ -208,8 +226,8 @@ class Admin:
|
||||
if len(prefixes) > 10:
|
||||
await ctx.send(f'Only 10 prefixes are allowed per guild.\nPlease remove some before adding more.')
|
||||
prefixes = prefixes[:10]
|
||||
self.bot.con.run('update guild_config set prefix = %(prefixes)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'prefixes': prefixes})
|
||||
await self.bot.db_con.execute('update guild_config set prefix = $2 where guild_id = $1',
|
||||
ctx.guild.id, prefixes)
|
||||
await ctx.guild.me.edit(nick=f'[{prefixes[0]}] Geeksbot')
|
||||
await ctx.send(f"Updated. You currently have {len(prefixes)} "
|
||||
f"{'prefix' if len(prefixes) == 1 else 'prefixes'} "
|
||||
@@ -222,10 +240,13 @@ class Admin:
|
||||
@remove.command(aliases=['prefix', 'p'])
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||
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 checks.is_admin(self.bot, ctx):
|
||||
prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
prefixes = await self.bot.db_con.fetchval('select prefix from guild_config where guild_id = $1',
|
||||
ctx.guild.id)
|
||||
found = 0
|
||||
if prefix is None:
|
||||
await ctx.send(prefixes)
|
||||
@@ -242,8 +263,8 @@ class Admin:
|
||||
else:
|
||||
await ctx.send(f'The prefix {p} is not in the config for this guild.')
|
||||
if found:
|
||||
self.bot.con.run('update guild_config set prefix = %(prefixes)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'prefixes': prefixes})
|
||||
await self.bot.db_con.execute('update guild_config set prefix = $2 where guild_id = $1',
|
||||
ctx.guild.id, prefixes)
|
||||
await ctx.guild.me.edit(nick=f'[{prefixes[0] if len(prefixes) != 0 else self.bot.default_prefix}] '
|
||||
f'Geeksbot')
|
||||
await ctx.send(f"Updated. You currently have {len(prefixes)} "
|
||||
@@ -258,16 +279,19 @@ class Admin:
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||
@commands.check(checks.is_guild_owner)
|
||||
async def _add_admin_role(self, ctx, role=None):
|
||||
"""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)
|
||||
if role is not None:
|
||||
roles = json.loads(self.bot.con.one('select admin_roles from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id}))
|
||||
roles = json.loads(await self.bot.db_con.fetchval('select admin_roles from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
if role.name in roles:
|
||||
await ctx.send(f'{role.name} is already registered as an admin role in this guild.')
|
||||
else:
|
||||
roles[role.name] = role.id
|
||||
self.bot.con.run('update guild_config set admin_roles = %(roles)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'roles': json.dumps(roles)})
|
||||
await self.bot.db_con.execute('update guild_config set admin_roles = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(roles))
|
||||
await ctx.send(f'{role.name} has been added to the list of admin roles for this guild.')
|
||||
else:
|
||||
await ctx.send('You must include a role with this command.')
|
||||
@@ -276,14 +300,15 @@ class Admin:
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.guild)
|
||||
@commands.check(checks.is_guild_owner)
|
||||
async def _remove_admin_role(self, ctx, role=None):
|
||||
"""Removes role from admin list in current guild"""
|
||||
role = discord.utils.get(ctx.guild.roles, name=role)
|
||||
if role is not None:
|
||||
roles = json.loads(self.bot.con.one('select admin_roles from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id}))
|
||||
roles = json.loads(await self.bot.db_con.fetchval('select admin_roles from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
if role.name in roles:
|
||||
del roles[role.name]
|
||||
self.bot.con.run('update guild_config set admin_roles = %(roles)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'roles': roles})
|
||||
await self.bot.db_con.execute('update guild_config set admin_roles = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(roles))
|
||||
await ctx.send(f'{role.name} has been removed from the list of admin roles for this guild.')
|
||||
else:
|
||||
await ctx.send(f'{role.name} is not registered as an admin role in this guild.')
|
||||
@@ -1,9 +1,10 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import json
|
||||
import re
|
||||
from .imports import utils
|
||||
from src.imports import utils
|
||||
|
||||
config_dir = 'config/'
|
||||
admin_id_file = 'admin_ids'
|
||||
@@ -50,17 +51,20 @@ class BotEvents:
|
||||
config_str = f'{config_str}\n{" "*4}{config}: {guild_config[config]}'
|
||||
return config_str
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
async def on_raw_message_delete(self, msg_id, chan_id):
|
||||
self.bot.con.run('update messages set deleted_at = %(time)s where id = %(id)s',
|
||||
{'time': datetime.utcnow(), 'id': msg_id})
|
||||
await self.bot.db_con.execute('update messages set deleted_at = $1 where id = $2',
|
||||
datetime.utcnow(), msg_id)
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
async def on_raw_bulk_message_delete(self, msg_ids, chan_id):
|
||||
sql = ''
|
||||
del_time = datetime.utcnow()
|
||||
for msg_id in msg_ids:
|
||||
sql += f';update messages set deleted_at = %(time)s where id = {msg_id}'
|
||||
self.bot.con.run(sql, {'time': datetime.utcnow()})
|
||||
await self.bot.db_con.execute('update messages set deleted_at = $1 where id = $2',
|
||||
del_time, msg_id)
|
||||
|
||||
async def on_message(self, ctx):
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
if ctx.author in self.bot.infected:
|
||||
if datetime.now().timestamp() > self.bot.infected[ctx.author][1] + 300:
|
||||
@@ -73,36 +77,22 @@ class BotEvents:
|
||||
sql = 'insert into messages (id, tts, type, content, embeds, channel, mention_everyone, mentions,\
|
||||
channel_mentions, role_mentions, webhook, attachments, pinned, reactions, guild, created_at,\
|
||||
system_content, author) \
|
||||
values (%(id)s, %(tts)s, %(type)s, %(content)s, %(embeds)s, %(channel)s, %(mention_everyone)s, %(mentions)s,\
|
||||
%(channel_mentions)s, %(role_mentions)s, %(webhook)s, %(attachments)s, %(pinned)s, %(reactions)s, %(guild)s,\
|
||||
%(created_at)s, %(system_content)s, %(author)s)'
|
||||
msg_data = dict()
|
||||
msg_data['id'] = ctx.id
|
||||
msg_data['tts'] = ctx.tts
|
||||
msg_data['type'] = str(ctx.type)
|
||||
msg_data['content'] = ctx.content
|
||||
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)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)'
|
||||
msg_data = [ctx.id, ctx.tts, str(ctx.type), ctx.content, [json.dumps(e.to_dict()) for e in ctx.embeds],
|
||||
ctx.channel.id, ctx.mention_everyone, [user.id for user in ctx.mentions],
|
||||
[channel.id for channel in ctx.channel_mentions], [role.id for role in ctx.role_mentions],
|
||||
ctx.webhook_id, [json.dumps({'id': a.id, 'size': a.size, 'height': a.height, 'width': a.width,
|
||||
'filename': a.filename, 'url': a.url}) for a in ctx.attachments],
|
||||
ctx.pinned, [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions],
|
||||
ctx.guild.id if ctx.guild else ctx.author.id, ctx.created_at, ctx.system_content, ctx.author.id]
|
||||
await self.bot.db_con.execute(sql, *msg_data)
|
||||
if ctx.guild:
|
||||
if ctx.author != ctx.guild.me:
|
||||
if self.bot.con.one(f"select pg_filter from guild_config where guild_id = {ctx.guild.id}"):
|
||||
if await self.bot.db_con.fetchval("select pg_filter from guild_config where guild_id = $1",
|
||||
ctx.guild.id):
|
||||
profane = 0
|
||||
for word in self.bot.con.one('select profane_words from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id}):
|
||||
for word in await self.bot.db_con.fetchval('select profane_words from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id):
|
||||
word = word.strip()
|
||||
if word in ctx.content.lower():
|
||||
events_log.info(f'Found non PG word {word}')
|
||||
@@ -129,57 +119,44 @@ class BotEvents:
|
||||
await react.message.channel.send(f"You can't Poop on me {user.mention} :P")
|
||||
reactions = react.message.reactions
|
||||
reacts = [json.dumps({'emoji': r.emoji, 'count': r.count}) for r in reactions]
|
||||
self.bot.con.run('update messages set reactions = %(reacts)s where id = %(id)s',
|
||||
{'id': react.message.id, 'reacts': reacts})
|
||||
await self.bot.db_con.execute('update messages set reactions = $2 where id = $1',
|
||||
react.message.id, reacts)
|
||||
|
||||
async def on_message_edit(self, before, ctx):
|
||||
previous_content = self.bot.con.one('select previous_content from messages where id = %(id)s', {'id': ctx.id})
|
||||
previous_content = await self.bot.db_con.fetchval('select previous_content from messages where id = $1', ctx.id)
|
||||
if previous_content:
|
||||
previous_content.append(before.content)
|
||||
else:
|
||||
previous_content = [before.content]
|
||||
previous_embeds = self.bot.con.one('select previous_embeds from messages where id = %(id)s', {'id': ctx.id})
|
||||
previous_embeds = await self.bot.db_con.fetchval('select previous_embeds from messages where id = $1', ctx.id)
|
||||
if previous_embeds:
|
||||
previous_embeds.append([json.dumps(e.to_dict()) for e in before.embeds])
|
||||
else:
|
||||
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, channel, mention_everyone, mentions, channel_mentions, role_mentions, webhook,\
|
||||
attachments, pinned, reactions, guild, created_at, system_content, author) \
|
||||
= (%(edited_at)s, %(previous_content)s, %(previous_embeds)s, %(tts)s, %(type)s, %(content)s,\
|
||||
%(embeds)s, %(channel)s, %(mention_everyone)s, %(mentions)s, %(channel_mentions)s, %(role_mentions)s,\
|
||||
%(webhook)s, %(attachments)s, %(pinned)s, %(reactions)s, %(guild)s, %(created_at)s, %(system_content)s,\
|
||||
%(author)s) where id = %(id)s'
|
||||
msg_data = dict()
|
||||
msg_data['id'] = ctx.id
|
||||
msg_data['tts'] = ctx.tts
|
||||
msg_data['type'] = str(ctx.type)
|
||||
msg_data['content'] = ctx.content
|
||||
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'] = 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)
|
||||
sql = 'update messages set (edited_at, previous_content, previous_embeds, tts, type, content, embeds, ' \
|
||||
'channel, mention_everyone, mentions, channel_mentions, role_mentions, webhook, 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)' \
|
||||
'where id = $21'
|
||||
msg_data = [datetime.utcnow(), previous_content, previous_embeds, ctx.tts, str(ctx.type), ctx.content,
|
||||
[json.dumps(e.to_dict()) for e in ctx.embeds], ctx.channel.id, ctx.mention_everyone,
|
||||
[user.id for user in ctx.mentions], [channel.id for channel in ctx.channel_mentions],
|
||||
[role.id for role in ctx.role_mentions], ctx.webhook_id,
|
||||
[json.dumps({'id': a.id, 'size': a.size, 'height': a.height, 'width': a.width,
|
||||
'filename': a.filename, 'url': a.url}) for a in ctx.attachments], ctx.pinned,
|
||||
[json.dumps({'emoji': r.emoji, 'count': r.count}) for r in ctx.reactions], ctx.guild.id,
|
||||
ctx.created_at, ctx.system_content, ctx.author.id, ctx.id]
|
||||
await self.bot.db_con.execute(sql, *msg_data)
|
||||
|
||||
@staticmethod
|
||||
async def on_command_error(ctx, error):
|
||||
if ctx.channel.id == 418452585683484680 and type(error) == discord.ext.commands.errors.CommandNotFound:
|
||||
return
|
||||
for page in utils.paginate(error):
|
||||
await ctx.send(page)
|
||||
# noinspection PyMethodMayBeStatic
|
||||
async def on_command_error(self, ctx, error):
|
||||
pag = utils.Paginator(ctx.bot, embed=True, max_line_length=48)
|
||||
pag.set_embed_meta(title=f'Command Error',
|
||||
color=self.bot.error_color,
|
||||
thumbnail=f'{ctx.guild.me.avatar_url}')
|
||||
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):
|
||||
with open(f"{config_dir}{default_guild_config_file}", 'r') as file:
|
||||
@@ -189,31 +166,25 @@ class BotEvents:
|
||||
default_config['name'] = guild.name.replace("'", "\\'")
|
||||
default_config['guild_id'] = guild.id
|
||||
events_log.info(default_config)
|
||||
self.bot.con.run("insert into guild_config(guild_id, guild_name, admin_roles, rcon_enabled, channel_lockdown,\
|
||||
raid_status, pg_filter, patreon_enabled, referral_enabled)\
|
||||
values (%(guild_id)s, %(name)s, %(admin_roles)s, %(rcon_enabled)s, %(channel_lockdown)s,\
|
||||
%(raid_status)s, %(pg_filter)s, %(patreon_enabled)s, %(referral_enabled)s)",
|
||||
{'guild_id': default_config['guild_id'],
|
||||
'name': default_config['name'],
|
||||
'admin_roles': json.dumps(default_config['admin_roles']),
|
||||
'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']
|
||||
})
|
||||
await self.bot.db_con.execute("insert into guild_config(guild_id, guild_name, admin_roles, rcon_enabled, "
|
||||
"channel_lockdown, raid_status, pg_filter, patreon_enabled, referral_enabled) "
|
||||
"values ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
default_config['guild_id'], default_config['name'],
|
||||
json.dumps(default_config['admin_roles']), default_config['rcon_enabled'],
|
||||
default_config['channel_lockdown'], default_config['raid_status'],
|
||||
default_config['pg_filter'], default_config['patreon_enabled'],
|
||||
default_config['referral_enabled'])
|
||||
events_log.info(f'Entry Created for {guild.name}')
|
||||
await guild.me.edit(nick='[g$] Geeksbot')
|
||||
|
||||
async def on_guild_remove(self, guild):
|
||||
self.bot.con.run(f'delete from guild_config where guild_id = %(id)s', {'id': guild.id})
|
||||
await self.bot.db_con.execute(f'delete from guild_config where guild_id = $1', guild.id)
|
||||
events_log.info(f'Left the {guild.name} guild.')
|
||||
|
||||
async def on_member_join(self, member):
|
||||
events_log.info(f'Member joined: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}')
|
||||
join_chan = self.bot.con.one('select join_leave_chat from guild_config where guild_id = %(id)s',
|
||||
{'id': member.guild.id})
|
||||
join_chan = await self.bot.db_con.fetchval('select join_leave_chat from guild_config where guild_id = $1',
|
||||
member.guild.id)
|
||||
if join_chan:
|
||||
em = discord.Embed(style='rich',
|
||||
color=embed_color
|
||||
@@ -226,31 +197,30 @@ class BotEvents:
|
||||
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)
|
||||
await discord.utils.get(member.guild.channels, id=join_chan).send(embed=em)
|
||||
mem_data = {'id': member.id,
|
||||
'name': member.name,
|
||||
'discriminator': member.discriminator,
|
||||
'bot': member.bot
|
||||
}
|
||||
mem = self.bot.con.one('select guilds,nicks from user_data where id = %(id)s', {'id': member.id})
|
||||
mem_data = [member.id,
|
||||
member.name,
|
||||
member.discriminator,
|
||||
member.bot
|
||||
]
|
||||
mem = await self.bot.db_con.fetchval('select guilds,nicks from user_data where id = $1', member.id)
|
||||
if mem:
|
||||
mem[1].append(json.dumps({member.guild.id: member.display_name}))
|
||||
mem[0].append(member.guild.id)
|
||||
mem_data['nicks'] = mem[1]
|
||||
mem_data['guilds'] = mem[0]
|
||||
self.bot.con.run('update user_data set (name, discriminator, bot, nicks, guilds) =\
|
||||
(%(name)s, %(discriminator)s, %(bot)s, %(nicks)s, %(guilds)s) where\
|
||||
id = %(id)s', mem_data)
|
||||
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)
|
||||
else:
|
||||
mem_data['nicks'] = [json.dumps({member.guild.id: member.display_name})]
|
||||
mem_data['guilds'] = [member.guild.id]
|
||||
self.bot.con.run('insert into user_data (id, name, discriminator, bot, nicks, guilds) values\
|
||||
(%(id)s, %(name)s, %(discriminator)s, %(bot)s, %(nicks)s, %(guilds)s)', mem_data)
|
||||
mem_data.append([json.dumps({member.guild.id: member.display_name})])
|
||||
mem_data.append([member.guild.id])
|
||||
self.bot.con.run('insert into user_data (id, name, discriminator, bot, nicks, guilds) '
|
||||
'values ($1, $2, $3, $4, $5, $6)', *mem_data)
|
||||
|
||||
async def on_member_remove(self, member):
|
||||
leave_time = datetime.utcnow()
|
||||
events_log.info(f'Member left: {member.name} {member.id} Guild: {member.guild.name} {member.guild.id}')
|
||||
join_chan = self.bot.con.one('select join_leave_chat from guild_config where guild_id = %(id)s',
|
||||
{'id': member.guild.id})
|
||||
join_chan = await self.bot.db_con.fetchval('select join_leave_chat from guild_config where guild_id = $1',
|
||||
member.guild.id)
|
||||
if join_chan:
|
||||
em = discord.Embed(style='rich',
|
||||
color=red_color
|
||||
@@ -273,6 +243,19 @@ class BotEvents:
|
||||
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)
|
||||
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):
|
||||
@@ -33,6 +33,9 @@ class Fun:
|
||||
@commands.command()
|
||||
@commands.cooldown(1, 30, type=commands.BucketType.user)
|
||||
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:
|
||||
await ctx.send(f'You rolled a Critical Fail...\nInfection bounces off and rebounds on the attacker.')
|
||||
member = ctx.author
|
||||
@@ -48,6 +51,7 @@ class Fun:
|
||||
@commands.command()
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
async def heal(self, ctx, member: discord.Member):
|
||||
"""Removes infection from user."""
|
||||
if ctx.author == member and ctx.author.id != owner_id:
|
||||
await ctx.send('You can\'t heal yourself silly...')
|
||||
else:
|
||||
@@ -57,7 +61,7 @@ class Fun:
|
||||
else:
|
||||
await ctx.send(f'{member.display_name} is not infected...')
|
||||
|
||||
@commands.command()
|
||||
@commands.command(hidden=True)
|
||||
@commands.is_owner()
|
||||
async def print_infections(self, ctx):
|
||||
await ctx.author.send(f'```{self.bot.infected}```')
|
||||
@@ -65,13 +69,15 @@ class Fun:
|
||||
@commands.command()
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
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:
|
||||
await ctx.send(f'You rolled a Critical Fail...\nThe trout bounces off and rebounds on the attacker.')
|
||||
await ctx.send(f'{ctx.author.mention} '
|
||||
f'You slap yourself in the face with a large trout <:trout:408543365085397013>')
|
||||
f'You slap yourself in the face with a large trout {trout}')
|
||||
else:
|
||||
await ctx.send(f'{ctx.author.display_name} slaps '
|
||||
f'{member.mention} around a bit with a large trout <:trout:408543365085397013>')
|
||||
f'{member.mention} around a bit with a large trout {trout}')
|
||||
|
||||
@staticmethod
|
||||
def get_factorial(number):
|
||||
@@ -80,22 +86,23 @@ class Fun:
|
||||
a = a * (i + 1)
|
||||
return a
|
||||
|
||||
# @commands.command()
|
||||
# @commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
# async def fact(self, ctx, number:int):
|
||||
# if number < 20001 and number > 0:
|
||||
# n = 1990
|
||||
# with ctx.channel.typing():
|
||||
# a = await self.bot.loop.run_in_executor(None, self.get_factorial, number)
|
||||
# if len(str(a)) > 6000:
|
||||
# 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.send(f"{ctx.author.mention} Check your DMs.")
|
||||
# else:
|
||||
# for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]:
|
||||
# await ctx.send(f'```py\n{b}```')
|
||||
# else:
|
||||
# await ctx.send("Invalid number. Please enter a number between 0 and 20,000")
|
||||
@commands.command()
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
async def fact(self, ctx, number: int):
|
||||
"""Returns the given factorial up to 20,000!"""
|
||||
if 0 < number < 20001:
|
||||
n = 1990
|
||||
with ctx.channel.typing():
|
||||
a = await self.bot.loop.run_in_executor(None, self.get_factorial, number)
|
||||
if len(str(a)) > 6000:
|
||||
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.send(f"{ctx.author.mention} Check your DMs.")
|
||||
else:
|
||||
for b in [str(a)[i:i+n] for i in range(0, len(str(a)), n)]:
|
||||
await ctx.send(f'```py\n{b}```')
|
||||
else:
|
||||
await ctx.send("Invalid number. Please enter a number between 0 and 20,000")
|
||||
|
||||
@commands.command(hidden=True)
|
||||
@commands.is_owner()
|
||||
@@ -141,11 +148,28 @@ class Fun:
|
||||
else:
|
||||
await ctx.send('Not connected to that voice channel.')
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@commands.command(hidden=True)
|
||||
@commands.is_owner()
|
||||
async def volume(self, ctx, volume: float):
|
||||
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):
|
||||
bot.add_cog(Fun(bot))
|
||||
@@ -0,0 +1,64 @@
|
||||
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))
|
||||
@@ -1,7 +1,7 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
import json
|
||||
from .imports import checks
|
||||
from src.imports import checks
|
||||
|
||||
config_dir = 'config'
|
||||
extension_dir = 'extensions'
|
||||
@@ -17,12 +17,11 @@ class Patreon:
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
async def get_patreon_links(self, ctx, target: discord.Member=None):
|
||||
"""Prints Patreon information for creators on the server."""
|
||||
if self.bot.con.one('select patreon_enabled from guild_config where guild_id = %(id)s', {'id': ctx.guild.id}):
|
||||
patreon_info = self.bot.con.one('select patreon_message,patreon_links\
|
||||
from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
message = patreon_info[0].replace('\\n', '\n')
|
||||
patreon_links = json.loads(patreon_info[1])
|
||||
if await self.bot.db_con.fetchval('select patreon_enabled from guild_config where guild_id = $1', ctx.guild.id):
|
||||
patreon_info = await self.bot.db_con.fetchrow('select patreon_message,patreon_links from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id)
|
||||
message = patreon_info['patreon_message'].replace('\\n', '\n')
|
||||
patreon_links = json.loads(patreon_info['patreon_links'])
|
||||
for key in patreon_links:
|
||||
message = message + '\n{0}: {1}'.format(key, patreon_links[key])
|
||||
if target is None:
|
||||
@@ -34,23 +33,23 @@ class Patreon:
|
||||
|
||||
@commands.command(aliases=['patreon_message'])
|
||||
async def set_patreon_message(self, ctx, message):
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
patreon_message = self.bot.con.one('select patreon_message from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
patreon_message = await self.bot.db_con.fetchval('select patreon_message from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id)
|
||||
if message == patreon_message:
|
||||
await ctx.send('That is already the current message for this guild.')
|
||||
else:
|
||||
self.bot.con.run('update guild_config set patreon_message = %(message)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'message': message})
|
||||
await self.bot.db_con.execute('update guild_config set patreon_message = $2 where guild_id = $1',
|
||||
ctx.guild.id, message)
|
||||
await ctx.send(f'The patreon message for this guild has been set to:\n{message}')
|
||||
else:
|
||||
await ctx.send(f'You are not authorized to run this command.')
|
||||
|
||||
@commands.command(aliases=['add_patreon', 'set_patreon'])
|
||||
async def add_patreon_info(self, ctx, name, url):
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
patreon_info = self.bot.con.one('select patreon_links from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
patreon_info = await self.bot.db_con.fetchval('select patreon_links from guild_config where guild_id = $1',
|
||||
ctx.guild.id)
|
||||
patreon_links = {}
|
||||
update = 0
|
||||
if patreon_info:
|
||||
@@ -58,8 +57,8 @@ class Patreon:
|
||||
if name in patreon_links:
|
||||
update = 1
|
||||
patreon_links[name] = url
|
||||
self.bot.con.run('update guild_config set patreon_links = %(links)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'links': json.dumps(patreon_links)})
|
||||
await self.bot.db_con.execute('update guild_config set patreon_links = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(patreon_links))
|
||||
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.'}")
|
||||
else:
|
||||
@@ -67,15 +66,15 @@ class Patreon:
|
||||
|
||||
@commands.command(aliases=['remove_patreon'])
|
||||
async def remove_patreon_info(self, ctx, name):
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
patreon_info = self.bot.con.one('select patreon_links from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
patreon_info = await self.bot.db_con.fetchval('select patreon_links from guild_config where guild_id = $1',
|
||||
ctx.guild.id)
|
||||
if patreon_info:
|
||||
patreon_links = json.loads(patreon_info)
|
||||
if name in patreon_links:
|
||||
del patreon_links[name]
|
||||
self.bot.con.run('update guild_config set patreon_links = %(links)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'links': json.dumps(patreon_links)})
|
||||
await self.bot.db_con.execute('update guild_config set patreon_links = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(patreon_links))
|
||||
await ctx.send(f'The Patreon link for {name} has been removed from the config for this guild.')
|
||||
return
|
||||
else:
|
||||
@@ -87,18 +86,18 @@ class Patreon:
|
||||
|
||||
@commands.command()
|
||||
async def enable_patreon(self, ctx, state: bool=True):
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
patreon_status = self.bot.con.one('select patreon_enabled from guild_config where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id})
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
patreon_status = await self.bot.db_con.fetchval('select patreon_enabled from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id)
|
||||
if patreon_status and state:
|
||||
await ctx.send('Patreon is already enabled for this guild.')
|
||||
elif patreon_status and not state:
|
||||
self.bot.con.run('update guild_config set patreon_enabled = %(state)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'state': state})
|
||||
await self.bot.db_con.execute('update guild_config set patreon_enabled = $2 where guild_id = $1',
|
||||
ctx.guild.id, state)
|
||||
await ctx.send('Patreon has been disabled for this guild.')
|
||||
elif not patreon_status and state:
|
||||
self.bot.con.run('update guild_config set patreon_enabled = %(state)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'state': state})
|
||||
await self.bot.db_con.execute('update guild_config set patreon_enabled = $2 where guild_id = $1',
|
||||
ctx.guild.id, state)
|
||||
await ctx.send('Patreon has been enabled for this guild.')
|
||||
elif not patreon_status and not state:
|
||||
await ctx.send('Patreon is already disabled for this guild.')
|
||||
@@ -107,9 +106,10 @@ class Patreon:
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
async def referral_links(self, ctx, target: discord.Member=None):
|
||||
"""Prints G-Portal Referral Links."""
|
||||
if self.bot.con.one('select referral_enabled from guild_config where guild_id = %(id)s', {'id': ctx.guild.id}):
|
||||
referral_info = self.bot.con.one('select referral_message,referral_links from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||
if await self.bot.db_con.fetchval('select referral_enabled from guild_config where guild_id = $1',
|
||||
ctx.guild.id):
|
||||
referral_info = await self.bot.db_con.fetchval('select referral_message,referral_links from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id)
|
||||
message = referral_info[0]
|
||||
referral_links = json.loads(referral_info[1])
|
||||
for key in referral_links:
|
||||
@@ -7,7 +7,7 @@ import logging
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
import traceback
|
||||
from .imports import checks
|
||||
from src.imports import checks
|
||||
|
||||
config_dir = 'config'
|
||||
admin_id_file = 'admin_ids'
|
||||
@@ -120,6 +120,7 @@ class Rcon:
|
||||
True)
|
||||
con.exec_command('ServerChatToPlayer "{0}" GeeksBot: Admin Geeks have been notified you need assistance. '
|
||||
'Please be patient.'.format(player))
|
||||
# noinspection PyProtectedMember
|
||||
con._sock.close()
|
||||
for role in admin_roles:
|
||||
msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=admin_roles[role]).mention)
|
||||
@@ -137,15 +138,16 @@ class Rcon:
|
||||
first_last
|
||||
"first last"
|
||||
To view all the valid ARK servers for this guild see list_ark_servers."""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
if server is not None:
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections '
|
||||
'from guild_config where guild_id = $1',
|
||||
ctx.guild.id))
|
||||
server = server.replace('_', ' ').title()
|
||||
if server in rcon_connections:
|
||||
rcon_connections[server]["monitoring_chat"] = 1
|
||||
self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
|
||||
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(rcon_connections))
|
||||
channel = self.bot.get_channel(rcon_connections[server]['game_chat_chan_id'])
|
||||
await channel.send('Started monitoring on the {0} server.'.format(server))
|
||||
await ctx.message.add_reaction('✅')
|
||||
@@ -158,6 +160,7 @@ class Rcon:
|
||||
True)
|
||||
messages = await self.bot.loop.run_in_executor(None, self.server_chat_background_process,
|
||||
ctx.guild.id, con)
|
||||
# noinspection PyProtectedMember
|
||||
con._sock.close()
|
||||
except TimeoutError:
|
||||
rcon_log.error(traceback.format_exc())
|
||||
@@ -180,8 +183,10 @@ class Rcon:
|
||||
message = func(ctx, message, rcon_connections['server'])
|
||||
await channel.send('{0}'.format(message))
|
||||
await asyncio.sleep(1)
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections '
|
||||
'from guild_config '
|
||||
'where guild_id = $1',
|
||||
ctx.guild.id))
|
||||
await channel.send('Monitoring Stopped')
|
||||
else:
|
||||
await ctx.send(f'Server not found: {server}')
|
||||
@@ -195,15 +200,16 @@ class Rcon:
|
||||
async def end_monitor_chat(self, ctx, *, server=None):
|
||||
"""Ends chat monitoring on the specified server.
|
||||
Context is the same as monitor_chat"""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
if server is not None:
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections '
|
||||
'from guild_config where guild_id = $1',
|
||||
ctx.guild.id))
|
||||
server = server.replace('_', ' ').title()
|
||||
if server in rcon_connections:
|
||||
rcon_connections[server]["monitoring_chat"] = 0
|
||||
self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
|
||||
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(rcon_connections))
|
||||
else:
|
||||
await ctx.send(f'Server not found: {server}')
|
||||
else:
|
||||
@@ -223,9 +229,9 @@ class Rcon:
|
||||
first_last
|
||||
"first last"
|
||||
To view all the valid ARK servers for this guild see list_ark_servers."""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
if server is not None:
|
||||
server = server.replace('_', ' ').title()
|
||||
if server in rcon_connections:
|
||||
@@ -241,9 +247,10 @@ class Rcon:
|
||||
'"ip" port "password" if you would like to get info from it.'.format(server))
|
||||
else:
|
||||
for server in rcon_connections:
|
||||
msg = await ctx.send('Getting Data for the {0} server'.format(server.title()))
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
connection_info = rcon_connections[server]
|
||||
msg = await ctx.send('Getting Data for the {0} server'.format(server.title()))
|
||||
async with ctx.channel.typing():
|
||||
message = self._listplayers(connection_info)
|
||||
except Exception as e:
|
||||
@@ -260,10 +267,10 @@ class Rcon:
|
||||
async def add_rcon_server(self, ctx, server, ip, port, password):
|
||||
"""Adds the specified server to the current guild\'s rcon config.
|
||||
All strings (<server>, <ip>, <password>) must be contained inside double quotes."""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
server = server.title()
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
if server not in rcon_connections:
|
||||
rcon_connections[server] = {
|
||||
'ip': ip,
|
||||
@@ -274,8 +281,8 @@ class Rcon:
|
||||
'msg_chan_id': 0,
|
||||
'monitoring_chat': 0
|
||||
}
|
||||
self.bot.con.run('update guild_config set rcon_connections = %(connections)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'connections': json.dumps(rcon_connections)})
|
||||
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(rcon_connections))
|
||||
await ctx.send('{0} server has been added to my configuration.'.format(server))
|
||||
else:
|
||||
await ctx.send('This server name is already in my configuration. Please choose another.')
|
||||
@@ -289,14 +296,14 @@ class Rcon:
|
||||
async def remove_rcon_server(self, ctx, server):
|
||||
"""removes the specified server from the current guild\'s rcon config.
|
||||
All strings <server> must be contained inside double quotes."""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
server = server.title()
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
if server in rcon_connections:
|
||||
del rcon_connections[server]
|
||||
self.bot.con.run('update guild_config set rcon_connections = %(connections)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'connections': json.dumps(rcon_connections)})
|
||||
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(rcon_connections))
|
||||
await ctx.send('{0} has been removed from my configuration.'.format(server))
|
||||
else:
|
||||
await ctx.send('{0} is not in my configuration.'.format(server))
|
||||
@@ -309,10 +316,11 @@ class Rcon:
|
||||
"""Adds the included Steam 64 IDs to the running whitelist on all the ARK servers in the current guild\'s rcon config.
|
||||
Steam 64 IDs should be a comma seperated list of IDs.
|
||||
Example: 76561198024193239,76561198024193239,76561198024193239"""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
if steam_ids is not None:
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections '
|
||||
'from guild_config where guild_id = $1',
|
||||
ctx.guild.id))
|
||||
error = 0
|
||||
error_msg = ''
|
||||
success_msg = 'Adding to the running whitelist on all servers.'
|
||||
@@ -356,9 +364,9 @@ class Rcon:
|
||||
"""Runs SaveWorld on the specified ARK server.
|
||||
If a server is not specified it will default to running saveworld on all servers in the guild\'s config.
|
||||
Will print out "World Saved" for each server when the command completes successfully."""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
success_msg = 'Running saveworld'
|
||||
if server is None:
|
||||
success_msg += ' on all the servers:'
|
||||
@@ -369,7 +377,7 @@ class Rcon:
|
||||
await msg.edit(content=success_msg.strip())
|
||||
message = await self.bot.loop.run_in_executor(None, self._saveworld, rcon_connections[server])
|
||||
except Exception as e:
|
||||
success_msg = '{0}\n{1}'.format(success_msg, e.strip())
|
||||
success_msg = '{0}\n{1}'.format(success_msg, e)
|
||||
await msg.edit(content=success_msg.strip())
|
||||
else:
|
||||
success_msg = '{0}\n{1}'.format(success_msg, message.strip())
|
||||
@@ -398,9 +406,9 @@ class Rcon:
|
||||
"""Sends a broadcast message to all servers in the guild config.
|
||||
The message will be prefixed with the Discord name of the person running the command.
|
||||
Will print "Success" for each server once the broadcast is sent."""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
if message is not None:
|
||||
message = f'{ctx.author.display_name}: {message}'
|
||||
success_msg = f'Broadcasting "{message}" to all servers.'
|
||||
@@ -414,7 +422,7 @@ class Rcon:
|
||||
rcon_connections[server],
|
||||
message)
|
||||
except Exception as e:
|
||||
success_msg = '{0}\n{1}'.format(success_msg, e.strip())
|
||||
success_msg = '{0}\n{1}'.format(success_msg, e)
|
||||
await msg.edit(content=success_msg.strip())
|
||||
else:
|
||||
for mesg in messages:
|
||||
@@ -435,9 +443,9 @@ class Rcon:
|
||||
The message will be prefixed with the Discord name of the person running the command.
|
||||
If <server> has more than one word in it's name it will either need to be surrounded
|
||||
by double quotes or the words separated by _"""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
if server is not None:
|
||||
server = server.replace('_', ' ').title()
|
||||
if message is not None:
|
||||
@@ -473,9 +481,9 @@ class Rcon:
|
||||
and these channel's permissions will be synced to the category.
|
||||
These channels will be added to the guild's rcon config and are where the
|
||||
server chat messages will be sent when monitor_chat is run."""
|
||||
if checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
if await checks.is_rcon_admin(self.bot, ctx):
|
||||
rcon_connections = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
edited = 0
|
||||
category = discord.utils.get(ctx.guild.categories, name='Server Chats')
|
||||
if category is None:
|
||||
@@ -498,8 +506,8 @@ class Rcon:
|
||||
rcon_connections[server]['game_chat_chan_id'] = chan.id
|
||||
edited = 1
|
||||
if edited == 1:
|
||||
self.bot.con.run('update guild_config set rcon_connections = %(json)s where guild_id = %(id)s',
|
||||
{'id': ctx.guild.id, 'json': json.dumps(rcon_connections)})
|
||||
await self.bot.db_con.execute('update guild_config set rcon_connections = $2 where guild_id = $1',
|
||||
ctx.guild.id, json.dumps(rcon_connections))
|
||||
await ctx.message.add_reaction('✅')
|
||||
else:
|
||||
await ctx.send(f'You are not authorized to run this command.')
|
||||
@@ -509,8 +517,8 @@ class Rcon:
|
||||
@commands.check(checks.is_restricted_chan)
|
||||
async def list_ark_servers(self, ctx):
|
||||
"""Returns a list of all the ARK servers in the current guild\'s config."""
|
||||
servers = json.loads(self.bot.con.one('select rcon_connections from guild_config\
|
||||
where guild_id = %(id)s', {'id': ctx.guild.id}))
|
||||
servers = json.loads(await self.bot.db_con.fetchval('select rcon_connections from guild_config '
|
||||
'where guild_id = $1', ctx.guild.id))
|
||||
em = discord.Embed(style='rich',
|
||||
title=f'__**There are currently {len(servers)} ARK servers in my config:**__',
|
||||
color=discord.Colour.green()
|
||||
@@ -7,7 +7,7 @@ import inspect
|
||||
import textwrap
|
||||
from contextlib import redirect_stdout
|
||||
import io
|
||||
from .imports.utils import paginate, run_command
|
||||
from src.imports.utils import run_command, format_output, Paginator
|
||||
|
||||
ownerids = [351794468870946827, 275280442884751360]
|
||||
ownerid = 351794468870946827
|
||||
@@ -37,6 +37,7 @@ class Repl:
|
||||
async def _eval(self, ctx, *, body: str):
|
||||
if ctx.author.id != ownerid:
|
||||
return
|
||||
pag = Paginator(self.bot)
|
||||
env = {
|
||||
'bot': self.bot,
|
||||
'ctx': ctx,
|
||||
@@ -60,8 +61,10 @@ class Repl:
|
||||
with redirect_stdout(stdout):
|
||||
ret = await func()
|
||||
except Exception:
|
||||
value = stdout.getvalue()
|
||||
await ctx.send('```py\n{}{}\n```'.format(value, traceback.format_exc()))
|
||||
pag.add(stdout.getvalue())
|
||||
pag.add(traceback.format_exc())
|
||||
for page in pag.pages():
|
||||
await ctx.send(page)
|
||||
else:
|
||||
value = stdout.getvalue()
|
||||
# noinspection PyBroadException
|
||||
@@ -69,17 +72,12 @@ class Repl:
|
||||
await ctx.message.add_reaction('✅')
|
||||
except Exception:
|
||||
pass
|
||||
if ret is None:
|
||||
if value:
|
||||
for page in paginate(value):
|
||||
await ctx.send(page)
|
||||
else:
|
||||
self._last_result = ret
|
||||
if value:
|
||||
for page in paginate(value):
|
||||
await ctx.send(page)
|
||||
for page in paginate(ret):
|
||||
await ctx.send(page)
|
||||
value = format_output(value)
|
||||
pag.add(value)
|
||||
pag.add(f'\nReturned: {ret}')
|
||||
self._last_result = ret
|
||||
for page in pag.pages():
|
||||
await ctx.send(page)
|
||||
|
||||
@commands.command(hidden=True)
|
||||
async def repl(self, ctx):
|
||||
@@ -143,12 +141,11 @@ class Repl:
|
||||
fmt = '{}'.format(value)
|
||||
try:
|
||||
if fmt is not None:
|
||||
if len(fmt) > 1990:
|
||||
for page in paginate(fmt):
|
||||
await response.channel.send(page)
|
||||
await ctx.send(response.channel)
|
||||
else:
|
||||
await response.channel.send(f'```py\n{fmt}\n```')
|
||||
pag = Paginator(self.bot)
|
||||
pag.add(fmt)
|
||||
for page in pag.pages():
|
||||
await response.channel.send(page)
|
||||
await ctx.send(response.channel)
|
||||
except discord.Forbidden:
|
||||
pass
|
||||
except discord.HTTPException as e:
|
||||
@@ -159,16 +156,14 @@ class Repl:
|
||||
if ctx.author.id != ownerid:
|
||||
return
|
||||
try:
|
||||
body = self.cleanup_code(body).split(' ')
|
||||
result = await asyncio.wait_for(self.bot.loop.create_task(run_command(body)), 10)
|
||||
value = result
|
||||
for page in paginate(value):
|
||||
body = self.cleanup_code(body)
|
||||
pag = Paginator(self.bot)
|
||||
pag.add(await asyncio.wait_for(self.bot.loop.create_task(run_command(body)), 10))
|
||||
for page in pag.pages():
|
||||
await ctx.send(page)
|
||||
await ctx.message.add_reaction('✅')
|
||||
except asyncio.TimeoutError:
|
||||
value = f"Command did not complete in the time allowed."
|
||||
for page in paginate(value):
|
||||
await ctx.send(page)
|
||||
await ctx.send(f"Command did not complete in the time allowed.")
|
||||
await ctx.message.add_reaction('❌')
|
||||
|
||||
|
||||
@@ -7,10 +7,19 @@ import psutil
|
||||
from datetime import datetime, timedelta
|
||||
import asyncio
|
||||
import async_timeout
|
||||
from .imports import checks
|
||||
from src.imports import checks, utils
|
||||
import pytz
|
||||
import gspread
|
||||
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/'
|
||||
admin_id_file = 'admin_ids'
|
||||
@@ -22,6 +31,7 @@ invite_match = '(https?://)?(www.)?discord(app.com/(invite|oauth2)|.gg|.io)/[\w\
|
||||
|
||||
utils_log = logging.getLogger('utils')
|
||||
clock_emojis = ['🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚']
|
||||
replace_tzs = {'MST': 'US/Mountain', 'HST': 'US/Hawaii', 'EST': 'US/Eastern'}
|
||||
|
||||
|
||||
class Utils:
|
||||
@@ -44,20 +54,20 @@ class Utils:
|
||||
msg = await self.bot.wait_for('message', timeout=5, check=check)
|
||||
self.bot.ping_times[i]['rec'] = msg
|
||||
|
||||
@commands.command()
|
||||
@commands.command(hidden=True)
|
||||
async def channel_ping(self, ctx, wait_time: float=10, message: str='=bump', channel: int=265828729970753537):
|
||||
await ctx.send('Starting Background Process.')
|
||||
self.bot.loop.create_task(self._4_hour_ping(channel, message, wait_time))
|
||||
|
||||
@commands.command()
|
||||
@commands.command(hidden=True)
|
||||
@commands.is_owner()
|
||||
async def sysinfo(self, ctx):
|
||||
await ctx.send(f'''
|
||||
```ml
|
||||
CPU Percentages: {psutil.cpu_percent(percpu=True)}
|
||||
Memory Usage: {psutil.virtual_memory().percent}%
|
||||
Disc Usage: {psutil.disk_usage("/").percent}%
|
||||
```''')
|
||||
"""Gets system status for my server."""
|
||||
await ctx.send(f'```ml\n'
|
||||
f'CPU Percentages: {psutil.cpu_percent(percpu=True)}\n'
|
||||
f'Memory Usage: {psutil.virtual_memory().percent}%\n'
|
||||
f'Disc Usage: {psutil.disk_usage("/").percent}%\n'
|
||||
f'```')
|
||||
|
||||
@commands.command(hidden=True)
|
||||
async def role(self, ctx, role: str):
|
||||
@@ -168,7 +178,10 @@ class Utils:
|
||||
@commands.command()
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
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',
|
||||
title=f'Pong 🏓',
|
||||
color=discord.Colour.green()
|
||||
@@ -176,8 +189,8 @@ class Utils:
|
||||
msg = await ctx.send(embed=em)
|
||||
time1 = ctx.message.created_at
|
||||
time = (msg.created_at - time1).total_seconds() * 1000
|
||||
em.description = f'''Response Time: **{math.ceil(time)}ms**
|
||||
Discord Latency: **{math.ceil(self.bot.latency*1000)}ms**'''
|
||||
em.description = f'Response Time: **{math.ceil(time)}ms**\n' \
|
||||
f'Discord Latency: **{math.ceil(self.bot.latency*1000)}ms**'
|
||||
await msg.edit(embed=em)
|
||||
|
||||
if mode == 'comp':
|
||||
@@ -206,8 +219,8 @@ class Utils:
|
||||
time = time.total_seconds()
|
||||
times.append(time)
|
||||
value = f"Message Sent:" \
|
||||
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')}" \
|
||||
f"{datetime.strftime(self.bot.ping_times[i]['snd'].created_at, '%H:%M:%S.%f')}\n" \
|
||||
f"Response Received: {datetime.strftime(now, '%H:%M:%S.%f')}\n" \
|
||||
f"Total Time: {math.ceil(time * 1000)}ms"
|
||||
await self.bot.ping_times[i]['rec'].delete()
|
||||
em.add_field(name=f'Ping Test {i}', value=value, inline=True)
|
||||
@@ -220,11 +233,11 @@ class Utils:
|
||||
em.add_field(value=f'Total Time for Comprehensive test: {math.ceil(total_time)}ms',
|
||||
name=f'Average: **{round(total_time/count,1)}ms**',
|
||||
inline=False)
|
||||
await msg.edit(embed=em)
|
||||
await msg.edit(embed=em)
|
||||
|
||||
@commands.group(case_insensitive=True)
|
||||
async def admin(self, ctx):
|
||||
"""Run help admin for more info"""
|
||||
"""Group for Admin help requests"""
|
||||
pass
|
||||
|
||||
@admin.command(name='new', aliases=['nr'])
|
||||
@@ -236,25 +249,20 @@ class Utils:
|
||||
if ctx.guild:
|
||||
if request_msg is not None:
|
||||
if len(request_msg) < 1000:
|
||||
self.bot.con.run('insert into admin_requests (issuing_member_id, guild_orig, request_text,'
|
||||
'request_time) values (%(member_id)s, %(guild_id)s, %(text)s, %(time)s)',
|
||||
{'member_id': ctx.author.id, 'guild_id': ctx.guild.id, 'text': request_msg,
|
||||
'time': ctx.message.created_at})
|
||||
channel = self.bot.con.one(f'select admin_chat from guild_config where guild_id = {ctx.guild.id}')
|
||||
await self.bot.db_con.execute('insert into admin_requests (issuing_member_id, guild_orig, '
|
||||
'request_text, request_time) values ($1, $2, $3, $4)',
|
||||
ctx.author.id, ctx.guild.id, request_msg, ctx.message.created_at)
|
||||
channel = await self.bot.db_con.fetchval(f'select admin_chat from guild_config where guild_id = $1',
|
||||
ctx.guild.id)
|
||||
if channel:
|
||||
chan = discord.utils.get(ctx.guild.channels, id=channel)
|
||||
msg = ''
|
||||
admin_roles = []
|
||||
roles = self.bot.con.one(f'select admin_roles,rcon_admin_roles from guild_config where '
|
||||
f'guild_id = %(id)s', {'id': ctx.guild.id})
|
||||
request_id = self.bot.con.one(f'select id from admin_requests where '
|
||||
f'issuing_member_id = %(member_id)s and request_time = %(time)s',
|
||||
{'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])
|
||||
roles = await self.bot.db_con.fetchval(f'select admin_roles,rcon_admin_roles from guild_config '
|
||||
f'where guild_id = $1', ctx.guild.id)
|
||||
request_id = await self.bot.db_con.fetchval(f'select id from admin_requests where '
|
||||
f'issuing_member_id = $1 and request_time = $2',
|
||||
ctx.author.id, ctx.message.created_at)
|
||||
admin_roles = json.loads(roles).values()
|
||||
for role in admin_roles:
|
||||
msg = '{0} {1}'.format(msg, discord.utils.get(ctx.guild.roles, id=role).mention)
|
||||
msg += f"New Request ID: {request_id} " \
|
||||
@@ -273,7 +281,7 @@ class Utils:
|
||||
@admin.command(name='list', aliases=['lr'])
|
||||
@commands.cooldown(1, 5, type=commands.BucketType.user)
|
||||
async def list_admin_requests(self, ctx, assigned_to: discord.Member=None):
|
||||
"""Returns a list of all active Admin help requests for this guild
|
||||
"""List of all active Admin help requests
|
||||
|
||||
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.
|
||||
@@ -286,10 +294,10 @@ class Utils:
|
||||
title=f'Admin Help Requests',
|
||||
color=discord.Colour.green()
|
||||
)
|
||||
if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx):
|
||||
if await checks.is_admin(self.bot, ctx) or await checks.is_rcon_admin(self.bot, ctx):
|
||||
if assigned_to is None:
|
||||
requests = self.bot.con.all(f'select * from admin_requests where guild_orig = %(guild_id)s '
|
||||
f'and completed_time is null', {'guild_id': ctx.guild.id})
|
||||
requests = await self.bot.db_con.fetch(f'select * from admin_requests where guild_orig = $1 '
|
||||
f'and completed_time is null', ctx.guild.id)
|
||||
em.title = f'Admin help requests for {ctx.guild.name}'
|
||||
if requests:
|
||||
for request in requests:
|
||||
@@ -306,11 +314,11 @@ class Utils:
|
||||
else:
|
||||
em.add_field(name='There are no pending requests for this guild.', value='', inline=False)
|
||||
else:
|
||||
if checks.check_admin_role(self.bot, ctx, assigned_to)\
|
||||
or checks.check_rcon_role(self.bot, ctx, assigned_to):
|
||||
requests = self.bot.con.all('select * from admin_requests where assigned_to = %(admin_id)s '
|
||||
'and guild_orig = %(guild_id)s and completed_time is null',
|
||||
{'admin_id': assigned_to.id, 'guild_id': ctx.guild.id})
|
||||
if await checks.check_admin_role(self.bot, ctx, assigned_to)\
|
||||
or await checks.check_rcon_role(self.bot, ctx, assigned_to):
|
||||
requests = await self.bot.db_con.fetch('select * from admin_requests where assigned_to = $1 '
|
||||
'and guild_orig = $2 and completed_time is null',
|
||||
assigned_to.id, ctx.guild.id)
|
||||
em.title = f'Admin help requests assigned to {assigned_to.display_name} in {ctx.guild.name}'
|
||||
if requests:
|
||||
for request in requests:
|
||||
@@ -322,15 +330,16 @@ class Utils:
|
||||
"",
|
||||
inline=False)
|
||||
else:
|
||||
em.add_field(name=f'There are no pending requests for {assigned_to.display_name} on this guild.',
|
||||
em.add_field(name=f'There are no pending requests for '
|
||||
f'{assigned_to.display_name} on this guild.',
|
||||
value='',
|
||||
inline=False)
|
||||
else:
|
||||
em.title = f'{assigned_to.display_name} is not an admin in this guild.'
|
||||
else:
|
||||
requests = self.bot.con.all('select * from admin_requests where issuing_member_id = %(member_id)s '
|
||||
'and guild_orig = %(guild_id)s and completed_time is null',
|
||||
{'member_id': ctx.author.id, 'guild_id': ctx.guild.id})
|
||||
requests = await self.bot.db_con.fetch('select * from admin_requests where issuing_member_id = $1 '
|
||||
'and guild_orig = $2 and completed_time is null',
|
||||
ctx.author.id, ctx.guild.id)
|
||||
em.title = f'Admin help requests for {ctx.author.display_name}'
|
||||
if requests:
|
||||
for request in requests:
|
||||
@@ -352,7 +361,7 @@ class Utils:
|
||||
"""Allows Admin to close admin help tickets.
|
||||
[request_id] must be a valid integer pointing to an open Request ID
|
||||
"""
|
||||
if checks.is_admin(self.bot, ctx) or checks.is_rcon_admin(self.bot, ctx):
|
||||
if await checks.is_admin(self.bot, ctx) or await checks.is_rcon_admin(self.bot, ctx):
|
||||
if request_ids:
|
||||
request_ids = request_ids.replace(' ', '').split(',')
|
||||
for request_id in request_ids:
|
||||
@@ -361,14 +370,13 @@ class Utils:
|
||||
except ValueError:
|
||||
await ctx.send(f'{request_id} is not a valid request id.')
|
||||
else:
|
||||
request = self.bot.con.one(f'select * from admin_requests where id = %(request_id)s',
|
||||
{'request_id': request_id})
|
||||
request = await self.bot.db_con.fetchrow(f'select * from admin_requests where id = $1',
|
||||
request_id)
|
||||
if request:
|
||||
if request[3] == ctx.guild.id:
|
||||
if request[6] is None:
|
||||
self.bot.con.run('update admin_requests set completed_time = %(time_now)s where '
|
||||
'id = %(request_id)s',
|
||||
{'time_now': ctx.message.created_at, 'request_id': request_id})
|
||||
await self.bot.db_con.execute('update admin_requests set completed_time = $1 where '
|
||||
'id = $2', ctx.message.created_at, request_id)
|
||||
await ctx.send(f'Request {request_id} by '
|
||||
f'{ctx.guild.get_member(request[1]).display_name}'
|
||||
f' has been marked complete.')
|
||||
@@ -386,7 +394,8 @@ class Utils:
|
||||
@commands.command(name='weather', aliases=['wu'])
|
||||
@commands.cooldown(5, 15, type=commands.BucketType.default)
|
||||
async def get_weather(self, ctx, *, location='palmer ak'):
|
||||
"""Gets the weather data for the location provided,
|
||||
"""Gets the weather data for the location given
|
||||
|
||||
If no location is included then it will get the weather for the Bot's home location.
|
||||
"""
|
||||
try:
|
||||
@@ -423,7 +432,12 @@ class Utils:
|
||||
@commands.command(name='localtime', aliases=['time', 'lt'])
|
||||
@commands.cooldown(1, 3, type=commands.BucketType.user)
|
||||
async def get_localtime(self, ctx, timezone: str='Anchorage'):
|
||||
"""Shows the current time in the timezone given
|
||||
|
||||
This defaults to the Bot's local timezone of Anchorage Alaska USA if none are given."""
|
||||
|
||||
em = discord.Embed()
|
||||
|
||||
try:
|
||||
tz = pytz.timezone(timezone)
|
||||
localtime = datetime.now(tz=tz)
|
||||
@@ -444,13 +458,91 @@ class Utils:
|
||||
em.colour = discord.Colour.red()
|
||||
await ctx.send(embed=em)
|
||||
|
||||
# noinspection PyUnboundLocalVariable
|
||||
@commands.command(name='gettimein', aliases=['timein', 'gti'])
|
||||
@commands.cooldown(1, 3, type=commands.BucketType.user)
|
||||
async def get_time_in_timezone(self, ctx, timezone: str='US/Eastern', *, time: str=None):
|
||||
"""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.cooldown(1, 3, type=commands.BucketType.user)
|
||||
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):
|
||||
nonlocal prefixes
|
||||
if message.author == self.bot.user:
|
||||
return True
|
||||
prefixes = self.bot.con.one('select prefix from guild_config where guild_id = %(id)s', {'id': ctx.guild.id})
|
||||
|
||||
if prefixes:
|
||||
for prefix in prefixes:
|
||||
if message.content.startswith(prefix):
|
||||
@@ -464,7 +556,7 @@ class Utils:
|
||||
def is_author(message):
|
||||
return message.author == ctx.author
|
||||
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
if member:
|
||||
deleted = await ctx.channel.purge(limit=number, check=is_member)
|
||||
if member != ctx.author:
|
||||
@@ -480,7 +572,12 @@ class Utils:
|
||||
@commands.command(name='purge_all', aliases=['cls', 'clear'])
|
||||
@commands.cooldown(1, 3, type=commands.BucketType.user)
|
||||
async def purge_all(self, ctx, number: int=20, contents: str='all'):
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
"""Purge all messages from the current channel
|
||||
|
||||
Will delete all of the last <number> of messages from the channel
|
||||
If <contents> is not 'all' then only messages containing <contents>
|
||||
will be deleted."""
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
if contents != 'all':
|
||||
deleted = await ctx.channel.purge(limit=number, check=lambda message: message.content == contents)
|
||||
else:
|
||||
@@ -493,19 +590,24 @@ class Utils:
|
||||
|
||||
@commands.command(name='google', aliases=['g', 'search'])
|
||||
async def google_search(self, ctx, *, search):
|
||||
"""WIP Search Google for the given string"""
|
||||
res = self.bot.gcs_service.cse().list(q=search, cx=self.bot.bot_secrets['cx']).execute()
|
||||
results = res['items'][:4]
|
||||
em = discord.Embed()
|
||||
em.title = f'Google Search'
|
||||
em.description = f'Top 4 results for "{search}"'
|
||||
em.colour = embed_color
|
||||
results = res['items']
|
||||
pag = utils.Paginator(self.bot, max_line_length=100, embed=True)
|
||||
pag.set_embed_meta(title='Google Search', description=f'Top results for "{search}"', color=self.bot.embed_color)
|
||||
for result in results:
|
||||
em.add_field(name=f'{result["title"]}', value=f'{result["snippet"]}\n{result["link"]}')
|
||||
await ctx.send(embed=em)
|
||||
pag.add(f'\uFFF6{result["title"]}\n{result["link"]}', keep_intact=True)
|
||||
pag.add(f'{result["snippet"]}')
|
||||
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')
|
||||
async def google_sheets(self, ctx, member: discord.Member):
|
||||
if checks.is_admin(self.bot, ctx):
|
||||
"""Access Google Sheets and looks for the member"""
|
||||
|
||||
if await checks.is_admin(self.bot, ctx):
|
||||
scope = ['https://spreadsheets.google.com/feeds',
|
||||
'https://www.googleapis.com/auth/drive']
|
||||
credentials = ServiceAccountCredentials.from_json_keyfile_name('config/google_client_secret.json', scope)
|
||||
@@ -528,6 +630,152 @@ class Utils:
|
||||
value=f'Steam ID: {steam[i]}\nPatreon Level: {tier[i]}\nPatron of: {patron[i]}')
|
||||
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):
|
||||
bot.add_cog(Utils(bot))
|
||||
@@ -1,46 +1,46 @@
|
||||
import discord
|
||||
import json
|
||||
from . import utils
|
||||
from src.imports import utils
|
||||
|
||||
owner_id = 351794468870946827
|
||||
|
||||
|
||||
def check_admin_role(bot, ctx, member):
|
||||
admin_roles = json.loads(bot.con.one(f"select admin_roles from guild_config where guild_id = %(id)s",
|
||||
{'id': ctx.guild.id}))
|
||||
async 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",
|
||||
ctx.guild.id))
|
||||
for role in admin_roles:
|
||||
if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in member.roles:
|
||||
return True
|
||||
return member.id == ctx.guild.owner.id or member.id == owner_id
|
||||
|
||||
|
||||
def check_rcon_role(bot, ctx, member):
|
||||
rcon_admin_roles = json.loads(bot.con.one("select rcon_admin_roles from guild_config where guild_id = %(id)s",
|
||||
{'id': ctx.guild.id}))
|
||||
async def check_rcon_role(bot, ctx, member):
|
||||
rcon_admin_roles = json.loads(await bot.db_con.fetchval("select rcon_admin_roles from guild_config "
|
||||
"where guild_id = $1", ctx.guild.id))
|
||||
for role in rcon_admin_roles:
|
||||
if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in member.roles:
|
||||
return True
|
||||
return member.id == ctx.guild.owner.id or member.id == owner_id
|
||||
|
||||
|
||||
def is_admin(bot, ctx):
|
||||
admin_roles = json.loads(bot.con.one("select admin_roles from guild_config where guild_id = %(id)s",
|
||||
{'id': ctx.guild.id}))
|
||||
async def is_admin(bot, ctx):
|
||||
admin_roles = json.loads(await bot.db_con.fetchval("select admin_roles from guild_config where guild_id = $1",
|
||||
ctx.guild.id))
|
||||
for role in admin_roles:
|
||||
if discord.utils.get(ctx.guild.roles, id=admin_roles[role]) in ctx.message.author.roles:
|
||||
return True
|
||||
return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id
|
||||
|
||||
|
||||
def is_guild_owner(ctx):
|
||||
async def is_guild_owner(ctx):
|
||||
if ctx.guild:
|
||||
return ctx.message.author.id == ctx.guild.owner.id or ctx.message.author.id == owner_id
|
||||
return False
|
||||
|
||||
|
||||
def is_rcon_admin(bot, ctx):
|
||||
rcon_admin_roles = json.loads(bot.con.one("select rcon_admin_roles from guild_config where guild_id = %(id)s",
|
||||
{'id': ctx.guild.id}))
|
||||
async def is_rcon_admin(bot, ctx):
|
||||
rcon_admin_roles = json.loads(await bot.db_con.fetchval("select rcon_admin_roles from guild_config "
|
||||
"where guild_id = $1", ctx.guild.id))
|
||||
for role in rcon_admin_roles:
|
||||
if discord.utils.get(ctx.guild.roles, id=rcon_admin_roles[role]) in ctx.message.author.roles:
|
||||
return True
|
||||
@@ -0,0 +1,449 @@
|
||||
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())
|
||||
@@ -0,0 +1,23 @@
|
||||
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
|
||||
Reference in New Issue
Block a user