├── tests ├── __init__.py ├── injectables │ ├── __init__.py │ └── discord_dependencies.py ├── test_plugin_roleinfo.py ├── test_plugin_user_info.py ├── test_plugin_auto_role.py ├── test_plugin_template.py ├── test_plugin_search_react.py ├── test_plugin_butts.py ├── test_plugin_num_users.py ├── test_plugin_pet_ozzy.py ├── test_plugin_pet_monty.py ├── test_plugin_poop.py ├── test_plugin_ozzy_stats.py ├── test_plugin_monty_stats.py ├── test_plugin_help.py ├── test_plugin_giveaway.py └── test_plugin_poll.py ├── utils ├── __init__.py ├── Examples.py └── config_utils.py ├── plugins ├── __init__.py ├── configs │ └── __init__.py ├── plugin_butts.py ├── plugin_poop.py ├── plugin_template.py ├── plugin_numusers.py ├── plugin_roleinfo.py ├── plugin_slap.py ├── plugin_set_status.py ├── plugin_ozzy_stats.py ├── plugin_monty_stats.py ├── plugin_sarcasm.py ├── plugin_help.py ├── plugin_services.py ├── plugin_server_info.py ├── plugin_pet_ozzy.py ├── plugin_pet_monty.py ├── plugin_search_react.py ├── plugin_user_info.py ├── plugin_code_generator.py ├── plugin_founders.py ├── plugin_auto_role.py ├── plugin_search_and_destroy.py ├── plugin_react_role.py ├── plugin_cleanup_raid.py └── plugin_giveaway.py ├── startup.sh ├── .coverage ├── .gitattributes ├── requirements.txt ├── .vscode └── settings.json ├── .gitignore ├── users.txt ├── Dockerfile ├── logger.py ├── README.md ├── .travis.yml ├── .github └── workflows │ ├── deploy_production.yml │ ├── test.yml │ └── build_push.yml └── discord_bot.py /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugins/configs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/injectables/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_plugin_roleinfo.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/test_plugin_user_info.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /startup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | screen -S discordbot -d -m python discord_bot.py -------------------------------------------------------------------------------- /.coverage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justcallmekoko/PythonDiscordBot/HEAD/.coverage -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asyncio 2 | discord 3 | mcrcon 4 | python-dotenv 5 | requests 6 | pytz 7 | pytest-cov 8 | codecov 9 | youtube_dl 10 | PyNaCl 11 | openai 12 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.testing.pytestArgs": [ 3 | "tests" 4 | ], 5 | "python.testing.unittestEnabled": false, 6 | "python.testing.pytestEnabled": true 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | *_conf.json 3 | montystats.json 4 | ozzystats.json 5 | users.txt 6 | plugin_add_user.py 7 | plugin_remove_user.py 8 | plugin_prune_minecraft.py 9 | __pycache__/ 10 | *.log 11 | users.txt -------------------------------------------------------------------------------- /users.txt: -------------------------------------------------------------------------------- 1 | ElderMohawk#6517:jeremiahharmon 2 | mlodawy#6969:mlodawy 3 | trisp3ar#7694:trisp3ar 4 | Exzed#1817:TUBICINO 5 | jmort#5552:Jaimort 6 | WillStunForFood#8607:Phantom_Revived 7 | melzie_bee#5717:Melz554877 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8-slim-buster 2 | 3 | RUN mkdir -p /var/log/discord_bot 4 | 5 | WORKDIR /app 6 | 7 | RUN apt-get update && apt-get install -y procps net-tools screen 8 | 9 | COPY requirements.txt requirements.txt 10 | RUN pip3 install -r requirements.txt 11 | 12 | COPY . . 13 | 14 | # Run app directly 15 | CMD [ "python3", "discord_bot.py" ] 16 | 17 | # Run app in a screen session via startup script 18 | #CMD [ "startup.sh" ] -------------------------------------------------------------------------------- /tests/test_plugin_auto_role.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import asyncio 4 | #import pytest 5 | import unittest 6 | sys.dont_write_bytecode = True 7 | sys.path.append(os.path.abspath('plugins')) 8 | 9 | from plugin_auto_role import AutoRole 10 | 11 | class TestAutoRole(unittest.TestCase): 12 | 13 | def test_check_bits(self): 14 | autorole = AutoRole() 15 | autorole.startService() 16 | assert autorole.checkBits(0) == False 17 | 18 | def test_check_cat_true(self): 19 | autorole = AutoRole() 20 | autorole.startService() 21 | assert autorole.checkCat('admin') == True 22 | 23 | def test_check_cat_false(self): 24 | autorole = AutoRole() 25 | autorole.startService() 26 | assert autorole.checkCat('arrow') == False 27 | 28 | if __name__ == '__main__': 29 | unittest.main() -------------------------------------------------------------------------------- /logger.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | from logging.handlers import RotatingFileHandler 4 | 5 | logger = logging.getLogger("discord_bot") 6 | logger.setLevel(logging.DEBUG) 7 | 8 | # Check/Create log dir 9 | if not os.path.exists('log'): 10 | os.makedirs('log') 11 | 12 | # Add a file rotating handler to logger 13 | try: 14 | handler = RotatingFileHandler('log/discord_bot.log', maxBytes=1000000, backupCount=10) 15 | except: 16 | handler = RotatingFileHandler('discord_bot.log', maxBytes=1000000, backupCount=10) 17 | 18 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(filename)s:%(lineno)d: %(message)s') 19 | handler.setFormatter(formatter) 20 | logger.addHandler(handler) 21 | 22 | streamhandler = logging.StreamHandler() 23 | streamhandler.setFormatter(formatter) 24 | logger.addHandler(streamhandler) 25 | 26 | logger.debug('Logger started') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PythonDiscordBot 2 | [![Test](https://github.com/justcallmekoko/PythonDiscordBot/actions/workflows/test.yml/badge.svg)](https://github.com/justcallmekoko/PythonDiscordBot/actions/workflows/test.yml) 3 | [![Travis Test](https://img.shields.io/travis/justcallmekoko/pythondiscordbot/main?label=Travis%20Test)](https://app.travis-ci.com/github/justcallmekoko/PythonDiscordBot) 4 | [![Build and Push](https://github.com/justcallmekoko/PythonDiscordBot/actions/workflows/build_push.yml/badge.svg)](https://github.com/justcallmekoko/PythonDiscordBot/actions/workflows/build_push.yml) 5 | [![codecov](https://codecov.io/gh/justcallmekoko/PythonDiscordBot/branch/main/graph/badge.svg?token=TNCWYVYCM2)](https://codecov.io/gh/justcallmekoko/PythonDiscordBot) 6 | 7 | A modular Python bot that integrates with Discord 8 | 9 | Read the [Contribution Guide](https://github.com/justcallmekoko/PythonDiscordBot/wiki/contribution-guide) if you would like to help 10 | 11 | If you have any questions, feel free to hop onto the [Discord Server](https://discord.gg/invite/w5JmasxvKA) 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: python 4 | python: 3.8 5 | 6 | before_install: 7 | - sudo rm -rf /var/lib/apt/lists/* 8 | - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - 9 | - sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) edge" 10 | - sudo apt-get update 11 | - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce 12 | - mkdir -vp ~/.docker/cli-plugins/ 13 | - curl --silent -L "https://github.com/docker/buildx/releases/download/v0.3.0/buildx-v0.3.0.linux-amd64" > ~/.docker/cli-plugins/docker-buildx 14 | - chmod a+x ~/.docker/cli-plugins/docker-buildx 15 | 16 | install: 17 | - pip3 install -r requirements.txt 18 | 19 | script: 20 | # - pytest --cov-report term --cov=plugins tests/ 21 | - docker buildx version 22 | - coverage run --source=plugins -m unittest discover tests -v 23 | - coverage report 24 | # - pytest --cov-report xml:cov.xml --cov=plugins tests/ 25 | 26 | after_success: 27 | - codecov 28 | 29 | notifications: 30 | email: 31 | on_success: always 32 | on_failure: always 33 | webhooks: 34 | on_success: never 35 | on_failure: always 36 | -------------------------------------------------------------------------------- /tests/test_plugin_template.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | sys.dont_write_bytecode = True 5 | sys.path.append(os.path.abspath('plugins')) 6 | sys.path.append(os.path.abspath('tests')) 7 | 8 | from plugin_template import Template 9 | from injectables.discord_dependencies import * 10 | 11 | class TestTemplate(unittest.TestCase): 12 | obj = Template() 13 | 14 | def test_check_bits(self): 15 | assert self.obj.checkBits(0) == False 16 | 17 | def test_check_cat_true(self): 18 | assert self.obj.checkCat('admin') == True 19 | 20 | def test_check_cat_false(self): 21 | assert self.obj.checkCat('arrow') == False 22 | 23 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 24 | obj = Template() 25 | 26 | a_channel = channel() 27 | a_message = message('!help', a_channel) 28 | a_plugin = plugin('!help', 'help menu', '!help') 29 | 30 | obj_list = [a_plugin] 31 | 32 | async def test_run_cheer(self): 33 | assert await self.obj.runCheer('potato', 0) == True 34 | 35 | async def test_run(self): 36 | assert await self.obj.run(self.a_message, 0) == True 37 | 38 | async def test_stop(self): 39 | await self.obj.stop('potato') 40 | 41 | if __name__ == '__main__': 42 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_search_react.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | sys.dont_write_bytecode = True 5 | sys.path.append(os.path.abspath('plugins')) 6 | sys.path.append(os.path.abspath('tests')) 7 | 8 | from plugin_search_react import SearchReact 9 | from injectables.discord_dependencies import * 10 | 11 | class TestSearchReact(unittest.TestCase): 12 | obj = SearchReact() 13 | 14 | def test_check_bits(self): 15 | assert self.obj.checkBits(0) == False 16 | 17 | def test_check_cat_true(self): 18 | assert self.obj.checkCat('admin') == True 19 | 20 | def test_check_cat_false(self): 21 | assert self.obj.checkCat('arrow') == False 22 | 23 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 24 | obj = SearchReact() 25 | 26 | a_channel = channel() 27 | a_message = message('!searchreact 1 @User', a_channel) 28 | a_plugin = plugin('!help', 'help menu', '!help') 29 | 30 | obj_list = [a_plugin] 31 | 32 | async def test_run_cheer(self): 33 | assert await self.obj.runCheer('potato', 0) == True 34 | 35 | async def test_run(self): 36 | assert await self.obj.run(self.a_message, 0) == True 37 | 38 | async def test_stop(self): 39 | await self.obj.stop('potato') 40 | 41 | if __name__ == '__main__': 42 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_butts.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | #import pytest 4 | import unittest 5 | sys.dont_write_bytecode = True 6 | sys.path.append(os.path.abspath('plugins')) 7 | sys.path.append(os.path.abspath('tests')) 8 | 9 | from plugin_butts import Butts 10 | from injectables.discord_dependencies import * 11 | 12 | class TestButts(unittest.TestCase): 13 | 14 | def test_check_bits(self): 15 | butt = Butts() 16 | assert butt.checkBits(0) == False 17 | 18 | def test_check_cat_true(self): 19 | butt = Butts() 20 | assert butt.checkCat('admin') == True 21 | 22 | def test_check_cat_false(self): 23 | butt = Butts() 24 | assert butt.checkCat('arrow') == False 25 | 26 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 27 | async def test_run_cheer(self): 28 | butt = Butts() 29 | assert await butt.runCheer('potato', 0) == True 30 | 31 | async def test_run(self): 32 | butt = Butts() 33 | a_channel = channel() 34 | a_message = message('!help', a_channel) 35 | a_plugin = plugin('!help', 'help menu', '!help') 36 | 37 | obj_list = [a_plugin] 38 | 39 | assert await butt.run(a_message, obj_list) 40 | 41 | async def test_stop(self): 42 | butt = Butts() 43 | await butt.stop('potato') 44 | 45 | if __name__ == '__main__': 46 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_num_users.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | #import pytest 4 | import unittest 5 | sys.dont_write_bytecode = True 6 | sys.path.append(os.path.abspath('plugins')) 7 | sys.path.append(os.path.abspath('tests')) 8 | 9 | from plugin_numusers import Numusers 10 | from injectables.discord_dependencies import * 11 | 12 | class TestNumusers(unittest.TestCase): 13 | numusers = Numusers() 14 | 15 | def test_check_bits(self): 16 | assert self.numusers.checkBits(0) == False 17 | 18 | def test_check_cat_true(self): 19 | assert self.numusers.checkCat('admin') == True 20 | 21 | def test_check_cat_false(self): 22 | assert self.numusers.checkCat('arrow') == False 23 | 24 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 25 | numusers = Numusers() 26 | 27 | async def test_run_cheer(self): 28 | assert await self.numusers.runCheer('potato', 0) == True 29 | 30 | async def test_run(self): 31 | a_channel = channel() 32 | a_message = message('!numusers', a_channel) 33 | a_plugin = plugin('!help', 'help menu', '!help') 34 | 35 | obj_list = [a_plugin] 36 | 37 | assert await self.numusers.run(a_message, obj_list) == True 38 | 39 | async def test_stop(self): 40 | await self.numusers.stop('potato') 41 | 42 | if __name__ == '__main__': 43 | unittest.main() -------------------------------------------------------------------------------- /.github/workflows/deploy_production.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Deploy 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | workflow_run: 9 | workflows: ["Build and Push"] 10 | branches: [main] 11 | types: 12 | - completed 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | jobs: 19 | # This workflow contains a single job called "build" 20 | deploy: 21 | if: github.event.workflow_run.conclusion == 'success' 22 | name: Deploy 23 | environment: PRODUCTION 24 | env: 25 | key1: ${{ secrets.TEST_VAR }} 26 | # The type of runner that the job will run on 27 | runs-on: ubuntu-latest 28 | 29 | # Steps represent a sequence of tasks that will be executed as part of the job 30 | steps: 31 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 32 | - uses: actions/checkout@v3 33 | 34 | - name: Use Test Secret 35 | run: echo ${{ env.key1 }} 36 | 37 | - name: Show workflow event 38 | run: echo ${{ github.event.workflow_run.event }} 39 | 40 | -------------------------------------------------------------------------------- /tests/test_plugin_pet_ozzy.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | sys.dont_write_bytecode = True 5 | sys.path.append(os.path.abspath('plugins')) 6 | sys.path.append(os.path.abspath('tests')) 7 | 8 | from plugin_pet_ozzy import PetOzzy 9 | from injectables.discord_dependencies import * 10 | 11 | class TestPetOzzy(unittest.TestCase): 12 | obj = PetOzzy() 13 | 14 | def test_check_bits(self): 15 | assert self.obj.checkBits(0) == False 16 | 17 | def test_check_cat_true(self): 18 | assert self.obj.checkCat('admin') == True 19 | 20 | def test_check_cat_false(self): 21 | assert self.obj.checkCat('arrow') == False 22 | 23 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 24 | obj = PetOzzy() 25 | 26 | a_channel = channel() 27 | a_message = message('!petozzy', a_channel) 28 | a_plugin = plugin('!help', 'help menu', '!help') 29 | 30 | obj_list = [a_plugin] 31 | 32 | async def test_run_cheer(self): 33 | assert await self.obj.runCheer('potato', 0) == True 34 | 35 | async def test_run_twice(self): 36 | print('discord_user: ' + str(self.a_message.author)) 37 | assert await self.obj.run(self.a_message, self.obj_list) == True 38 | assert await self.obj.run(self.a_message, self.obj_list) == True 39 | 40 | async def test_stop(self): 41 | await self.obj.stop('potato') 42 | 43 | if __name__ == '__main__': 44 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_pet_monty.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | sys.dont_write_bytecode = True 5 | sys.path.append(os.path.abspath('plugins')) 6 | sys.path.append(os.path.abspath('tests')) 7 | 8 | from plugin_pet_monty import PetMonty 9 | from injectables.discord_dependencies import * 10 | 11 | class TestPetMonty(unittest.TestCase): 12 | obj = PetMonty() 13 | 14 | def test_check_bits(self): 15 | assert self.obj.checkBits(0) == False 16 | 17 | def test_check_cat_true(self): 18 | assert self.obj.checkCat('admin') == True 19 | 20 | def test_check_cat_false(self): 21 | assert self.obj.checkCat('arrow') == False 22 | 23 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 24 | obj = PetMonty() 25 | 26 | a_channel = channel() 27 | a_message = message('!petmonty', a_channel) 28 | a_plugin = plugin('!help', 'help menu', '!help') 29 | 30 | obj_list = [a_plugin] 31 | 32 | async def test_run_cheer(self): 33 | assert await self.obj.runCheer('potato', 0) == True 34 | 35 | async def test_run_twice(self): 36 | print('discord_user: ' + str(self.a_message.author)) 37 | assert await self.obj.run(self.a_message, self.obj_list) == True 38 | assert await self.obj.run(self.a_message, self.obj_list) == True 39 | 40 | async def test_stop(self): 41 | await self.obj.stop('potato') 42 | 43 | if __name__ == '__main__': 44 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_poop.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | sys.dont_write_bytecode = True 5 | sys.path.append(os.path.abspath('plugins')) 6 | sys.path.append(os.path.abspath('tests')) 7 | 8 | from plugin_poop import Poop 9 | from injectables.discord_dependencies import * 10 | 11 | class TestPoop(unittest.TestCase): 12 | def test_checkCat_admin(self): 13 | poop = Poop() 14 | assert poop.checkCat('admin') == True 15 | 16 | def test_checkCat_false(self): 17 | poop = Poop() 18 | assert poop.checkCat('poop') == False 19 | 20 | def test_checkCat_empty(self): 21 | poop = Poop() 22 | assert poop.checkCat(None) == False 23 | 24 | def test_checkBits_true(self): 25 | poop = Poop() 26 | assert poop.checkBits('Please kill me') == False 27 | 28 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 29 | async def test_runCheer(self): 30 | poop = Poop() 31 | assert await poop.runCheer('Your Mom', 100) == True 32 | 33 | async def test_run(self): 34 | poop = Poop() 35 | chan = channel() 36 | msg = message('Yo', chan) 37 | assert await poop.run(msg, '') == True 38 | 39 | async def test_stop(self): 40 | poop = Poop() 41 | await poop.stop('poop') 42 | 43 | if __name__ == '__main__': 44 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_ozzy_stats.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | sys.dont_write_bytecode = True 5 | sys.path.append(os.path.abspath('plugins')) 6 | sys.path.append(os.path.abspath('tests')) 7 | 8 | from plugin_ozzy_stats import OzzyStats 9 | from injectables.discord_dependencies import * 10 | 11 | class TestOzzyStats(unittest.TestCase): 12 | obj = OzzyStats() 13 | def test_check_bits(self): 14 | assert self.obj.checkBits(0) == False 15 | 16 | def test_check_cat_true(self): 17 | assert self.obj.checkCat('admin') == True 18 | 19 | def test_check_cat_false(self): 20 | assert self.obj.checkCat('arrow') == False 21 | 22 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 23 | obj = OzzyStats() 24 | 25 | async def test_run_cheer(self): 26 | assert await self.obj.runCheer('potato', 0) == True 27 | 28 | async def test_run_file_not_exist(self): 29 | a_channel = channel() 30 | a_message = message('!ozzystats', a_channel) 31 | a_plugin = plugin('!help', 'help menu', '!help') 32 | 33 | if os.path.isfile('ozzystats.json'): 34 | os.remove('ozzystats.json') 35 | 36 | obj_list = [a_plugin] 37 | 38 | assert await self.obj.run(a_message, obj_list) == True 39 | 40 | if os.path.isfile('ozzystats.json'): 41 | os.remove('ozzystats.json') 42 | 43 | async def test_stop(self): 44 | await self.obj.stop('potato') 45 | 46 | if __name__ == '__main__': 47 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_monty_stats.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import unittest 4 | sys.dont_write_bytecode = True 5 | sys.path.append(os.path.abspath('plugins')) 6 | sys.path.append(os.path.abspath('tests')) 7 | 8 | from plugin_monty_stats import MontyStats 9 | from injectables.discord_dependencies import * 10 | 11 | class TestTemplate(unittest.TestCase): 12 | 13 | def test_check_bits(self): 14 | monty = MontyStats() 15 | assert monty.checkBits(0) == False 16 | 17 | def test_check_cat_true(self): 18 | monty = MontyStats() 19 | assert monty.checkCat('admin') == True 20 | 21 | def test_check_cat_false(self): 22 | monty = MontyStats() 23 | assert monty.checkCat('arrow') == False 24 | 25 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 26 | async def test_run_cheer(self): 27 | monty = MontyStats() 28 | assert await monty.runCheer('potato', 0) == True 29 | 30 | async def test_run_file_not_exist(self): 31 | a_channel = channel() 32 | a_message = message('!montystats', a_channel) 33 | a_plugin = plugin('!help', 'help menu', '!help') 34 | 35 | if os.path.isfile('montystats.json'): 36 | os.remove('montystats.json') 37 | 38 | obj_list = [a_plugin] 39 | 40 | monty = MontyStats() 41 | assert await monty.run(a_message, obj_list) == True 42 | 43 | if os.path.isfile('montystats.json'): 44 | os.remove('montystats.json') 45 | 46 | async def test_stop(self): 47 | monty = MontyStats() 48 | await monty.stop('potato') 49 | 50 | if __name__ == '__main__': 51 | unittest.main() -------------------------------------------------------------------------------- /tests/test_plugin_help.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | #import pytest 4 | import asyncio 5 | import unittest 6 | sys.dont_write_bytecode = True 7 | sys.path.append(os.path.abspath('plugins')) 8 | sys.path.append(os.path.abspath('tests')) 9 | 10 | from plugin_help import Help 11 | from injectables.discord_dependencies import * 12 | 13 | class TestHelp(unittest.TestCase): 14 | 15 | def test_check_bits(self): 16 | help = Help() 17 | assert help.checkBits(0) == False 18 | 19 | def test_check_cat_true(self): 20 | help = Help() 21 | assert help.checkCat('admin') == True 22 | 23 | def test_check_cat_false(self): 24 | help = Help() 25 | assert help.checkCat('arrow') == False 26 | 27 | 28 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 29 | async def test_run_cheer(self): 30 | help = Help() 31 | assert await help.runCheer('potato', 0) == True 32 | 33 | async def test_run_help(self): 34 | help = Help() 35 | a_channel = channel() 36 | a_message = message('!help', a_channel) 37 | a_plugin = plugin('!help', 'help menu', '!help') 38 | 39 | print('message content: ' + str(a_message.content)) 40 | 41 | obj_list = [a_plugin] 42 | 43 | await help.run(a_message, obj_list) 44 | 45 | async def test_run_help_command(self): 46 | help = Help() 47 | a_channel = channel() 48 | a_message = message('!help !command', a_channel) 49 | a_plugin = plugin('!command', 'Random Command', '!command') 50 | 51 | print('message content: ' + str(a_message.content)) 52 | 53 | obj_list = [a_plugin] 54 | 55 | await help.run(a_message, obj_list) 56 | 57 | async def test_stop(self): 58 | help = Help() 59 | await help.stop('potato') 60 | 61 | if __name__ == '__main__': 62 | unittest.main() -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Test 4 | 5 | # Controls when the workflow will run 6 | on: [push, pull_request, workflow_dispatch] 7 | # push: 8 | # branches: 9 | # - main 10 | # pull_request: 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | # workflow_dispatch: 14 | 15 | concurrency: 16 | group: ${{ github.workflow }}-${{ github.ref }} 17 | cancel-in-progress: true 18 | 19 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 20 | jobs: 21 | # This workflow contains a single job called "build" 22 | lint-test: 23 | # The type of runner that the job will run on 24 | runs-on: ubuntu-latest 25 | 26 | # Steps represent a sequence of tasks that will be executed as part of the job 27 | steps: 28 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 29 | - name: Checkout repository 30 | uses: actions/checkout@v2 31 | 32 | - name: Setup python 33 | id: python 34 | uses: actions/setup-python@v2 35 | with: 36 | python-version: '3.8' 37 | 38 | - name: Install Dependencies 39 | run: pip3 install -r requirements.txt 40 | 41 | - name: Create log folder 42 | run: | 43 | sudo mkdir -p /var/log/discord_bot 44 | sudo chmod 755 /var/log/discord_bot 45 | 46 | - name: Run Unit Tests 47 | run: coverage run --source=plugins -m unittest discover tests -v 48 | 49 | - name: Generate Unit Test Report 50 | run: coverage report 51 | 52 | - name: Publish Code Coverage Report 53 | run: codecov 54 | -------------------------------------------------------------------------------- /tests/injectables/discord_dependencies.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | class plugin(): 4 | name = None 5 | desc = None 6 | synt = None 7 | 8 | def __init__(self, name, desc, synt): 9 | self.name = name 10 | self.desc = desc 11 | self.synt = synt 12 | 13 | class Guild(): 14 | name = None 15 | id = None 16 | channels = [] 17 | roles = [] 18 | members = [] 19 | member_count = 0 20 | 21 | def __init__(self): 22 | a_channel = channel() 23 | self.channels.append(a_channel) 24 | self.name = 'Guild' 25 | self.id = 0 26 | 27 | class Permissions(): 28 | administrator = None 29 | 30 | def __init__(self, admin): 31 | self.administrator = admin 32 | 33 | class Role(): 34 | name = None 35 | permissions = None 36 | 37 | def __init__(self, name): 38 | permissions = Permissions(True) 39 | self.permissions = permissions 40 | self.name = name 41 | 42 | class User(): 43 | roles = [] 44 | mention = None 45 | name = None 46 | 47 | def __init__(self, roles, name): 48 | self.roles = roles 49 | self.mention = name + '#0000' 50 | self.name = name 51 | 52 | def __str__(self): 53 | return self.mention 54 | 55 | class Reaction(): 56 | count = 0 57 | emoji = None 58 | 59 | def __init__(self, emoji): 60 | self.emoji = emoji 61 | self.count = self.count + 1 62 | 63 | class message(): 64 | reactions = [] 65 | mentions = [] 66 | id = None 67 | content = None 68 | channel = None 69 | embed = None 70 | author = None 71 | guild = None 72 | created_at = None 73 | 74 | def __init__(self, content, channel): 75 | id = 0 76 | role = Role('member') 77 | user = User([role], 'user') 78 | guild = Guild() 79 | self.guild = guild 80 | self.author = user 81 | self.content = content 82 | self.channel = channel 83 | self.created_at = datetime.now() 84 | channel = None 85 | 86 | async def add_reaction(self, reaction): 87 | self.reactions.append(reaction) 88 | 89 | async def edit(self, embed=None): 90 | self.embed = embed 91 | 92 | class channel(): 93 | name = None 94 | mention = None 95 | 96 | def __init__(self): 97 | self.name = "Channel" 98 | self.mention = '<#0>' 99 | return 100 | 101 | async def send(self, content=None, embed=None): 102 | a_message = message('potato', self) 103 | return a_message 104 | 105 | async def fetch_message(self, message_id): 106 | a_message = message('potato', self) 107 | return a_message -------------------------------------------------------------------------------- /.github/workflows/build_push.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: Build and Push 4 | 5 | # Controls when the workflow will run 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the main branch 8 | workflow_run: 9 | workflows: ["Test"] 10 | branches: [main] 11 | types: 12 | - completed 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 18 | jobs: 19 | # This workflow contains a single job called "build" 20 | build: 21 | if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' 22 | name: Build and Push 23 | # The type of runner that the job will run on 24 | runs-on: ubuntu-latest 25 | 26 | # Steps represent a sequence of tasks that will be executed as part of the job 27 | steps: 28 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 29 | - name: Create SHA Container Tag 30 | id: sha_tag 31 | run: | 32 | tag=$(cut -c 1-7 <<< $GITHUB_SHA) 33 | echo "::set-output name=tag::$tag" 34 | 35 | - name: Checkout Code 36 | uses: actions/checkout@v3 37 | 38 | - name: Setup QEMU 39 | uses: docker/setup-qemu-action@v1 40 | 41 | - name: Setup Docker Buildx 42 | uses: docker/setup-buildx-action@v1 43 | 44 | - name: Github Container Registry Login 45 | uses: docker/login-action@v1 46 | with: 47 | registry: ghcr.io 48 | username: ${{ github.repository_owner }} 49 | password: ${{ secrets.GITHUB_TOKEN }} 50 | 51 | - name: Give X perms to startup.sh 52 | run: | 53 | chmod +x startup.sh 54 | ls -la | grep startup.sh 55 | 56 | - name: Build and Push 57 | uses: docker/build-push-action@v2 58 | with: 59 | context: . 60 | platforms: linux/arm64,linux/amd64 61 | file: ./Dockerfile 62 | push: true 63 | cache-from: type=registry,ref=ghcr.io/justcallmekoko/pythondiscordbot:latest 64 | cache-to: type=inline 65 | tags: | 66 | ghcr.io/justcallmekoko/pythondiscordbot:latest 67 | ghcr.io/justcallmekoko/pythondiscordbot:${{ steps.sha_tag.outputs.tag }} 68 | build-args: git_sha=${{ github.sha }} 69 | -------------------------------------------------------------------------------- /tests/test_plugin_giveaway.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | #import pytest 4 | import unittest 5 | sys.dont_write_bytecode = True 6 | sys.path.append(os.path.abspath('plugins')) 7 | sys.path.append(os.path.abspath('tests')) 8 | 9 | from plugin_giveaway import Giveaway 10 | from injectables.discord_dependencies import * 11 | 12 | class TestGiveaway(unittest.TestCase): 13 | 14 | def test_check_bits(self): 15 | giveaway = Giveaway() 16 | assert giveaway.checkBits(0) == False 17 | 18 | def test_check_cat_true(self): 19 | giveaway = Giveaway() 20 | assert giveaway.checkCat('admin') == True 21 | 22 | def test_check_cat_false(self): 23 | giveaway = Giveaway() 24 | assert giveaway.checkCat('arrow') == False 25 | 26 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 27 | async def test_run_cheer(self): 28 | giveaway = Giveaway() 29 | assert await giveaway.runCheer('potato', 0) == True 30 | 31 | async def test_run_no_giveaways(self): 32 | giveaway = Giveaway() 33 | a_channel = channel() 34 | 35 | a_message = message('!giveaway', a_channel) 36 | 37 | a_plugin = plugin('!giveaway', 'Giveaway', '!giveaway ') 38 | 39 | #a_message.content = '!adduser ' + str(a_message.author.name) 40 | #role = Role('Restricted') 41 | #a_message.author.roles.append(role) 42 | 43 | print('message content: ' + str(a_message.content)) 44 | 45 | obj_list = [a_plugin] 46 | 47 | assert await giveaway.run(a_message, obj_list) == True 48 | 49 | async def test_fail_post_channel(self): 50 | giveaway = Giveaway() 51 | a_channel = channel() 52 | 53 | giveaway_name = 'This is a test giveaway' 54 | 55 | a_message = message('!giveaway start ' + str(giveaway_name), a_channel) 56 | 57 | a_plugin = plugin('!giveaway', 'Giveaway', '!giveaway ') 58 | 59 | role = Role('Moderator') 60 | a_message.author.roles.append(role) 61 | 62 | print('message content: ' + str(a_message.content)) 63 | 64 | obj_list = [a_plugin] 65 | 66 | a_channel.name = 'potato' 67 | 68 | a_message.guild.channels.append(a_channel) 69 | 70 | result = await giveaway.run(a_message, obj_list) 71 | 72 | self.assertEqual(result, False) 73 | 74 | async def test_start_giveaway(self): 75 | giveaway = Giveaway() 76 | a_channel = channel() 77 | 78 | giveaway_name = 'This is a test giveaway' 79 | 80 | a_message = message('!giveaway start ' + str(giveaway_name), a_channel) 81 | 82 | a_plugin = plugin('!giveaway', 'Giveaway', '!giveaway ') 83 | 84 | role = Role('Moderator') 85 | a_message.author.roles.append(role) 86 | 87 | print('message content: ' + str(a_message.content)) 88 | 89 | obj_list = [a_plugin] 90 | 91 | a_channel.name = 'giveaways' 92 | 93 | a_message.guild.channels.append(a_channel) 94 | 95 | result = await giveaway.run(a_message, obj_list) 96 | 97 | #self.assertEqual(result, giveaway_name) 98 | 99 | async def test_stop(self): 100 | giveaway = Giveaway() 101 | await giveaway.stop('potato') 102 | 103 | if __name__ == '__main__': 104 | unittest.main() -------------------------------------------------------------------------------- /plugins/plugin_butts.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class Butts(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!butts' 22 | 23 | desc = 'Use this only if you like butts' 24 | 25 | synt = '!butts' 26 | 27 | default_config = {} 28 | default_config['protected'] = {} 29 | default_config['protected']['name'] = __file__ 30 | default_config['protected']['guild'] = None 31 | default_config['standard_groups'] = {} 32 | default_config['standard_groups']['value'] = [] 33 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 34 | default_config['admin_groups'] = {} 35 | default_config['admin_groups']['value'] = [] 36 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 37 | default_config['blacklisted'] = {} 38 | default_config['blacklisted']['value'] = [] 39 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 40 | default_config['post_channel'] = {} 41 | default_config['post_channel']['value'] = "" 42 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 43 | 44 | looping = False 45 | 46 | group = '@everyone' 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return True 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | # Do Specific Plugin Stuff 110 | 111 | await message.channel.send(message.author.mention + ' likes butts.') 112 | return True 113 | 114 | async def stop(self, message): 115 | self.looping = False 116 | -------------------------------------------------------------------------------- /plugins/plugin_poop.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class Poop(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!poop' 22 | 23 | desc = 'It makes you poop' 24 | 25 | synt = '!poop' 26 | 27 | default_config = {} 28 | default_config['protected'] = {} 29 | default_config['protected']['name'] = __file__ 30 | default_config['protected']['guild'] = None 31 | default_config['standard_groups'] = {} 32 | default_config['standard_groups']['value'] = [] 33 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 34 | default_config['admin_groups'] = {} 35 | default_config['admin_groups']['value'] = [] 36 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 37 | default_config['blacklisted'] = {} 38 | default_config['blacklisted']['value'] = [] 39 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 40 | default_config['post_channel'] = {} 41 | default_config['post_channel']['value'] = "" 42 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 43 | 44 | looping = False 45 | 46 | group = 'members' 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | # This will be deleted soon 91 | return False 92 | 93 | async def runCheer(self, user, amount): 94 | return True 95 | 96 | async def run(self, message, obj_list): 97 | # Permissions check 98 | if not self.configutils.hasPerms(message, False, self.guild_confs): 99 | await message.channel.send(message.author.mention + ' Permission denied') 100 | return False 101 | 102 | # Parse args 103 | arg = self.getArgs(message) 104 | 105 | # Config set/get check 106 | if arg != None: 107 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 108 | return True 109 | 110 | # Do Specific Plugin Stuff 111 | 112 | await message.channel.send(message.author.mention + ' just pooped') 113 | return True 114 | 115 | async def stop(self, message): 116 | self.looping = False 117 | -------------------------------------------------------------------------------- /plugins/plugin_template.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class Template(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | guild_confs = [] 19 | 20 | configutils = None 21 | 22 | name = '!template' 23 | 24 | desc = 'This does nothing. Developer only' 25 | 26 | synt = '!template [config|get |set |add/remove ]' 27 | 28 | is_service = False 29 | 30 | client = None 31 | 32 | looping = False 33 | 34 | full_conf_file = None 35 | 36 | default_config = {} 37 | default_config['protected'] = {} 38 | default_config['protected']['name'] = __file__ 39 | default_config['protected']['guild'] = None 40 | default_config['standard_groups'] = {} 41 | default_config['standard_groups']['value'] = [] 42 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 43 | default_config['admin_groups'] = {} 44 | default_config['admin_groups']['value'] = [] 45 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 46 | default_config['blacklisted'] = {} 47 | default_config['blacklisted']['value'] = [] 48 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 49 | default_config['post_channel'] = {} 50 | default_config['post_channel']['value'] = "" 51 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 52 | 53 | # Server configurable 54 | 55 | group = '@everyone' 56 | 57 | admin = False 58 | 59 | cheer = -1 60 | 61 | cat = 'admin' 62 | 63 | def __init__(self, client = None): 64 | self.client = client 65 | self.configutils = ConfigUtils() 66 | 67 | # Load configuration if it exists 68 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 69 | 70 | 71 | logger.debug('\n\nConfigs Loaded:') 72 | for config in self.guild_confs: 73 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 74 | 75 | def getArgs(self, message): 76 | cmd = str(message.content) 77 | seg = str(message.content).split(' ') 78 | 79 | if len(seg) > 1: 80 | return seg 81 | else: 82 | return None 83 | 84 | def generatePluginConfig(self, file_name): 85 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 86 | self.guild_confs.append(new_conf) 87 | 88 | def checkCat(self, check_cat): 89 | if self.cat == check_cat: 90 | return True 91 | else: 92 | return False 93 | 94 | def checkBits(self, bits): 95 | return False 96 | 97 | async def runCheer(self, user, amount): 98 | return True 99 | 100 | async def run(self, message, obj_list): 101 | # Permissions check 102 | if not self.configutils.hasPerms(message, False, self.guild_confs): 103 | await message.channel.send(message.author.mention + ' Permission denied') 104 | return False 105 | 106 | # Parse args 107 | arg = self.getArgs(message) 108 | 109 | # Config set/get check 110 | if arg != None: 111 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 112 | return True 113 | 114 | # Do Specific Plugin Stuff 115 | 116 | return True 117 | 118 | async def stop(self, message): 119 | self.looping = False 120 | -------------------------------------------------------------------------------- /plugins/plugin_numusers.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class Numusers(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!numusers' 22 | 23 | desc = 'Get the number of users in the server' 24 | 25 | synt = '!numusers' 26 | 27 | default_config = {} 28 | default_config['protected'] = {} 29 | default_config['protected']['name'] = __file__ 30 | default_config['protected']['guild'] = None 31 | default_config['standard_groups'] = {} 32 | default_config['standard_groups']['value'] = [] 33 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 34 | default_config['admin_groups'] = {} 35 | default_config['admin_groups']['value'] = [] 36 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 37 | default_config['blacklisted'] = {} 38 | default_config['blacklisted']['value'] = [] 39 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 40 | default_config['post_channel'] = {} 41 | default_config['post_channel']['value'] = "" 42 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 43 | 44 | looping = False 45 | 46 | group = 'members' 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return True 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | # Do Specific Plugin Stuff 110 | 111 | num_users = len(message.guild.members) 112 | await message.channel.send(message.author.mention + ', There are ' + str(num_users) + ' users on the server') 113 | return True 114 | 115 | async def stop(self, message): 116 | self.looping = False 117 | -------------------------------------------------------------------------------- /plugins/plugin_roleinfo.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class Roleinfo(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!roleinfo' 22 | 23 | desc = 'Get list of roles and number of users in the roles' 24 | 25 | synt = '!roleinfo' 26 | 27 | default_config = {} 28 | default_config['protected'] = {} 29 | default_config['protected']['name'] = __file__ 30 | default_config['protected']['guild'] = None 31 | default_config['standard_groups'] = {} 32 | default_config['standard_groups']['value'] = [] 33 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 34 | default_config['admin_groups'] = {} 35 | default_config['admin_groups']['value'] = [] 36 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 37 | default_config['blacklisted'] = {} 38 | default_config['blacklisted']['value'] = [] 39 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 40 | default_config['post_channel'] = {} 41 | default_config['post_channel']['value'] = "" 42 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 43 | 44 | looping = False 45 | 46 | group = 'members' 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | # Do Specific Plugin Stuff 110 | 111 | roles = message.guild.roles 112 | output = '' 113 | for role in roles: 114 | if role.name == '@everyone': 115 | continue 116 | output = output + str(role.name) + ': ' + str(len(role.members)) + '\n' 117 | 118 | await message.channel.send(message.author.mention + '\n' + output) 119 | 120 | async def stop(self, message): 121 | self.looping = False 122 | -------------------------------------------------------------------------------- /plugins/plugin_slap.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import sys 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class Slap(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!slap' 22 | 23 | desc = 'Slap someone on the server' 24 | 25 | synt = '!slap |[config|get |set |add/remove ]' 26 | 27 | looping = False 28 | 29 | group = '@everyone' 30 | 31 | admin = False 32 | 33 | cheer = -1 34 | 35 | cat = 'admin' 36 | 37 | is_service = False 38 | 39 | client = None 40 | 41 | default_config = {} 42 | default_config['protected'] = {} 43 | default_config['protected']['name'] = __file__ 44 | default_config['protected']['guild'] = None 45 | default_config['standard_groups'] = {} 46 | default_config['standard_groups']['value'] = [] 47 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 48 | default_config['admin_groups'] = {} 49 | default_config['admin_groups']['value'] = [] 50 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 51 | default_config['blacklisted'] = {} 52 | default_config['blacklisted']['value'] = [] 53 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 54 | default_config['post_channel'] = {} 55 | default_config['post_channel']['value'] = "" 56 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return True 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | if len(message.mentions) <= 0: 110 | return False 111 | 112 | new_msg = message.author.mention + ' just slapped ' 113 | for member in message.mentions: 114 | new_msg = new_msg + member.mention + ', ' 115 | 116 | new_msg = new_msg[:-2] 117 | 118 | await message.channel.send(new_msg) 119 | 120 | return True 121 | 122 | async def stop(self, message): 123 | self.looping = False 124 | -------------------------------------------------------------------------------- /plugins/plugin_set_status.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class SetStatus(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | guild_confs = [] 19 | 20 | configutils = None 21 | 22 | name = '!setstatus' 23 | 24 | desc = 'Set the status of the bot' 25 | 26 | synt = '!setstatus ' 27 | 28 | default_config = {} 29 | default_config['protected'] = {} 30 | default_config['protected']['name'] = __file__ 31 | default_config['protected']['guild'] = None 32 | default_config['standard_groups'] = {} 33 | default_config['standard_groups']['value'] = [] 34 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 35 | default_config['admin_groups'] = {} 36 | default_config['admin_groups']['value'] = [] 37 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 38 | default_config['blacklisted'] = {} 39 | default_config['blacklisted']['value'] = [] 40 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 41 | default_config['post_channel'] = {} 42 | default_config['post_channel']['value'] = "" 43 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 44 | 45 | looping = False 46 | 47 | group = 'Owner' 48 | 49 | admin = False 50 | 51 | cheer = -1 52 | 53 | cat = 'admin' 54 | 55 | is_service = False 56 | 57 | client = None 58 | 59 | status = None 60 | 61 | def __init__(self, client = None): 62 | self.client = client 63 | self.configutils = ConfigUtils() 64 | 65 | # Load configuration if it exists 66 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 67 | 68 | 69 | logger.debug('\n\nConfigs Loaded:') 70 | for config in self.guild_confs: 71 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 72 | 73 | def getArgs(self, message): 74 | cmd = str(message.content) 75 | seg = str(message.content).split(' ') 76 | 77 | if len(seg) > 1: 78 | return seg 79 | else: 80 | return None 81 | 82 | def generatePluginConfig(self, file_name): 83 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 84 | self.guild_confs.append(new_conf) 85 | 86 | def checkCat(self, check_cat): 87 | if self.cat == check_cat: 88 | return True 89 | else: 90 | return False 91 | 92 | def checkBits(self, bits): 93 | return False 94 | 95 | async def runCheer(self, user, amount): 96 | return 97 | 98 | async def run(self, message, obj_list): 99 | # Permissions check 100 | if not self.configutils.hasPerms(message, True, self.guild_confs): 101 | await message.channel.send(message.author.mention + ' Permission denied') 102 | return False 103 | 104 | # Parse args 105 | arg = self.getArgs(message) 106 | 107 | # Config set/get check 108 | if arg != None: 109 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 110 | return True 111 | 112 | # Do Specific Plugin Stuff 113 | 114 | cmd = str(message.content) 115 | seg = str(message.content).split(' ') 116 | 117 | test_name = '' 118 | for i in range(1, len(seg)): 119 | test_name = test_name + seg[i] + ' ' 120 | self.status = test_name[:-1] 121 | 122 | await self.client.change_presence(activity = discord.Game(str(self.status))) 123 | return 124 | 125 | async def stop(self, message): 126 | self.looping = False 127 | -------------------------------------------------------------------------------- /plugins/plugin_ozzy_stats.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class OzzyStats(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!ozzystats' 22 | 23 | desc = 'Check how many times Ozzy was pet' 24 | 25 | synt = '!ozzystats' 26 | 27 | default_config = {} 28 | default_config['protected'] = {} 29 | default_config['protected']['name'] = __file__ 30 | default_config['protected']['guild'] = None 31 | default_config['standard_groups'] = {} 32 | default_config['standard_groups']['value'] = [] 33 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 34 | default_config['admin_groups'] = {} 35 | default_config['admin_groups']['value'] = [] 36 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 37 | default_config['blacklisted'] = {} 38 | default_config['blacklisted']['value'] = [] 39 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 40 | default_config['post_channel'] = {} 41 | default_config['post_channel']['value'] = "" 42 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 43 | 44 | looping = False 45 | 46 | group = '@everyone' 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return True 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | # Do Specific Plugin Stuff 110 | 111 | # Create fresh stat file 112 | if not os.path.isfile('ozzystats.json'): 113 | # Create fresh json template 114 | data = {} 115 | data['name'] = 'Ozzy' 116 | data['pets'] = 0 117 | data['members'] = [] 118 | with open('ozzystats.json', 'w') as f: 119 | json.dump(data, f) 120 | 121 | # Get json data 122 | f = open('ozzystats.json',) 123 | 124 | json_data = json.load(f) 125 | 126 | f.close() 127 | 128 | await message.channel.send(message.author.mention + ', Ozzy has been pet ' + str(json_data['pets']) + ' time(s)') 129 | return True 130 | 131 | async def stop(self, message): 132 | self.looping = False 133 | -------------------------------------------------------------------------------- /plugins/plugin_monty_stats.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class MontyStats(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!montystats' 22 | 23 | desc = 'Check how many times Monty was pet' 24 | 25 | synt = '!montystats' 26 | 27 | default_config = {} 28 | default_config['protected'] = {} 29 | default_config['protected']['name'] = __file__ 30 | default_config['protected']['guild'] = None 31 | default_config['standard_groups'] = {} 32 | default_config['standard_groups']['value'] = [] 33 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 34 | default_config['admin_groups'] = {} 35 | default_config['admin_groups']['value'] = [] 36 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 37 | default_config['blacklisted'] = {} 38 | default_config['blacklisted']['value'] = [] 39 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 40 | default_config['post_channel'] = {} 41 | default_config['post_channel']['value'] = "" 42 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 43 | 44 | looping = False 45 | 46 | group = '@everyone' 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return True 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | # Do Specific Plugin Stuff 110 | 111 | # Create fresh stat file 112 | if not os.path.isfile('montystats.json'): 113 | # Create fresh json template 114 | data = {} 115 | data['name'] = 'Monty' 116 | data['pets'] = 0 117 | data['members'] = [] 118 | with open('montystats.json', 'w') as f: 119 | json.dump(data, f) 120 | 121 | # Get json data 122 | f = open('montystats.json',) 123 | 124 | json_data = json.load(f) 125 | 126 | f.close() 127 | 128 | await message.channel.send(message.author.mention + ', Monty has been pet ' + str(json_data['pets']) + ' time(s)') 129 | 130 | return True 131 | async def stop(self, message): 132 | self.looping = False 133 | -------------------------------------------------------------------------------- /plugins/plugin_sarcasm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class Sarcasm(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | guild_confs = [] 19 | 20 | configutils = None 21 | 22 | name = '!sarcasm' 23 | 24 | desc = 'When you want to be sarcastic' 25 | 26 | synt = '!sarcasm | [config|get |set |add/remove ]' 27 | 28 | is_service = False 29 | 30 | client = None 31 | 32 | looping = False 33 | 34 | full_conf_file = None 35 | 36 | default_config = {} 37 | default_config['protected'] = {} 38 | default_config['protected']['name'] = __file__ 39 | default_config['protected']['guild'] = None 40 | default_config['standard_groups'] = {} 41 | default_config['standard_groups']['value'] = [] 42 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 43 | default_config['admin_groups'] = {} 44 | default_config['admin_groups']['value'] = [] 45 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 46 | default_config['blacklisted'] = {} 47 | default_config['blacklisted']['value'] = [] 48 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 49 | default_config['post_channel'] = {} 50 | default_config['post_channel']['value'] = "" 51 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 52 | 53 | # Server configurable 54 | 55 | group = '@everyone' 56 | 57 | admin = False 58 | 59 | cheer = -1 60 | 61 | cat = 'admin' 62 | 63 | def __init__(self, client = None): 64 | self.client = client 65 | self.configutils = ConfigUtils() 66 | 67 | # Load configuration if it exists 68 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 69 | 70 | 71 | logger.debug('\n\nConfigs Loaded:') 72 | for config in self.guild_confs: 73 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 74 | 75 | def getArgs(self, message): 76 | cmd = str(message.content) 77 | seg = str(message.content).split(' ') 78 | 79 | if len(seg) > 1: 80 | return seg 81 | else: 82 | return None 83 | 84 | def generatePluginConfig(self, file_name): 85 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 86 | self.guild_confs.append(new_conf) 87 | 88 | def checkCat(self, check_cat): 89 | if self.cat == check_cat: 90 | return True 91 | else: 92 | return False 93 | 94 | def checkBits(self, bits): 95 | return False 96 | 97 | async def runCheer(self, user, amount): 98 | return True 99 | 100 | async def run(self, message, obj_list): 101 | # Permissions check 102 | if not self.configutils.hasPerms(message, False, self.guild_confs): 103 | await message.channel.send(message.author.mention + ' Permission denied') 104 | return False 105 | 106 | # Parse args 107 | arg = self.getArgs(message) 108 | 109 | # Config set/get check 110 | if arg != None: 111 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 112 | return True 113 | 114 | # Do Specific Plugin Stuff 115 | the_string = message.content.replace(self.name + ' ', '') 116 | 117 | response = message.author.mention + '\n' 118 | cap = True 119 | for char in the_string: 120 | if char.isalpha(): 121 | if cap: 122 | response += char.upper() 123 | else: 124 | response += char.lower() 125 | cap = not cap 126 | else: 127 | response += char 128 | 129 | await message.channel.send(response) 130 | 131 | return True 132 | 133 | async def stop(self, message): 134 | self.looping = False 135 | -------------------------------------------------------------------------------- /plugins/plugin_help.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class Help(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | name = '!help' 19 | 20 | desc = 'Display list of commands or specific command usage' 21 | 22 | synt = '!help [command]' 23 | 24 | looping = False 25 | 26 | default_config = {} 27 | default_config['protected'] = {} 28 | default_config['protected']['name'] = __file__ 29 | default_config['protected']['guild'] = None 30 | default_config['standard_groups'] = {} 31 | default_config['standard_groups']['value'] = ["@everyone"] 32 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 33 | default_config['admin_groups'] = {} 34 | default_config['admin_groups']['value'] = [] 35 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 36 | default_config['blacklisted'] = {} 37 | default_config['blacklisted']['value'] = [] 38 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 39 | default_config['post_channel'] = {} 40 | default_config['post_channel']['value'] = "" 41 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 42 | 43 | group = '@everyone' 44 | 45 | admin = False 46 | 47 | cheer = -1 48 | 49 | cat = 'admin' 50 | 51 | is_service = False 52 | 53 | client = None 54 | 55 | def __init__(self, client = None): 56 | self.client = client 57 | self.configutils = ConfigUtils() 58 | 59 | # Load configuration if it exists 60 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 61 | 62 | 63 | logger.debug('\n\nConfigs Loaded:') 64 | for config in self.guild_confs: 65 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 66 | 67 | def getArgs(self, message): 68 | cmd = str(message.content) 69 | seg = str(message.content).split(' ') 70 | 71 | if len(seg) > 1: 72 | return seg 73 | else: 74 | return None 75 | 76 | def generatePluginConfig(self, file_name): 77 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 78 | self.guild_confs.append(new_conf) 79 | 80 | def checkCat(self, check_cat): 81 | if self.cat == check_cat: 82 | return True 83 | else: 84 | return False 85 | 86 | def checkBits(self, bits): 87 | return False 88 | 89 | async def runCheer(self, user, amount): 90 | return True 91 | 92 | async def run(self, message, obj_list): 93 | # Permissions check 94 | if not self.configutils.hasPerms(message, False, self.guild_confs): 95 | await message.channel.send(message.author.mention + ' Permission denied') 96 | return False 97 | 98 | # Parse args 99 | arg = self.getArgs(message) 100 | 101 | # Config set/get check 102 | if arg != None: 103 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 104 | return True 105 | 106 | embed = discord.Embed(title="Help", 107 | color=discord.Color.blue()) 108 | 109 | if str(message.content) == '!help': 110 | for obj in obj_list: 111 | embed.add_field(name=str(obj.name), value='`' + str(obj.desc) + '`', inline=False) 112 | 113 | await message.channel.send(embed=embed) 114 | elif '!help ' in str(message.content): 115 | for obj in obj_list: 116 | if str(message.content).split(' ')[1] == str(obj.name): 117 | embed.add_field(name="Command", value='`' + str(obj.name) + '`', inline=True) 118 | embed.add_field(name="Description", value='`' + str(obj.synt) + '`', inline=True) 119 | await message.channel.send(embed=embed) 120 | 121 | return 122 | 123 | async def stop(self, message): 124 | self.looping = False 125 | -------------------------------------------------------------------------------- /plugins/plugin_services.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class Services(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | guild_confs = [] 19 | 20 | configutils = None 21 | 22 | name = '!services' 23 | 24 | desc = 'Displays a list of services and their status' 25 | 26 | synt = '!services [config|get |set |add/remove ]' 27 | 28 | default_config = {} 29 | default_config['protected'] = {} 30 | default_config['protected']['name'] = __file__ 31 | default_config['protected']['guild'] = None 32 | default_config['standard_groups'] = {} 33 | default_config['standard_groups']['value'] = [] 34 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 35 | default_config['admin_groups'] = {} 36 | default_config['admin_groups']['value'] = [] 37 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 38 | default_config['blacklisted'] = {} 39 | default_config['blacklisted']['value'] = [] 40 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 41 | default_config['post_channel'] = {} 42 | default_config['post_channel']['value'] = "" 43 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 44 | 45 | looping = False 46 | 47 | group = 'Moderator' 48 | 49 | admin = False 50 | 51 | cheer = -1 52 | 53 | cat = 'admin' 54 | 55 | is_service = False 56 | 57 | client = None 58 | 59 | def __init__(self, client = None): 60 | self.client = client 61 | self.configutils = ConfigUtils() 62 | 63 | # Load configuration if it exists 64 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 65 | 66 | 67 | logger.debug('\n\nConfigs Loaded:') 68 | for config in self.guild_confs: 69 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 70 | 71 | def getArgs(self, message): 72 | cmd = str(message.content) 73 | seg = str(message.content).split(' ') 74 | 75 | if len(seg) > 1: 76 | return seg 77 | else: 78 | return None 79 | 80 | def generatePluginConfig(self, file_name): 81 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 82 | self.guild_confs.append(new_conf) 83 | 84 | def checkCat(self, check_cat): 85 | if self.cat == check_cat: 86 | return True 87 | else: 88 | return False 89 | 90 | def checkBits(self, bits): 91 | return False 92 | 93 | async def runCheer(self, user, amount): 94 | return 95 | 96 | async def run(self, message, obj_list): 97 | # Permissions check 98 | if not self.configutils.hasPerms(message, True, self.guild_confs): 99 | await message.channel.send(message.author.mention + ' Permission denied') 100 | return False 101 | 102 | # Parse args 103 | arg = self.getArgs(message) 104 | 105 | # Config set/get check 106 | if arg != None: 107 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 108 | return True 109 | 110 | # Do Specific Plugin Stuff 111 | 112 | embed=discord.Embed(title="Services", 113 | color=discord.Color.blue()) 114 | response = '' 115 | 116 | for obj in obj_list: 117 | if obj.is_service: 118 | response = response + str(obj.name) 119 | try: 120 | if await obj.getStatus(message): 121 | response = response + ': `running`\n' 122 | else: 123 | response = response + ': `not running`\n' 124 | except: 125 | response = response + ': `not running(old config)`\n' 126 | 127 | embed.add_field(name='Status', value='```' + str(response) + '```', inline=True) 128 | await message.channel.send(embed=embed) 129 | 130 | return 131 | 132 | async def stop(self, message): 133 | self.looping = False 134 | -------------------------------------------------------------------------------- /plugins/plugin_server_info.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from datetime import datetime 7 | from dotenv import load_dotenv 8 | from discord.ext.tasks import loop 9 | from requests import get 10 | 11 | sys.path.append(os.path.abspath('utils')) 12 | 13 | from utils.config_utils import ConfigUtils 14 | 15 | class ServerInfo(): 16 | # Required for all plugins 17 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 18 | 19 | guild_confs = [] 20 | 21 | configutils = None 22 | 23 | name = '!serverinfo' 24 | 25 | desc = 'Get server information' 26 | 27 | synt = '!serverinfo' 28 | 29 | default_config = {} 30 | default_config['protected'] = {} 31 | default_config['protected']['name'] = __file__ 32 | default_config['protected']['guild'] = None 33 | default_config['standard_groups'] = {} 34 | default_config['standard_groups']['value'] = [] 35 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 36 | default_config['admin_groups'] = {} 37 | default_config['admin_groups']['value'] = [] 38 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 39 | default_config['blacklisted'] = {} 40 | default_config['blacklisted']['value'] = [] 41 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 42 | default_config['post_channel'] = {} 43 | default_config['post_channel']['value'] = "" 44 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 45 | 46 | looping = False 47 | 48 | group = 'members' 49 | 50 | admin = False 51 | 52 | cheer = -1 53 | 54 | cat = 'admin' 55 | 56 | is_service = False 57 | 58 | client = None 59 | 60 | def __init__(self, client = None): 61 | self.client = client 62 | self.configutils = ConfigUtils() 63 | 64 | # Load configuration if it exists 65 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 66 | 67 | 68 | logger.debug('\n\nConfigs Loaded:') 69 | for config in self.guild_confs: 70 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 71 | 72 | def getArgs(self, message): 73 | cmd = str(message.content) 74 | seg = str(message.content).split(' ') 75 | 76 | if len(seg) > 1: 77 | return seg 78 | else: 79 | return None 80 | 81 | def generatePluginConfig(self, file_name): 82 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 83 | self.guild_confs.append(new_conf) 84 | 85 | def checkCat(self, check_cat): 86 | if self.cat == check_cat: 87 | return True 88 | else: 89 | return False 90 | 91 | def checkBits(self, bits): 92 | return False 93 | 94 | async def runCheer(self, user, amount): 95 | return 96 | 97 | async def run(self, message, obj_list): 98 | # Permissions check 99 | if not self.configutils.hasPerms(message, False, self.guild_confs): 100 | await message.channel.send(message.author.mention + ' Permission denied') 101 | return False 102 | 103 | # Parse args 104 | arg = self.getArgs(message) 105 | 106 | # Config set/get check 107 | if arg != None: 108 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 109 | return True 110 | 111 | # Do Specific Plugin Stuff 112 | 113 | embed = discord.Embed(title="Server Information", 114 | color=discord.Color.blue()) 115 | 116 | embed.add_field(name='Name', value='```' + str(message.guild.name) + '```', inline=True) 117 | embed.add_field(name='Owner', value='```' + str(message.guild.owner) + '```', inline=True) 118 | embed.add_field(name='Created', value='```' + str(message.guild.created_at) + '```', inline=True) 119 | 120 | # embed.add_field(name = chr(173), value = chr(173)) 121 | 122 | embed.add_field(name='Members', value='```' + str(message.guild.member_count) + '```', inline=True) 123 | embed.add_field(name='Roles', value='```' + str(len(message.guild.roles)) + '```', inline=True) 124 | 125 | await message.channel.send(embed=embed) 126 | return 127 | 128 | async def stop(self, message): 129 | self.looping = False 130 | -------------------------------------------------------------------------------- /utils/Examples.py: -------------------------------------------------------------------------------- 1 | examples = [ 2 | ['Write a character class in python with members for strength, intelligence, and perception and methods to get and set those members.', 3 | """ 4 | class Character: 5 | def __init__(self, strength, intelligence, perception): 6 | self.strength = strength 7 | self.intelligence = intelligence 8 | self.perception = perception 9 | 10 | def get_strength(self): 11 | return self.strength 12 | 13 | def set_strength(self, strength): 14 | self.strength = strength 15 | 16 | def get_intelligence(self): 17 | return self.intelligence 18 | 19 | def set_intelligence(self, intelligence): 20 | self.intelligence = intelligence 21 | 22 | def get_perception(self): 23 | return self.perception 24 | 25 | def set_perception(self, perception): 26 | self.perception = perception"""], 27 | ['Write a python script that print the date and time in 5 different formats', 28 | """ 29 | from datetime import datetime 30 | 31 | # get the current date and time 32 | now = datetime.now() 33 | 34 | # format the date and time using different formats 35 | format1 = now.strftime("%m/%d/%Y %H:%M:%S") 36 | format2 = now.strftime("%Y-%m-%d %H:%M:%S") 37 | format3 = now.strftime("%d %b %Y %H:%M:%S") 38 | format4 = now.strftime("%B %d, %Y %I:%M:%S %p") 39 | format5 = now.strftime("%A, %B %d %Y %I:%M:%S %p") 40 | 41 | # print the date and time in the different formats 42 | print("Format 1:", format1) 43 | print("Format 2:", format2) 44 | print("Format 3:", format3) 45 | print("Format 4:", format4) 46 | print("Format 5:", format5)"""], 47 | ['Write a python script that will play rock, paper, scissors with a user.', 48 | """ 49 | # list of possible moves 50 | moves = ['rock', 'paper', 'scissors'] 51 | 52 | # get the user's move 53 | user_move = input("Enter your move (rock, paper, or scissors): ") 54 | 55 | # check if the user's move is valid 56 | if user_move not in moves: 57 | print("Invalid move!") 58 | else: 59 | # generate the computer's move 60 | computer_move = random.choice(moves) 61 | 62 | # determine the winner 63 | if user_move == computer_move: 64 | print("It's a tie!") 65 | elif user_move == 'rock' and computer_move == 'scissors': 66 | print("You win!") 67 | elif user_move == 'paper' and computer_move == 'rock': 68 | print("You win!") 69 | elif user_move == 'scissors' and computer_move == 'paper': 70 | print("You win!") 71 | else: 72 | print("You lose!")"""], 73 | ['Write a python script for the game of mastermind.', 74 | """ 75 | # function to generate a random code 76 | def generate_code(): 77 | # list of possible colors for the code 78 | colors = ['R', 'G', 'B', 'Y', 'O', 'P'] 79 | 80 | # generate a random code by choosing 4 colors 81 | code = random.choices(colors, k=4) 82 | 83 | return code 84 | 85 | # function to check the user's guess 86 | def check_guess(guess, code): 87 | # initialize the number of correct colors and positions 88 | correct_colors = 0 89 | correct_positions = 0 90 | 91 | # check the number of correct colors 92 | for color in guess: 93 | if color in code: 94 | correct_colors += 1 95 | 96 | # check the number of correct positions 97 | for i in range(len(guess)): 98 | if guess[i] == code[i]: 99 | correct_positions += 1 100 | 101 | return correct_colors, correct_positions 102 | 103 | # generate the code to be guessed 104 | code = generate_code() 105 | 106 | # initialize the number of guesses 107 | num_guesses = 0 108 | 109 | # keep playing until the code is guessed or the maximum number of guesses is reached 110 | while num_guesses < 10: 111 | # get the user's guess 112 | guess = input("Enter your guess (4 colors): ") 113 | 114 | # check if the guess is valid 115 | if len(guess) != 4: 116 | print("Invalid guess!") 117 | else: 118 | # check the user's guess 119 | correct_colors, correct_positions = check_guess(guess, code) 120 | 121 | # print the results of the guess 122 | print("Correct colors:", correct_colors) 123 | print("Correct positions:", correct_positions) 124 | 125 | # check if the code has been guessed 126 | if correct_positions == 4: 127 | print("You win!") 128 | break 129 | 130 | # increment the number of guesses 131 | num_guesses += 1 132 | 133 | # if the code has not been guessed, print the correct code 134 | if num_guesses == 10: 135 | print("You lose! The correct code was:", code)"""]] -------------------------------------------------------------------------------- /plugins/plugin_pet_ozzy.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class PetOzzy(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!petozzy' 22 | 23 | desc = 'Give Ozzy a pet. He deserves it' 24 | 25 | synt = '!petozzy [config|get |set |add/remove ]' 26 | 27 | looping = False 28 | 29 | group = '@everyone' 30 | 31 | default_config = {} 32 | default_config['protected'] = {} 33 | default_config['protected']['name'] = __file__ 34 | default_config['protected']['guild'] = None 35 | default_config['standard_groups'] = {} 36 | default_config['standard_groups']['value'] = ["@everyone"] 37 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 38 | default_config['admin_groups'] = {} 39 | default_config['admin_groups']['value'] = [] 40 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 41 | default_config['blacklisted'] = {} 42 | default_config['blacklisted']['value'] = [] 43 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 44 | default_config['post_channel'] = {} 45 | default_config['post_channel']['value'] = "" 46 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return True 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | # Create fresh stat file 110 | if not os.path.isfile('ozzystats.json'): 111 | # Create fresh json template 112 | data = {} 113 | data['name'] = 'Ozzy' 114 | data['pets'] = 0 115 | data['members'] = [] 116 | with open('ozzystats.json', 'w') as f: 117 | json.dump(data, f) 118 | 119 | # Get json data 120 | f = open('ozzystats.json',) 121 | 122 | json_data = json.load(f) 123 | 124 | f.close() 125 | 126 | # Adjust number of pets by 1 127 | json_data['pets'] = int(json_data['pets']) + 1 128 | 129 | discord_user = str(message.author) 130 | 131 | found = False 132 | 133 | # Search for member in list of members 134 | for member in json_data['members']: 135 | if member['name'] == discord_user: 136 | found = True 137 | break 138 | 139 | if not found: 140 | # Add user to list of members who have done a pet if not there 141 | json_data['members'].append({'name': str(discord_user)}) 142 | 143 | # Write new json data to json file 144 | with open('ozzystats.json', 'w') as f: 145 | json.dump(json_data, f) 146 | 147 | await message.channel.send(message.author.mention + ' just pet Ozzy') 148 | 149 | return True 150 | 151 | async def stop(self, message): 152 | self.looping = False 153 | -------------------------------------------------------------------------------- /plugins/plugin_pet_monty.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | from logger import logger 5 | from dotenv import load_dotenv 6 | from discord.ext.tasks import loop 7 | from requests import get 8 | 9 | sys.path.append(os.path.abspath('utils')) 10 | 11 | from utils.config_utils import ConfigUtils 12 | 13 | class PetMonty(): 14 | # Required for all plugins 15 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 16 | 17 | guild_confs = [] 18 | 19 | configutils = None 20 | 21 | name = '!petmonty' 22 | 23 | desc = 'Give Monty a pet. He deserves it' 24 | 25 | synt = '!petmonty [config|get |set |add/remove ]' 26 | 27 | looping = False 28 | 29 | group = '@everyone' 30 | 31 | default_config = {} 32 | default_config['protected'] = {} 33 | default_config['protected']['name'] = __file__ 34 | default_config['protected']['guild'] = None 35 | default_config['standard_groups'] = {} 36 | default_config['standard_groups']['value'] = ["@everyone"] 37 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 38 | default_config['admin_groups'] = {} 39 | default_config['admin_groups']['value'] = [] 40 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 41 | default_config['blacklisted'] = {} 42 | default_config['blacklisted']['value'] = [] 43 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 44 | default_config['post_channel'] = {} 45 | default_config['post_channel']['value'] = "" 46 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 47 | 48 | admin = False 49 | 50 | cheer = -1 51 | 52 | cat = 'admin' 53 | 54 | is_service = False 55 | 56 | client = None 57 | 58 | def __init__(self, client = None): 59 | self.client = client 60 | self.configutils = ConfigUtils() 61 | 62 | # Load configuration if it exists 63 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 64 | 65 | 66 | logger.debug('\n\nConfigs Loaded:') 67 | for config in self.guild_confs: 68 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 69 | 70 | def getArgs(self, message): 71 | cmd = str(message.content) 72 | seg = str(message.content).split(' ') 73 | 74 | if len(seg) > 1: 75 | return seg 76 | else: 77 | return None 78 | 79 | def generatePluginConfig(self, file_name): 80 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 81 | self.guild_confs.append(new_conf) 82 | 83 | def checkCat(self, check_cat): 84 | if self.cat == check_cat: 85 | return True 86 | else: 87 | return False 88 | 89 | def checkBits(self, bits): 90 | return False 91 | 92 | async def runCheer(self, user, amount): 93 | return True 94 | 95 | async def run(self, message, obj_list): 96 | # Permissions check 97 | if not self.configutils.hasPerms(message, False, self.guild_confs): 98 | await message.channel.send(message.author.mention + ' Permission denied') 99 | return False 100 | 101 | # Parse args 102 | arg = self.getArgs(message) 103 | 104 | # Config set/get check 105 | if arg != None: 106 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 107 | return True 108 | 109 | # Create fresh stat file 110 | if not os.path.isfile('montystats.json'): 111 | # Create fresh json template 112 | data = {} 113 | data['name'] = 'Monty' 114 | data['pets'] = 0 115 | data['members'] = [] 116 | with open('montystats.json', 'w') as f: 117 | json.dump(data, f) 118 | 119 | # Get json data 120 | f = open('montystats.json',) 121 | 122 | json_data = json.load(f) 123 | 124 | f.close() 125 | 126 | # Adjust number of pets by 1 127 | json_data['pets'] = int(json_data['pets']) + 1 128 | 129 | discord_user = str(message.author) 130 | 131 | found = False 132 | 133 | # Search for member in list of members 134 | for member in json_data['members']: 135 | if member['name'] == discord_user: 136 | found = True 137 | break 138 | 139 | if not found: 140 | # Add user to list of members who have done a pet if not there 141 | json_data['members'].append({'name': str(discord_user)}) 142 | 143 | # Write new json data to json file 144 | with open('montystats.json', 'w') as f: 145 | json.dump(json_data, f) 146 | 147 | await message.channel.send(message.author.mention + ' just pet Monty') 148 | return True 149 | 150 | async def stop(self, message): 151 | self.looping = False 152 | -------------------------------------------------------------------------------- /plugins/plugin_search_react.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class SearchReact(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | guild_confs = [] 19 | 20 | configutils = None 21 | 22 | name = '!searchreact' 23 | 24 | desc = 'Search a message to see if a user has reacted' 25 | 26 | synt = '!searchreact [config|get |set |add/remove ][ <@user>]' 27 | 28 | is_service = False 29 | 30 | client = None 31 | 32 | looping = False 33 | 34 | full_conf_file = None 35 | 36 | default_config = {} 37 | default_config['protected'] = {} 38 | default_config['protected']['name'] = __file__ 39 | default_config['protected']['guild'] = None 40 | default_config['standard_groups'] = {} 41 | default_config['standard_groups']['value'] = [] 42 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 43 | default_config['admin_groups'] = {} 44 | default_config['admin_groups']['value'] = [] 45 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 46 | default_config['blacklisted'] = {} 47 | default_config['blacklisted']['value'] = [] 48 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 49 | default_config['post_channel'] = {} 50 | default_config['post_channel']['value'] = "" 51 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 52 | 53 | # Server configurable 54 | 55 | group = '@everyone' 56 | 57 | admin = False 58 | 59 | cheer = -1 60 | 61 | cat = 'admin' 62 | 63 | def __init__(self, client = None): 64 | self.client = client 65 | self.configutils = ConfigUtils() 66 | 67 | # Load configuration if it exists 68 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 69 | 70 | 71 | logger.debug('\n\nConfigs Loaded:') 72 | for config in self.guild_confs: 73 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 74 | 75 | def getArgs(self, message): 76 | cmd = str(message.content) 77 | seg = str(message.content).split(' ') 78 | 79 | if len(seg) > 1: 80 | return seg 81 | else: 82 | return None 83 | 84 | def generatePluginConfig(self, file_name): 85 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 86 | self.guild_confs.append(new_conf) 87 | 88 | def checkCat(self, check_cat): 89 | if self.cat == check_cat: 90 | return True 91 | else: 92 | return False 93 | 94 | def checkBits(self, bits): 95 | return False 96 | 97 | async def runCheer(self, user, amount): 98 | return True 99 | 100 | async def getMessageById(self, guild, id): 101 | target_message = None 102 | for channel in guild.channels: 103 | try: 104 | target_message = await channel.fetch_message(id) 105 | #print('Found ' + str(id) + ' in ' + str(channel.name)) 106 | break 107 | except: 108 | continue 109 | 110 | return target_message 111 | 112 | 113 | async def runCheer(self, user, amount): 114 | return True 115 | 116 | async def run(self, message, obj_list): 117 | # Permissions check 118 | if not self.configutils.hasPerms(message, False, self.guild_confs): 119 | await message.channel.send(message.author.mention + ' Permission denied') 120 | return False 121 | 122 | # Parse args 123 | arg = self.getArgs(message) 124 | 125 | # Config set/get check 126 | if arg != None: 127 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 128 | return True 129 | 130 | await message.channel.send("Searching...") 131 | 132 | # Do Specific Plugin Stuff 133 | message_id = str(arg[1]) 134 | 135 | target_message = await self.getMessageById(message.guild, message_id) 136 | 137 | for real_member in message.mentions: 138 | message_content = message.author.mention + ' User ' + str(real_member.name) + ' Reacted with: ' 139 | found = False 140 | for reaction in target_message.reactions: 141 | async for member in reaction.users(): 142 | if member.name == real_member.name: 143 | found = True 144 | message_content = message_content + str(reaction.emoji) 145 | if found: 146 | await message.channel.send(message_content) 147 | else: 148 | await message.channel.send(message.author.mention + ' User ' + str(real_member.name) + ' has not reacted') 149 | 150 | return True 151 | 152 | async def stop(self, message): 153 | self.looping = False 154 | -------------------------------------------------------------------------------- /plugins/plugin_user_info.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from datetime import datetime 7 | from dotenv import load_dotenv 8 | from discord.ext.tasks import loop 9 | from requests import get 10 | 11 | sys.path.append(os.path.abspath('utils')) 12 | 13 | from utils.config_utils import ConfigUtils 14 | 15 | class UserInfo(): 16 | # Required for all plugins 17 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 18 | 19 | name = '!userinfo' 20 | 21 | desc = 'Get information regarding discord user' 22 | 23 | synt = '!userinfo |[config|get |set |add/remove ]' 24 | 25 | looping = False 26 | 27 | group = '@everyone' 28 | 29 | admin = False 30 | 31 | guild_confs = [] 32 | 33 | configutils = None 34 | 35 | cheer = -1 36 | 37 | cat = 'admin' 38 | 39 | default_config = {} 40 | default_config['protected'] = {} 41 | default_config['protected']['name'] = __file__ 42 | default_config['protected']['guild'] = None 43 | default_config['standard_groups'] = {} 44 | default_config['standard_groups']['value'] = [] 45 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 46 | default_config['admin_groups'] = {} 47 | default_config['admin_groups']['value'] = [] 48 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 49 | default_config['blacklisted'] = {} 50 | default_config['blacklisted']['value'] = [] 51 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 52 | default_config['post_channel'] = {} 53 | default_config['post_channel']['value'] = "" 54 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 55 | 56 | is_service = False 57 | 58 | client = None 59 | 60 | def __init__(self, client = None): 61 | self.client = client 62 | self.configutils = ConfigUtils() 63 | 64 | # Load configuration if it exists 65 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 66 | 67 | 68 | logger.debug('\n\nConfigs Loaded:') 69 | for config in self.guild_confs: 70 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 71 | 72 | def getArgs(self, message): 73 | cmd = str(message.content) 74 | seg = str(message.content).split(' ') 75 | 76 | if len(seg) > 1: 77 | return seg 78 | else: 79 | return None 80 | 81 | def generatePluginConfig(self, file_name): 82 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 83 | self.guild_confs.append(new_conf) 84 | 85 | def checkCat(self, check_cat): 86 | if self.cat == check_cat: 87 | return True 88 | else: 89 | return False 90 | 91 | def checkBits(self, bits): 92 | return False 93 | 94 | async def runCheer(self, user, amount): 95 | return 96 | 97 | async def run(self, message, obj_list): 98 | # Permissions check 99 | if not self.configutils.hasPerms(message, False, self.guild_confs): 100 | await message.channel.send(message.author.mention + ' Permission denied') 101 | return False 102 | 103 | # Parse args 104 | arg = self.getArgs(message) 105 | 106 | # Config set/get check 107 | if arg != None: 108 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 109 | return True 110 | 111 | # target_user = str(message.content).split(' ')[1] 112 | 113 | # Search for the user 114 | # found = False 115 | 116 | for real_member in message.mentions: 117 | # for member in message.guild.members: 118 | # if str(member.display_name) == target_user: 119 | # found = True 120 | # real_member = member 121 | # break 122 | 123 | # Did not find the user 124 | # if not found: 125 | # await message.channel.send(message.author.mention + ', The user was not found') 126 | # Found the user 127 | # else: 128 | embed=discord.Embed(title="User Info", 129 | color=discord.Color.blue()) 130 | 131 | member_roles = [] 132 | member_perms = '' 133 | mem_join = real_member.joined_at 134 | now = datetime.now() 135 | logger.debug(str(mem_join) + ' - ' + str(now)) 136 | join_days = (now - mem_join).days 137 | for role in real_member.roles: 138 | member_roles.append(role.name) 139 | for perm in real_member.permissions_in(message.channel): 140 | if perm[1]: 141 | member_perms = member_perms + str(perm[0]) + '\n' 142 | 143 | # await message.channel.send(message.author.mention + ', ' + real_member.mention + ' joined ' + str(join_days) + ' day(s) ago') 144 | embed.add_field(name="Username", value='`' + str(real_member.name) + '#' + str(real_member.discriminator) + '`', inline=True) 145 | embed.add_field(name="User ID", value='`' + str(real_member.id) + '`', inline=True) 146 | embed.add_field(name="Nickname", value='`' + str(real_member.display_name) + '`', inline=True) 147 | 148 | embed.add_field(name="Created", value='```' + str(real_member.created_at) + '```', inline=False) 149 | 150 | embed.add_field(name="Joined", value='```' + str(mem_join) + '```', inline=True) 151 | embed.add_field(name="Days on server", value='```' + str(join_days) + '```', inline=True) 152 | 153 | embed.add_field(name="Roles", value='```' + str(member_roles) + '```', inline=False) 154 | 155 | embed.add_field(name="Permissions in this channel", value='```' + str(member_perms) + '```', inline=False) 156 | 157 | await message.channel.send(embed=embed) 158 | 159 | return True 160 | 161 | async def stop(self, message): 162 | self.looping = False 163 | -------------------------------------------------------------------------------- /discord_bot.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import sys 4 | import openai 5 | import asyncio 6 | import discord 7 | import time 8 | import socket 9 | import threading 10 | from logger import logger 11 | from string import printable 12 | from dotenv import load_dotenv 13 | from mcrcon import MCRcon 14 | from discord.ext.tasks import loop 15 | from datetime import datetime 16 | import random 17 | import pkgutil 18 | 19 | sys.dont_write_bytecode = True 20 | 21 | W = '\033[0m' # white (normal) 22 | R = '\033[31m' # red 23 | G = '\033[32m' # green 24 | O = '\033[33m' # orange 25 | B = '\033[34m' # blue 26 | P = '\033[35m' # purple 27 | C = '\033[36m' # cyan 28 | GR = '\033[37m' # gray 29 | T = '\033[93m' # tan 30 | 31 | # Chicken 32 | 33 | load_dotenv() 34 | TOKEN = os.getenv('DISCORD_TOKEN') 35 | GUILD = os.getenv('DISCORD_GUILD') 36 | RCONIP = os.getenv('RCON_IP') 37 | PASSW = os.getenv('RCON_PASSWORD') 38 | OPENAI_KEY = os.getenv('OPENAI_KEY') 39 | 40 | global obj_list 41 | global channels_list 42 | global modules 43 | obj_list = [] 44 | channels_list = [] 45 | 46 | path = os.path.join(os.path.dirname(__file__), "plugins") 47 | modules = pkgutil.iter_modules(path=[path]) 48 | 49 | class CustomClient(discord.Client): 50 | global obj_list 51 | global members_list 52 | global channels_list 53 | global modules 54 | 55 | conf_path = os.path.join(os.path.dirname(__file__), "plugins/configs") 56 | #conf_path = os.path.dirname(os.path.abspath(__file__)) 57 | 58 | members_list = [] 59 | start_chat_log = '''Human: Hello, who are you? 60 | AI: I am doing great. How can I help you today? 61 | ''' 62 | chat_log = None 63 | 64 | openai.api_key = os.environ.get('OPENAI_KEY') 65 | completion = openai.Completion() 66 | 67 | def __init__(self, discord_intents: discord.Intents): 68 | super().__init__(intents=discord_intents) 69 | 70 | logger.debug('Init done') 71 | 72 | def get_class_name(self, mod_name): 73 | output = "" 74 | 75 | words = mod_name.split("_")[1:] 76 | 77 | for word in words: 78 | output += word.title() 79 | return output 80 | 81 | 82 | # Bot connects to discord server 83 | async def on_ready(self): 84 | logger.debug (f'{self.user} has connected to Discord!') 85 | 86 | for guild in client.guilds: 87 | 88 | logger.debug( 89 | f'\n{client.user} is connected to the following guild:\n' 90 | f'{guild.name}(id: {guild.id})\n' 91 | ) 92 | 93 | file_name = str(guild.name) + '_' + str(guild.id) + '_conf.json' 94 | 95 | if not os.path.isfile(os.path.join(self.conf_path, file_name)): 96 | logger.debug('Guild configuration file not found. Creating...') 97 | with open(os.path.join(self.conf_path, file_name), 'w'): 98 | pass 99 | 100 | logger.debug('Member count: ' + str(guild.member_count)) 101 | 102 | for member in guild.members: 103 | members_list.append(member.name) 104 | 105 | logger.debug('len(members_list): ' + str(len(members_list))) 106 | 107 | logger.debug('Guild Roles:') 108 | for role in guild.roles: 109 | logger.debug('\t' + role.name) 110 | 111 | 112 | print () 113 | 114 | logger.debug('Guild text channels:') 115 | for channel in guild.channels: 116 | if str(channel.type) == 'text': 117 | channels_list.append(channel) 118 | logger.debug('\t' + str(channel.name)) 119 | 120 | print () 121 | 122 | # This needs to be AFTER creating/importing guild config files 123 | for loader, mod_name, ispkg in modules: 124 | if (mod_name not in sys.modules) and (mod_name.startswith('plugin_')): 125 | 126 | loaded_mod = __import__(path+"."+mod_name, fromlist=[mod_name]) 127 | 128 | class_name = self.get_class_name(mod_name) 129 | loaded_class = getattr(loaded_mod, class_name) 130 | 131 | instance = loaded_class(client) 132 | obj_list.append(instance) 133 | 134 | # Show all plugins and start all services that can be started 135 | logger.debug('Plugins loaded:') 136 | for obj in obj_list: 137 | logger.debug('\t' + str(obj.name)) 138 | if obj.is_service: 139 | try: 140 | await obj.startService() 141 | except: 142 | continue 143 | 144 | async def on_guild_join(self, guild): 145 | file_name = str(guild.name) + '_' + str(guild.id) + '_conf.json' 146 | 147 | if not os.path.isfile(os.path.join(self.conf_path, file_name)): 148 | logger.debug('Guild configuration file not found. Creating...') 149 | with open(os.path.join(self.conf_path, file_name), 'w'): 150 | pass 151 | 152 | for obj in obj_list: 153 | logger.debug('Generating config for ' + str(obj.name)) 154 | obj.generatePluginConfig(file_name) 155 | 156 | async def ask_openai(self, question, chat_log = None): 157 | if self.chat_log is None: 158 | self.chat_log = self.start_chat_log 159 | prompt = f'{chat_log}Human: {question}\nAI:' 160 | response = self.completion.create( 161 | prompt=prompt, engine='davinci', stop=['\nHuman'], temperature=0.9, 162 | top_p=1, frequency_penalty=0, presence_penalty=0.6, best_of=1, 163 | max_tokens=150) 164 | answer = response.choices[0].text.strip() 165 | return answer 166 | 167 | async def append_interaction_to_chat_log(self, question, answer, chat_log=None): 168 | if self.chat_log is None: 169 | self.chat_log = self.start_chat_log 170 | return f'{chat_log}Human: {question}\nAI: {answer}\n' 171 | 172 | # Bot received a message on discord server 173 | async def on_message(self, message): 174 | try: 175 | output = '[' + str(datetime.now()) + '][' + str(message.channel.name) + ']' 176 | except: 177 | output = '[' + str(datetime.now()) + ']' 178 | 179 | # Ignore bot's own messages 180 | if message.author == client.user: 181 | return 182 | 183 | output = output + ' ' + message.author.name + ': ' + message.content 184 | 185 | logger.info(output) 186 | 187 | # Check if having conversation 188 | if client.user.mentioned_in(message): 189 | new_question = message.content.replace(client.user.mention + ' ', '') 190 | logger.debug('Getting openAI response to: ' + str(new_question)) 191 | 192 | try: 193 | answer = await self.ask_openai(new_question, self.chat_log) 194 | except Exception as e: 195 | logger.error('Could not prompt OpenAI: ' + str(e)) 196 | 197 | logger.info(answer) 198 | 199 | try: 200 | self.chat_log = await self.append_interaction_to_chat_log(message.content, answer, self.chat_log) 201 | except Exception as e: 202 | logger.error('Could not add to chat log') 203 | 204 | # await message.channel.send(message.author.mention + str(answer)) 205 | await message.channel.send(str(answer), reference=message) 206 | 207 | # Work response 208 | if message.content == '!muster': 209 | await message.channel.send(message.author.mention + ' Here') 210 | 211 | # Check if multipart command 212 | if ' ' in str(message.content): 213 | cmd = str(message.content).split(' ')[0] 214 | else: 215 | cmd = str(message.content) 216 | 217 | for obj in obj_list: 218 | if cmd == obj.name: 219 | await obj.run(message, obj_list) 220 | break 221 | 222 | intents = discord.Intents.all() 223 | client = CustomClient(intents) 224 | 225 | client.run(TOKEN) 226 | client.main.start() 227 | -------------------------------------------------------------------------------- /plugins/plugin_code_generator.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | import openai 6 | from logger import logger 7 | from dotenv import load_dotenv 8 | from discord.ext.tasks import loop 9 | from requests import get 10 | 11 | sys.path.append(os.path.abspath('utils')) 12 | 13 | from utils.config_utils import ConfigUtils 14 | from utils.Examples import examples 15 | 16 | class Example(): 17 | """Stores an input, output pair and formats it to prime the model.""" 18 | 19 | def __init__(self, inp, out): 20 | self.input = inp 21 | self.output = out 22 | 23 | def get_input(self): 24 | """Returns the input of the example.""" 25 | return self.input 26 | 27 | def get_output(self): 28 | """Returns the intended output of the example.""" 29 | return self.output 30 | 31 | def format(self): 32 | """Formats the input, output pair.""" 33 | return f"input: {self.input}\noutput: {self.output}\n" 34 | 35 | class GPT: 36 | """The main class for a user to interface with the OpenAI API. 37 | A user can add examples and set parameters of the API request.""" 38 | 39 | def __init__(self, engine='code-davinci-002', 40 | temperature=0.5, 41 | max_tokens=150): 42 | self.examples = [] 43 | self.engine = engine 44 | self.temperature = temperature 45 | self.max_tokens = max_tokens 46 | 47 | def add_example(self, ex): 48 | """Adds an example to the object. Example must be an instance 49 | of the Example class.""" 50 | assert isinstance(ex, Example), "Please create an Example object." 51 | self.examples.append(ex.format()) 52 | 53 | def get_prime_text(self): 54 | """Formats all examples to prime the model.""" 55 | return '\n'.join(self.examples) + '\n' 56 | 57 | def get_engine(self): 58 | """Returns the engine specified for the API.""" 59 | return self.engine 60 | 61 | def get_temperature(self): 62 | """Returns the temperature specified for the API.""" 63 | return self.temperature 64 | 65 | def get_max_tokens(self): 66 | """Returns the max tokens specified for the API.""" 67 | return self.max_tokens 68 | 69 | def craft_query(self, prompt): 70 | """Creates the query for the API request.""" 71 | return self.get_prime_text() + "input: " + prompt + "\n" 72 | 73 | def submit_request(self, prompt): 74 | """Calls the OpenAI API with the specified parameters.""" 75 | response = openai.Completion.create(engine=self.get_engine(), 76 | prompt=self.craft_query(prompt), 77 | max_tokens=self.get_max_tokens(), 78 | temperature=self.get_temperature(), 79 | top_p=1, 80 | n=1, 81 | stream=False, 82 | stop="\ninput:") 83 | return response 84 | 85 | def get_top_reply(self, prompt): 86 | """Obtains the best result as returned by the API.""" 87 | response = self.submit_request(prompt) 88 | return response['choices'][0]['text'] 89 | 90 | 91 | 92 | class CodeGenerator(): 93 | # Required for all plugins 94 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 95 | 96 | guild_confs = [] 97 | 98 | configutils = None 99 | 100 | name = '!codegenerator' 101 | 102 | desc = 'Ask it for some Python code. (TESTING)' 103 | 104 | synt = '!codegenerator [][config|get |set |add/remove ]' 105 | 106 | is_service = False 107 | 108 | client = None 109 | 110 | looping = False 111 | 112 | full_conf_file = None 113 | 114 | default_config = {} 115 | default_config['protected'] = {} 116 | default_config['protected']['name'] = __file__ 117 | default_config['protected']['guild'] = None 118 | default_config['standard_groups'] = {} 119 | default_config['standard_groups']['value'] = [] 120 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 121 | default_config['admin_groups'] = {} 122 | default_config['admin_groups']['value'] = [] 123 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 124 | default_config['blacklisted'] = {} 125 | default_config['blacklisted']['value'] = [] 126 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 127 | default_config['post_channel'] = {} 128 | default_config['post_channel']['value'] = "" 129 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 130 | 131 | # Server configurable 132 | 133 | group = '@everyone' 134 | 135 | admin = False 136 | 137 | cheer = -1 138 | 139 | cat = 'admin' 140 | 141 | gpt = None 142 | 143 | def __init__(self, client = None): 144 | self.client = client 145 | self.configutils = ConfigUtils() 146 | 147 | load_dotenv() 148 | openai.api_key = os.environ.get('OPENAI_KEY') 149 | self.gpt = GPT(engine="code-davinci-002", temperature=0.5, max_tokens=500) 150 | 151 | # Load configuration if it exists 152 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 153 | 154 | for example in examples: 155 | logger.debug('Loading example: ' + example[0]) 156 | self.gpt.add_example(Example(example[0], example[1])) 157 | 158 | 159 | logger.debug('\n\nConfigs Loaded:') 160 | for config in self.guild_confs: 161 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 162 | 163 | def getArgs(self, message): 164 | cmd = str(message.content) 165 | seg = str(message.content).split(' ') 166 | 167 | if len(seg) > 1: 168 | return seg 169 | else: 170 | return None 171 | 172 | def generatePluginConfig(self, file_name): 173 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 174 | self.guild_confs.append(new_conf) 175 | 176 | def checkCat(self, check_cat): 177 | if self.cat == check_cat: 178 | return True 179 | else: 180 | return False 181 | 182 | def checkBits(self, bits): 183 | return False 184 | 185 | async def runCheer(self, user, amount): 186 | return True 187 | 188 | async def get_post_channel(self, message, the_config): 189 | # Find where the bot will be posting its announcements 190 | for channel in message.guild.channels: 191 | try: 192 | if str(channel.mention) == str(the_config['post_channel']['value']): 193 | logger.debug('Found post_channel: ' + str(channel.mention)) 194 | return channel 195 | except: 196 | return None 197 | return None 198 | 199 | async def run(self, message, obj_list): 200 | # Permissions check 201 | if not self.configutils.hasPerms(message, False, self.guild_confs): 202 | await message.channel.send(message.author.mention + ' Permission denied') 203 | return False 204 | 205 | # Parse args 206 | arg = self.getArgs(message) 207 | 208 | # Config set/get check 209 | if arg != None: 210 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 211 | return True 212 | 213 | # Do Specific Plugin Stuff 214 | 215 | prompt = message.content.replace(self.name + ' ', '') 216 | 217 | output = self.gpt.submit_request(prompt) 218 | 219 | new_output = '' 220 | if output.choices[0].text.startswith('output: '): 221 | new_output = output.choices[0].text[9:] 222 | 223 | embed = discord.Embed(title="Code Reponse", 224 | color=discord.Color.green()) 225 | 226 | embed.add_field(name='Prompt', value = '```' + str(prompt) + '```', inline=False) 227 | embed.add_field(name='Completion Tokens', value='```' + str(output.usage.completion_tokens) + '```', inline=True) 228 | embed.add_field(name='Prompt Tokens', value='```' + str(output.usage.prompt_tokens) + '```', inline=True) 229 | embed.add_field(name='Total Tokens', value='```' + str(output.usage.total_tokens) + '```', inline=True) 230 | embed.add_field(name='Code Body', value='```Python\n' + new_output + '```', inline=False) 231 | 232 | the_config = self.configutils.getGuildConfig(message, self.guild_confs) 233 | 234 | local_post_channel = await self.get_post_channel(message, the_config) 235 | 236 | if not local_post_channel: 237 | await message.channel.send("Here is your code", reference=message, embed=embed) 238 | else: 239 | await local_post_channel.send("Here is your code, " + message.author.mention, embed=embed) 240 | 241 | return True 242 | 243 | async def stop(self, message): 244 | self.looping = False 245 | -------------------------------------------------------------------------------- /plugins/plugin_founders.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import asyncio 5 | from logger import logger 6 | from datetime import datetime 7 | from dotenv import load_dotenv 8 | from discord.ext.tasks import loop 9 | from discord.utils import get 10 | from requests import get 11 | 12 | sys.path.append(os.path.abspath('utils')) 13 | 14 | from utils.config_utils import ConfigUtils 15 | 16 | class Founders(): 17 | # Required for all plugins 18 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 19 | 20 | guild_confs = [] 21 | 22 | configutils = None 23 | 24 | name = '!founders' 25 | 26 | desc = 'Apply roles automatically based time since server start' 27 | 28 | synt = '!founders [config|get |set |add/remove |start|stop]' 29 | 30 | looping = False 31 | 32 | group = '@everyone' 33 | 34 | default_config = {} 35 | default_config['protected'] = {} 36 | default_config['protected']['name'] = __file__ 37 | default_config['protected']['guild'] = None 38 | default_config['standard_groups'] = {} 39 | default_config['standard_groups']['value'] = [] 40 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 41 | default_config['admin_groups'] = {} 42 | default_config['admin_groups']['value'] = [] 43 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 44 | default_config['blacklisted'] = {} 45 | default_config['blacklisted']['value'] = [] 46 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 47 | default_config['post_channel'] = {} 48 | default_config['post_channel']['value'] = "" 49 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 50 | default_config['founder_role'] = {} 51 | default_config['founder_role']['value'] = "" 52 | default_config['founder_role']['description'] = "Days since server started and role to give 'days:role'" 53 | default_config['required_role'] = {} 54 | default_config['required_role']['value'] = "@everyone" 55 | default_config['required_role']['description'] = "Role required in order to be given an autorole" 56 | 57 | running_guilds = [] 58 | 59 | admin = False 60 | 61 | cheer = -1 62 | 63 | cat = 'admin' 64 | 65 | is_service = True 66 | 67 | client = None 68 | 69 | def __init__(self, client = None): 70 | self.client = client 71 | self.configutils = ConfigUtils() 72 | 73 | # Load configuration if it exists 74 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 75 | 76 | 77 | logger.debug('\n\nConfigs Loaded:') 78 | for config in self.guild_confs: 79 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 80 | 81 | 82 | def getArgs(self, message): 83 | cmd = str(message.content) 84 | seg = str(message.content).split(' ') 85 | 86 | if len(seg) > 1: 87 | return seg 88 | else: 89 | return None 90 | 91 | def generatePluginConfig(self, file_name): 92 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 93 | self.guild_confs.append(new_conf) 94 | 95 | 96 | @loop(seconds = 3600) 97 | async def loop_func(self): 98 | if self.looping: 99 | # Loop through all guilds running this service 100 | for the_guild_string in self.running_guilds: 101 | # Get the roles and days 102 | the_config = self.configutils.getGuildConfigByGuildConfigName(the_guild_string, self.guild_confs) 103 | 104 | # Get the actual guild 105 | guild = None 106 | for g in self.client.guilds: 107 | list_guild_string = str(g.name) + str(g.id) 108 | if list_guild_string == the_guild_string: 109 | guild = g 110 | 111 | # Skip to next guild if we can't find the current guild in client 112 | if guild == None: 113 | continue 114 | 115 | # Loop through all members in the guild 116 | logger.debug('Running founder role check...') 117 | for member in guild.members: 118 | if not self.looping: 119 | logger.debug('Exiting from founder role check...') 120 | self.loop_func.stop() 121 | return 122 | 123 | # Get days since member joined server 124 | mem_join = member.joined_at 125 | now = datetime.now() 126 | #print(str(mem_join) + ' - ' + str(now)) 127 | join_days = (now - mem_join).days 128 | 129 | # Get days since server was started 130 | guild_start = guild.created_at 131 | guild_days = (now - guild_start).days 132 | 133 | days_dif = guild_days - join_days 134 | 135 | role_index = str(the_config['founder_role']['value']).split(':') 136 | 137 | # Check if user surpassed days 138 | if int(days_dif) <= int(role_index[0]): 139 | the_role = None 140 | # Get the actual role from the guild using the saved mention 141 | for role in guild.roles: 142 | if role.mention == str(role_index[1]): 143 | the_role = role 144 | #print('Found the role: ' + str(the_role.name)) 145 | break 146 | 147 | # Could not find the founder role 148 | if the_role == None: 149 | continue 150 | 151 | # Member already has the role 152 | if the_role in member.roles: 153 | continue 154 | 155 | # Check if member has required role 156 | req_role = None 157 | for r_role in guild.roles: 158 | if r_role.mention == str(the_config['required_role']['value']): 159 | req_role = r_role 160 | break 161 | 162 | # Couldn't find required role 163 | if req_role == None: 164 | logger.debug("[!] Could not get required role for autorole") 165 | continue 166 | 167 | # Member doesn't have required role 168 | if req_role not in member.roles: 169 | continue 170 | 171 | # One last check to make sure this member is still on the server 172 | if guild.get_member(member.id) is not None: 173 | await member.add_roles(the_role) 174 | logger.info('Gave \'' + str(role_index[1]) + '\' to ' + member.name) 175 | 176 | 177 | def checkCat(self, check_cat): 178 | if self.cat == check_cat: 179 | return True 180 | else: 181 | return False 182 | 183 | def checkBits(self, bits): 184 | return False 185 | 186 | # Required method for services (content may vary) 187 | async def getStatus(self, message): 188 | # Return True if there is a giveaway running in the source message's server 189 | for index in self.running_guilds: 190 | if str(message.guild.name) + str(message.guild.id) == index: 191 | return True 192 | 193 | return False 194 | 195 | async def startService(self): 196 | if not self.looping: 197 | self.looping = True 198 | self.loop_func.start() 199 | return True 200 | return False 201 | 202 | async def runCheer(self, user, amount): 203 | return 204 | 205 | async def run(self, message, obj_list): 206 | # Permissions check 207 | if not self.configutils.hasPerms(message, True, self.guild_confs): 208 | await message.channel.send(message.author.mention + ' Permission denied') 209 | return False 210 | 211 | # Parse args 212 | arg = self.getArgs(message) 213 | 214 | # Config set/get check 215 | if arg != None: 216 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 217 | return True 218 | 219 | 220 | self.global_message = message 221 | cmd = message.content 222 | if len(cmd.split(' ')) > 1: 223 | arg = cmd.split(' ')[1] 224 | else: 225 | arg = None 226 | 227 | if cmd == self.name: 228 | if self.looping: 229 | await message.channel.send(message.author.mention + ' ' + self.name + ' is running') 230 | else: 231 | await message.channel.send(message.author.mention + ' ' + self.name + ' is not running') 232 | if arg == None: 233 | return 234 | 235 | the_guild = str(message.guild.name) + str(message.guild.id) 236 | 237 | if str(arg) == 'start': 238 | if the_guild not in self.running_guilds: 239 | #self.looping = True 240 | self.running_guilds.append(the_guild) 241 | logger.debug('Guilds running ' + str(self.name) + ':') 242 | for gu in self.running_guilds: 243 | logger.debug('\t' + gu) 244 | await message.channel.send(message.author.mention + ' Starting ' + str(self.name)) 245 | #self.loop_func.start() 246 | return True 247 | 248 | if str(arg) == 'stop': 249 | if the_guild in self.running_guilds: 250 | #self.looping = False 251 | self.running_guilds.remove(the_guild) 252 | await message.channel.send(message.author.mention + ' Stopping ' + str(self.name)) 253 | for gu in self.running_guilds: 254 | logger.debug('\t' + gu) 255 | #self.loop_func.stop() 256 | return True 257 | return 258 | 259 | async def stop(self, message): 260 | self.looping = False 261 | -------------------------------------------------------------------------------- /plugins/plugin_auto_role.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import asyncio 5 | from logger import logger 6 | from datetime import datetime 7 | from dotenv import load_dotenv 8 | from discord.ext.tasks import loop 9 | from discord.utils import get 10 | from requests import get 11 | 12 | sys.path.append(os.path.abspath('utils')) 13 | 14 | from utils.config_utils import ConfigUtils 15 | 16 | class AutoRole(): 17 | # Required for all plugins 18 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 19 | 20 | guild_confs = [] 21 | 22 | configutils = None 23 | 24 | name = '!autorole' 25 | 26 | desc = 'Apply roles automatically based on time spent in server' 27 | 28 | synt = '!autorole [config|get |set |add/remove |start|stop]' 29 | 30 | looping = False 31 | 32 | group = '@everyone' 33 | 34 | default_config = {} 35 | default_config['protected'] = {} 36 | default_config['protected']['name'] = __file__ 37 | default_config['protected']['guild'] = None 38 | default_config['standard_groups'] = {} 39 | default_config['standard_groups']['value'] = [] 40 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 41 | default_config['admin_groups'] = {} 42 | default_config['admin_groups']['value'] = [] 43 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 44 | default_config['blacklisted'] = {} 45 | default_config['blacklisted']['value'] = [] 46 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 47 | default_config['post_channel'] = {} 48 | default_config['post_channel']['value'] = "" 49 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 50 | default_config['time_groups'] = {} 51 | default_config['time_groups']['value'] = [] 52 | default_config['time_groups']['description'] = "List of roles and days required to join roles 'days:group'" 53 | default_config['required_role'] = {} 54 | default_config['required_role']['value'] = "@everyone" 55 | default_config['required_role']['description'] = "Role required in order to be given an autorole" 56 | 57 | running_guilds = [] 58 | 59 | admin = False 60 | 61 | cheer = -1 62 | 63 | cat = 'admin' 64 | 65 | is_service = True 66 | 67 | client = None 68 | 69 | def __init__(self, client = None): 70 | self.client = client 71 | self.configutils = ConfigUtils() 72 | 73 | # Load configuration if it exists 74 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 75 | 76 | 77 | logger.debug('\n\nConfigs Loaded:') 78 | for config in self.guild_confs: 79 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 80 | 81 | # loop = asyncio.new_event_loop() 82 | # asyncio.set_event_loop(loop) 83 | 84 | # self.looping = True 85 | # self.loop_func.start() 86 | 87 | 88 | def getArgs(self, message): 89 | cmd = str(message.content) 90 | seg = str(message.content).split(' ') 91 | 92 | if len(seg) > 1: 93 | return seg 94 | else: 95 | return None 96 | 97 | def generatePluginConfig(self, file_name): 98 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 99 | self.guild_confs.append(new_conf) 100 | 101 | 102 | @loop(seconds = 3600) 103 | async def loop_func(self): 104 | if self.looping: 105 | # Loop through all guilds running this service 106 | for the_guild_string in self.running_guilds: 107 | # Get the roles and days 108 | the_config = self.configutils.getGuildConfigByGuildConfigName(the_guild_string, self.guild_confs) 109 | 110 | # Get the actual guild 111 | guild = None 112 | for g in self.client.guilds: 113 | list_guild_string = str(g.name) + str(g.id) 114 | if list_guild_string == the_guild_string: 115 | guild = g 116 | 117 | # Skip to next guild if we can't find the current guild in client 118 | if guild == None: 119 | continue 120 | 121 | # Loop through all members in the guild 122 | logger.debug('Running auto role check...') 123 | for member in guild.members: 124 | if not self.looping: 125 | logger.debug('Exiting from auto role check...') 126 | self.loop_func.stop() 127 | return 128 | # Get days since member joined server 129 | mem_join = member.joined_at 130 | now = datetime.now() 131 | #print(str(mem_join) + ' - ' + str(now)) 132 | join_days = (now - mem_join).days 133 | 134 | # Check days list and the role for those days 135 | for day_role_group in the_config['time_groups']['value']: 136 | # Unpack list of days to roles 137 | role_index = day_role_group.split(':') 138 | 139 | # Check if user surpassed days 140 | if int(join_days) >= int(role_index[0]): 141 | the_role = None 142 | # Get the actual role from the guild using the saved mention 143 | for role in guild.roles: 144 | if role.mention == str(role_index[1]): 145 | the_role = role 146 | #print('Found the role: ' + str(the_role.name)) 147 | break 148 | if the_role == None: 149 | continue 150 | 151 | if the_role in member.roles: 152 | continue 153 | 154 | req_role = None 155 | for r_role in guild.roles: 156 | if r_role.mention == str(the_config['required_role']['value']): 157 | req_role = r_role 158 | break 159 | 160 | if req_role == None: 161 | logger.debug("[!] Could not get required role for autorole") 162 | continue 163 | 164 | if req_role not in member.roles: 165 | #print('[!] ' + member.name + ' does not have required role to be given autorole') 166 | continue 167 | 168 | # role = get(self.global_message.guild.roles, name = str(role_index[1])) 169 | # One last check to make sure this member is still on the server 170 | if guild.get_member(member.id) is not None: 171 | await member.add_roles(the_role) 172 | logger.info('Gave \'' + str(role_index[1]) + '\' to ' + member.name) 173 | # Get rid of this else. Just for debugging 174 | #else: 175 | # print(member.name + ' is no longer on the server') 176 | 177 | def checkCat(self, check_cat): 178 | if self.cat == check_cat: 179 | return True 180 | else: 181 | return False 182 | 183 | def checkBits(self, bits): 184 | return False 185 | 186 | # Required method for services (content may vary) 187 | async def getStatus(self, message): 188 | # Return True if there is a giveaway running in the source message's server 189 | for index in self.running_guilds: 190 | if str(message.guild.name) + str(message.guild.id) == index: 191 | return True 192 | 193 | return False 194 | 195 | async def startService(self): 196 | if not self.looping: 197 | self.looping = True 198 | self.loop_func.start() 199 | return True 200 | return False 201 | 202 | async def runCheer(self, user, amount): 203 | return 204 | 205 | async def run(self, message, obj_list): 206 | # Permissions check 207 | if not self.configutils.hasPerms(message, True, self.guild_confs): 208 | await message.channel.send(message.author.mention + ' Permission denied') 209 | return False 210 | 211 | # Parse args 212 | arg = self.getArgs(message) 213 | 214 | # Config set/get check 215 | if arg != None: 216 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 217 | return True 218 | 219 | 220 | self.global_message = message 221 | cmd = message.content 222 | if len(cmd.split(' ')) > 1: 223 | arg = cmd.split(' ')[1] 224 | else: 225 | arg = None 226 | 227 | if cmd == '!autorole': 228 | if self.looping: 229 | await message.channel.send(message.author.mention + ' autorole is running') 230 | else: 231 | await message.channel.send(message.author.mention + ' autorole is not running') 232 | if arg == None: 233 | return 234 | 235 | the_guild = str(message.guild.name) + str(message.guild.id) 236 | 237 | if str(arg) == 'start': 238 | if the_guild not in self.running_guilds: 239 | #self.looping = True 240 | self.running_guilds.append(the_guild) 241 | logger.debug('Guilds running ' + str(self.name) + ':') 242 | for gu in self.running_guilds: 243 | logger.debug('\t' + gu) 244 | await message.channel.send(message.author.mention + ' Starting ' + str(self.name)) 245 | #self.loop_func.start() 246 | return True 247 | 248 | if str(arg) == 'stop': 249 | if the_guild in self.running_guilds: 250 | #self.looping = False 251 | self.running_guilds.remove(the_guild) 252 | await message.channel.send(message.author.mention + ' Stopping ' + str(self.name)) 253 | for gu in self.running_guilds: 254 | logger.debug('\t' + gu) 255 | #self.loop_func.stop() 256 | return True 257 | return 258 | 259 | async def stop(self, message): 260 | self.looping = False 261 | -------------------------------------------------------------------------------- /plugins/plugin_search_and_destroy.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import random 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class SearchAndDestroy(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | guild_confs = [] 19 | 20 | configutils = None 21 | 22 | name = '!searchanddestroy' 23 | 24 | desc = 'Plant the bomb. The server will have to defuse it.' 25 | 26 | synt = '''`!searchanddestroy [bombplant|bombdefuse ]` 27 | 28 | The bomb defusal code is randomly generated once it's planted. 29 | The code is only 4 digits long, the digits can repeat themselves, 30 | and it will only contain integers. 31 | If you guess the correct number in the correct index, you will receive an 'X'. 32 | If you guess the correct number in the incorrect index, you will receive an 'O'. 33 | For example, the code is 1234 but you guess 1235. 34 | You would receive an 'XXX'. 35 | If you guess 1243, you would receive an 'XXOO'. 36 | 37 | Get the number of guess you have left by typing `!searchanddestroy`''' 38 | 39 | default_config = {} 40 | default_config['protected'] = {} 41 | default_config['protected']['name'] = __file__ 42 | default_config['protected']['guild'] = None 43 | default_config['standard_groups'] = {} 44 | default_config['standard_groups']['value'] = [] 45 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 46 | default_config['admin_groups'] = {} 47 | default_config['admin_groups']['value'] = [] 48 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 49 | default_config['blacklisted'] = {} 50 | default_config['blacklisted']['value'] = [] 51 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 52 | default_config['post_channel'] = {} 53 | default_config['post_channel']['value'] = "" 54 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 55 | 56 | bombs = [] 57 | 58 | looping = False 59 | 60 | group = '@everyone' 61 | 62 | admin = False 63 | 64 | cheer = -1 65 | 66 | cat = 'admin' 67 | 68 | defuse_code = '' 69 | 70 | remaining_guesses = 10 71 | 72 | is_service = False 73 | 74 | client = None 75 | 76 | def __init__(self, client = None): 77 | self.client = client 78 | self.configutils = ConfigUtils() 79 | 80 | # Load configuration if it exists 81 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 82 | 83 | 84 | logger.debug('\n\nConfigs Loaded:') 85 | for config in self.guild_confs: 86 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 87 | 88 | def getArgs(self, message): 89 | cmd = str(message.content) 90 | seg = str(message.content).split(' ') 91 | 92 | if len(seg) > 1: 93 | return seg 94 | else: 95 | return None 96 | 97 | def generatePluginConfig(self, file_name): 98 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 99 | self.guild_confs.append(new_conf) 100 | 101 | def checkCat(self, check_cat): 102 | if self.cat == check_cat: 103 | return True 104 | else: 105 | return False 106 | 107 | def checkBits(self, bits): 108 | return False 109 | 110 | async def runCheer(self, user, amount): 111 | return 112 | 113 | async def run(self, message, obj_list): 114 | # Permissions check 115 | if not self.configutils.hasPerms(message, False, self.guild_confs): 116 | await message.channel.send(message.author.mention + ' Permission denied') 117 | return False 118 | 119 | # Parse args 120 | arg = self.getArgs(message) 121 | 122 | # Config set/get check 123 | if arg != None: 124 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 125 | return True 126 | 127 | # Do Specific Plugin Stuff 128 | 129 | # Get the guild config for this message 130 | the_config = self.configutils.getGuildConfig(message, self.guild_confs) 131 | 132 | announcements_channel = None 133 | 134 | # Find where the bot will be posting its announcements 135 | for channel in message.guild.channels: 136 | if str(channel.mention) == str(the_config['post_channel']['value']): 137 | announcements_channel = channel 138 | break 139 | 140 | cmd = str(message.content) 141 | seg = cmd.split(' ') 142 | 143 | # Check input syntax 144 | if len(seg) < 1: 145 | await message.channel.send(message.author.mention + '`' + str(message.content) + '` is not the proper syntax') 146 | return 147 | 148 | # Check if there is a bomb for this channel 149 | check_bomb = None 150 | for bomb in self.bombs: 151 | if str(bomb[0]) == str(message.guild.name) + str(message.guild.id): 152 | check_bomb = bomb 153 | break 154 | 155 | # Show status of the bomb 156 | if message.content == self.name: 157 | if check_bomb != None: 158 | await message.channel.send(message.author.mention + ' A bomb has been planted. Locate and defuse it. Remaining guesses: ' + str(self.remaining_guesses)) 159 | else: 160 | await message.channel.send(message.author.mention + ' There are no bombs planted.') 161 | return 162 | 163 | command = seg[1] 164 | 165 | # Plant the bomb 166 | if command == 'bombplant': 167 | # Check if this guild already has a bomb planted 168 | found = False 169 | for bomb in self.bombs: 170 | if str(bomb[0]) == str(message.guild.name) + str(message.guild.id): 171 | found = True 172 | break 173 | 174 | # The bomb has already been planted 175 | if found: 176 | await message.channel.send(message.author.mention + ' A bomb has *already* been planted') 177 | return 178 | 179 | # Build this guild's bomb [guild, defuse, remaining, message] 180 | remaining_guesses = 10 181 | defuse_code = str(random.randint(0, 9)) + str(random.randint(0, 9)) + str(random.randint(0, 9)) + str(random.randint(0, 9)) 182 | self.bombs.append([str(message.guild.name) + str(message.guild.id), defuse_code, remaining_guesses, message]) 183 | 184 | # Let everyone know the bomb has been planted 185 | if announcements_channel != None: 186 | await announcements_channel.send(message.author.mention + ' has planted the bomb') 187 | else: 188 | await message.channel.send(message.author.mention + ' has planted the bomb') 189 | 190 | # Show backend what bombs are active 191 | logger.info('Planted bombs:') 192 | for bomb in self.bombs: 193 | logger.info('\t' + str(bomb)) 194 | 195 | # Defuse the bomb 196 | if command == 'bombdefuse': 197 | # Check if there is even a bomb planted for this guild 198 | check_bomb = None 199 | for bomb in self.bombs: 200 | if str(bomb[0]) == str(message.guild.name) + str(message.guild.id): 201 | check_bomb = bomb 202 | break 203 | 204 | # Exit if no bomb 205 | if check_bomb == None: 206 | return 207 | 208 | xs = 0 209 | oss = 0 210 | code_guess = seg[2] 211 | 212 | code_guess_list = list(code_guess) 213 | defuse_code_list = list(check_bomb[1]) 214 | 215 | # User did not send numeric code 216 | if not code_guess.isnumeric(): 217 | await message.channel.send(message.author.mention + ' The code may only contain numbers.') 218 | 219 | # Bomb has been defused 220 | if str(code_guess) == str(check_bomb[1]): 221 | if announcements_channel != None: 222 | await announcements_channel.send(message.author.mention + ' has defused the bomb!') 223 | else: 224 | await message.channel.send(message.author.mention + ' has defused the bomb!') 225 | 226 | self.bombs.remove(check_bomb) 227 | 228 | # Show backend what bombs are active 229 | logger.info('Planted bombs:') 230 | for bomb in self.bombs: 231 | logger.info('\t' + str(bomb)) 232 | 233 | return True 234 | 235 | for i in range(0, 4): 236 | # Correct integer, correct index 237 | if code_guess_list[i] == defuse_code_list[i]: 238 | xs = xs + 1 239 | code_guess_list[i] = "-1" 240 | defuse_code_list[i] = "-1" 241 | 242 | try: 243 | while True: 244 | code_guess_list.remove('-1') 245 | except ValueError: 246 | pass 247 | 248 | try: 249 | while True: 250 | defuse_code_list.remove('-1') 251 | except ValueError: 252 | pass 253 | 254 | logger.debug('Comparing: ' + str(code_guess_list) + ' | ' + str(defuse_code_list)) 255 | 256 | for i in range(0, len(defuse_code_list)): 257 | # Correct integer, incorrect index 258 | try: 259 | if code_guess_list[i] in defuse_code_list: 260 | oss = oss + 1 261 | defuse_code_list.remove(code_guess_list[i]) 262 | code_guess_list[i] = '-1' 263 | except: 264 | fuck = True 265 | 266 | check_bomb[2] = check_bomb[2] - 1 267 | 268 | # Guesses used up 269 | if check_bomb[2] <= 0: 270 | if announcements_channel != None: 271 | await announcements_channel.send(message.author.mention + ' You are out of guesses. The bomb has detonated!') 272 | 273 | await message.channel.send(message.author.mention + ' You are out of guesses. The bomb has detonated!') 274 | self.bombs.remove(check_bomb) 275 | 276 | # Show backend what bombs are active 277 | logger.info('Planted bombs:') 278 | for bomb in self.bombs: 279 | logger.info('\t' + str(bomb)) 280 | 281 | return False 282 | 283 | response_string = ('X' * xs) + ('O' * oss) + ' remaining attempts: ' + str(check_bomb[2]) 284 | 285 | await message.channel.send(message.author.mention + ' ' + str(response_string)) 286 | 287 | return True 288 | 289 | async def stop(self, message): 290 | self.looping = False 291 | -------------------------------------------------------------------------------- /plugins/plugin_react_role.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | sys.path.append(os.path.abspath('utils')) 11 | 12 | from utils.config_utils import ConfigUtils 13 | 14 | class ReactRole(): 15 | # Required for all plugins 16 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 17 | 18 | guild_confs = [] 19 | 20 | configutils = None 21 | 22 | name = '!reactrole' 23 | 24 | desc = 'Assign a "react to receive role" capability to a message' 25 | 26 | synt = '!reactrole [ ; ...][delete ][config|get |set |add/remove ]\nOptions can be specified with ";" followed by an emote and the role of the option like so...\n!reactrole @role1; @role2; @role3' 27 | 28 | is_service = True 29 | 30 | client = None 31 | 32 | looping = False 33 | 34 | full_conf_file = None 35 | 36 | default_config = {} 37 | default_config['protected'] = {} 38 | default_config['protected']['name'] = __file__ 39 | default_config['protected']['guild'] = None 40 | default_config['standard_groups'] = {} 41 | default_config['standard_groups']['value'] = [] 42 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 43 | default_config['admin_groups'] = {} 44 | default_config['admin_groups']['value'] = [] 45 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 46 | default_config['blacklisted'] = {} 47 | default_config['blacklisted']['value'] = [] 48 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 49 | default_config['backend'] = {} 50 | default_config['backend']['reaction_messages'] = {} 51 | default_config['backend']['reaction_messages']['value'] = [] 52 | default_config['backend']['reaction_messages']['description'] = "Messages that have been given the \"react to receive role\" capability" 53 | 54 | running_guilds = [] 55 | 56 | # Server configurable 57 | 58 | group = '@everyone' 59 | 60 | admin = False 61 | 62 | cheer = -1 63 | 64 | cat = 'admin' 65 | 66 | def __init__(self, client = None): 67 | self.client = client 68 | self.configutils = ConfigUtils() 69 | 70 | # Load configuration if it exists 71 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 72 | 73 | 74 | logger.debug('\n\nConfigs Loaded:') 75 | for config in self.guild_confs: 76 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 77 | 78 | def getArgs(self, message): 79 | cmd = str(message.content) 80 | seg = str(message.content).split(' ') 81 | 82 | if len(seg) > 1: 83 | return seg 84 | else: 85 | return None 86 | 87 | def generatePluginConfig(self, file_name): 88 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 89 | self.guild_confs.append(new_conf) 90 | 91 | # Required method for services (content may vary) 92 | async def getStatus(self, message): 93 | # Return True if there is a giveaway running in the source message's server 94 | for index in self.running_guilds: 95 | if str(message.guild.name) + str(message.guild.id) == index: 96 | return True 97 | 98 | return False 99 | 100 | async def startService(self): 101 | if not self.looping: 102 | self.looping = True 103 | self.loop_func.start() 104 | return True 105 | return False 106 | 107 | def checkCat(self, check_cat): 108 | if self.cat == check_cat: 109 | return True 110 | else: 111 | return False 112 | 113 | def checkBits(self, bits): 114 | return False 115 | 116 | async def getMessageById(self, guild, id): 117 | target_message = None 118 | for channel in guild.channels: 119 | try: 120 | target_message = await channel.fetch_message(id) 121 | #print('Found ' + str(id) + ' in ' + str(channel.name)) 122 | break 123 | except: 124 | continue 125 | 126 | return target_message 127 | 128 | 129 | async def runCheer(self, user, amount): 130 | return True 131 | 132 | @loop(seconds = 5) 133 | async def loop_func(self): 134 | if self.looping: 135 | # Loop through all guilds 136 | for guild in self.client.guilds: 137 | # Only run on guilds that have the service enabled 138 | if str(guild.name) + str(guild.id) not in self.running_guilds: 139 | continue 140 | 141 | # Get the configuration of THIS guild 142 | guild_conf = self.configutils.getGuildConfigByGuild(guild, self.guild_confs) 143 | 144 | # Loop though each reaction message in THIS guild 145 | for msg in guild_conf['backend']['reaction_messages']['value']: 146 | real_message = await self.getMessageById(guild, msg['id']) 147 | if real_message == None: # The message does not exist in this server anymore 148 | continue 149 | 150 | #Loop through each reaction of THIS message 151 | for reaction in real_message.reactions: 152 | # Get the corresponding emote/role structure 153 | targ_given_reaction = None 154 | for given_reaction in msg['reactions']: 155 | if given_reaction['emote'] == str(reaction): 156 | targ_given_reaction = given_reaction 157 | 158 | # Loop through each user in THIS reaction 159 | try: 160 | async for member in reaction.users(): 161 | # Check if user already has THIS role 162 | the_role = None 163 | for role in guild.roles: 164 | if str(role.mention) == targ_given_reaction['role']: 165 | the_role = role 166 | 167 | if the_role == None: 168 | continue 169 | 170 | # Remove user emotes to keep things quick and clean 171 | if member != self.client.user: 172 | await reaction.remove(member) 173 | 174 | try: 175 | if the_role in member.roles: 176 | continue 177 | except Exception as e: 178 | continue 179 | 180 | try: 181 | await member.add_roles(the_role) 182 | logger.debug('Gave \'' + str(the_role.mention) + '\' to ' + member.name) 183 | except Exception as e: 184 | continue 185 | 186 | except Exception as e: 187 | continue 188 | 189 | return 190 | 191 | async def run(self, message, obj_list): 192 | # Permissions check 193 | if not self.configutils.hasPerms(message, True, self.guild_confs): 194 | await message.channel.send(message.author.mention + ' Permission denied') 195 | return False 196 | 197 | # Parse args 198 | arg = self.getArgs(message) 199 | 200 | # Config set/get check 201 | if arg != None: 202 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 203 | return True 204 | 205 | # Do Specific Plugin Stuff 206 | 207 | seg = str(message.content).split(' ') 208 | the_guild = str(message.guild.name) + str(message.guild.id) 209 | guild_conf = self.configutils.getGuildConfig(message, self.guild_confs) 210 | 211 | # Do service stuff 212 | if len(seg) == 2: 213 | # Check if user has admin permissions to run the service 214 | if not self.configutils.hasPerms(message, True, self.guild_confs): 215 | await message.channel.send(message.author.mention + ' Permission denied') 216 | return False 217 | 218 | if str(seg[1]) == 'start': 219 | if the_guild not in self.running_guilds: 220 | self.running_guilds.append(the_guild) 221 | logger.debug('Guilds running ' + str(self.name) + ':') 222 | for gu in self.running_guilds: 223 | logger.debug('\t' + gu) 224 | await message.channel.send(message.author.mention + ' Starting ' + str(self.name)) 225 | return True 226 | 227 | if str(seg[1]) == 'stop': 228 | if the_guild in self.running_guilds: 229 | self.running_guilds.remove(the_guild) 230 | await message.channel.send(message.author.mention + ' Stopping ' + str(self.name)) 231 | logger.debug('Guilds running ' + str(self.name) + ':') 232 | for gu in self.running_guilds: 233 | logger.debug('\t' + gu) 234 | return True 235 | 236 | return False 237 | 238 | elif len(seg) == 3: 239 | if str(seg[1]) == 'delete': 240 | targ_message_id = str(seg[2]) 241 | # Remove this react message from the plugin config 242 | for msg in guild_conf['backend']['reaction_messages']['value']: 243 | if msg['id'] == targ_message_id: 244 | guild_conf['backend']['reaction_messages']['value'].remove(msg) 245 | self.configutils.saveConfig(str(message.guild.name) + '_' + str(message.guild.id), self.guild_confs, self.conf_path) 246 | 247 | # Show us all of the configurations 248 | logger.debug('All react messages for this server:') 249 | for msg2 in guild_conf['backend']['reaction_messages']['value']: 250 | logger.debug('\t' + str(msg2['id'])) 251 | 252 | return True 253 | return False 254 | 255 | 256 | # User wants status of service 257 | elif len(seg) == 1: 258 | if self.looping: 259 | await message.channel.send(message.author.mention + ' ' + str(self.name) + ' is running') 260 | else: 261 | await message.channel.send(message.author.mention + ' ' + str(self.name) + ' is not running') 262 | return True 263 | 264 | # Get the message id the user wants 265 | targ_message_id = str(seg[1]) 266 | logger.debug('Target message id: ' + targ_message_id) 267 | 268 | # Get the target message by id 269 | real_targ_message = await self.getMessageById(message.guild, targ_message_id) 270 | if real_targ_message == None: 271 | await message.channel.send(message.author.mention + ' ' + targ_message_id + ' does not exist on this server') 272 | return False 273 | 274 | # Try to get the options 275 | options_arg = str(message.content).replace(self.name + ' ' + targ_message_id + ' ', '') 276 | options = [] 277 | for i in range(0, len(options_arg.split('; '))): 278 | option_emote = options_arg.split('; ')[i].split(' ')[0] 279 | option_text = options_arg.split('; ')[i].replace(str(option_emote) + ' ', '') 280 | full_option = [option_emote, option_text] 281 | options.append(full_option) 282 | 283 | # Show us options we received from user 284 | logger.debug('Received options:') 285 | for option in options: 286 | logger.debug('\t' + str(option)) 287 | 288 | # Check if the given message has a role react 289 | reaction_messages = guild_conf['backend']['reaction_messages']['value'] 290 | for msg in reaction_messages: 291 | if msg['id'] == targ_message_id: 292 | await message.channel.send(message.author.mention + ' ' + targ_message_id + ' already has a role reaction capability') 293 | return False 294 | 295 | # Create json structure 296 | json_obj = {} 297 | json_obj['id'] = targ_message_id 298 | json_obj['reactions'] = [] 299 | for option in options: 300 | reaction = {} 301 | reaction['emote'] = option[0] 302 | reaction['role'] = option[1] 303 | json_obj['reactions'].append(reaction) 304 | 305 | logger.debug('JSON React Roles:') 306 | logger.debug(json.dumps(json_obj, indent=4, sort_keys=True)) 307 | 308 | # Add the new react role message configuration to the plugin configuration 309 | guild_conf['backend']['reaction_messages']['value'].append(json_obj) 310 | 311 | # Save the configuration to the file (dangerous) 312 | self.configutils.saveConfig(str(message.guild.name) + '_' + str(message.guild.id), self.guild_confs, self.conf_path) 313 | 314 | # Show us all of the configurations 315 | logger.debug('All react messages for this server:') 316 | for msg in guild_conf['backend']['reaction_messages']['value']: 317 | logger.debug('\t' + str(msg['id'])) 318 | 319 | # Add the reactions to the target message 320 | for option in options: 321 | await real_targ_message.add_reaction(option[0]) 322 | 323 | # End of plugin stuff 324 | 325 | return True 326 | 327 | async def stop(self, message): 328 | self.looping = False 329 | -------------------------------------------------------------------------------- /tests/test_plugin_poll.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import discord 4 | import unittest 5 | sys.dont_write_bytecode = True 6 | sys.path.append(os.path.abspath('plugins')) 7 | sys.path.append(os.path.abspath('tests')) 8 | 9 | from plugin_poll import Poll 10 | from injectables.discord_dependencies import * 11 | 12 | class TestPoll(unittest.TestCase): 13 | obj = Poll() 14 | 15 | def test_check_bits(self): 16 | assert self.obj.checkBits(0) == False 17 | 18 | def test_check_cat_true(self): 19 | assert self.obj.checkCat('admin') == True 20 | 21 | def test_check_cat_false(self): 22 | assert self.obj.checkCat('arrow') == False 23 | 24 | class TestAsyncMethods(unittest.IsolatedAsyncioTestCase): 25 | obj = Poll() 26 | 27 | #a_channel = channel() 28 | #a_message = message('!help', a_channel) 29 | #a_plugin = plugin('!help', 'help menu', '!help') 30 | 31 | #obj_list = [a_plugin] 32 | 33 | async def test_run_cheer(self): 34 | assert await self.obj.runCheer('potato', 0) == True 35 | 36 | async def test_poll_role_not_found(self): 37 | a_channel = channel() 38 | a_message = message('!poll start', a_channel) 39 | a_plugin = plugin('!help', 'help menu', '!help') 40 | role = Role('Owner') 41 | a_message.author.roles.append(role) 42 | 43 | obj_list = [a_plugin] 44 | #assert await self.obj.run(a_message, obj_list) == False 45 | 46 | async def test_poll_check_service_not_running(self): 47 | a_channel = channel() 48 | a_message = message('!poll', a_channel) 49 | a_plugin = plugin('!help', 'help menu', '!help') 50 | role = Role('Moderator') 51 | a_message.author.roles.append(role) 52 | 53 | obj_list = [a_plugin] 54 | assert await self.obj.run(a_message, obj_list) == True 55 | 56 | async def test_poll_check_service_running(self): 57 | a_channel = channel() 58 | a_message = message('!poll start', a_channel) 59 | a_plugin = plugin('!help', 'help menu', '!help') 60 | role = Role('Moderator') 61 | a_message.author.roles.append(role) 62 | 63 | obj_list = [a_plugin] 64 | await self.obj.run(a_message, obj_list) 65 | a_message.content = '!poll' 66 | assert await self.obj.run(a_message, obj_list) == True 67 | 68 | async def test_poll_restricted(self): 69 | a_channel = channel() 70 | a_message = message('!poll start', a_channel) 71 | a_plugin = plugin('!help', 'help menu', '!help') 72 | role = Role('Restricted') 73 | a_message.author.roles.append(role) 74 | 75 | obj_list = [a_plugin] 76 | #assert await self.obj.run(a_message, obj_list) == False 77 | 78 | async def test_start_service(self): 79 | new_obj = Poll() 80 | 81 | a_channel = channel() 82 | #a_channel.name = new_obj.post_channel 83 | a_message = message('!poll start', a_channel) 84 | a_message.guild.channels.append(a_channel) 85 | a_plugin = plugin('!help', 'help menu', '!help') 86 | role = Role('Moderator') 87 | a_message.author.roles.append(role) 88 | 89 | print('Channel name: ' + str(a_message.channel.name)) 90 | 91 | obj_list = [a_plugin] 92 | #assert await new_obj.run(a_message, obj_list) == True 93 | 94 | async def test_stop_service_not_running(self): 95 | a_channel = channel() 96 | #a_channel.name = self.obj.post_channel 97 | a_message = message('!poll stop', a_channel) 98 | a_plugin = plugin('!help', 'help menu', '!help') 99 | role = Role('Moderator') 100 | a_message.author.roles.append(role) 101 | 102 | obj_list = [a_plugin] 103 | #assert await self.obj.run(a_message, obj_list) == False 104 | 105 | async def test_poll_stop_service_running(self): 106 | new_obj = Poll() 107 | 108 | a_channel = channel() 109 | #a_channel.name = new_obj.post_channel 110 | a_message = message('!poll start', a_channel) 111 | a_plugin = plugin('!help', 'help menu', '!help') 112 | role = Role('Moderator') 113 | a_message.author.roles.append(role) 114 | 115 | obj_list = [a_plugin] 116 | await new_obj.run(a_message, obj_list) 117 | a_message.content = '!poll stop' 118 | #assert await new_obj.run(a_message, obj_list) == True 119 | 120 | async def test_start_new_poll(self): 121 | new_obj = Poll() 122 | 123 | a_channel = channel() 124 | #a_channel.name = new_obj.post_channel 125 | a_message = message('!poll This is a test poll', a_channel) 126 | a_message.guild.channels.append(a_channel) 127 | a_plugin = plugin('!help', 'help menu', '!help') 128 | role = Role('Moderator') 129 | a_message.author.roles.append(role) 130 | 131 | print('Channel name: ' + str(a_message.channel.name)) 132 | 133 | obj_list = [a_plugin] 134 | assert await new_obj.run(a_message, obj_list) == True 135 | 136 | async def test_update_poll_embed_base_fields(self): 137 | new_obj = Poll() 138 | 139 | a_channel = channel() 140 | #a_channel.name = new_obj.post_channel 141 | a_message = message('', a_channel) 142 | a_message.guild.channels.append(a_channel) 143 | role = Role('Moderator') 144 | a_message.author.roles.append(role) 145 | 146 | embed = discord.Embed(title="Poll", 147 | color=discord.Color.blue()) 148 | 149 | embed.add_field(name='Poll Description', value='This is a test poll', inline=False) 150 | embed.add_field(name='Creator', value=str(a_message.author.mention), inline=False) 151 | 152 | embed.add_field(name='Status', value='```OPEN```', inline = False) 153 | 154 | a_message.embed = embed 155 | 156 | assert await new_obj.update_poll_embed(a_message, embed, 10) == True 157 | 158 | async def test_update_poll_embed_new_fields(self): 159 | new_obj = Poll() 160 | 161 | a_channel = channel() 162 | #a_channel.name = new_obj.post_channel 163 | a_message = message('', a_channel) 164 | a_message.guild.channels.append(a_channel) 165 | role = Role('Moderator') 166 | a_message.author.roles.append(role) 167 | 168 | embed = discord.Embed(title="Poll", 169 | color=discord.Color.blue()) 170 | 171 | embed.add_field(name='Poll Description', value='This is a test poll', inline=False) 172 | embed.add_field(name='Creator', value=str(a_message.author.mention), inline=False) 173 | 174 | embed.add_field(name='Status', value='```OPEN```', inline = False) 175 | 176 | embed.add_field(name='Time Left', value='```' + str(10) + '```', inline=False) 177 | 178 | embed.add_field(name='Minimum required "yes" votes', value='```' + str(10) + '```', inline=False) 179 | 180 | a_message.embed = embed 181 | 182 | assert await new_obj.update_poll_embed(a_message, embed, 10) == True 183 | 184 | async def test_check_poll_embed_new(self): 185 | new_obj = Poll() 186 | 187 | a_channel = channel() 188 | #a_channel.name = new_obj.post_channel 189 | a_message = message('', a_channel) 190 | a_message.guild.channels.append(a_channel) 191 | role = Role('Moderator') 192 | a_message.author.roles.append(role) 193 | 194 | embed = discord.Embed(title="Poll", 195 | color=discord.Color.blue()) 196 | 197 | embed.add_field(name='Poll Description', value='This is a test poll', inline=False) 198 | embed.add_field(name='Creator', value=str(a_message.author.mention), inline=False) 199 | 200 | embed.add_field(name='Status', value='```OPEN```', inline = False) 201 | 202 | embed.add_field(name='Time Left', value='```' + str(10) + '```', inline=False) 203 | 204 | embed.add_field(name='Minimum required "yes" votes', value='```' + str(10) + '```', inline=False) 205 | 206 | a_message.embed = embed 207 | 208 | assert await new_obj.check_poll_embed(a_message, embed) == True 209 | 210 | async def test_close_poll_embed_tie(self): 211 | new_obj = Poll() 212 | 213 | a_channel = channel() 214 | #a_channel.name = new_obj.post_channel 215 | a_message = message('', a_channel) 216 | a_message.guild.channels.append(a_channel) 217 | role = Role('Moderator') 218 | a_message.author.roles.append(role) 219 | 220 | embed = discord.Embed(title="Poll", 221 | color=discord.Color.blue()) 222 | 223 | embed.add_field(name='Poll Description', value='This is a test poll', inline=False) 224 | embed.add_field(name='Creator', value=str(a_message.author.mention), inline=False) 225 | 226 | embed.add_field(name='Status', value='```OPEN```', inline = False) 227 | 228 | embed.add_field(name='Time Left', value='```' + str(10) + '```', inline=False) 229 | 230 | embed.add_field(name='Minimum required "yes" votes', value='```' + str(10) + '```', inline=False) 231 | 232 | #reaction = Reaction(new_obj.yes_vote) 233 | 234 | #a_message.reactions.append(reaction) 235 | 236 | #reaction = Reaction(new_obj.no_vote) 237 | 238 | #a_message.reactions.append(reaction) 239 | 240 | a_message.embed = embed 241 | 242 | assert await new_obj.close_poll_embed(a_message, embed) == True 243 | 244 | async def test_close_poll_embed_lose(self): 245 | new_obj = Poll() 246 | 247 | a_channel = channel() 248 | #a_channel.name = new_obj.post_channel 249 | a_message = message('', a_channel) 250 | a_message.guild.channels.append(a_channel) 251 | role = Role('Moderator') 252 | a_message.author.roles.append(role) 253 | 254 | embed = discord.Embed(title="Poll", 255 | color=discord.Color.blue()) 256 | 257 | embed.add_field(name='Poll Description', value='This is a test poll', inline=False) 258 | embed.add_field(name='Creator', value=str(a_message.author.mention), inline=False) 259 | 260 | embed.add_field(name='Status', value='```OPEN```', inline = False) 261 | 262 | embed.add_field(name='Time Left', value='```' + str(10) + '```', inline=False) 263 | 264 | embed.add_field(name='Minimum required "yes" votes', value='```' + str(10) + '```', inline=False) 265 | 266 | #reaction = Reaction(new_obj.no_vote) 267 | 268 | #a_message.reactions.append(reaction) 269 | 270 | a_message.embed = embed 271 | 272 | assert await new_obj.close_poll_embed(a_message, embed) == True 273 | 274 | async def test_close_poll_embed_win(self): 275 | new_obj = Poll() 276 | 277 | a_channel = channel() 278 | #a_channel.name = new_obj.post_channel 279 | a_message = message('', a_channel) 280 | a_message.guild.channels.append(a_channel) 281 | role = Role('Moderator') 282 | a_message.author.roles.append(role) 283 | 284 | embed = discord.Embed(title="Poll", 285 | color=discord.Color.blue()) 286 | 287 | embed.add_field(name='Poll Description', value='This is a test poll', inline=False) 288 | embed.add_field(name='Creator', value=str(a_message.author.mention), inline=False) 289 | 290 | embed.add_field(name='Status', value='```OPEN```', inline = False) 291 | 292 | embed.add_field(name='Time Left', value='```' + str(10) + '```', inline=False) 293 | 294 | embed.add_field(name='Minimum required "yes" votes', value='```' + str(10) + '```', inline=False) 295 | 296 | #reaction = Reaction(new_obj.yes_vote) 297 | #reaction.count = 100 298 | 299 | #a_message.reactions.append(reaction) 300 | 301 | a_message.embed = embed 302 | 303 | assert await new_obj.close_poll_embed(a_message, embed) == True 304 | 305 | async def test_close_poll_embed_missing_votes(self): 306 | new_obj = Poll() 307 | 308 | a_channel = channel() 309 | #a_channel.name = new_obj.post_channel 310 | a_message = message('', a_channel) 311 | a_message.guild.channels.append(a_channel) 312 | role = Role('Moderator') 313 | a_message.author.roles.append(role) 314 | 315 | embed = discord.Embed(title="Poll", 316 | color=discord.Color.blue()) 317 | 318 | embed.add_field(name='Poll Description', value='This is a test poll', inline=False) 319 | embed.add_field(name='Creator', value=str(a_message.author.mention), inline=False) 320 | 321 | embed.add_field(name='Status', value='```OPEN```', inline = False) 322 | 323 | embed.add_field(name='Time Left', value='```' + str(10) + '```', inline=False) 324 | 325 | embed.add_field(name='Minimum required "yes" votes', value='```' + str(10) + '```', inline=False) 326 | 327 | #reaction = Reaction(new_obj.yes_vote) 328 | #reaction.count = -1 329 | 330 | #a_message.reactions.append(reaction) 331 | 332 | #reaction = Reaction(new_obj.no_vote) 333 | #reaction.count = -2 334 | 335 | #a_message.reactions.append(reaction) 336 | 337 | a_message.embed = embed 338 | 339 | assert await new_obj.close_poll_embed(a_message, embed) == True 340 | 341 | async def test_stop(self): 342 | await self.obj.stop('potato') 343 | 344 | if __name__ == '__main__': 345 | unittest.main() -------------------------------------------------------------------------------- /plugins/plugin_cleanup_raid.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import discord 5 | import pytz 6 | from datetime import datetime 7 | from logger import logger 8 | from dotenv import load_dotenv 9 | from discord.ext.tasks import loop 10 | from requests import get 11 | 12 | sys.path.append(os.path.abspath('utils')) 13 | 14 | from utils.config_utils import ConfigUtils 15 | 16 | class CleanupRaid(): 17 | # Required for all plugins 18 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 19 | 20 | guild_confs = [] 21 | 22 | configutils = None 23 | 24 | name = '!cleanupraid' 25 | 26 | desc = 'Remove all users who joined at a specific datetime except for those with <@exempt role>' 27 | 28 | synt = '!cleanupraid <@exempt role> / [start/stop] / [config|get |set |add/remove ]' 29 | 30 | is_service = True 31 | 32 | client = None 33 | 34 | looping = False 35 | 36 | full_conf_file = None 37 | 38 | time_zone = 'US/Eastern' 39 | 40 | default_config = {} 41 | default_config['protected'] = {} 42 | default_config['protected']['name'] = __file__ 43 | default_config['protected']['guild'] = None 44 | default_config['standard_groups'] = {} 45 | default_config['standard_groups']['value'] = [] 46 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 47 | default_config['admin_groups'] = {} 48 | default_config['admin_groups']['value'] = [] 49 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 50 | default_config['blacklisted'] = {} 51 | default_config['blacklisted']['value'] = [] 52 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 53 | default_config['post_channel'] = {} 54 | default_config['post_channel']['value'] = "" 55 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 56 | default_config['yes_vote'] = {} 57 | default_config['yes_vote']['value'] = "" 58 | default_config['yes_vote']['description'] = "The emote to signify 'yes'" 59 | default_config['no_vote'] = {} 60 | default_config['no_vote']['value'] = "" 61 | default_config['no_vote']['description'] = "The emote to signify 'no'" 62 | 63 | # Server configurable 64 | 65 | group = '@everyone' 66 | 67 | admin = False 68 | 69 | cheer = -1 70 | 71 | cat = 'admin' 72 | 73 | current_draft = [] 74 | 75 | running_guilds = [] 76 | 77 | def __init__(self, client = None): 78 | self.client = client 79 | self.configutils = ConfigUtils() 80 | 81 | # Load configuration if it exists 82 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 83 | 84 | 85 | logger.debug('\n\nConfigs Loaded:') 86 | for config in self.guild_confs: 87 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 88 | 89 | def getArgs(self, message): 90 | cmd = str(message.content) 91 | seg = str(message.content).split(' ') 92 | 93 | if len(seg) > 1: 94 | return seg 95 | else: 96 | return None 97 | 98 | def generatePluginConfig(self, file_name): 99 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 100 | self.guild_confs.append(new_conf) 101 | 102 | def checkCat(self, check_cat): 103 | if self.cat == check_cat: 104 | return True 105 | else: 106 | return False 107 | 108 | def checkBits(self, bits): 109 | return False 110 | 111 | async def runCheer(self, user, amount): 112 | return True 113 | 114 | async def convert_to_datetime(self, datetime_str): 115 | # Parse the input string 116 | date, time = datetime_str.split(';') 117 | year, month, day = date[:4], date[4:6], date[6:] 118 | hour, minute = time.split(':') 119 | 120 | # Convert the year, month, day, hour, and minute to integers 121 | year, month, day, hour, minute = map(int, [year, month, day, hour, minute]) 122 | 123 | # Create and return the datetime object 124 | return datetime(year, month, day, hour, minute) 125 | 126 | async def get_users_by_join_time(self, message, str_time, role): 127 | usest = pytz.timezone(self.time_zone) 128 | 129 | # Convert the join time string to a datetime object in the UTC timezone 130 | join_time = usest.localize(await self.convert_to_datetime(str_time)) 131 | 132 | # Get the guild associated with the message 133 | guild = message.guild 134 | 135 | # Get a list of members in the guild 136 | members = guild.members 137 | 138 | # Initialize an empty list to store the filtered members 139 | filtered_members = [] 140 | 141 | # Iterate through the members in the guild 142 | for member in members: 143 | # Convert the member's join time to the UTC timezone 144 | member_join_time = pytz.utc.localize(member.joined_at) 145 | # Check if the member joined at the specified time and does not have the specified role 146 | if member_join_time.strftime("%Y%m%d;%H:%M") == join_time.strftime("%Y%m%d;%H:%M") and role not in member.roles: 147 | # Add the member to the filtered list 148 | filtered_members.append(member) 149 | 150 | # Print the name and join time of the member 151 | #logger.debug(str(member.name) + ': ' + str(member_join_time.strftime("%Y%m%d;%H:%M"))) 152 | 153 | return filtered_members 154 | 155 | async def get_role_from_mention(self, message, role_mention): 156 | # Get the guild associated with the message 157 | guild = message.guild 158 | 159 | # Get the role ID from the role mention string 160 | role_id = role_mention.strip("<@&>") 161 | 162 | # Get the role object from the guild 163 | role = discord.utils.get(guild.roles, id=role_id) 164 | 165 | logger.debug('Got role: ' + str(role.name)) 166 | 167 | return role 168 | 169 | # Required method for services (content may vary) 170 | async def getStatus(self, message): 171 | # Return True if there is a giveaway running in the source message's server 172 | for index in self.running_guilds: 173 | if str(message.guild.name) + str(message.guild.id) == index: 174 | return True 175 | 176 | return False 177 | 178 | async def startService(self): 179 | if not self.looping: 180 | self.looping = True 181 | self.loop_func.start() 182 | return True 183 | return False 184 | 185 | async def executeEmoji(self, message, cleanup_list = None, do_kick = False): 186 | if not do_kick: 187 | await message.channel.send('Canceling cleanup') 188 | else: 189 | for user in cleanup_list: 190 | logger.debug('Kicking: ' + str(user.name)) 191 | await user.kick() 192 | await message.channel.send('The referenced users have been kicked from the server') 193 | 194 | return 195 | 196 | 197 | @loop(seconds = 5) 198 | async def loop_func(self): 199 | if self.looping: 200 | for guild in self.client.guilds: 201 | # Only run on guilds that have the service enabled 202 | if str(guild.name) + str(guild.id) not in self.running_guilds: 203 | continue 204 | 205 | # Get the current guild conf 206 | guild_conf = self.configutils.getGuildConfigByGuild(guild, self.guild_confs) 207 | 208 | # Get the current guild drafts 209 | this_guild_draft = None 210 | 211 | for draft in self.current_draft: 212 | if draft[0].guild.id == guild.id: 213 | this_guild_draft = draft 214 | break 215 | 216 | if this_guild_draft == None: 217 | continue 218 | 219 | msg = this_guild_draft[0] 220 | executor = this_guild_draft[1] 221 | 222 | # Check reactions of this draft's message 223 | targ_reaction = None 224 | cache_message = await msg.channel.fetch_message(msg.id) 225 | 226 | # Check if the reaction if from the executor 227 | for reaction in cache_message.reactions: 228 | async for user in reaction.users(): 229 | if user.id == executor.id: 230 | targ_reaction = reaction 231 | break 232 | 233 | if not targ_reaction: 234 | continue 235 | 236 | # Execute based on reaction 237 | logger.debug('Executor\'s reaction: ' + str(targ_reaction.emoji)) 238 | if targ_reaction.emoji == guild_conf['yes_vote']['value']: 239 | logger.debug('Executor confirmed cleanup: ' + str(msg.id)) 240 | await self.executeEmoji(msg, this_guild_draft[2], True) 241 | elif targ_reaction.emoji == guild_conf['no_vote']['value']: 242 | logger.debug('Executor canceled cleanup: ' + str(msg.id)) 243 | await self.executeEmoji(msg) 244 | else: 245 | logger.debug('Executor did not properly react: ' + str(msg.id)) 246 | 247 | self.current_draft.remove(this_guild_draft) 248 | 249 | 250 | 251 | 252 | async def run(self, message, obj_list): 253 | # Permissions check 254 | if not self.configutils.hasPerms(message, False, self.guild_confs): 255 | await message.channel.send(message.author.mention + ' Permission denied') 256 | return False 257 | 258 | # Parse args 259 | arg = self.getArgs(message) 260 | 261 | # Config set/get check 262 | if arg != None: 263 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 264 | return True 265 | 266 | # Do Specific Plugin Stuff 267 | 268 | the_guild = str(message.guild.name) + str(message.guild.id) 269 | 270 | # Do service stuff 271 | if len(arg) == 2: 272 | # Check if user has admin permissions to run the service 273 | if not self.configutils.hasPerms(message, True, self.guild_confs): 274 | await message.channel.send(message.author.mention + ' Permission denied') 275 | return False 276 | 277 | if str(arg[1]) == 'start': 278 | if the_guild not in self.running_guilds: 279 | self.running_guilds.append(the_guild) 280 | logger.debug('Guilds running ' + str(self.name) + ':') 281 | for gu in self.running_guilds: 282 | logger.debug('\t' + gu) 283 | await message.channel.send(message.author.mention + ' Starting ' + str(self.name)) 284 | return True 285 | 286 | if str(arg[1]) == 'stop': 287 | if the_guild in self.running_guilds: 288 | self.running_guilds.remove(the_guild) 289 | await message.channel.send(message.author.mention + ' Stopping ' + str(self.name)) 290 | logger.debug('Guilds running ' + str(self.name) + ':') 291 | for gu in self.running_guilds: 292 | logger.debug('\t' + gu) 293 | return True 294 | 295 | return False 296 | 297 | # User wants status of service 298 | elif len(arg) == 1: 299 | if self.looping: 300 | await message.channel.send(message.author.mention + ' ' + str(self.name) + ' is running') 301 | else: 302 | await message.channel.send(message.author.mention + ' ' + str(self.name) + ' is not running') 303 | return True 304 | 305 | # Only take the first mention in the message 306 | exempt_role = message.role_mentions[0] 307 | datetime_str = arg[1] 308 | 309 | cleanup_list = await self.get_users_by_join_time(message, datetime_str, exempt_role) 310 | 311 | guild_conf = self.configutils.getGuildConfig(message, self.guild_confs) 312 | 313 | yes_emote = guild_conf['yes_vote']['value'] 314 | no_emote = guild_conf['no_vote']['value'] 315 | 316 | if (yes_emote == '') or (no_emote == ''): 317 | await message.channel.send(message.author.mention + ', The yes and no emotes have not been set') 318 | return 319 | 320 | str_users = '' 321 | 322 | logger.debug('Cleanup List:') 323 | for item in cleanup_list: 324 | logger.debug('\t' + str(item.name)) 325 | str_users = str_users + str(item.name) + '\n' 326 | logger.debug('Done with cleanup list') 327 | 328 | # Build message with embed to confirm 329 | embed = discord.Embed(title="Raid Cleanup Draft", 330 | color=discord.Color.red()) 331 | 332 | embed.add_field(name='Executor', value = '```' + str(message.author.name) + '```', inline=False) 333 | embed.add_field(name='Datetime', value = '```' + str(datetime_str) + '```', inline=True) 334 | embed.add_field(name='Exempt Role', value = '```' + str(exempt_role.name) + '```', inline=True) 335 | embed.add_field(name='Users To Kick', value = '```' + str(str_users) + '```', inline=False) 336 | embed.add_field(name='Confirmation', value = 'Select ' + str(yes_emote) + ' to confirm or ' + str(no_emote) + ' to cancel', inline=False) 337 | 338 | msg = await message.channel.send("Here is your cleanup draft", reference=message, embed=embed) 339 | 340 | await msg.add_reaction(yes_emote) 341 | await msg.add_reaction(no_emote) 342 | 343 | self.current_draft.append([msg, message.author, cleanup_list]) 344 | 345 | #logger.debug('Message ID: ' + str(self.current_draft[0].id) + ' -> User: ' + str(self.current_draft[1].name)) 346 | 347 | return True 348 | 349 | async def stop(self, message): 350 | self.looping = False 351 | -------------------------------------------------------------------------------- /plugins/plugin_giveaway.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import random 5 | import discord 6 | from logger import logger 7 | from datetime import datetime 8 | from dotenv import load_dotenv 9 | from discord.ext.tasks import loop 10 | from requests import get 11 | 12 | sys.path.append(os.path.abspath('utils')) 13 | 14 | from utils.config_utils import ConfigUtils 15 | 16 | class Giveaway(): 17 | # Required for all plugins 18 | conf_path = os.path.join(os.path.dirname(__file__), 'configs') 19 | 20 | guild_confs = [] 21 | 22 | configutils = None 23 | 24 | name = '!giveaway' 25 | 26 | desc = 'Start, stop, manage, and join giveaways' 27 | 28 | synt = '!giveaway [start |stop |pick ][config|get |set |add/remove ]' 29 | 30 | default_config = {} 31 | default_config['protected'] = {} 32 | default_config['protected']['name'] = __file__ 33 | default_config['protected']['guild'] = None 34 | default_config['standard_groups'] = {} 35 | default_config['standard_groups']['value'] = [] 36 | default_config['standard_groups']['description'] = "Authorized groups to use this command" 37 | default_config['admin_groups'] = {} 38 | default_config['admin_groups']['value'] = [] 39 | default_config['admin_groups']['description'] = "Authorized groups to use admin functions of this command" 40 | default_config['blacklisted'] = {} 41 | default_config['blacklisted']['value'] = [] 42 | default_config['blacklisted']['description'] = "Groups explicitly denied access to this command" 43 | default_config['post_channel'] = {} 44 | default_config['post_channel']['value'] = "" 45 | default_config['post_channel']['description'] = "Desitination channel to post messages from this plugin" 46 | 47 | running_giveaways = [] 48 | 49 | looping = False 50 | 51 | group = '@everyone' 52 | 53 | admin = False 54 | 55 | cheer = -1 56 | 57 | cat = 'admin' 58 | 59 | is_service = True 60 | 61 | client = None 62 | 63 | giveaway_name = None 64 | 65 | users = [] 66 | 67 | winner = None 68 | 69 | winner_list = [] 70 | 71 | running = False 72 | 73 | start_date = datetime.now() 74 | 75 | def __init__(self, client = None): 76 | self.client = client 77 | self.configutils = ConfigUtils() 78 | 79 | # Load configuration if it exists 80 | self.guild_confs = self.configutils.loadConfig(self.conf_path, self.default_config, __file__) 81 | 82 | 83 | logger.debug('\n\nConfigs Loaded:') 84 | for config in self.guild_confs: 85 | logger.debug('\t' + config['protected']['name'] + ': ' + config['protected']['guild']) 86 | 87 | def getArgs(self, message): 88 | cmd = str(message.content) 89 | seg = str(message.content).split(' ') 90 | 91 | if len(seg) > 1: 92 | return seg 93 | else: 94 | return None 95 | 96 | def generatePluginConfig(self, file_name): 97 | for new_conf in self.configutils.generateConfig(self.conf_path, self.default_config, file_name, __file__): 98 | self.guild_confs.append(new_conf) 99 | 100 | # Required method for services (content may vary) 101 | async def getStatus(self, message): 102 | # Return True if there is a giveaway running in the source message's server 103 | for index in self.running_giveaways: 104 | if index[0].guild == message.guild: 105 | return True 106 | 107 | return False 108 | 109 | # Required method for services 110 | async def startService(self): 111 | if not self.looping: 112 | self.looping = True 113 | self.loop_func.start() 114 | return True 115 | return False 116 | 117 | def checkCat(self, check_cat): 118 | if self.cat == check_cat: 119 | return True 120 | else: 121 | return False 122 | 123 | def checkBits(self, bits): 124 | return False 125 | 126 | async def runCheer(self, user, amount): 127 | return True 128 | 129 | @loop(seconds = 1) 130 | async def loop_func(self): 131 | if self.looping: 132 | # Loop through all running giveaway messages 133 | for index in self.running_giveaways: 134 | msg = index[0] 135 | cache_message = await msg.channel.fetch_message(msg.id) 136 | for reaction in cache_message.reactions: 137 | async for user in reaction.users(): 138 | real_member = user 139 | 140 | # Get the configuration of this specific message 141 | the_config = self.configutils.getGuildConfig(msg, self.guild_confs) 142 | if not self.configutils.hasPermsUser(cache_message, real_member, False, self.guild_confs): 143 | continue 144 | 145 | if (real_member not in index[1]) and (self.looping): 146 | logger.info('Adding ' + str(real_member) + ' to giveaway: ' + str(msg.id)) 147 | index[1].append(real_member) 148 | await self.update_giveaway_embed(index) 149 | 150 | async def update_giveaway_embed(self, index): 151 | msg = index[0] 152 | the_embed = None 153 | for embed in msg.embeds: 154 | if embed.title == 'Giveaway': 155 | the_embed = embed 156 | break 157 | 158 | for i in range(0, len(the_embed.fields)): 159 | if the_embed.fields[i].name=='Participants': 160 | the_embed.set_field_at(i, name=embed.fields[i].name, value='```' + str(len(index[1])) + '```', inline=True) 161 | 162 | await msg.edit(embed=the_embed) 163 | 164 | async def get_post_channel(self, message, the_config): 165 | # Find where the bot will be posting its announcements 166 | for channel in message.guild.channels: 167 | try: 168 | if str(channel.mention) == str(the_config['post_channel']['value']): 169 | logger.debug('Found post_channel: ' + str(channel.mention)) 170 | return channel 171 | except: 172 | return None 173 | return None 174 | 175 | async def run(self, message, obj_list): 176 | # Permissions check 177 | if not self.configutils.hasPerms(message, False, self.guild_confs): 178 | await message.channel.send(message.author.mention + ' Permission denied') 179 | return False 180 | 181 | the_config = self.configutils.getGuildConfig(message, self.guild_confs) 182 | 183 | # Parse args 184 | arg = self.getArgs(message) 185 | 186 | # Config set/get check 187 | if arg != None: 188 | if await self.configutils.runConfig(message, arg, self.guild_confs, self.conf_path): 189 | return True 190 | 191 | # Do Specific Plugin Stuff 192 | 193 | embed = discord.Embed(title="Giveaway", 194 | color=discord.Color.green()) 195 | 196 | cmd = str(message.content) 197 | seg = str(message.content).split(' ') 198 | if len(seg) < 1: 199 | await message.channel.send(message.author.mention + '`' + str(message.content) + '` is not the proper syntax') 200 | return False 201 | 202 | # User just wants status of giveaway 203 | if message.content == '!giveaway': 204 | return True 205 | 206 | # Get commands args...again 207 | command = seg[1] 208 | 209 | user_groups = [] 210 | for role in message.author.roles: 211 | user_groups.append(role.name) 212 | 213 | # Anything beyond this point requires admin privs 214 | 215 | # Another permissions check 216 | if not self.configutils.hasPerms(message, True, self.guild_confs): 217 | await message.channel.send(message.author.mention + ' Permission denied') 218 | return False 219 | 220 | # Starting giveaway 221 | if command == 'start': 222 | test_name = '' 223 | for i in range(2, len(seg)): 224 | test_name = test_name + seg[i] + ' ' 225 | self.giveaway_name = test_name[:-1] 226 | self.users.clear() 227 | self.winner = None 228 | self.running = True 229 | self.start_time = datetime.now() 230 | embed.add_field(name='Title', value='```' + str(self.giveaway_name) + '```', inline=True) 231 | embed.add_field(name='Participants', value='```' + str(len(self.users)) + '```', inline=True) 232 | 233 | embed.add_field(name = chr(173), value = chr(173)) 234 | 235 | embed.add_field(name='Started at', value='```' + str(self.start_time) + '```', inline=False) 236 | 237 | role_string = '' 238 | try: 239 | for standard_role in the_config['standard_groups']['value']: 240 | for role in message.guild.roles: 241 | if str(role.mention) == standard_role: 242 | role_string = role_string + role.mention + ' ' 243 | continue 244 | except: 245 | pass 246 | 247 | embed.add_field(name='Required Roles (at least one)', value=str(role_string), inline=False) 248 | embed.add_field(name='How to join', value='```React with any emote```', inline=False) 249 | embed.add_field(name='Status', value='```OPEN```', inline=False) 250 | embed.add_field(name='Winners', value='``` ```', inline=False) 251 | 252 | # Get the channel where stuff is going to be posted 253 | local_post_channel = await self.get_post_channel(message, the_config) 254 | if local_post_channel == None: 255 | return False 256 | giveaway_message = await local_post_channel.send('@everyone', embed=embed) 257 | 258 | # Add giveaway message to list of running giveaways 259 | self.running_giveaways.append([giveaway_message, [], [], test_name[:-1]]) 260 | 261 | # Show us the list of running giveaway messages 262 | logger.debug('Giveaway messages: ') 263 | for index in self.running_giveaways: 264 | msg = index[0] 265 | logger.debug('\t' + str(msg.id)) 266 | 267 | return self.giveaway_name 268 | 269 | # Stoping giveaway 270 | if command == 'stop': 271 | check_msg_id = str(arg[2]) 272 | logger.debug('Checking ' + check_msg_id) 273 | 274 | # Check if this message is even part of the user's server 275 | # This will prevent someone with admin privs on another server from picking 276 | # A winner on a server where they don't have admin privs 277 | the_index = None 278 | for index in self.running_giveaways: 279 | msg = index[0] 280 | if (msg.guild == message.guild) and (check_msg_id == str(msg.id)): 281 | the_index = index 282 | break 283 | 284 | # User tried to pick a winner for a giveaway that wasn't running in their server 285 | if the_index == None: 286 | logger.debug('Source msg guild and target giveaway guild did not match') 287 | await message.channel.send(message.author.mention + ' That giveaway is not running on this server') 288 | return False 289 | 290 | # Remove the target giveaway from the list of running giveaways 291 | self.running_giveaways.remove(the_index) 292 | await message.channel.send(message.author.mention + '`' + str(self.giveaway_name) + '` giveaway stopped') 293 | the_embed = None 294 | for embed in the_index[0].embeds: 295 | if embed.title == 'Giveaway': 296 | the_embed = embed 297 | break 298 | 299 | for i in range(0, len(the_embed.fields)): 300 | if the_embed.fields[i].name=='Status': 301 | embed.set_field_at(i, name=embed.fields[i].name, value='```CLOSED```', inline=False) 302 | 303 | await the_index[0].edit(embed=the_embed) 304 | 305 | # Show us the list of running giveaway messages 306 | logger.debug('Giveaway messages: ') 307 | for index in self.running_giveaways: 308 | msg = index[0] 309 | logger.debug('\t' + str(msg.id)) 310 | 311 | # Pick winner 312 | if command == 'pick': 313 | 314 | check_msg_id = str(arg[2]) 315 | logger.debug('Checking ' + check_msg_id) 316 | 317 | # Check if this message is even part of the user's server 318 | # This will prevent someone with admin privs on another server from picking 319 | # A winner on a server where they don't have admin privs 320 | the_index = None 321 | for index in self.running_giveaways: 322 | msg = index[0] 323 | if (msg.guild == message.guild) and (check_msg_id == str(msg.id)): 324 | the_index = index 325 | break 326 | 327 | # User tried to pick a winner for a giveaway that wasn't running in their server 328 | if the_index == None: 329 | logger.debug('Source msg guild and target giveaway guild did not match') 330 | await message.channel.send(message.author.mention + ' That giveaway is not running on this server') 331 | return False 332 | 333 | if len(the_index[1]) <= 0: 334 | await message.channel.send(message.author.mention + ' There are no particpants for that giveaway') 335 | return False 336 | 337 | self.winner = random.choice(the_index[1]) 338 | the_index[2].append(self.winner) # Add winner to index list of winners 339 | the_index[1].remove(self.winner) # Remove winner from index list of users 340 | await message.channel.send('The winner of `' + the_index[3] + '` is ' + str(self.winner.mention)) 341 | 342 | # Update the embed...again 343 | the_embed = None 344 | for embed in the_index[0].embeds: 345 | if embed.title == 'Giveaway': 346 | the_embed = embed 347 | break 348 | 349 | for i in range(0, len(the_embed.fields)): 350 | if the_embed.fields[i].name=='Winners': 351 | value_str = '' 352 | for winr in the_index[2]: 353 | value_str = value_str + str(winr) + '\n' 354 | embed.set_field_at(i, name=embed.fields[i].name, value='```' + str(value_str) + '```', inline=False) 355 | 356 | await the_index[0].edit(embed=the_embed) 357 | await self.update_giveaway_embed(the_index) 358 | 359 | # Join giveaway 360 | if command == 'asdfahsdlfkjahwefw8efh23487fhwed8f7ahsdfkqw43h': 361 | # Giveaway is not running 362 | if not self.running: 363 | await message.channel.send(message.author.mention + ' There are no giveaways running') 364 | return 365 | 366 | # Improper syntax 367 | if len(seg) != 2: 368 | await message.channel.send(message.author.mention + '`' + str(message.content) + '` is not the proper syntax') 369 | return 370 | discord_user = message.author 371 | 372 | # They are already in the giveaway 373 | if discord_user in self.users: 374 | await message.channel.send(message.author.mention + '`' + str(message.content) + '` You have *already* joined the giveaway') 375 | # Here is your ticket 376 | else: 377 | self.users.append(discord_user) 378 | await message.channel.send(message.author.mention + 'Welcome to the giveaway: `' + self.giveaway_name + '`') 379 | 380 | await self.update_giveaway_embed() 381 | 382 | return True 383 | 384 | async def stop(self, message): 385 | self.looping = False 386 | try: 387 | self.loop_func.stop() 388 | except: 389 | return 390 | -------------------------------------------------------------------------------- /utils/config_utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import copy 3 | import json 4 | import discord 5 | from logger import logger 6 | from dotenv import load_dotenv 7 | from discord.ext.tasks import loop 8 | from requests import get 9 | 10 | class ConfigUtils(): 11 | protected_key = 'protected' 12 | backend_key = 'backend' 13 | 14 | def generateConfig(self, conf_path, default_config, file_name, plugin_name): 15 | guild_name = file_name.replace('_config.json', '').replace('_', '') 16 | configs = [] 17 | full_conf_file = os.path.join(conf_path, file_name) 18 | 19 | # Try to get json stuff 20 | f = open(full_conf_file) 21 | try: 22 | json_data = json.load(f) 23 | except: 24 | json_data = {} 25 | f.close() 26 | 27 | # If plugins json doesn't exist, write the key 28 | if 'plugins' not in json_data: 29 | logger.debug('JSON config does not exist. Creating...') 30 | data = {} 31 | data['plugins'] = [] 32 | with open(full_conf_file, 'w') as f: 33 | json.dump(data, f) 34 | 35 | # Get plugin configuration 36 | with open(full_conf_file) as f: 37 | json_data = json.load(f) 38 | 39 | the_config = None 40 | for plugin in json_data['plugins']: 41 | if plugin[self.protected_key]['name'] == file_name: 42 | the_config = plugin 43 | break 44 | 45 | if the_config == None: 46 | new_conf = None 47 | new_conf = copy.copy(default_config) 48 | new_conf[self.protected_key]['guild'] = guild_name 49 | logger.debug('Could not find plugin configuration. Creating...' + str(new_conf[self.protected_key]['guild'])) 50 | configs.append(copy.deepcopy(new_conf)) # Needs to be deepcopy or all list items are changed 51 | json_data['plugins'].append(new_conf) 52 | with open(full_conf_file, 'w') as f: 53 | json.dump(json_data, f, indent=4) 54 | else: 55 | configs.append(the_config) 56 | 57 | return configs 58 | 59 | 60 | def loadConfig(self, conf_path, default_config, file_name): 61 | configs = [] 62 | 63 | for entity in os.listdir(conf_path): 64 | # Make sure the file is a config file 65 | if (os.path.isfile(os.path.join(conf_path, entity))) and (entity.endswith('_conf.json')): 66 | guild_name = entity.split('_')[0] + entity.split('_')[1] 67 | 68 | full_conf_file = os.path.join(conf_path, entity) 69 | logger.debug(file_name + ': Loading conf...' + str(entity)) 70 | 71 | guild_name = entity.split('_')[0] + entity.split('_')[1] 72 | 73 | # Try to get json stuff 74 | f = open(full_conf_file) 75 | try: 76 | json_data = json.load(f) 77 | except: 78 | json_data = {} 79 | f.close() 80 | 81 | # If plugins json doesn't exist, write the key 82 | if 'plugins' not in json_data: 83 | logger.debug('JSON config does not exist. Creating...') 84 | data = {} 85 | data['plugins'] = [] 86 | with open(full_conf_file, 'w') as f: 87 | json.dump(data, f) 88 | 89 | # Get plugin configuration 90 | with open(full_conf_file) as f: 91 | json_data = json.load(f) 92 | 93 | the_config = None 94 | for plugin in json_data['plugins']: 95 | if plugin[self.protected_key]['name'] == file_name: 96 | the_config = plugin 97 | break 98 | 99 | if the_config == None: 100 | new_conf = None 101 | new_conf = copy.copy(default_config) 102 | new_conf[self.protected_key]['guild'] = guild_name 103 | logger.debug('Could not find plugin configuration. Creating...' + str(new_conf[self.protected_key]['guild'])) 104 | configs.append(copy.deepcopy(new_conf)) # Needs to be deepcopy or all list items are changed 105 | json_data['plugins'].append(new_conf) 106 | with open(full_conf_file, 'w') as f: 107 | json.dump(json_data, f, indent=4) 108 | else: 109 | configs.append(the_config) 110 | 111 | return configs 112 | 113 | def getGuildConfigByGuildConfigName(self, guild_config_name, configs): 114 | for config in configs: 115 | if config[self.protected_key]['guild'] == guild_config_name: 116 | return config 117 | 118 | return {} 119 | 120 | def getGuildConfigByGuild(self, guild, configs): 121 | guild_config_name = guild.name + str(guild.id) 122 | 123 | for config in configs: 124 | if config[self.protected_key]['guild'] == guild_config_name: 125 | return config 126 | 127 | return {} 128 | 129 | def getGuildConfig(self, message, configs): 130 | guild_config_name = message.guild.name + str(message.guild.id) 131 | 132 | for config in configs: 133 | #print(config[self.protected_key]['guild']) 134 | if config[self.protected_key]['guild'] == guild_config_name: 135 | #print('Found config') 136 | return config 137 | 138 | return {} 139 | 140 | def hasPermsUser(self, message, user, admin_req, configs): 141 | for role in user.roles: 142 | if (role.permissions.administrator): 143 | #print(role.name + ' has administrator permissions') 144 | return True 145 | 146 | if admin_req: 147 | return False 148 | else: 149 | user_roles = [] 150 | for role in user.roles: 151 | user_roles.append(role.name) 152 | 153 | 154 | config = self.getGuildConfig(message, configs) 155 | if 'standard_groups' not in config: 156 | return False 157 | if 'admin_groups' not in config: 158 | return False 159 | 160 | # Check if user is blacklisted 161 | for role in user.roles: 162 | if 'value' in config['blacklisted']: 163 | if role.mention in config['blacklisted']['value']: 164 | #print(str(role.mention) + ' found in blacklisted') 165 | return False 166 | 167 | # Check role objects 168 | for role in user.roles: 169 | if 'value' in config['standard_groups']: 170 | if role.mention in config['standard_groups']['value']: 171 | #print(str(role.mention) + ' found in standard_groups') 172 | return True 173 | if 'value' in config['admin_groups']: 174 | if role.mention in config['admin_groups']['value']: 175 | #print(str(role.mention) + ' found in admin_groups') 176 | return True 177 | 178 | # Check string roles (old) 179 | for user_role in user_roles: 180 | if 'value' in config['standard_groups']: 181 | if user_role in config['standard_groups']['value']: 182 | #print(user_role + ' found in standard_groups') 183 | return True 184 | if 'value' in config['admin_groups']: 185 | if user_role in config['admin_groups']['value']: 186 | #print(user_role + ' found in admin_groups') 187 | return True 188 | 189 | return False 190 | 191 | def hasPerms(self, message, admin_req, configs): 192 | for role in message.author.roles: 193 | if (role.permissions.administrator): 194 | #print(role.name + ' has administrator permissions') 195 | return True 196 | 197 | if admin_req: 198 | return False 199 | else: 200 | user_roles = [] 201 | for role in message.author.roles: 202 | user_roles.append(role.name) 203 | 204 | 205 | config = self.getGuildConfig(message, configs) 206 | if 'standard_groups' not in config: 207 | return False 208 | if 'admin_groups' not in config: 209 | return False 210 | 211 | # Check if user is blacklisted 212 | for role in message.author.roles: 213 | if 'value' in config['blacklisted']: 214 | if role.mention in config['blacklisted']['value']: 215 | #print(str(role.mention) + ' found in blacklisted') 216 | return False 217 | 218 | # Check role objects 219 | for role in message.author.roles: 220 | if 'value' in config['standard_groups']: 221 | if role.mention in config['standard_groups']['value']: 222 | #print(str(role.mention) + ' found in standard_groups') 223 | return True 224 | if 'value' in config['admin_groups']: 225 | if role.mention in config['admin_groups']['value']: 226 | #print(str(role.mention) + ' found in admin_groups') 227 | return True 228 | 229 | # Check string roles (old) 230 | for user_role in user_roles: 231 | if 'value' in config['standard_groups']: 232 | if user_role in config['standard_groups']['value']: 233 | #print(user_role + ' found in standard_groups') 234 | return True 235 | if 'value' in config['admin_groups']: 236 | if user_role in config['admin_groups']['value']: 237 | #print(user_role + ' found in admin_groups') 238 | return True 239 | 240 | return False 241 | 242 | def saveConfig(self, targ_guild, configs, conf_path): 243 | for entity in os.listdir(conf_path): 244 | if (os.path.isfile(os.path.join(conf_path, entity))) and (entity == str(targ_guild) + '_conf.json'): 245 | 246 | logger.debug('Found target conf file to save') 247 | 248 | full_conf_file = os.path.join(conf_path, entity) 249 | 250 | # Get plugin configuration 251 | with open(full_conf_file) as f: 252 | json_data = json.load(f) 253 | 254 | 255 | new_json = {} 256 | new_json['plugins'] = [] 257 | 258 | for that_config in json_data['plugins']: 259 | found = False 260 | for this_config in configs: 261 | # This if statement is really fucking disgusting 262 | if (that_config[self.protected_key]['name'] == this_config[self.protected_key]['name']) and (that_config[self.protected_key]['guild'] == this_config[self.protected_key]['guild']): 263 | logger.debug('Found target config to save: ' + str(that_config[self.protected_key]['name'])) 264 | new_json['plugins'].append(this_config) 265 | found = True 266 | if not found: 267 | new_json['plugins'].append(that_config) 268 | 269 | logger.debug('Writing to configuration file: ' + str(full_conf_file)) 270 | with open(full_conf_file, 'w') as f: 271 | json.dump(new_json, f, indent=4) 272 | 273 | #print(json.dumps(new_json, indent=4, sort_keys=True)) 274 | 275 | async def runConfig(self, message, arg, configs, conf_path): 276 | # Get all available config keys 277 | if arg[1] == 'config': 278 | if not self.hasPerms(message, True, configs): 279 | return True 280 | embed = discord.Embed(title=str(arg[0]), 281 | color=discord.Color.blue()) 282 | 283 | the_config = self.getGuildConfig(message, configs) 284 | 285 | for key in the_config.keys(): 286 | if (key == self.protected_key) or (key == self.backend_key): 287 | continue 288 | if isinstance(the_config[key]['value'], str): 289 | embed.add_field(name=str(key), value=str(the_config[key]['description'] + '\nset/get'), inline=False) 290 | else: 291 | embed.add_field(name=str(key), value=str(the_config[key]['description'] + '\nadd/remove/get'), inline=False) 292 | 293 | await message.channel.send(embed=embed) 294 | return True 295 | 296 | # Get a config key's value 297 | elif arg[1] == 'get': 298 | if not self.hasPerms(message, True, configs): 299 | return True 300 | embed = discord.Embed(title=str(arg[0]), 301 | color=discord.Color.blue()) 302 | 303 | the_conf = None 304 | for conf in configs: 305 | if conf[self.protected_key]['guild'] == message.guild.name + str(message.guild.id): 306 | the_conf = conf 307 | break 308 | 309 | if the_conf != None: 310 | if str(arg[2]) in the_conf: 311 | embed.add_field(name=str(arg[2]), value=str(the_conf[str(arg[2])]['value']), inline=False) 312 | else: 313 | embed.add_field(name=str(arg[2]), value='Not Found', inline=False) 314 | else: 315 | logger.debug('Did not find configuration') 316 | 317 | await message.channel.send(embed=embed) 318 | return True 319 | 320 | # Set a single value key 321 | elif arg[1] == 'set': 322 | # Check if the user has permissions to do this 323 | if not self.hasPerms(message, True, configs): 324 | return True 325 | embed = discord.Embed(title=str(arg[0]), 326 | color=discord.Color.blue()) 327 | 328 | # Get the specific guild config 329 | the_conf = None 330 | for conf in configs: 331 | if conf[self.protected_key]['guild'] == message.guild.name + str(message.guild.id): 332 | the_conf = conf 333 | break 334 | 335 | if the_conf != None: 336 | # Check if key exists, is not protected, and is not a list 337 | if (str(arg[2]) in the_conf) and (str(arg[2]) != self.protected_key) and (not isinstance(the_conf[str(arg[2])]['value'], list)): 338 | the_conf[str(arg[2])]['value'] = arg[3] 339 | else: 340 | return True 341 | 342 | # Set the config in the objects list of configs 343 | for conf in configs: 344 | if conf[self.protected_key]['guild'] == message.guild.name + str(message.guild.id): 345 | conf = the_conf 346 | #print(json.dumps(conf, indent=4, sort_keys=True)) 347 | 348 | # Save the new config and reply with message 349 | self.saveConfig(message.guild.name + '_' + str(message.guild.id), configs, conf_path) 350 | 351 | if 'value' in the_conf[str(arg[2])]: 352 | embed.add_field(name=str(arg[2]), value=str(the_conf[str(arg[2])]['value']), inline=False) 353 | else: 354 | embed.add_field(name=str(arg[2]), value=str(the_conf[str(arg[2])]), inline=False) 355 | 356 | await message.channel.send(embed=embed) 357 | return True 358 | 359 | # Add a value to a list key 360 | elif (arg[1] == 'add') or (arg[1] == 'remove'): 361 | # Check if the user has permissions to do this 362 | if not self.hasPerms(message, True, configs): 363 | return True 364 | embed = discord.Embed(title=str(arg[0]), 365 | color=discord.Color.blue()) 366 | 367 | # Get the specific guild config 368 | the_conf = None 369 | for conf in configs: 370 | if conf[self.protected_key]['guild'] == message.guild.name + str(message.guild.id): 371 | the_conf = conf 372 | break 373 | 374 | if the_conf != None: 375 | # Make sure key exists and is a list 376 | if (str(arg[2]) in the_conf) and (isinstance(the_conf[str(arg[2])]['value'], list)) and (arg[1] == 'add'): 377 | the_conf[str(arg[2])]['value'].append(str(arg[3])) 378 | elif (str(arg[2]) in the_conf) and (isinstance(the_conf[str(arg[2])]['value'], list)) and (arg[1] == 'remove'): 379 | the_conf[str(arg[2])]['value'].remove(str(arg[3])) 380 | else: 381 | return True 382 | else: 383 | return True 384 | 385 | # Set the config in the objects list of configs 386 | for conf in configs: 387 | if conf[self.protected_key]['guild'] == message.guild.name + str(message.guild.id): 388 | conf = the_conf 389 | 390 | # Save the new config and reply with message 391 | self.saveConfig(message.guild.name + '_' + str(message.guild.id), configs, conf_path) 392 | 393 | embed.add_field(name=str(arg[2]), value=str(the_conf[str(arg[2])]['value']), inline=False) 394 | 395 | await message.channel.send(embed=embed) 396 | return True 397 | return False 398 | --------------------------------------------------------------------------------