commit
fa7a8cdff5
20
.gitignore
vendored
20
.gitignore
vendored
@ -1,10 +1,18 @@
|
|||||||
|
Config.json
|
||||||
|
PrivateConfig.json
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
|
||||||
|
# Recurse
|
||||||
|
**/__pycache__
|
||||||
|
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*$py.class
|
*$py.class
|
||||||
|
|
||||||
# C extensions
|
# C extensions
|
||||||
*.so
|
*.so
|
||||||
|
# Object files
|
||||||
|
*.o
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
@ -103,6 +111,10 @@ venv.bak/
|
|||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
|
||||||
/src/config/Config.json
|
|
||||||
/src/config/PrivateConfig.json
|
|
||||||
.idea/
|
# Ignore dockerfiles
|
||||||
|
docker-compose.yml
|
||||||
|
|
||||||
|
.vscode/
|
||||||
|
node_modules/
|
||||||
65
README.md
65
README.md
@ -1,53 +1,57 @@
|
|||||||
# Sebi-Machine
|
# Sebi-Machine
|
||||||
Dedicated discord bot for Sebi's bot tutorial.
|
Dedicated Discord bot for [Sebi's bot tutorial](http://discord.gg/GWdhBSp).
|
||||||
|
|
||||||
http://discord.gg/GWdhBSp
|
|
||||||
|
|
||||||
## Important things to know
|
## Important things to know
|
||||||
|
|
||||||
This bot extends the rewrite version of discord.py. A couple of variables have been added to give you easy access to a couple of objects listed here.
|
This bot extends the rewrite version of discord.py. A couple of variables have been added to give you easy access to a couple of objects listed here.
|
||||||
|
|
||||||
> self.bot.ownerlist
|
> `self.bot.ownerlist`
|
||||||
|
|
||||||
self.ownerlist can be used to retrieve a `list` of user ID's. (`int`). Those ID's belong to contributors.
|
`self.ownerlist` can be used to retrieve a `list` of user ID's. (`int`). Those ID's belong to contributors.
|
||||||
> self.bot.defaultprefix
|
|
||||||
|
|
||||||
self.defaultprefix can be used to retrieve a `str` object of the default prefix.
|
> `self.bot.defaultprefix`
|
||||||
> self.bot.version
|
|
||||||
|
|
||||||
self.version can be used to retrieve a `float` which represent the version number of the bot.
|
`self.defaultprefix` can be used to retrieve a `str` object of the default prefix.
|
||||||
> self.bot.display_name
|
|
||||||
|
|
||||||
self.display_name returns a `str` which represent the display_name of the bot.
|
> `self.bot.version`
|
||||||
> self.bot.mainenance
|
|
||||||
|
`self.version` can be used to retrieve a `float` which represent the version number of the bot.
|
||||||
|
|
||||||
|
> `self.bot.display_name`
|
||||||
|
|
||||||
|
`self.display_name` returns a `str` which represent the `display_name` of the bot.
|
||||||
|
|
||||||
|
> `self.bot.mainenance`
|
||||||
|
|
||||||
|
`self.maintenance` is equal to `True` or `False`. If you would like to exclude code in the master branch, use this.
|
||||||
|
Make sure this one is installed. Example:
|
||||||
|
|
||||||
self.maintenance is equal to `True` or `False`. If you would like to exclude code in the master branch, use this.
|
|
||||||
Make sure this one is installed.
|
|
||||||
example:
|
|
||||||
```py
|
```py
|
||||||
if self.bot.mainenance:
|
if self.bot.mainenance:
|
||||||
print('I am in the development branch')
|
print('I am in the development branch')
|
||||||
|
|
||||||
if not self.bot.mainenance:
|
if not self.bot.mainenance:
|
||||||
print('I am in the master branch)
|
print('I am in the master branch')
|
||||||
```
|
```
|
||||||
With other words. self.mainenance returns False in production and True in developer modus.
|
In other words. `self.mainenance` returns `False` in production and `True` in developer modes.
|
||||||
|
|
||||||
> self.bot.embed_color
|
> `self.bot.embed_color`
|
||||||
|
|
||||||
self.embed_color can be used to use the default color of out embed theme.
|
`self.embed_color` can be used to use the default color of out embed theme.
|
||||||
```
|
|
||||||
|
```python
|
||||||
discord.Embed(title='Foo', description='bar', color=self.bot.embed_color)
|
discord.Embed(title='Foo', description='bar', color=self.bot.embed_color)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker environment
|
## Docker environment
|
||||||
This bot is heavly based on docker. This means it will run in a container. Other words. The code will run in a jail. Dont be afraid for bugs that cause harm. or commands that could potential restarts the server. Its safe.
|
This bot is heavly based on docker. This means it will run in a container. Other words. The code will run in a jail. Dont be afraid for bugs that cause harm. or commands that could potential restarts the server. It's safe.
|
||||||
|
|
||||||
There are a couple of things to know about docker within this project.
|
There are a couple of things to know about docker within this project.
|
||||||
1. Please read the docs of docker first before editing the docker files
|
|
||||||
2. If you need a pip package, place the name into requirements.txt, docker handles the rest.
|
1. Please read the docs of docker first before editing the docker files;
|
||||||
3. Everything in project folder is the workfolder of the docker container
|
2. If you need a pip package, place the name into requirements.txt: docker handles the rest;
|
||||||
4. Initialize cogs by adding them into cogs.txt. one line is one cogfile
|
3. Everything in project folder is the workfolder of the docker container;
|
||||||
|
4. Initialize cogs by adding them into `cogs.txt`: one line is one cogfile.
|
||||||
|
|
||||||
## Initialize a cog
|
## Initialize a cog
|
||||||
Put your cog in `src/cogs` and edit the `cogs.txt` file. Add the filename of your cog into `cogs.txt`. No absolute path, just the name.
|
Put your cog in `src/cogs` and edit the `cogs.txt` file. Add the filename of your cog into `cogs.txt`. No absolute path, just the name.
|
||||||
@ -57,7 +61,12 @@ There is a git command available provided by Dusty. `S!git pull` should pull the
|
|||||||
If you are stuck in any way shape or form you can always contact anyone who works on this project. Dont forget to check `S!help`.
|
If you are stuck in any way shape or form you can always contact anyone who works on this project. Dont forget to check `S!help`.
|
||||||
|
|
||||||
## Project links:
|
## Project links:
|
||||||
- http://discord.gg/GWdhBSp
|
- http://discord.gg/GWdhBSp
|
||||||
- http://chillout.ueuo.com
|
- http://chillout.ueuo.com
|
||||||
- http://trello.com/b/x02goBbW/sebis-bot-tutorial-roadmap
|
- http://trello.com/b/x02goBbW/sebis-bot-tutorial-roadmap
|
||||||
|
|
||||||
|
## Deploy to heroku
|
||||||
|
|
||||||
|
For testing purposes you can click the link below to build your own copy of this repo you just pick an app name fill in the config variables then switch it on in resources tab.
|
||||||
|
|
||||||
|
[](https://heroku.com/deploy?template=https://github.com/Annihilator708/Sebi-Machine/tree/development)
|
||||||
|
|||||||
15
app.json
Normal file
15
app.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name":"sebi-machine",
|
||||||
|
"stack":"container",
|
||||||
|
"env": {
|
||||||
|
|
||||||
|
"ownerlist": {
|
||||||
|
"description": "comma seperated list of owner ids",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"botkey": {
|
||||||
|
"description": "bot token",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,4 +16,4 @@ RUN python3.6 -m pip install --upgrade pip && \
|
|||||||
python3.6 -m pip install -r requirements.txt && \
|
python3.6 -m pip install -r requirements.txt && \
|
||||||
python3.6 -m pip install -U git+https://github.com/Rapptz/discord.py@rewrite#egg=discord.py[voice]
|
python3.6 -m pip install -U git+https://github.com/Rapptz/discord.py@rewrite#egg=discord.py[voice]
|
||||||
|
|
||||||
cmd ["python3.6","run.py"]
|
cmd ["python3.6","-m","src"]
|
||||||
|
|||||||
3
heroku.yml
Normal file
3
heroku.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
build:
|
||||||
|
docker:
|
||||||
|
worker: dockerfile
|
||||||
59
package-lock.json
generated
Normal file
59
package-lock.json
generated
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"name": "sebi-machine",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"async-limiter": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
|
||||||
|
},
|
||||||
|
"discord.js": {
|
||||||
|
"version": "11.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-11.3.2.tgz",
|
||||||
|
"integrity": "sha512-Abw9CTMX3Jb47IeRffqx2VNSnXl/OsTdQzhvbw/JnqCyqc2imAocc7pX2HoRmgKd8CgSqsjBFBneusz/E16e6A==",
|
||||||
|
"requires": {
|
||||||
|
"long": "^4.0.0",
|
||||||
|
"prism-media": "^0.0.2",
|
||||||
|
"snekfetch": "^3.6.4",
|
||||||
|
"tweetnacl": "^1.0.0",
|
||||||
|
"ws": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"long": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||||
|
},
|
||||||
|
"prism-media": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-L6yc8P5NVG35ivzvfI7bcTYzqFV+K8gTfX9YaJbmIFfMXTs71RMnAupvTQPTCteGsiOy9QcNLkQyWjAafY/hCQ=="
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||||
|
},
|
||||||
|
"snekfetch": {
|
||||||
|
"version": "3.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/snekfetch/-/snekfetch-3.6.4.tgz",
|
||||||
|
"integrity": "sha512-NjxjITIj04Ffqid5lqr7XdgwM7X61c/Dns073Ly170bPQHLm6jkmelye/eglS++1nfTWktpP6Y2bFXjdPlQqdw=="
|
||||||
|
},
|
||||||
|
"tweetnacl": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-cT2LgY2kIGh0C/aDhtBHnmb8ins="
|
||||||
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
|
||||||
|
"requires": {
|
||||||
|
"async-limiter": "~1.0.0",
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
package.json
Normal file
22
package.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"name": "sebi-machine",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Dedicated Discord bot for [Sebi's bot tutorial](http://discord.gg/GWdhBSp).",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/dustinpianalto/Sebi-Machine.git"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/dustinpianalto/Sebi-Machine/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/dustinpianalto/Sebi-Machine#readme",
|
||||||
|
"dependencies": {
|
||||||
|
"discord.js": "^11.3.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,10 @@
|
|||||||
yarl<1.2
|
yarl<1.2
|
||||||
numpy==1.14.0
|
numpy==1.14.0
|
||||||
uvloop
|
aiofiles
|
||||||
|
# aiomultiprocess
|
||||||
|
# aiosqlite
|
||||||
|
# asyncpg
|
||||||
|
# dataclasses
|
||||||
|
# cached_property
|
||||||
|
uvloop==0.9.1
|
||||||
|
aiohttp==3.2.1
|
||||||
83
run.py
83
run.py
@ -1,83 +0,0 @@
|
|||||||
# !/usr/bin/python
|
|
||||||
# -*- coding: utf8 -*-
|
|
||||||
|
|
||||||
# Import packages
|
|
||||||
import asyncio
|
|
||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
import json
|
|
||||||
import traceback
|
|
||||||
import random
|
|
||||||
|
|
||||||
# Import custom files
|
|
||||||
from src.config.config import LoadConfig
|
|
||||||
|
|
||||||
# If uvloop is installed, change to that eventloop policy as it
|
|
||||||
# is more efficient
|
|
||||||
try:
|
|
||||||
import uvloop
|
|
||||||
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
|
||||||
del uvloop
|
|
||||||
except BaseException as ex:
|
|
||||||
print(f'Could not load uvloop. {type(ex).__name__}: {ex};',
|
|
||||||
'reverting to default impl.')
|
|
||||||
else:
|
|
||||||
print(f'Using uvloop for asyncio event loop policy.')
|
|
||||||
|
|
||||||
|
|
||||||
# Bot Class
|
|
||||||
class SebiMachine(commands.Bot, LoadConfig):
|
|
||||||
"""This discord is dedicated to http://www.discord.gg/GWdhBSp"""
|
|
||||||
def __init__(self):
|
|
||||||
# Initialize and attach config / settings
|
|
||||||
LoadConfig.__init__(self)
|
|
||||||
commands.Bot.__init__(self, command_prefix=self.defaultprefix)
|
|
||||||
|
|
||||||
|
|
||||||
# Load plugins
|
|
||||||
# Add your cog file name in this list
|
|
||||||
with open('cogs.txt', 'r') as cog_file:
|
|
||||||
cogs = cog_file.readlines()
|
|
||||||
for cog in cogs:
|
|
||||||
print(f'Loaded:{cog}')
|
|
||||||
cog = cog.replace('\n', '')
|
|
||||||
self.load_extension(f'src.cogs.{cog}')
|
|
||||||
|
|
||||||
async def on_ready(self):
|
|
||||||
"""On ready function"""
|
|
||||||
if self.maintenance:
|
|
||||||
print('MAINTENANCE ACTIVE')
|
|
||||||
|
|
||||||
async def on_command_error(self, ctx, error):
|
|
||||||
"""
|
|
||||||
The event triggered when an error is raised while invoking a command.
|
|
||||||
ctx : Context
|
|
||||||
error : Exception
|
|
||||||
"""
|
|
||||||
jokes = ["I\'m a bit tipsy, I took to many screenshots...",
|
|
||||||
"I am rushing to the 24/7 store to get myself anti-bug spray...",
|
|
||||||
"Organizing turtle race...",
|
|
||||||
"There is no better place then 127.0.0.1...",
|
|
||||||
"Recycling Hex Decimal...",
|
|
||||||
"No worry, I get fixed :^)...",
|
|
||||||
"R.I.P, press F for respect...",
|
|
||||||
"The bug repellent dit not work...",
|
|
||||||
"You found a bug in the program. Unfortunately the joke did not fit here, better luck next time..."]
|
|
||||||
|
|
||||||
# catch error
|
|
||||||
error = error.__cause__ or error
|
|
||||||
tb = traceback.format_exception(type(error), error, error.__traceback__, limit=2, chain=False)
|
|
||||||
tb = ''.join(tb)
|
|
||||||
joke = random.choice(jokes)
|
|
||||||
fmt = f'**`{self.defaultprefix}{ctx.command}`**\n{joke}\n\n**{type(error).__name__}:**:\n```py\n{tb}\n```'
|
|
||||||
simple_fmt = f'**`{self.defaultprefix}{ctx.command}`**\n{joke}\n\n**{type(error).__name__}:**:\n**`{error}`**'
|
|
||||||
await ctx.send(fmt)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
client = SebiMachine()
|
|
||||||
# Make sure the key stays private.
|
|
||||||
with open('src/config/PrivateConfig.json') as fp:
|
|
||||||
PrivateConfig = json.load(fp)
|
|
||||||
fp.close()
|
|
||||||
client.run(PrivateConfig["bot-key"])
|
|
||||||
15
src/__init__.py
Normal file
15
src/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env python3.6
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Sebi-Machine.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__author__ = 'Annihilator708'
|
||||||
|
# TODO: add yourselves here. I can't remember everyones handles.
|
||||||
|
__contributors__ = (__author__, 'Neko404NotFound', 'Dusty.P', 'davfsa')
|
||||||
|
__license__ = 'MIT'
|
||||||
|
__title__ = 'Sebi-Machine'
|
||||||
|
__version__ = 'tbd'
|
||||||
|
|
||||||
|
__repository__ = f'https://github.com/{__author__}/{__title__}'
|
||||||
|
__url__ = __repository__
|
||||||
144
src/__main__.py
Normal file
144
src/__main__.py
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
# !/usr/bin/python
|
||||||
|
# -*- coding: utf8 -*-
|
||||||
|
"""
|
||||||
|
App entry point.
|
||||||
|
|
||||||
|
Something meaningful here, eventually.
|
||||||
|
"""
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import traceback
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
|
||||||
|
from src.config.config import LoadConfig
|
||||||
|
from src.shared_libs.loggable import Loggable
|
||||||
|
from src.shared_libs.ioutils import in_here
|
||||||
|
|
||||||
|
|
||||||
|
# Init logging to output on INFO level to stderr.
|
||||||
|
logging.basicConfig(level='INFO')
|
||||||
|
|
||||||
|
|
||||||
|
# If uvloop is installed, change to that eventloop policy as it
|
||||||
|
# is more efficient
|
||||||
|
try:
|
||||||
|
# https://stackoverflow.com/a/45700730
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
loop = asyncio.ProactorEventLoop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
logging.warning('Detected Windows. Changing event loop to ProactorEventLoop.')
|
||||||
|
else:
|
||||||
|
import uvloop
|
||||||
|
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
||||||
|
del uvloop
|
||||||
|
except BaseException as ex:
|
||||||
|
logging.warning(f'Could not load uvloop. {type(ex).__qualname__}: {ex};',
|
||||||
|
'reverting to default impl.')
|
||||||
|
else:
|
||||||
|
logging.info(f'Using uvloop for asyncio event loop policy.')
|
||||||
|
|
||||||
|
|
||||||
|
# Bot Class
|
||||||
|
# Might be worth moving this to it's own file?
|
||||||
|
class SebiMachine(commands.Bot, LoadConfig, Loggable):
|
||||||
|
"""This discord is dedicated to http://www.discord.gg/GWdhBSp"""
|
||||||
|
def __init__(self):
|
||||||
|
# Initialize and attach config / settings
|
||||||
|
LoadConfig.__init__(self)
|
||||||
|
commands.Bot.__init__(self, command_prefix=self.defaultprefix)
|
||||||
|
|
||||||
|
# Load plugins
|
||||||
|
# Add your cog file name in this list
|
||||||
|
with open(in_here('cogs.txt')) as cog_file:
|
||||||
|
cogs = cog_file.readlines()
|
||||||
|
|
||||||
|
for cog in cogs:
|
||||||
|
# Could this just be replaced with `strip()`?
|
||||||
|
cog = cog.replace('\n', '')
|
||||||
|
self.load_extension(f'src.cogs.{cog}')
|
||||||
|
self.logger.info(f'Loaded: {cog}')
|
||||||
|
|
||||||
|
async def on_ready(self):
|
||||||
|
"""On ready function"""
|
||||||
|
self.maintenance and self.logger.warning('MAINTENANCE ACTIVE')
|
||||||
|
|
||||||
|
async def on_command_error(self, ctx, error):
|
||||||
|
"""
|
||||||
|
The event triggered when an error is raised while invoking a command.
|
||||||
|
ctx : Context
|
||||||
|
error : Exception
|
||||||
|
"""
|
||||||
|
jokes = ["I\'m a bit tipsy, I took to many screenshots...",
|
||||||
|
"I am rushing to the 24/7 store to get myself anti-bug spray...",
|
||||||
|
"Organizing turtle race...",
|
||||||
|
"There is no better place then 127.0.0.1...",
|
||||||
|
"Recycling Hex Decimal...",
|
||||||
|
"No worry, I get fixed :^)...",
|
||||||
|
"R.I.P, press F for respect...",
|
||||||
|
"The bug repellent dit not work...",
|
||||||
|
"You found a bug in the program. Unfortunately the joke did not fit here, better luck next time..."]
|
||||||
|
|
||||||
|
# CommandErrors triggered by other propagating errors tend to get wrapped. This means
|
||||||
|
# if we have a cause, we should probably consider unwrapping that so we get a useful
|
||||||
|
# message.
|
||||||
|
|
||||||
|
# If command is not found, return
|
||||||
|
if isinstance(error, discord.ext.commands.errors.CommandNotFound):
|
||||||
|
return
|
||||||
|
|
||||||
|
error = error.__cause__ or error
|
||||||
|
tb = traceback.format_exception(type(error), error, error.__traceback__, limit=2, chain=False)
|
||||||
|
tb = ''.join(tb)
|
||||||
|
joke = random.choice(jokes)
|
||||||
|
fmt = f'**`{self.defaultprefix}{ctx.command}`**\n{joke}\n\n**{type(error).__name__}:**:\n```py\n{tb}\n```'
|
||||||
|
|
||||||
|
# Stops the error handler erroring.
|
||||||
|
try:
|
||||||
|
await ctx.send(fmt)
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
async def on_message(self, message):
|
||||||
|
# Make sure people can't change the username
|
||||||
|
if message.guild:
|
||||||
|
if message.guild.me.display_name != self.display_name:
|
||||||
|
try:
|
||||||
|
await message.guild.me.edit(nick=self.display_name)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if ('exec' in message.content or 'repl' in message.content or 'token' in message.content) \
|
||||||
|
and message.author != self.user:
|
||||||
|
await self.get_user(351794468870946827).send(f'{message.author.name} ({message.author.id}) is using me '
|
||||||
|
f'in DMs\n{message.content}')
|
||||||
|
|
||||||
|
# If author is a bot, ignore the message
|
||||||
|
if message.author.bot: return
|
||||||
|
|
||||||
|
# Make sure the command get processed as if it was typed with lowercase
|
||||||
|
# Split message.content one first space
|
||||||
|
command = message.content.split(None, 1)
|
||||||
|
if command:
|
||||||
|
command[0] = command[0].lower()
|
||||||
|
message.content = ' '.join(command)
|
||||||
|
message.content = ' '.join(command)
|
||||||
|
|
||||||
|
# process command
|
||||||
|
await self.process_commands(message)
|
||||||
|
|
||||||
|
|
||||||
|
client = SebiMachine()
|
||||||
|
# Make sure the key stays private.
|
||||||
|
# I am 99% certain this is valid!
|
||||||
|
with open(in_here('config', 'PrivateConfig.json')) as fp:
|
||||||
|
PrivateConfig = json.load(fp)
|
||||||
|
if PrivateConfig["bot-key"] == '':
|
||||||
|
PrivateConfig["bot-key"] = os.getenv('botkey')
|
||||||
|
|
||||||
|
client.run(PrivateConfig["bot-key"])
|
||||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
2
src/cogs/__init__.py
Normal file
2
src/cogs/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env python3.6
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
125
src/cogs/code.py
125
src/cogs/code.py
@ -1,5 +1,7 @@
|
|||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import traceback
|
import traceback
|
||||||
|
import discord
|
||||||
|
import inspect
|
||||||
import textwrap
|
import textwrap
|
||||||
from contextlib import redirect_stdout
|
from contextlib import redirect_stdout
|
||||||
import io
|
import io
|
||||||
@ -45,6 +47,7 @@ class REPL:
|
|||||||
if ctx.author.id not in self.bot.ownerlist:
|
if ctx.author.id not in self.bot.ownerlist:
|
||||||
return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10)
|
return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10)
|
||||||
|
|
||||||
|
|
||||||
if body is None:
|
if body is None:
|
||||||
return await ctx.send(
|
return await ctx.send(
|
||||||
'Please, use\n'
|
'Please, use\n'
|
||||||
@ -73,12 +76,13 @@ class REPL:
|
|||||||
exec(to_compile, env)
|
exec(to_compile, env)
|
||||||
except SyntaxError as e:
|
except SyntaxError as e:
|
||||||
try:
|
try:
|
||||||
msg = await ctx.send(f'```py\n{self.get_syntax_error(e)}\n```')
|
await ctx.send(f'```py\n{self.get_syntax_error(e)}\n```')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error = [self.get_syntax_error(e)[i:i+2000] for i in range(0, len(self.get_syntax_error(e)), 2000)]
|
error = [self.get_syntax_error(e)[i:i + 2000] for i in
|
||||||
|
range(0, len(self.get_syntax_error(e)), 2000)]
|
||||||
for i in error:
|
for i in error:
|
||||||
msg = await ctx.send(f'```py\n{i}\n```')
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
|
|
||||||
func = env['func']
|
func = env['func']
|
||||||
try:
|
try:
|
||||||
@ -87,40 +91,141 @@ class REPL:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
value = stdout.getvalue()
|
value = stdout.getvalue()
|
||||||
try:
|
try:
|
||||||
msg = await ctx.send(f'```py\n{value}{traceback.format_exc()}\n```')
|
await ctx.send(f'```py\n{value}{traceback.format_exc()}\n```')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error = [value[i:i + 2000] for i in range(0, len(value), 2000)]
|
error = [value[i:i + 2000] for i in range(0, len(value), 2000)]
|
||||||
for i in error:
|
for i in error:
|
||||||
await ctx.send(f'```py\n{i}\n```')
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
|
|
||||||
tracebackerror = [traceback.format_exc()[i:i + 2000] for i in range(0, len(traceback.format_exc()), 2000)]
|
tracebackerror = [traceback.format_exc()[i:i + 2000] for i in
|
||||||
|
range(0, len(traceback.format_exc()), 2000)]
|
||||||
for i in tracebackerror:
|
for i in tracebackerror:
|
||||||
msg = await ctx.send(f'```py\n{i}\n```')
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
else:
|
else:
|
||||||
value = stdout.getvalue()
|
value = stdout.getvalue()
|
||||||
if ret is None:
|
if ret is None:
|
||||||
if value:
|
if value:
|
||||||
try:
|
try:
|
||||||
msg = await ctx.send(f'```py\n{value}\n```')
|
await ctx.send(f'```py\n{value}\n```')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
code = [value[i:i + 1980] for i in range(0, len(value), 1980)]
|
code = [value[i:i + 1980] for i in range(0, len(value), 1980)]
|
||||||
for i in code:
|
for i in code:
|
||||||
msg = await ctx.send(f'```py\n{i}\n```')
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
else:
|
else:
|
||||||
self._last_result = ret
|
self._last_result = ret
|
||||||
try:
|
try:
|
||||||
code = [value[i:i + 1980] for i in range(0, len(value), 1980)]
|
code = [value[i:i + 1980] for i in range(0, len(value), 1980)]
|
||||||
for i in code:
|
for i in code:
|
||||||
msg = await ctx.send(f'```py\n{i}\n```')
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
code = [value[i:i + 1980] for i in range(0, len(value), 1980)]
|
code = [value[i:i + 1980] for i in range(0, len(value), 1980)]
|
||||||
for i in code:
|
for i in code:
|
||||||
await ctx.send(f'```py\n{i}\n```')
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
modifyd_ret = [ret[i:i + 1980] for i in range(0, len(ret), 1980)]
|
modifyd_ret = [ret[i:i + 1980] for i in range(0, len(ret), 1980)]
|
||||||
for i in modifyd_ret:
|
for i in modifyd_ret:
|
||||||
msg = await ctx.send(f'```py\n{i}\n```')
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
|
|
||||||
|
@commands.command(hidden=True)
|
||||||
|
async def repl(self, ctx):
|
||||||
|
"""
|
||||||
|
Start a interactive python shell in chat.
|
||||||
|
Only the owner of this bot can use this command.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
- repl < python code >
|
||||||
|
Example:
|
||||||
|
- repl print(205554)
|
||||||
|
"""
|
||||||
|
if ctx.author.id not in self.bot.ownerlist:
|
||||||
|
return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10)
|
||||||
|
|
||||||
|
msg = ctx.message
|
||||||
|
|
||||||
|
variables = {
|
||||||
|
'ctx': ctx,
|
||||||
|
'bot': self.bot,
|
||||||
|
'message': msg,
|
||||||
|
'server': msg.guild,
|
||||||
|
'channel': msg.channel,
|
||||||
|
'author': msg.author,
|
||||||
|
'_': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.channel.id in self.sessions:
|
||||||
|
msg = await ctx.send('Already running a REPL session in this channel. Exit it with `quit`.')
|
||||||
|
|
||||||
|
self.sessions.add(msg.channel.id)
|
||||||
|
|
||||||
|
await ctx.send('Enter code to execute or evaluate. `exit()` or `quit` to exit.')
|
||||||
|
|
||||||
|
while True:
|
||||||
|
response = await self.bot.wait_for('message', check=lambda m: m.content.startswith(
|
||||||
|
'`') and m.author == ctx.author and m.channel == ctx.channel)
|
||||||
|
|
||||||
|
cleaned = self.cleanup_code(response.content)
|
||||||
|
|
||||||
|
if cleaned in ('quit', 'exit', 'exit()'):
|
||||||
|
msg = await ctx.send('Exiting.')
|
||||||
|
self.sessions.remove(msg.channel.id)
|
||||||
|
return
|
||||||
|
|
||||||
|
executor = exec
|
||||||
|
if cleaned.count('\n') == 0:
|
||||||
|
# single statement, potentially 'eval'
|
||||||
|
try:
|
||||||
|
code = compile(cleaned, '<repl session>', 'eval')
|
||||||
|
except SyntaxError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
executor = eval
|
||||||
|
|
||||||
|
if executor is exec:
|
||||||
|
try:
|
||||||
|
code = compile(cleaned, '<repl session>', 'exec')
|
||||||
|
except SyntaxError as e:
|
||||||
|
try:
|
||||||
|
await ctx.send(f'```Python\n{self.get_syntax_error(e)}\n```')
|
||||||
|
except Exception as e:
|
||||||
|
error = [self.get_syntax_error(e)[i:i + 2000] for i in
|
||||||
|
range(0, len(self.get_syntax_error(e)), 2000)]
|
||||||
|
for i in error:
|
||||||
|
await ctx.send(f'```Python\n{i}\n```')
|
||||||
|
|
||||||
|
variables['message'] = response
|
||||||
|
fmt = None
|
||||||
|
stdout = io.StringIO()
|
||||||
|
try:
|
||||||
|
with redirect_stdout(stdout):
|
||||||
|
result = executor(code, variables)
|
||||||
|
if inspect.isawaitable(result):
|
||||||
|
result = await result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
value = stdout.getvalue()
|
||||||
|
await ctx.send(f'```Python\n{value}{traceback.format_exc()}\n```')
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
value = stdout.getvalue()
|
||||||
|
if result is not None:
|
||||||
|
fmt = '{}{}'.format(value, result)
|
||||||
|
variables['_'] = result
|
||||||
|
elif value:
|
||||||
|
fmt = value
|
||||||
|
|
||||||
|
try:
|
||||||
|
if fmt is not None:
|
||||||
|
if len(fmt) > 1980:
|
||||||
|
code = [fmt[i:i + 1980] for i in range(0, len(fmt), 1980)]
|
||||||
|
for i in code:
|
||||||
|
await ctx.send(f'```py\n{i}\n```')
|
||||||
|
else:
|
||||||
|
await ctx.send(fmt)
|
||||||
|
|
||||||
|
except discord.Forbidden:
|
||||||
|
pass
|
||||||
|
except discord.HTTPException as e:
|
||||||
|
await ctx.send(f'Unexpected error: `{e}`')
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
bot.add_cog(REPL(bot))
|
bot.add_cog(REPL(bot))
|
||||||
@ -1,9 +1,10 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: <encoding name> -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import os
|
import discord
|
||||||
import traceback
|
import traceback
|
||||||
|
import aiofiles
|
||||||
|
|
||||||
class Upload:
|
class Upload:
|
||||||
"""
|
"""
|
||||||
@ -82,11 +83,37 @@ class Upload:
|
|||||||
await ctx.send(f'Loaded `{extension}`.')
|
await ctx.send(f'Loaded `{extension}`.')
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def err(self, ctx):
|
async def permunload(self, ctx, extension=None):
|
||||||
"""triggers error to test traceback"""
|
"""Disables permanently a cog."""
|
||||||
await ctx.send(a)
|
await ctx.trigger_typing()
|
||||||
|
if ctx.author.id not in self.bot.ownerlist:
|
||||||
|
return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10)
|
||||||
|
|
||||||
|
if cog is None:
|
||||||
|
return await ctx.send("Please provide a extension. Do `help permunload` for more info")
|
||||||
|
|
||||||
|
extension = extension.lower()
|
||||||
|
|
||||||
|
async with aiofiles.open("extension.txt") as fp:
|
||||||
|
lines=fp.readlines()
|
||||||
|
|
||||||
|
removed = False
|
||||||
|
async with aiofiles.open("extension.txt", "w") as fp:
|
||||||
|
for i in lines:
|
||||||
|
if i.replace("\n", "") != extension:
|
||||||
|
fp.write(i)
|
||||||
|
else:
|
||||||
|
removed = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if removed is True:
|
||||||
|
try:
|
||||||
|
self.bot.unload_extension(extension)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return await ctx.send("Extension removed successfully")
|
||||||
|
|
||||||
|
await ctx.send("Extension not found")
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
bot.add_cog(Upload(bot))
|
bot.add_cog(Upload(bot))
|
||||||
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: <encoding name> -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import discord
|
import discord
|
||||||
@ -17,7 +17,7 @@ class CogName:
|
|||||||
now = ctx.message.created_at
|
now = ctx.message.created_at
|
||||||
msg = await ctx.send('Pong')
|
msg = await ctx.send('Pong')
|
||||||
sub = msg.created_at - now
|
sub = msg.created_at - now
|
||||||
await msg.edit(content=f'Pong, {sub.total_seconds() * 1000}')
|
await msg.edit(content=f'🏓Pong, **{sub.total_seconds() * 1000}ms**')
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: <encoding name> -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import discord
|
import discord
|
||||||
|
|||||||
@ -29,24 +29,27 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import logging
|
from src.shared_libs.utils import paginate, run_command
|
||||||
from ..shared_libs.utils import paginate, run_command
|
from src.shared_libs.loggable import Loggable
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
git_log = logging.getLogger('git')
|
|
||||||
|
|
||||||
|
class Git(Loggable):
|
||||||
class Git:
|
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@commands.group(case_insensitive=True)
|
@commands.group(case_insensitive=True, invoke_without_command=True)
|
||||||
async def git(self, ctx):
|
async def git(self, ctx):
|
||||||
"""Run help git for more info"""
|
"""Run help git for more info"""
|
||||||
pass
|
await ctx.send('https://github.com/dustinpianalto/Sebi-Machine/')
|
||||||
|
|
||||||
|
@commands.command(case_insensitive=True, brief='Gets the Trello link.')
|
||||||
|
async def trello(self, ctx):
|
||||||
|
await ctx.send('<https://trello.com/b/x02goBbW/sebis-bot-tutorial-roadmap>')
|
||||||
|
|
||||||
@git.command()
|
@git.command()
|
||||||
async def pull(self, ctx):
|
async def pull(self, ctx):
|
||||||
|
self.logger.warning('Invoking git-pull')
|
||||||
await ctx.trigger_typing()
|
await ctx.trigger_typing()
|
||||||
if ctx.author.id not in self.bot.ownerlist:
|
if ctx.author.id not in self.bot.ownerlist:
|
||||||
return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10)
|
return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10)
|
||||||
@ -55,6 +58,8 @@ class Git:
|
|||||||
color=self.bot.embed_color)
|
color=self.bot.embed_color)
|
||||||
em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}')
|
em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}')
|
||||||
|
|
||||||
|
# Pretty sure you can just do await run_command() if that is async,
|
||||||
|
# or run in a TPE otherwise.
|
||||||
result = await asyncio.wait_for(self.bot.loop.create_task(
|
result = await asyncio.wait_for(self.bot.loop.create_task(
|
||||||
run_command('git fetch --all')), 120) + '\n'
|
run_command('git fetch --all')), 120) + '\n'
|
||||||
result += await asyncio.wait_for(self.bot.loop.create_task(
|
result += await asyncio.wait_for(self.bot.loop.create_task(
|
||||||
|
|||||||
44
src/cogs/moderation.py
Normal file
44
src/cogs/moderation.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from discord.ext import commands
|
||||||
|
import discord
|
||||||
|
|
||||||
|
class Moderation:
|
||||||
|
"""
|
||||||
|
Moderation Commands
|
||||||
|
"""
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def sar(self, ctx):
|
||||||
|
"""Assign or remove self assigned roles."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def kick(self, ctx, member: discord.Member = None):
|
||||||
|
"""
|
||||||
|
Kick a discord member from your server.
|
||||||
|
Only contributors can use this command.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
- kick <discord.member>
|
||||||
|
|
||||||
|
"""
|
||||||
|
await ctx.trigger_typing()
|
||||||
|
if ctx.author.id not in self.bot.ownerlist:
|
||||||
|
return await ctx.send('Only my contributors can use me like this :blush:', delete_after=10)
|
||||||
|
|
||||||
|
if member is None:
|
||||||
|
await ctx.send('Are you sure you are capable of this command?')
|
||||||
|
try:
|
||||||
|
await member.kick()
|
||||||
|
await ctx.send(f'You kicked **`{member.name}`** from **`{ctx.guild.name}`**')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send('You may not use this command you do not have permission in server:\n\n**`{ctx.guild.name}`**'
|
||||||
|
f'\n\n```py\n{e}\n```')
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Moderation(bot))
|
||||||
67
src/cogs/sar.js
Normal file
67
src/cogs/sar.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
const Discord = require("discord.js");
|
||||||
|
|
||||||
|
exports.run = async function(client, message, args) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
aliases: sar, selfrole, selfroles
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- S!selfrole get 1 (adds heroku helper role)
|
||||||
|
- S!sar remove 3 (removes rewrite helper role)
|
||||||
|
- S!sar list (shows all roles)
|
||||||
|
*/
|
||||||
|
|
||||||
|
function roleFinder(query) {
|
||||||
|
return message.guild.roles.find(function(r) {
|
||||||
|
return r.name.includes(query)
|
||||||
|
}).id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = args[0]; // can be get, remove or list
|
||||||
|
|
||||||
|
if (type == "list" || type == undefined) {
|
||||||
|
|
||||||
|
const embed = new Discord.RichEmbed()
|
||||||
|
.setTitle("List of Self Assigned Roles")
|
||||||
|
.setDescription("Usage: `S!sar [ get | remove | list ] [ number ]`")
|
||||||
|
.addField("1. Heroku Helper", "S!sar get 1", true)
|
||||||
|
.addField("2. JS Helper", "S!sar get 2", true)
|
||||||
|
.addField("3. Rewrite Helper", "S!sar get 3", true)
|
||||||
|
.setColor("AQUA");
|
||||||
|
|
||||||
|
return message.channel.send({
|
||||||
|
embed: embed
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const roles = [roleFinder("Heroku"), roleFinder("JS"), roleFinder("Rewrite")];
|
||||||
|
|
||||||
|
let choice = args[1]; // can be 1, 2 or 3
|
||||||
|
|
||||||
|
// if the choice is not 1, 2 or 3
|
||||||
|
if (/^[123]$/.test(choice) == false) {
|
||||||
|
return message.channel.send("Enter a valid role number!"); // returns error message
|
||||||
|
} else {
|
||||||
|
choice -= 1; // because array indexing starts from 0. when they choose 1 it should be roles[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
case "get":
|
||||||
|
message.member.addRole(roles[choice]);
|
||||||
|
message.channel.send("Added the role you specified!"); // confirmation message
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "remove":
|
||||||
|
message.member.removeRole(roles[choice]);
|
||||||
|
message.channel.send("Removed the role you specified!"); // confirmation message
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return; // when it is neither get nor remove
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
97
src/cogs/tag.py
Normal file
97
src/cogs/tag.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
import json
|
||||||
|
import aiofiles
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
class Tag:
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
with open("src/shared_libs/tags.json", "r") as fp:
|
||||||
|
json_data = fp.read()
|
||||||
|
global tags
|
||||||
|
tags = json.loads(json_data)
|
||||||
|
|
||||||
|
@commands.group(case_insensitive=True, invoke_without_command=True)
|
||||||
|
async def tag(self, ctx, tag=None):
|
||||||
|
"""Gets a tag"""
|
||||||
|
await ctx.trigger_typing()
|
||||||
|
if tag is None:
|
||||||
|
return await ctx.send('Please provide a argument. Do `help tag` for more info')
|
||||||
|
|
||||||
|
found = tags.get(tag, None)
|
||||||
|
|
||||||
|
if found is None:
|
||||||
|
return await ctx.send('Tag not found')
|
||||||
|
|
||||||
|
await ctx.send(found)
|
||||||
|
|
||||||
|
@tag.command(case_insensitive=True)
|
||||||
|
async def list(self, ctx):
|
||||||
|
"""Lists available tags"""
|
||||||
|
await ctx.trigger_typing()
|
||||||
|
desc = ""
|
||||||
|
for i in tags:
|
||||||
|
desc = desc + i + "\n"
|
||||||
|
|
||||||
|
if desc == "":
|
||||||
|
desc = "None"
|
||||||
|
|
||||||
|
em = discord.Embed(title='Available tags:', description=desc ,colour=discord.Colour(0x00FFFF))
|
||||||
|
|
||||||
|
await ctx.send(embed=em)
|
||||||
|
|
||||||
|
@tag.command(case_insensitive=True)
|
||||||
|
async def add(self, ctx, tag_name=None, *, tag_info=None):
|
||||||
|
"""Adds a new tag"""
|
||||||
|
await ctx.trigger_typing()
|
||||||
|
if not ctx.author.guild_permissions.manage_guild:
|
||||||
|
return await ctx.send("You are not allowed to do this")
|
||||||
|
|
||||||
|
if tag_name is None or tag_info is None:
|
||||||
|
return await ctx.send("Please provide a tag name and the tag info. Do `help tag` for more info")
|
||||||
|
|
||||||
|
exists = False
|
||||||
|
for i in tags:
|
||||||
|
if i == tag_name:
|
||||||
|
exists = True
|
||||||
|
|
||||||
|
if not exists:
|
||||||
|
tags.update({tag_name : tag_info})
|
||||||
|
|
||||||
|
async with aiofiles.open("src/shared_libs/tags.json", "w") as fp:
|
||||||
|
json_data = json.dumps(tags)
|
||||||
|
await fp.write(json_data)
|
||||||
|
|
||||||
|
return await ctx.send("The tag has been added")
|
||||||
|
|
||||||
|
await ctx.send("The tag already exists")
|
||||||
|
|
||||||
|
@tag.command(case_insensitive=True)
|
||||||
|
async def remove(self, ctx, tag=None):
|
||||||
|
"""Remove a existing tag"""
|
||||||
|
await ctx.trigger_typing()
|
||||||
|
if not ctx.author.guild_permissions.manage_guild:
|
||||||
|
return await ctx.send("You are not allowed to do this")
|
||||||
|
|
||||||
|
if tag is None:
|
||||||
|
return await ctx.send("Please provide a tag name and the tag info. Do `help tag` for more info")
|
||||||
|
|
||||||
|
found = None
|
||||||
|
for i in tags:
|
||||||
|
if i == tag:
|
||||||
|
found = i
|
||||||
|
|
||||||
|
if found is not None:
|
||||||
|
del tags[found]
|
||||||
|
async with aiofiles.open("src/shared_libs/tags.json", "w") as fp:
|
||||||
|
json_data = json.dumps(tags)
|
||||||
|
await fp.write(json_data)
|
||||||
|
|
||||||
|
return await ctx.send("The tag has been removed")
|
||||||
|
|
||||||
|
await ctx.send("The tag has not been found")
|
||||||
|
|
||||||
|
|
||||||
|
def setup(bot):
|
||||||
|
bot.add_cog(Tag(bot))
|
||||||
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 0.1,
|
|
||||||
"display_name" : "[S!] Sebi-Machine",
|
|
||||||
"maintenance": "True",
|
|
||||||
"ownerlist": [],
|
|
||||||
"prefix": "S!"
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"bot-key": ""
|
|
||||||
}
|
|
||||||
2
src/config/__init__.py
Normal file
2
src/config/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env python3.6
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import discord
|
import discord
|
||||||
|
import os
|
||||||
|
|
||||||
class LoadConfig:
|
class LoadConfig:
|
||||||
"""
|
"""
|
||||||
@ -14,6 +15,7 @@ class LoadConfig:
|
|||||||
self.config = json.load(fp)
|
self.config = json.load(fp)
|
||||||
|
|
||||||
# Initialize config
|
# Initialize config
|
||||||
|
|
||||||
self.ownerlist = self.config["ownerlist"]
|
self.ownerlist = self.config["ownerlist"]
|
||||||
self.defaultprefix = self.config["prefix"]
|
self.defaultprefix = self.config["prefix"]
|
||||||
self.version = self.config["version"]
|
self.version = self.config["version"]
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
example
|
example
|
||||||
contributors
|
contributors
|
||||||
|
tag
|
||||||
code
|
code
|
||||||
git
|
git
|
||||||
fun
|
fun
|
||||||
|
moderation
|
||||||
47
src/run.js
Normal file
47
src/run.js
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
const Discord = require("discord.js");
|
||||||
|
const client = new Discord.Client();
|
||||||
|
|
||||||
|
const config = require("./config/Config.json");
|
||||||
|
const prefix = config.prefix;
|
||||||
|
|
||||||
|
const aliases = require("./shared_libs/aliases.json");
|
||||||
|
const privateConfig = require("./config/PrivateConfig.json");
|
||||||
|
|
||||||
|
const fs = require("fs");
|
||||||
|
const commands = fs.readdirSync("./cogs").filter(function(e) {
|
||||||
|
return e.endsWith(".js");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
client.on("message", function(message) {
|
||||||
|
|
||||||
|
if (message.guild.id != "265828729970753537") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.content.startsWith(config.prefix) == false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = message.content.replace(prefix, "");
|
||||||
|
|
||||||
|
let command = msg.split(" ")[0];
|
||||||
|
const args = msg.split(" ").slice(1);
|
||||||
|
|
||||||
|
command = aliases[command];
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (commands.includes(`${command}.js`)) {
|
||||||
|
require(`./cogs/${command}.js`).run(client, message, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
|
||||||
|
// handling errors
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
client.login(privateConfig["bot-key"]);
|
||||||
2
src/shared_libs/__init__.py
Normal file
2
src/shared_libs/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env python3.6
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
62
src/shared_libs/ioutils.py
Normal file
62
src/shared_libs/ioutils.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python3.6
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
IO stuff.
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ('in_here',)
|
||||||
|
|
||||||
|
|
||||||
|
def in_here(first_path_bit: str, *path_bits: str, stack_depth: int=0) -> str:
|
||||||
|
"""
|
||||||
|
A somewhat voodooish and weird piece of code. This enables us to
|
||||||
|
directly refer to a file in the same directory as the code that
|
||||||
|
calls this method, without any due regard for where in the file
|
||||||
|
system tree it is.
|
||||||
|
|
||||||
|
Apart from that, this will behave just like os.path.join, meaning
|
||||||
|
that varaidic strings will be joined with the appropriate
|
||||||
|
directory separator for the current platform.
|
||||||
|
|
||||||
|
This works by inspecting the stack frame for the caller.
|
||||||
|
If you are planning on nesting this call in another utility and wish
|
||||||
|
for the stack to refer to that caller, you should increment
|
||||||
|
the ``stack_depth`` arg for each nested call you make. By default,
|
||||||
|
you can ignore this and it will default to 0.
|
||||||
|
|
||||||
|
:param first_path_bit: the initial path bit to operate with. This is
|
||||||
|
required.
|
||||||
|
:param path_bits: additional bits of path to construct relative to here.
|
||||||
|
These are entirely optional.
|
||||||
|
:param stack_depth: optional, defaults to 0. How many nested calls
|
||||||
|
we expect this to be called in. Affects the relative directory
|
||||||
|
that is used.
|
||||||
|
:returns: the absolute path to the given relative path provided.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
frame = inspect.stack()[1 + stack_depth]
|
||||||
|
except IndexError:
|
||||||
|
raise RuntimeError('Could not find a stack record. Interpreter has '
|
||||||
|
'been shot.')
|
||||||
|
else:
|
||||||
|
module = inspect.getmodule(frame[0])
|
||||||
|
assert hasattr(module, '__file__'), 'No `__file__\' attr, welp.'
|
||||||
|
|
||||||
|
# https://docs.python.org/3/library/inspect.html#the-interpreter-stack
|
||||||
|
# If Python caches strings rather than copying when we move them
|
||||||
|
# around or modify them, then this may cause a referential cycle which
|
||||||
|
# will consume more memory and stop the garbage collection system
|
||||||
|
# from working correctly. Best thing to do here is deepcopy anything
|
||||||
|
# we need and prevent this occuring. Del the references to allow them
|
||||||
|
# to be freed.
|
||||||
|
file = module.__file__
|
||||||
|
file = copy.deepcopy(file)
|
||||||
|
del module, frame
|
||||||
|
dir_name = os.path.dirname(file)
|
||||||
|
abs_dir_name = os.path.abspath(dir_name)
|
||||||
|
pathish = os.path.join(abs_dir_name, first_path_bit, *path_bits)
|
||||||
|
return pathish
|
||||||
24
src/shared_libs/loggable.py
Normal file
24
src/shared_libs/loggable.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env python3.6
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Neko404NotFound 2018, MIT
|
||||||
|
|
||||||
|
A mixin class that injects a suitably named logger into class scope
|
||||||
|
at runtime.
|
||||||
|
|
||||||
|
Chosen to make this a slotted class, which means (as far as I can remember)
|
||||||
|
that it is not suitable to be made into an abc.ABC class. Slots will
|
||||||
|
enable derived slotted classes to be a bit more efficient at runtime and
|
||||||
|
boast faster lookups.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ('Loggable',)
|
||||||
|
|
||||||
|
|
||||||
|
class Loggable:
|
||||||
|
__slots__ = ('logger',)
|
||||||
|
|
||||||
|
def __init_subclass__(cls, **_):
|
||||||
|
cls.logger = logging.getLogger(cls.__qualname__)
|
||||||
1
src/shared_libs/tags.json
Normal file
1
src/shared_libs/tags.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
Loading…
x
Reference in New Issue
Block a user