├── .env ├── .gitignore ├── LICENSE ├── README.md ├── apis ├── code_compiler.py ├── covid_api.py ├── fortnite_api.py └── perspective_api.py ├── assets ├── chat_leaderboard_template.png ├── fortnite_template.png ├── part - 1.png ├── spotify_template.png └── voice_leaderboard_template.png ├── commands ├── ai_moderation.py ├── chat_leaderboard.py ├── covid.py ├── fortnite.py ├── global_chat.py ├── poll.py ├── python_compiler.py ├── spotify.py ├── ticket.py ├── utility.py ├── voice_leaderboard.py └── voice_member_count.py ├── databases ├── chat_leaderboard.json ├── global_chat.json ├── karma.json ├── moderation.json ├── poll.json ├── scheduler.json ├── source_code.py ├── ticket.json ├── voice_leaderboard.json └── voice_member_count.json ├── main.py ├── requirements.txt └── theboldfont.ttf /.env: -------------------------------------------------------------------------------- 1 | TOKEN=Discord Bot Token Goes Here. 2 | CLIENTID=Jdoodle api clientId here. 3 | CLIENTSECRET=Jdoodle api clientSecret here. 4 | APIKEY= Google api key goes here for Perspective API. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Discord log file 132 | discord.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nishat 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![atl-text-1](https://img.shields.io/badge/Version-3.9.1-blue?logo=Python&style=flat)](https://www.python.org/) [![atl-text-3](https://img.shields.io/badge/License-MIT-blue?logo=License&style=flat)](https://github.com/DevStrikerTech) 2 | 3 | 4 | # Discord.py Bot Series 5 | 6 | [![Tutorial](https://raw.githubusercontent.com/DevStrikerTech/Discord.py-Bot-Series/main/assets/part%20-%201.png?token=AIQMJPAZU3J73HMS6PHPC2DAFLDOS)](https://www.youtube.com/watch?v=-uR6ivfMjHU) 7 | 8 | YouTube Playlist: https://www.youtube.com/playlist?list=PL9nZZVP3OGOAx2S75YdBkrIbVpiSL5oc5 9 | 10 | ## Installation 11 | ```bash 12 | pip install -r requirements.txt 13 | ``` 14 | 15 | ## Python Packages 16 | 17 | Discord.py: https://discordpy.readthedocs.io/en/latest/intro.html 18 | 19 | Pillow: https://pillow.readthedocs.io/en/stable/installation.html 20 | 21 | Python-dotenv: https://pypi.org/project/python-dotenv/ 22 | 23 | Requests: https://pypi.org/project/requests/ 24 | 25 | Dateutil: https://pypi.org/project/python-dateutil/ 26 | 27 | Tabulate: https://pypi.org/project/tabulate/ 28 | 29 | Matplotlib: https://matplotlib.org/stable/users/installing.html 30 | 31 | Pandas: https://pandas.pydata.org/docs/getting_started/index.html 32 | 33 | Black: https://black.readthedocs.io/en/stable/installation_and_usage.html 34 | 35 | ## Documentations 36 | 37 | Discord: https://discord.com/developers/docs/intro 38 | 39 | Discord.py: https://discordpy.readthedocs.io/en/latest/index.html 40 | 41 | Pillow: https://pillow.readthedocs.io/en/stable/index.html 42 | -------------------------------------------------------------------------------- /apis/code_compiler.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import requests 4 | from dotenv import load_dotenv 5 | 6 | # Loading data from .env file 7 | load_dotenv() 8 | client_id = os.getenv('CLIENTID') 9 | client_secret = os.getenv('CLIENTSECRET') 10 | 11 | 12 | def compiler(code): 13 | post_url = 'https://api.jdoodle.com/v1/execute' 14 | 15 | return json.loads(requests.post( 16 | post_url, 17 | json={ 18 | "script": f"{code}", 19 | "language": "python3", 20 | "versionIndex": "3", 21 | "clientId": f"{client_id}", 22 | "clientSecret": f"{client_secret}" 23 | } 24 | ).content) 25 | -------------------------------------------------------------------------------- /apis/covid_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib.request 3 | 4 | 5 | def covid_api_request(endpoint): 6 | covid_request_url = urllib.request.Request('https://api.covid19api.com/' + endpoint) 7 | covid_request_data = json.loads(urllib.request.urlopen(covid_request_url).read().decode('utf-8')) 8 | 9 | return covid_request_data 10 | -------------------------------------------------------------------------------- /apis/fortnite_api.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | 4 | 5 | def fortnite_api_request(username): 6 | request_url = f'https://fortnite-api.com/v1/stats/br/v2?name={username}' 7 | 8 | return json.loads(requests.get( 9 | request_url, 10 | params={ 11 | 'displayName': username 12 | } 13 | ).content) 14 | -------------------------------------------------------------------------------- /apis/perspective_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | from googleapiclient import discovery 4 | 5 | # Loading data from .env file 6 | load_dotenv() 7 | api_key = os.getenv('APIKEY') 8 | 9 | 10 | def perspective_api(chat_data): 11 | # set thresholds for when to trigger a response 12 | attributes_thresholds = { 13 | 'INSULT': 0.75, 14 | 'TOXICITY': 0.75, 15 | 'SPAM': 0.75 16 | } 17 | 18 | # This is the format that API expects 19 | requested_attributes = {} 20 | 21 | for key in attributes_thresholds: 22 | requested_attributes[key] = {} 23 | 24 | client = discovery.build( 25 | "commentanalyzer", 26 | "v1alpha1", 27 | developerKey=api_key, 28 | discoveryServiceUrl="https://commentanalyzer.googleapis.com/$discovery/rest?version=v1alpha1", 29 | static_discovery=False, 30 | ) 31 | 32 | analyze_request = { 33 | 'comment': {'text': f'{chat_data}'}, 34 | 'requestedAttributes': requested_attributes 35 | } 36 | 37 | response = client.comments().analyze(body=analyze_request).execute() 38 | 39 | data = {} 40 | 41 | for key in response['attributeScores']: 42 | data[key] = response['attributeScores'][key]['summaryScore']['value'] > attributes_thresholds[key] 43 | 44 | return data 45 | -------------------------------------------------------------------------------- /assets/chat_leaderboard_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevStrikerTech/Discord.py-Bot-Series/09a0dd374d3994caffdfcec6dd4066d68ded9913/assets/chat_leaderboard_template.png -------------------------------------------------------------------------------- /assets/fortnite_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevStrikerTech/Discord.py-Bot-Series/09a0dd374d3994caffdfcec6dd4066d68ded9913/assets/fortnite_template.png -------------------------------------------------------------------------------- /assets/part - 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevStrikerTech/Discord.py-Bot-Series/09a0dd374d3994caffdfcec6dd4066d68ded9913/assets/part - 1.png -------------------------------------------------------------------------------- /assets/spotify_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevStrikerTech/Discord.py-Bot-Series/09a0dd374d3994caffdfcec6dd4066d68ded9913/assets/spotify_template.png -------------------------------------------------------------------------------- /assets/voice_leaderboard_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevStrikerTech/Discord.py-Bot-Series/09a0dd374d3994caffdfcec6dd4066d68ded9913/assets/voice_leaderboard_template.png -------------------------------------------------------------------------------- /commands/ai_moderation.py: -------------------------------------------------------------------------------- 1 | import json 2 | from discord.ext import commands 3 | from apis.perspective_api import perspective_api 4 | 5 | 6 | class AiModeration(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.command() 11 | @commands.has_permissions(administrator=True) 12 | async def startmoderation(self, ctx, mod_channel): 13 | guild_id = ctx.message.guild.id 14 | mod_channel_id = int(mod_channel.strip('<>#')) 15 | 16 | with open('./databases/moderation.json', 'r') as file: 17 | moderation_data = json.load(file) 18 | moderation_guild = str(guild_id) 19 | 20 | # Update existing moderation data 21 | if moderation_guild in moderation_data: 22 | for item in list(moderation_data.values()): 23 | if str(mod_channel_id) not in item: 24 | moderation_data[moderation_guild] += [str(mod_channel_id)] 25 | with open('./databases/moderation.json', 'w') as update_moderation_data: 26 | json.dump(moderation_data, update_moderation_data, indent=4) 27 | 28 | await ctx.send(f':white_check_mark: ' 29 | f'**`{ctx.channel}` channel has been registered for moderation!**') 30 | 31 | else: 32 | await ctx.send(f':no_entry: ' 33 | f'**`{ctx.channel}` channel already registered for moderation!**') 34 | 35 | # Add new moderation data 36 | else: 37 | moderation_data[moderation_guild] = [str(mod_channel_id)] 38 | with open('./databases/moderation.json', 'w') as new_moderation_data: 39 | json.dump(moderation_data, new_moderation_data, indent=4) 40 | 41 | await ctx.send(f':white_check_mark: ' 42 | f'**`{ctx.channel}` channel has been registered for moderation!**') 43 | 44 | @commands.command() 45 | @commands.has_permissions(administrator=True) 46 | async def stopmoderation(self, ctx, mod_channel): 47 | mod_channel_id = int(mod_channel.strip('<>#')) 48 | 49 | with open('./databases/moderation.json', 'r') as file: 50 | moderation_data = json.load(file) 51 | 52 | for remove_keys, remove_values in moderation_data.items(): 53 | if str(mod_channel_id) in remove_values: 54 | remove_values.remove(str(mod_channel_id)) 55 | 56 | with open('./databases/moderation.json', 'w') as update_moderation_file: 57 | json.dump(moderation_data, update_moderation_file, indent=4) 58 | 59 | await ctx.send(f':white_check_mark: **`{ctx.channel}` channel has been removed from moderation!**') 60 | 61 | else: 62 | await ctx.send(f':no_entry: **`{ctx.channel}` channel is not registered for moderation!**') 63 | 64 | @commands.Cog.listener() 65 | async def on_guild_remove(self, guild): 66 | with open('./databases/moderation.json', 'r') as file: 67 | moderation_data = json.load(file) 68 | 69 | moderation_data.pop(str(guild.id)) 70 | 71 | with open('./databases/moderation.json', 'w') as update_file: 72 | json.dump(moderation_data, update_file, indent=4) 73 | 74 | @commands.Cog.listener() 75 | async def on_message(self, message): 76 | if not message.content.startswith('!'): 77 | if not message.author.bot: 78 | with open('./databases/moderation.json', 'r') as file: 79 | moderation_data = json.load(file) 80 | 81 | for guild_ids, channel_ids in moderation_data.items(): 82 | if str(message.channel.id) in channel_ids: 83 | 84 | # Unsupported message content 85 | if not message.content: 86 | return 87 | 88 | moderation_result = perspective_api(message.content) 89 | moderation_category = [key for (key, value) in moderation_result.items() if value is True] 90 | 91 | if any((category in moderation_category for category in ['INSULT', 'TOXICITY', 'SPAM'])): 92 | with open('./databases/karma.json', 'r') as karma_file: 93 | karma_data = json.load(karma_file) 94 | user_karma = str(message.author.id) 95 | 96 | if user_karma in karma_data: 97 | # Ban user 98 | if 4 in list(karma_data.values()): 99 | with open('./databases/karma.json', 'r') as karma_ban: 100 | karma_ban_user = json.load(karma_ban) 101 | 102 | karma_ban_user.pop(str(message.author.id)) 103 | 104 | with open('./databases/karma.json', 'w') as update_karma_ban: 105 | json.dump(karma_data, update_karma_ban, indent=4) 106 | 107 | await message.delete(delay=None) 108 | await message.author.ban() 109 | 110 | # Kick user 111 | elif 3 in list(karma_data.values()): 112 | karma_data[user_karma] += 1 113 | with open('./databases/karma.json', 'w') as update_karma_kick: 114 | json.dump(karma_data, update_karma_kick, indent=4) 115 | 116 | await message.delete(delay=None) 117 | await message.author.kick() 118 | 119 | # Warn user 120 | else: 121 | karma_data[user_karma] += 1 122 | with open('./databases/karma.json', 'w') as update_karma_warn: 123 | json.dump(karma_data, update_karma_warn, indent=4) 124 | 125 | await message.delete(delay=None) 126 | await message.channel.send(f'<@{message.author.id}> ' 127 | f':warning: **for insult/toxic/spam in chat!**') 128 | else: 129 | karma_data[user_karma] = 1 130 | with open('./databases/karma.json', 'w') as new_karma_data: 131 | json.dump(karma_data, new_karma_data, indent=4) 132 | 133 | await message.delete(delay=None) 134 | await message.channel.send(f'<@{message.author.id}> ' 135 | f':warning: **for insult/toxic/spam in chat!**') 136 | 137 | 138 | def setup(bot): 139 | bot.add_cog(AiModeration(bot)) 140 | -------------------------------------------------------------------------------- /commands/chat_leaderboard.py: -------------------------------------------------------------------------------- 1 | import json 2 | import discord 3 | from tabulate import tabulate 4 | from discord.ext import commands 5 | from PIL import Image, ImageFont, ImageDraw 6 | 7 | 8 | class ChatLeaderboard(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | @commands.Cog.listener() 13 | async def on_message(self, message): 14 | if not message.author.bot: 15 | if not message.content.startswith('!'): 16 | with open('.\\databases\\chat_leaderboard.json', 'r') as file: 17 | chat_data = json.load(file) 18 | new_user = str(message.author.id) 19 | 20 | # Update existing user 21 | if new_user in chat_data: 22 | chat_data[new_user] += 1 23 | with open('.\\databases\\chat_leaderboard.json', 'w') as update_user_data: 24 | json.dump(chat_data, update_user_data, indent=4) 25 | 26 | # Add new user 27 | else: 28 | chat_data[new_user] = 1 29 | with open('.\\databases\\chat_leaderboard.json', 'w') as new_user_data: 30 | json.dump(chat_data, new_user_data, indent=4) 31 | 32 | @commands.command() 33 | async def chat(self, ctx): 34 | with open('.\\databases\\chat_leaderboard.json', 'r') as file: 35 | chat_data = json.load(file) 36 | 37 | user_ids = list(chat_data.keys()) 38 | user_message_counts = list(chat_data.values()) 39 | 40 | new_leaderboard = [] 41 | 42 | for index, user_id in enumerate(user_ids, 1): 43 | new_leaderboard.append([user_id, user_message_counts[index - 1]]) 44 | 45 | # Sort leaderboard order by user message count 46 | new_leaderboard.sort(key=lambda items: items[1], reverse=True) 47 | 48 | user_rank_column = [] 49 | user_name_column = [] 50 | user_message_count_column = [] 51 | 52 | # User ranks 53 | for rank_index, rank_value in enumerate(new_leaderboard[:10]): 54 | user_rank_column.append([rank_index + 1]) 55 | 56 | # User names 57 | for name_index, name_value in enumerate(new_leaderboard[:10]): 58 | user_name_column.append([await self.bot.fetch_user(int(name_value[0]))]) 59 | 60 | # User message counts 61 | for message_count_index, message_count_value in enumerate(new_leaderboard[:10]): 62 | user_message_count_column.append([message_count_value[1]]) 63 | 64 | # Add column to table 65 | user_rank_table = tabulate(user_rank_column, tablefmt='plain', headers=['#\n'], numalign='left') 66 | user_name_table = tabulate(user_name_column, tablefmt='plain', headers=['Name\n'], numalign='left') 67 | user_message_count_table = tabulate(user_message_count_column, tablefmt='plain', headers=['Messages\n'], 68 | numalign='left') 69 | 70 | # Image 71 | image_template = Image.open('.\\assets\\chat_leaderboard_template.png') 72 | 73 | # Font 74 | font = ImageFont.truetype('theboldfont.ttf', 14) 75 | 76 | # Positions 77 | rank_text_position = 30, 50 78 | name_text_position = 80, 50 79 | message_count_text_position = 350, 50 80 | 81 | # Draws 82 | draw_on_image = ImageDraw.Draw(image_template) 83 | draw_on_image.text(rank_text_position, user_rank_table, 'white', font=font) 84 | draw_on_image.text(name_text_position, user_name_table, 'white', font=font) 85 | draw_on_image.text(message_count_text_position, user_message_count_table, 'white', font=font) 86 | 87 | # Save image 88 | image_template.convert('RGB').save('chat_leaderboard.jpg', 'JPEG') 89 | 90 | await ctx.send(file=discord.File('chat_leaderboard.jpg')) 91 | 92 | 93 | def setup(bot): 94 | bot.add_cog(ChatLeaderboard(bot)) 95 | -------------------------------------------------------------------------------- /commands/covid.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import pandas as pd 3 | from datetime import datetime 4 | from matplotlib import pyplot 5 | from discord.ext import commands 6 | from apis.covid_api import covid_api_request 7 | 8 | 9 | class Covid(commands.Cog): 10 | def __init__(self, bot): 11 | self.bot = bot 12 | 13 | @commands.command() 14 | async def covid(self, ctx, country): 15 | request_result = covid_api_request(f'dayone/country/{country}') 16 | 17 | data_set = [(datetime.strptime(date_index['Date'], '%Y-%m-%dT%H:%M:%SZ').strftime('%b'), death_index['Deaths']) 18 | for date_index, death_index in zip(request_result, request_result)] 19 | 20 | # Plot 21 | data_frame = pd.DataFrame(data_set) 22 | data_frame.plot(x=0, y=1, color='#00012C', label='Months') 23 | 24 | # Label 25 | pyplot.title(f'Showing Deaths in {country}') 26 | pyplot.xlabel('Months') 27 | pyplot.ylabel('Number of Deaths') 28 | 29 | # Legend 30 | pyplot.legend(loc='upper left') 31 | 32 | # Color 33 | pyplot.axes().set_facecolor('#9A1622') 34 | 35 | pyplot.savefig('.\\assets\\covid_death_graph.png', bbox_inches='tight') 36 | 37 | await ctx.send(file=discord.File('.\\assets\\covid_death_graph.png')) 38 | 39 | 40 | def setup(bot): 41 | bot.add_cog(Covid(bot)) 42 | -------------------------------------------------------------------------------- /commands/fortnite.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from PIL import Image, ImageFont, ImageDraw 4 | from apis.fortnite_api import fortnite_api_request 5 | 6 | 7 | class Fortnite(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | 11 | @commands.command() 12 | async def fs(self, ctx, *args): 13 | username = list(args) 14 | format_player_name = '%20'.join(username) 15 | 16 | fortnite_response = fortnite_api_request(username=format_player_name) 17 | 18 | if fortnite_response['status'] == 200: 19 | # Images 20 | fortnite_template_image = Image.open('.\\assets\\fortnite_template.png') 21 | 22 | # Fonts 23 | username_font = ImageFont.truetype('theboldfont.ttf', 50) 24 | stats_font = ImageFont.truetype('theboldfont.ttf', 40) 25 | 26 | # Positions 27 | username_position = 135, 163 28 | 29 | # Overall stats 30 | overall_wins_position = 43, 300 31 | overall_win_rate_position = 155, 300 32 | overall_kd_position = 285, 300 33 | overall_kpm_position = 400, 300 34 | overall_matches_position = 63, 450 35 | overall_kills_position = 210, 450 36 | overall_deaths_position = 350, 450 37 | 38 | # Solo stats 39 | solo_matches_position = 540, 130 40 | solo_wins_position = 685, 130 41 | solo_win_rate_position = 795, 130 42 | solo_kills_position = 910, 130 43 | solo_deaths_position = 1050, 130 44 | solo_kd_position = 1170, 130 45 | solo_kpm_position = 1270, 130 46 | 47 | # Duo stats 48 | duo_matches_position = 540, 345 49 | duo_wins_position = 685, 345 50 | duo_win_rate_position = 795, 345 51 | duo_kills_position = 910, 345 52 | duo_deaths_position = 1050, 345 53 | duo_kd_position = 1170, 345 54 | duo_kpm_position = 1270, 345 55 | 56 | # Squad stats 57 | squad_matches_position = 540, 560 58 | squad_wins_position = 685, 560 59 | squad_win_rate_position = 795, 560 60 | squad_kills_position = 910, 560 61 | squad_deaths_position = 1050, 560 62 | squad_kd_position = 1170, 560 63 | squad_kpm_position = 1270, 560 64 | 65 | # Draws 66 | draw_on_image = ImageDraw.Draw(fortnite_template_image) 67 | 68 | # Username 69 | draw_on_image.text(username_position, fortnite_response['data']['account']['name'], 'white', 70 | font=username_font) 71 | 72 | # Overall stats 73 | if fortnite_response['data']['stats']['all']['overall'] is not None: 74 | draw_on_image.text(overall_wins_position, 75 | str(fortnite_response['data']['stats']['all']['overall']['wins']), 76 | 'white', font=stats_font) 77 | draw_on_image.text(overall_win_rate_position, 78 | str(round(fortnite_response['data']['stats']['all']['overall']['winRate'], 2)), 79 | 'white', font=stats_font) 80 | draw_on_image.text(overall_kd_position, 81 | str(round(fortnite_response['data']['stats']['all']['overall']['kd'], 2)), 82 | 'white', font=stats_font) 83 | draw_on_image.text(overall_kpm_position, 84 | str(round(fortnite_response['data']['stats']['all']['overall']['killsPerMatch'], 2)), 85 | 'white', font=stats_font) 86 | draw_on_image.text(overall_matches_position, 87 | str(fortnite_response['data']['stats']['all']['overall']['matches']), 88 | 'white', font=stats_font) 89 | draw_on_image.text(overall_kills_position, 90 | str(fortnite_response['data']['stats']['all']['overall']['kills']), 91 | 'white', font=stats_font) 92 | draw_on_image.text(overall_deaths_position, 93 | str(fortnite_response['data']['stats']['all']['overall']['deaths']), 94 | 'white', font=stats_font) 95 | 96 | # Solo stats 97 | if fortnite_response['data']['stats']['all']['solo'] is not None: 98 | draw_on_image.text(duo_matches_position, 99 | str(fortnite_response['data']['stats']['all']['solo']['matches']), 100 | 'white', font=stats_font) 101 | draw_on_image.text(duo_wins_position, str(fortnite_response['data']['stats']['all']['solo']['wins']), 102 | 'white', font=stats_font) 103 | draw_on_image.text(duo_win_rate_position, 104 | str(round(fortnite_response['data']['stats']['all']['solo']['winRate'], 2)), 105 | 'white', font=stats_font) 106 | draw_on_image.text(duo_kills_position, 107 | str(fortnite_response['data']['stats']['all']['solo']['kills']), 108 | 'white', font=stats_font) 109 | draw_on_image.text(duo_deaths_position, 110 | str(fortnite_response['data']['stats']['all']['solo']['deaths']), 111 | 'white', font=stats_font) 112 | draw_on_image.text(duo_kd_position, 113 | str(round(fortnite_response['data']['stats']['all']['solo']['kd'], 2)), 114 | 'white', font=stats_font) 115 | draw_on_image.text(duo_kpm_position, 116 | str(round(fortnite_response['data']['stats']['all']['solo']['killsPerMatch'], 2)), 117 | 'white', font=stats_font) 118 | 119 | # Duo stats 120 | if fortnite_response['data']['stats']['all']['duo'] is not None: 121 | draw_on_image.text(solo_matches_position, 122 | str(fortnite_response['data']['stats']['all']['duo']['matches']), 123 | 'white', font=stats_font) 124 | draw_on_image.text(solo_wins_position, str(fortnite_response['data']['stats']['all']['duo']['wins']), 125 | 'white', font=stats_font) 126 | draw_on_image.text(solo_win_rate_position, 127 | str(round(fortnite_response['data']['stats']['all']['duo']['winRate'], 2)), 128 | 'white', font=stats_font) 129 | draw_on_image.text(solo_kills_position, 130 | str(fortnite_response['data']['stats']['all']['duo']['kills']), 131 | 'white', font=stats_font) 132 | draw_on_image.text(solo_deaths_position, 133 | str(fortnite_response['data']['stats']['all']['duo']['deaths']), 134 | 'white', font=stats_font) 135 | draw_on_image.text(solo_kd_position, 136 | str(round(fortnite_response['data']['stats']['all']['duo']['kd'], 2)), 137 | 'white', font=stats_font) 138 | draw_on_image.text(solo_kpm_position, 139 | str(round(fortnite_response['data']['stats']['all']['duo']['killsPerMatch'], 2)), 140 | 'white', font=stats_font) 141 | 142 | # Squad stats 143 | if fortnite_response['data']['stats']['all']['squad'] is not None: 144 | draw_on_image.text(squad_matches_position, 145 | str(fortnite_response['data']['stats']['all']['squad']['matches']), 146 | 'white', font=stats_font) 147 | draw_on_image.text(squad_wins_position, str(fortnite_response['data']['stats']['all']['squad']['wins']), 148 | 'white', font=stats_font) 149 | draw_on_image.text(squad_win_rate_position, 150 | str(round(fortnite_response['data']['stats']['all']['squad']['winRate'], 2)), 151 | 'white', font=stats_font) 152 | draw_on_image.text(squad_kills_position, 153 | str(fortnite_response['data']['stats']['all']['squad']['kills']), 154 | 'white', font=stats_font) 155 | draw_on_image.text(squad_deaths_position, 156 | str(fortnite_response['data']['stats']['all']['squad']['deaths']), 157 | 'white', font=stats_font) 158 | draw_on_image.text(squad_kd_position, 159 | str(round(fortnite_response['data']['stats']['all']['squad']['kd'], 2)), 160 | 'white', font=stats_font) 161 | draw_on_image.text(squad_kpm_position, 162 | str(round(fortnite_response['data']['stats']['all']['squad']['killsPerMatch'], 2)), 163 | 'white', font=stats_font) 164 | 165 | # Save image 166 | fortnite_template_image.convert('RGB').save('fortnite.jpg', 'JPEG') 167 | 168 | await ctx.send(file=discord.File('fortnite.jpg')) 169 | 170 | else: 171 | await ctx.send(f":no_entry: **{fortnite_response['error']}**") 172 | 173 | 174 | def setup(bot): 175 | bot.add_cog(Fortnite(bot)) 176 | -------------------------------------------------------------------------------- /commands/global_chat.py: -------------------------------------------------------------------------------- 1 | import json 2 | import discord 3 | import datetime 4 | from random import randint 5 | from discord.ext import commands 6 | 7 | 8 | class GlobalChat(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | @commands.command() 13 | @commands.has_permissions(administrator=True) 14 | async def globalchatstart(self, ctx, channel): 15 | guild_id = ctx.message.guild.id 16 | channel_id = int(channel.strip('<>#')) 17 | 18 | with open('.\\databases\\global_chat.json', 'r') as file: 19 | global_chat_data = json.load(file) 20 | new_global_chat = str(guild_id) 21 | 22 | # Existing global chat 23 | if new_global_chat in global_chat_data: 24 | await ctx.send(':no_entry: **Channel has already added to global chat!**') 25 | 26 | # Add new global chat 27 | else: 28 | global_chat_data[new_global_chat] = channel_id 29 | with open('.\\databases\\global_chat.json', 'w') as new_global_chat: 30 | json.dump(global_chat_data, new_global_chat, indent=4) 31 | 32 | await ctx.send(':white_check_mark: **Channel has been added to global chat!**') 33 | 34 | @commands.command() 35 | @commands.has_permissions(administrator=True) 36 | async def globalchatstop(self, ctx): 37 | guild_id = ctx.message.guild.id 38 | 39 | with open('.\\databases\\global_chat.json', 'r') as file: 40 | global_chat_data = json.load(file) 41 | 42 | global_chat_data.pop(str(guild_id)) 43 | 44 | # Update global chat 45 | with open('.\\databases\\global_chat.json', 'w') as update_global_chat_file: 46 | json.dump(global_chat_data, update_global_chat_file, indent=4) 47 | 48 | await ctx.send(':white_check_mark: **Channel has been removed from global chat!**') 49 | 50 | @commands.Cog.listener() 51 | async def on_message(self, message): 52 | if not message.author.bot: 53 | if not message.content.startswith('!'): 54 | with open('.\\databases\\global_chat.json', 'r') as file: 55 | global_chat_data = json.load(file) 56 | 57 | channel_id = list(global_chat_data.values()) 58 | 59 | # Message sender 60 | if message.channel.id in channel_id: 61 | 62 | # Unsupported message content 63 | if not message.content: 64 | return 65 | 66 | # Message receiver 67 | for ids in channel_id: 68 | if message.channel.id != ids: 69 | message_embed = discord.Embed(colour=randint(0, 0xffffff)) 70 | 71 | message_embed.timestamp = datetime.datetime.utcnow() 72 | message_embed.set_author(icon_url=message.author.avatar_url, name=f'{message.author}') 73 | message_embed.description = f'**`Said: {message.content}`**' 74 | message_embed.set_footer(icon_url=message.guild.icon_url, text=message.guild.name) 75 | 76 | await self.bot.get_channel(ids).send(embed=message_embed) 77 | 78 | 79 | def setup(bot): 80 | bot.add_cog(GlobalChat(bot)) 81 | -------------------------------------------------------------------------------- /commands/poll.py: -------------------------------------------------------------------------------- 1 | import json 2 | import discord 3 | from datetime import datetime 4 | from discord.ext import commands, tasks 5 | 6 | 7 | class Poll(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | self.emoji = ['1\u20e3', '2\u20e3', '3\u20e3', '4\u20e3', '5\u20e3', 11 | '6\u20e3', '7\u20e3', '8\u20e3', '9\u20e3', '\U0001F51F'] 12 | 13 | @commands.Cog.listener() 14 | async def on_ready(self): 15 | self.poll_result.start() 16 | 17 | @commands.Cog.listener() 18 | async def on_message_delete(self, message): 19 | # Remove poll 20 | with open('.\\databases\\poll.json', 'r') as file: 21 | poll_data = json.load(file) 22 | 23 | # Remove schedule 24 | with open('.\\databases\\scheduler.json', 'r') as file: 25 | scheduler_data = json.load(file) 26 | 27 | if str(message.id) in poll_data: 28 | poll_data.pop(str(message.id)) 29 | 30 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 31 | json.dump(poll_data, update_poll_data, indent=4) 32 | 33 | scheduler_data.pop(str(message.channel.id)) 34 | 35 | with open('.\\databases\\scheduler.json', 'w') as update_scheduler_data: 36 | json.dump(scheduler_data, update_scheduler_data, indent=4) 37 | 38 | @commands.command() 39 | @commands.has_permissions(administrator=True) 40 | async def poll(self, ctx, time: int, vote: int, title, *options): 41 | if len(options) > 10: 42 | await ctx.send(':no_entry: You can only have **10 options** at maximum!') 43 | 44 | elif time <= 15: 45 | await ctx.send(':no_entry: Please provide poll end time grater then **15 minute**!') 46 | 47 | elif 1000000 < vote: 48 | await ctx.send(':no_entry: Please provide poll maximum number of vote less then **million**!') 49 | 50 | else: 51 | with open('.\\databases\\scheduler.json', 'r') as scheduler_file: 52 | scheduler_data = json.load(scheduler_file) 53 | 54 | if str(ctx.message.channel.id) not in scheduler_data: 55 | polls = [('\u200b', 56 | '\n'.join([f'{self.emoji[index]} {option} \n' for index, option in enumerate(options)]), 57 | False)] 58 | 59 | embed = discord.Embed(title=title, 60 | description=f':stopwatch: Poll will end in **{time} minute**!', 61 | colour=0xFF0000) 62 | 63 | embed.set_thumbnail( 64 | url=f'https://cdn.discordapp.com/icons/{ctx.message.guild.id}/{ctx.message.guild.icon}.png') 65 | 66 | for name, value, inline in polls: 67 | embed.add_field(name=name, value=value, inline=inline) 68 | 69 | message = await ctx.send(embed=embed) 70 | 71 | for item in self.emoji[:len(options)]: 72 | await message.add_reaction(item) 73 | 74 | # Poll data 75 | with open('.\\databases\\poll.json', 'r') as poll_file: 76 | poll_data = json.load(poll_file) 77 | new_message = str(message.id) 78 | 79 | poll_dictionary = dict.fromkeys(list(options), 0) 80 | poll_data[new_message] = [poll_dictionary] 81 | 82 | with open('.\\databases\\poll.json', 'w') as new_poll_data: 83 | json.dump(poll_data, new_poll_data, indent=4) 84 | 85 | # Poll schedule 86 | scheduler_data[message.channel.id] = {'message_id': message.id, 'scheduler_time': time, 87 | 'poll_start_time': datetime.now().isoformat(), 88 | 'max_vote': vote} 89 | 90 | with open('.\\databases\\scheduler.json', 'w') as new_scheduler_data: 91 | json.dump(scheduler_data, new_scheduler_data, indent=4) 92 | 93 | else: 94 | await ctx.send(f':no_entry: **Channel is currently occupied with poll!**') 95 | 96 | @commands.Cog.listener() 97 | async def on_raw_reaction_add(self, payload): 98 | if payload.member.id != self.bot.user.id: 99 | message = await self.bot.get_channel(payload.channel_id).fetch_message(payload.message_id) 100 | reaction = discord.utils.get(message.reactions, emoji=payload.emoji.name) 101 | 102 | with open('.\\databases\\poll.json', 'r') as poll_file: 103 | poll_data = json.load(poll_file) 104 | 105 | if str(payload.message_id) in poll_data: 106 | # Time and max vote calculation 107 | with open('.\\databases\\scheduler.json', 'r') as schedule: 108 | scheduler_data = json.load(schedule) 109 | 110 | max_vote_count = 0 111 | time_counter = 0 112 | 113 | for item in scheduler_data.items(): 114 | if str(payload.channel_id) in item[0]: 115 | max_vote_count += item[1]['max_vote'] 116 | 117 | cur_time = datetime.now() 118 | prev_time = datetime.strptime(item[1]['poll_start_time'].replace('T', ' '), 119 | '%Y-%m-%d %H:%M:%S.%f') 120 | time_delta = (cur_time - prev_time) 121 | total_seconds = time_delta.total_seconds() 122 | calc_minutes = total_seconds / 60 123 | 124 | if int(calc_minutes) <= 0: 125 | time_counter += item[1]['scheduler_time'] 126 | else: 127 | time_counter += item[1]['scheduler_time'] - int(calc_minutes) 128 | 129 | # Add poll count 130 | if payload.emoji.name == '1⃣' and list(poll_data[str(payload.message_id)][0].values())[ 131 | 0] < max_vote_count: 132 | first_poll = list(poll_data[str(payload.message_id)][0].keys())[0] 133 | poll_data[str(payload.message_id)][0][first_poll] += 1 134 | 135 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 136 | json.dump(poll_data, update_poll_data, indent=4) 137 | 138 | await reaction.remove(payload.member) 139 | 140 | elif payload.emoji.name == '2⃣' and list(poll_data[str(payload.message_id)][0].values())[ 141 | 1] < max_vote_count: 142 | second_poll = list(poll_data[str(payload.message_id)][0].keys())[1] 143 | poll_data[str(payload.message_id)][0][second_poll] += 1 144 | 145 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 146 | json.dump(poll_data, update_poll_data, indent=4) 147 | 148 | await reaction.remove(payload.member) 149 | 150 | elif payload.emoji.name == '3⃣' and list(poll_data[str(payload.message_id)][0].values())[ 151 | 2] < max_vote_count: 152 | third_poll = list(poll_data[str(payload.message_id)][0].keys())[2] 153 | poll_data[str(payload.message_id)][0][third_poll] += 1 154 | 155 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 156 | json.dump(poll_data, update_poll_data, indent=4) 157 | 158 | await reaction.remove(payload.member) 159 | 160 | elif payload.emoji.name == '4⃣' and list(poll_data[str(payload.message_id)][0].values())[ 161 | 3] < max_vote_count: 162 | fourth_poll = list(poll_data[str(payload.message_id)][0].keys())[3] 163 | poll_data[str(payload.message_id)][0][fourth_poll] += 1 164 | 165 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 166 | json.dump(poll_data, update_poll_data, indent=4) 167 | 168 | await reaction.remove(payload.member) 169 | 170 | elif payload.emoji.name == '5⃣' and list(poll_data[str(payload.message_id)][0].values())[ 171 | 4] < max_vote_count: 172 | fifth_poll = list(poll_data[str(payload.message_id)][0].keys())[4] 173 | poll_data[str(payload.message_id)][0][fifth_poll] += 1 174 | 175 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 176 | json.dump(poll_data, update_poll_data, indent=4) 177 | 178 | await reaction.remove(payload.member) 179 | 180 | elif payload.emoji.name == '6⃣' and list(poll_data[str(payload.message_id)][0].values())[ 181 | 5] < max_vote_count: 182 | sixth_poll = list(poll_data[str(payload.message_id)][0].keys())[5] 183 | poll_data[str(payload.message_id)][0][sixth_poll] += 1 184 | 185 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 186 | json.dump(poll_data, update_poll_data, indent=4) 187 | 188 | await reaction.remove(payload.member) 189 | 190 | elif payload.emoji.name == '7⃣' and list(poll_data[str(payload.message_id)][0].values())[ 191 | 6] < max_vote_count: 192 | seventh_poll = list(poll_data[str(payload.message_id)][0].keys())[6] 193 | poll_data[str(payload.message_id)][0][seventh_poll] += 1 194 | 195 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 196 | json.dump(poll_data, update_poll_data, indent=4) 197 | 198 | await reaction.remove(payload.member) 199 | 200 | elif payload.emoji.name == '8⃣' and list(poll_data[str(payload.message_id)][0].values())[ 201 | 7] < max_vote_count: 202 | eighth_poll = list(poll_data[str(payload.message_id)][0].keys())[7] 203 | poll_data[str(payload.message_id)][0][eighth_poll] += 1 204 | 205 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 206 | json.dump(poll_data, update_poll_data, indent=4) 207 | 208 | await reaction.remove(payload.member) 209 | 210 | elif payload.emoji.name == '9⃣' and list(poll_data[str(payload.message_id)][0].values())[ 211 | 8] < max_vote_count: 212 | ninth_poll = list(poll_data[str(payload.message_id)][0].keys())[8] 213 | poll_data[str(payload.message_id)][0][ninth_poll] += 1 214 | 215 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 216 | json.dump(poll_data, update_poll_data, indent=4) 217 | 218 | await reaction.remove(payload.member) 219 | 220 | elif payload.emoji.name == '🔟' and list(poll_data[str(payload.message_id)][0].values())[ 221 | 9] < max_vote_count: 222 | tenth_poll = list(poll_data[str(payload.message_id)][0].keys())[9] 223 | poll_data[str(payload.message_id)][0][tenth_poll] += 1 224 | 225 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 226 | json.dump(poll_data, update_poll_data, indent=4) 227 | 228 | await reaction.remove(payload.member) 229 | 230 | else: 231 | await reaction.remove(payload.member) 232 | 233 | # Update embed 234 | options = list(poll_data[str(payload.message_id)][0].keys()) 235 | 236 | updated_polls = [('\u200b', 237 | '\n'.join( 238 | [f'{self.emoji[index]} {option} \n' for index, option in enumerate(options)]), 239 | False)] 240 | 241 | poll_graphs = [('\u200b', 242 | ''.join([f'{int(values / (max_vote_count / 7))} **{values}** ' 243 | for key, values in 244 | enumerate(list(poll_data[str(payload.message_id)][0].values()))]), 245 | False)] 246 | 247 | update_embed = discord.Embed(title=message.embeds[0].title, 248 | description=f':stopwatch: ' 249 | f'Poll will end in **{time_counter} minute**!', 250 | colour=0xFF0000) 251 | 252 | update_embed.set_thumbnail(url=message.embeds[0].thumbnail.url) 253 | 254 | for name, value, inline in updated_polls: 255 | update_embed.add_field(name=name, value=value, inline=inline) 256 | 257 | for graph_name, graph_value, graph_inline in poll_graphs: 258 | if reaction.emoji in self.emoji and len(message.reactions) == 1: 259 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 260 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 261 | 262 | graph_poll_count_1 = graph_value.split(' ')[1] 263 | 264 | update_embed.add_field(name=graph_name, 265 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 266 | inline=graph_inline) 267 | 268 | await message.edit(embed=update_embed) 269 | 270 | elif reaction.emoji in self.emoji and len(message.reactions) == 2: 271 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 272 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 273 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 274 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 275 | 276 | graph_poll_count_1 = graph_value.split(' ')[1] 277 | graph_poll_count_2 = graph_value.split(' ')[3] 278 | 279 | update_embed.add_field(name=graph_name, 280 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 281 | inline=graph_inline) 282 | 283 | update_embed.add_field(name=graph_name, 284 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 285 | inline=graph_inline) 286 | 287 | await message.edit(embed=update_embed) 288 | 289 | elif reaction.emoji in self.emoji and len(message.reactions) == 3: 290 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 291 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 292 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 293 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 294 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 295 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 296 | 297 | graph_poll_count_1 = graph_value.split(' ')[1] 298 | graph_poll_count_2 = graph_value.split(' ')[3] 299 | graph_poll_count_3 = graph_value.split(' ')[5] 300 | 301 | update_embed.add_field(name=graph_name, 302 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 303 | inline=graph_inline) 304 | 305 | update_embed.add_field(name=graph_name, 306 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 307 | inline=graph_inline) 308 | 309 | update_embed.add_field(name=graph_name, 310 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 311 | inline=graph_inline) 312 | 313 | await message.edit(embed=update_embed) 314 | 315 | elif reaction.emoji in self.emoji and len(message.reactions) == 4: 316 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 317 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 318 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 319 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 320 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 321 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 322 | graph_calculation_4 = int(graph_value.split(' ')[6]) * ':red_square:' + ( 323 | 7 - int(graph_value.split(' ')[6])) * ':white_large_square:' 324 | 325 | graph_poll_count_1 = graph_value.split(' ')[1] 326 | graph_poll_count_2 = graph_value.split(' ')[3] 327 | graph_poll_count_3 = graph_value.split(' ')[5] 328 | graph_poll_count_4 = graph_value.split(' ')[7] 329 | 330 | update_embed.add_field(name=graph_name, 331 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 332 | inline=graph_inline) 333 | 334 | update_embed.add_field(name=graph_name, 335 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 336 | inline=graph_inline) 337 | 338 | update_embed.add_field(name=graph_name, 339 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 340 | inline=graph_inline) 341 | 342 | update_embed.add_field(name=graph_name, 343 | value=f'4\u20e3 {graph_calculation_4} {graph_poll_count_4}', 344 | inline=graph_inline) 345 | 346 | await message.edit(embed=update_embed) 347 | 348 | elif reaction.emoji in self.emoji and len(message.reactions) == 5: 349 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 350 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 351 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 352 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 353 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 354 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 355 | graph_calculation_4 = int(graph_value.split(' ')[6]) * ':red_square:' + ( 356 | 7 - int(graph_value.split(' ')[6])) * ':white_large_square:' 357 | graph_calculation_5 = int(graph_value.split(' ')[8]) * ':red_square:' + ( 358 | 7 - int(graph_value.split(' ')[8])) * ':white_large_square:' 359 | 360 | graph_poll_count_1 = graph_value.split(' ')[1] 361 | graph_poll_count_2 = graph_value.split(' ')[3] 362 | graph_poll_count_3 = graph_value.split(' ')[5] 363 | graph_poll_count_4 = graph_value.split(' ')[7] 364 | graph_poll_count_5 = graph_value.split(' ')[9] 365 | 366 | update_embed.add_field(name=graph_name, 367 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 368 | inline=graph_inline) 369 | 370 | update_embed.add_field(name=graph_name, 371 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 372 | inline=graph_inline) 373 | 374 | update_embed.add_field(name=graph_name, 375 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 376 | inline=graph_inline) 377 | 378 | update_embed.add_field(name=graph_name, 379 | value=f'4\u20e3 {graph_calculation_4} {graph_poll_count_4}', 380 | inline=graph_inline) 381 | 382 | update_embed.add_field(name=graph_name, 383 | value=f'5\u20e3 {graph_calculation_5} {graph_poll_count_5}', 384 | inline=graph_inline) 385 | 386 | await message.edit(embed=update_embed) 387 | 388 | elif reaction.emoji in self.emoji and len(message.reactions) == 6: 389 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 390 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 391 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 392 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 393 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 394 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 395 | graph_calculation_4 = int(graph_value.split(' ')[6]) * ':red_square:' + ( 396 | 7 - int(graph_value.split(' ')[6])) * ':white_large_square:' 397 | graph_calculation_5 = int(graph_value.split(' ')[8]) * ':red_square:' + ( 398 | 7 - int(graph_value.split(' ')[8])) * ':white_large_square:' 399 | graph_calculation_6 = int(graph_value.split(' ')[10]) * ':red_square:' + ( 400 | 7 - int(graph_value.split(' ')[10])) * ':white_large_square:' 401 | 402 | graph_poll_count_1 = graph_value.split(' ')[1] 403 | graph_poll_count_2 = graph_value.split(' ')[3] 404 | graph_poll_count_3 = graph_value.split(' ')[5] 405 | graph_poll_count_4 = graph_value.split(' ')[7] 406 | graph_poll_count_5 = graph_value.split(' ')[9] 407 | graph_poll_count_6 = graph_value.split(' ')[11] 408 | 409 | update_embed.add_field(name=graph_name, 410 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 411 | inline=graph_inline) 412 | 413 | update_embed.add_field(name=graph_name, 414 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 415 | inline=graph_inline) 416 | 417 | update_embed.add_field(name=graph_name, 418 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 419 | inline=graph_inline) 420 | 421 | update_embed.add_field(name=graph_name, 422 | value=f'4\u20e3 {graph_calculation_4} {graph_poll_count_4}', 423 | inline=graph_inline) 424 | 425 | update_embed.add_field(name=graph_name, 426 | value=f'5\u20e3 {graph_calculation_5} {graph_poll_count_5}', 427 | inline=graph_inline) 428 | 429 | update_embed.add_field(name=graph_name, 430 | value=f'6\u20e3 {graph_calculation_6} {graph_poll_count_6}', 431 | inline=graph_inline) 432 | 433 | await message.edit(embed=update_embed) 434 | 435 | elif reaction.emoji in self.emoji and len(message.reactions) == 7: 436 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 437 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 438 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 439 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 440 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 441 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 442 | graph_calculation_4 = int(graph_value.split(' ')[6]) * ':red_square:' + ( 443 | 7 - int(graph_value.split(' ')[6])) * ':white_large_square:' 444 | graph_calculation_5 = int(graph_value.split(' ')[8]) * ':red_square:' + ( 445 | 7 - int(graph_value.split(' ')[8])) * ':white_large_square:' 446 | graph_calculation_6 = int(graph_value.split(' ')[10]) * ':red_square:' + ( 447 | 7 - int(graph_value.split(' ')[10])) * ':white_large_square:' 448 | graph_calculation_7 = int(graph_value.split(' ')[12]) * ':red_square:' + ( 449 | 7 - int(graph_value.split(' ')[12])) * ':white_large_square:' 450 | 451 | graph_poll_count_1 = graph_value.split(' ')[1] 452 | graph_poll_count_2 = graph_value.split(' ')[3] 453 | graph_poll_count_3 = graph_value.split(' ')[5] 454 | graph_poll_count_4 = graph_value.split(' ')[7] 455 | graph_poll_count_5 = graph_value.split(' ')[9] 456 | graph_poll_count_6 = graph_value.split(' ')[11] 457 | graph_poll_count_7 = graph_value.split(' ')[13] 458 | 459 | update_embed.add_field(name=graph_name, 460 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 461 | inline=graph_inline) 462 | 463 | update_embed.add_field(name=graph_name, 464 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 465 | inline=graph_inline) 466 | 467 | update_embed.add_field(name=graph_name, 468 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 469 | inline=graph_inline) 470 | 471 | update_embed.add_field(name=graph_name, 472 | value=f'4\u20e3 {graph_calculation_4} {graph_poll_count_4}', 473 | inline=graph_inline) 474 | 475 | update_embed.add_field(name=graph_name, 476 | value=f'5\u20e3 {graph_calculation_5} {graph_poll_count_5}', 477 | inline=graph_inline) 478 | 479 | update_embed.add_field(name=graph_name, 480 | value=f'6\u20e3 {graph_calculation_6} {graph_poll_count_6}', 481 | inline=graph_inline) 482 | 483 | update_embed.add_field(name=graph_name, 484 | value=f'7\u20e3 {graph_calculation_7} {graph_poll_count_7}', 485 | inline=graph_inline) 486 | 487 | await message.edit(embed=update_embed) 488 | 489 | elif reaction.emoji in self.emoji and len(message.reactions) == 8: 490 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 491 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 492 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 493 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 494 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 495 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 496 | graph_calculation_4 = int(graph_value.split(' ')[6]) * ':red_square:' + ( 497 | 7 - int(graph_value.split(' ')[6])) * ':white_large_square:' 498 | graph_calculation_5 = int(graph_value.split(' ')[8]) * ':red_square:' + ( 499 | 7 - int(graph_value.split(' ')[8])) * ':white_large_square:' 500 | graph_calculation_6 = int(graph_value.split(' ')[10]) * ':red_square:' + ( 501 | 7 - int(graph_value.split(' ')[10])) * ':white_large_square:' 502 | graph_calculation_7 = int(graph_value.split(' ')[12]) * ':red_square:' + ( 503 | 7 - int(graph_value.split(' ')[12])) * ':white_large_square:' 504 | graph_calculation_8 = int(graph_value.split(' ')[14]) * ':red_square:' + ( 505 | 7 - int(graph_value.split(' ')[14])) * ':white_large_square:' 506 | 507 | graph_poll_count_1 = graph_value.split(' ')[1] 508 | graph_poll_count_2 = graph_value.split(' ')[3] 509 | graph_poll_count_3 = graph_value.split(' ')[5] 510 | graph_poll_count_4 = graph_value.split(' ')[7] 511 | graph_poll_count_5 = graph_value.split(' ')[9] 512 | graph_poll_count_6 = graph_value.split(' ')[11] 513 | graph_poll_count_7 = graph_value.split(' ')[13] 514 | graph_poll_count_8 = graph_value.split(' ')[15] 515 | 516 | update_embed.add_field(name=graph_name, 517 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 518 | inline=graph_inline) 519 | 520 | update_embed.add_field(name=graph_name, 521 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 522 | inline=graph_inline) 523 | 524 | update_embed.add_field(name=graph_name, 525 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 526 | inline=graph_inline) 527 | 528 | update_embed.add_field(name=graph_name, 529 | value=f'4\u20e3 {graph_calculation_4} {graph_poll_count_4}', 530 | inline=graph_inline) 531 | 532 | update_embed.add_field(name=graph_name, 533 | value=f'5\u20e3 {graph_calculation_5} {graph_poll_count_5}', 534 | inline=graph_inline) 535 | 536 | update_embed.add_field(name=graph_name, 537 | value=f'6\u20e3 {graph_calculation_6} {graph_poll_count_6}', 538 | inline=graph_inline) 539 | 540 | update_embed.add_field(name=graph_name, 541 | value=f'7\u20e3 {graph_calculation_7} {graph_poll_count_7}', 542 | inline=graph_inline) 543 | 544 | update_embed.add_field(name=graph_name, 545 | value=f'8\u20e3 {graph_calculation_8} {graph_poll_count_8}', 546 | inline=graph_inline) 547 | 548 | await message.edit(embed=update_embed) 549 | 550 | elif reaction.emoji in self.emoji and len(message.reactions) == 9: 551 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 552 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 553 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 554 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 555 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 556 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 557 | graph_calculation_4 = int(graph_value.split(' ')[6]) * ':red_square:' + ( 558 | 7 - int(graph_value.split(' ')[6])) * ':white_large_square:' 559 | graph_calculation_5 = int(graph_value.split(' ')[8]) * ':red_square:' + ( 560 | 7 - int(graph_value.split(' ')[8])) * ':white_large_square:' 561 | graph_calculation_6 = int(graph_value.split(' ')[10]) * ':red_square:' + ( 562 | 7 - int(graph_value.split(' ')[10])) * ':white_large_square:' 563 | graph_calculation_7 = int(graph_value.split(' ')[12]) * ':red_square:' + ( 564 | 7 - int(graph_value.split(' ')[12])) * ':white_large_square:' 565 | graph_calculation_8 = int(graph_value.split(' ')[14]) * ':red_square:' + ( 566 | 7 - int(graph_value.split(' ')[14])) * ':white_large_square:' 567 | graph_calculation_9 = int(graph_value.split(' ')[16]) * ':red_square:' + ( 568 | 7 - int(graph_value.split(' ')[16])) * ':white_large_square:' 569 | 570 | graph_poll_count_1 = graph_value.split(' ')[1] 571 | graph_poll_count_2 = graph_value.split(' ')[3] 572 | graph_poll_count_3 = graph_value.split(' ')[5] 573 | graph_poll_count_4 = graph_value.split(' ')[7] 574 | graph_poll_count_5 = graph_value.split(' ')[9] 575 | graph_poll_count_6 = graph_value.split(' ')[11] 576 | graph_poll_count_7 = graph_value.split(' ')[13] 577 | graph_poll_count_8 = graph_value.split(' ')[15] 578 | graph_poll_count_9 = graph_value.split(' ')[17] 579 | 580 | update_embed.add_field(name=graph_name, 581 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 582 | inline=graph_inline) 583 | 584 | update_embed.add_field(name=graph_name, 585 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 586 | inline=graph_inline) 587 | 588 | update_embed.add_field(name=graph_name, 589 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 590 | inline=graph_inline) 591 | 592 | update_embed.add_field(name=graph_name, 593 | value=f'4\u20e3 {graph_calculation_4} {graph_poll_count_4}', 594 | inline=graph_inline) 595 | 596 | update_embed.add_field(name=graph_name, 597 | value=f'5\u20e3 {graph_calculation_5} {graph_poll_count_5}', 598 | inline=graph_inline) 599 | 600 | update_embed.add_field(name=graph_name, 601 | value=f'6\u20e3 {graph_calculation_6} {graph_poll_count_6}', 602 | inline=graph_inline) 603 | 604 | update_embed.add_field(name=graph_name, 605 | value=f'7\u20e3 {graph_calculation_7} {graph_poll_count_7}', 606 | inline=graph_inline) 607 | 608 | update_embed.add_field(name=graph_name, 609 | value=f'8\u20e3 {graph_calculation_8} {graph_poll_count_8}', 610 | inline=graph_inline) 611 | 612 | update_embed.add_field(name=graph_name, 613 | value=f'9\u20e3 {graph_calculation_9} {graph_poll_count_9}', 614 | inline=graph_inline) 615 | 616 | await message.edit(embed=update_embed) 617 | 618 | elif reaction.emoji in self.emoji and len(message.reactions) == 10: 619 | graph_calculation_1 = int(graph_value.split(' ')[0]) * ':red_square:' + ( 620 | 7 - int(graph_value.split(' ')[0])) * ':white_large_square:' 621 | graph_calculation_2 = int(graph_value.split(' ')[2]) * ':red_square:' + ( 622 | 7 - int(graph_value.split(' ')[2])) * ':white_large_square:' 623 | graph_calculation_3 = int(graph_value.split(' ')[4]) * ':red_square:' + ( 624 | 7 - int(graph_value.split(' ')[4])) * ':white_large_square:' 625 | graph_calculation_4 = int(graph_value.split(' ')[6]) * ':red_square:' + ( 626 | 7 - int(graph_value.split(' ')[6])) * ':white_large_square:' 627 | graph_calculation_5 = int(graph_value.split(' ')[8]) * ':red_square:' + ( 628 | 7 - int(graph_value.split(' ')[8])) * ':white_large_square:' 629 | graph_calculation_6 = int(graph_value.split(' ')[10]) * ':red_square:' + ( 630 | 7 - int(graph_value.split(' ')[10])) * ':white_large_square:' 631 | graph_calculation_7 = int(graph_value.split(' ')[12]) * ':red_square:' + ( 632 | 7 - int(graph_value.split(' ')[12])) * ':white_large_square:' 633 | graph_calculation_8 = int(graph_value.split(' ')[14]) * ':red_square:' + ( 634 | 7 - int(graph_value.split(' ')[14])) * ':white_large_square:' 635 | graph_calculation_9 = int(graph_value.split(' ')[16]) * ':red_square:' + ( 636 | 7 - int(graph_value.split(' ')[16])) * ':white_large_square:' 637 | graph_calculation_10 = int(graph_value.split(' ')[18]) * ':red_square:' + ( 638 | 7 - int(graph_value.split(' ')[18])) * ':white_large_square:' 639 | 640 | graph_poll_count_1 = graph_value.split(' ')[1] 641 | graph_poll_count_2 = graph_value.split(' ')[3] 642 | graph_poll_count_3 = graph_value.split(' ')[5] 643 | graph_poll_count_4 = graph_value.split(' ')[7] 644 | graph_poll_count_5 = graph_value.split(' ')[9] 645 | graph_poll_count_6 = graph_value.split(' ')[11] 646 | graph_poll_count_7 = graph_value.split(' ')[13] 647 | graph_poll_count_8 = graph_value.split(' ')[15] 648 | graph_poll_count_9 = graph_value.split(' ')[17] 649 | graph_poll_count_10 = graph_value.split(' ')[19] 650 | 651 | update_embed.add_field(name=graph_name, 652 | value=f'1\u20e3 {graph_calculation_1} {graph_poll_count_1}', 653 | inline=graph_inline) 654 | 655 | update_embed.add_field(name=graph_name, 656 | value=f'2\u20e3 {graph_calculation_2} {graph_poll_count_2}', 657 | inline=graph_inline) 658 | 659 | update_embed.add_field(name=graph_name, 660 | value=f'3\u20e3 {graph_calculation_3} {graph_poll_count_3}', 661 | inline=graph_inline) 662 | 663 | update_embed.add_field(name=graph_name, 664 | value=f'4\u20e3 {graph_calculation_4} {graph_poll_count_4}', 665 | inline=graph_inline) 666 | 667 | update_embed.add_field(name=graph_name, 668 | value=f'5\u20e3 {graph_calculation_5} {graph_poll_count_5}', 669 | inline=graph_inline) 670 | 671 | update_embed.add_field(name=graph_name, 672 | value=f'6\u20e3 {graph_calculation_6} {graph_poll_count_6}', 673 | inline=graph_inline) 674 | 675 | update_embed.add_field(name=graph_name, 676 | value=f'7\u20e3 {graph_calculation_7} {graph_poll_count_7}', 677 | inline=graph_inline) 678 | 679 | update_embed.add_field(name=graph_name, 680 | value=f'8\u20e3 {graph_calculation_8} {graph_poll_count_8}', 681 | inline=graph_inline) 682 | 683 | update_embed.add_field(name=graph_name, 684 | value=f'9\u20e3 {graph_calculation_9} {graph_poll_count_9}', 685 | inline=graph_inline) 686 | 687 | update_embed.add_field(name=graph_name, 688 | value=f'\U0001F51F {graph_calculation_10} {graph_poll_count_10}', 689 | inline=graph_inline) 690 | 691 | await message.edit(embed=update_embed) 692 | 693 | else: 694 | pass 695 | 696 | @tasks.loop(minutes=15) 697 | async def poll_result(self): 698 | with open('.\\databases\\scheduler.json', 'r') as schedule_file: 699 | scheduler_data = json.load(schedule_file) 700 | 701 | for item in scheduler_data.items(): 702 | cur_time = datetime.now() 703 | prev_time = datetime.strptime(item[1]['poll_start_time'].replace('T', ' '), '%Y-%m-%d %H:%M:%S.%f') 704 | 705 | time_delta = (cur_time - prev_time) 706 | total_seconds = time_delta.total_seconds() 707 | calc_minutes = total_seconds / 60 708 | 709 | channel = self.bot.get_channel(int(item[0])) 710 | message = await channel.fetch_message(item[1]['message_id']) 711 | 712 | with open('.\\databases\\poll.json', 'r') as poll_file: 713 | poll_data = json.load(poll_file) 714 | 715 | poll_outcome = max(poll_data[str(message.id)][0].items(), key=lambda i: i[1]) 716 | 717 | if int(calc_minutes) > item[1]['scheduler_time'] or poll_outcome[1] >= item[1]['max_vote']: 718 | if str(message.id) in poll_data: 719 | await channel.send( 720 | f':tada: **{poll_outcome[0]}** has won **{message.embeds[0].title}** poll ' 721 | f'with **{poll_outcome[1]}** votes!') 722 | 723 | # Remove poll 724 | poll_data.pop(str(message.id)) 725 | with open('.\\databases\\poll.json', 'w') as update_poll_data: 726 | json.dump(poll_data, update_poll_data, indent=4) 727 | 728 | # Remove schedule 729 | scheduler_data.pop(str(channel.id)) 730 | with open('.\\databases\\scheduler.json', 'w') as update_scheduler_data: 731 | json.dump(scheduler_data, update_scheduler_data, indent=4) 732 | 733 | break 734 | 735 | 736 | def setup(bot): 737 | bot.add_cog(Poll(bot)) 738 | -------------------------------------------------------------------------------- /commands/python_compiler.py: -------------------------------------------------------------------------------- 1 | import os 2 | from discord.ext import commands 3 | from apis.code_compiler import compiler 4 | 5 | 6 | class PythonCompiler(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.command() 11 | async def runpy(self, ctx, *args): 12 | format_args = list(args) 13 | 14 | if not format_args[1].startswith('def'): 15 | short_code_snippet = ' '.join(format_args[1:-1]) 16 | text_file = open('.\\databases\\source_code.py', 'w') 17 | text_file.write(short_code_snippet) 18 | text_file.close() 19 | 20 | os.system("black .\\databases\\source_code.py") 21 | 22 | else: 23 | long_code_snippet = ' '.join(format_args[1:-2]) 24 | callback_code_snippet = ''.join(format_args[-2]) 25 | 26 | text_file = open('.\\databases\\source_code.py', 'w') 27 | text_file.write(f'{long_code_snippet}\n{callback_code_snippet}') 28 | text_file.close() 29 | 30 | os.system("black .\\databases\\source_code.py") 31 | 32 | text_file = open('.\\databases\\source_code.py', 'r') 33 | await ctx.send(f"**Output:**\n```yaml\n{compiler(code=text_file.read())['output']}```") 34 | 35 | 36 | def setup(bot): 37 | bot.add_cog(PythonCompiler(bot)) 38 | -------------------------------------------------------------------------------- /commands/spotify.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import requests 3 | import dateutil.parser 4 | from discord.ext import commands 5 | from PIL import Image, ImageFont, ImageDraw 6 | 7 | 8 | class Spotify(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | @commands.command() 13 | async def track(self, ctx, user: discord.Member = None): 14 | user = user or ctx.author 15 | spotify_result = next((activity for activity in user.activities if isinstance(activity, discord.Spotify)), None) 16 | 17 | if spotify_result is None: 18 | await ctx.send(f'{user.name} is not listening to Spotify.') 19 | 20 | # Images 21 | track_background_image = Image.open('.\\assets\\spotify_template.png') 22 | album_image = Image.open(requests.get(spotify_result.album_cover_url, stream=True).raw).convert('RGBA') 23 | 24 | # Fonts 25 | title_font = ImageFont.truetype('theboldfont.ttf', 16) 26 | artist_font = ImageFont.truetype('theboldfont.ttf', 14) 27 | album_font = ImageFont.truetype('theboldfont.ttf', 14) 28 | start_duration_font = ImageFont.truetype('theboldfont.ttf', 12) 29 | end_duration_font = ImageFont.truetype('theboldfont.ttf', 12) 30 | 31 | # Positions 32 | title_text_position = 150, 30 33 | artist_text_position = 150, 60 34 | album_text_position = 150, 80 35 | start_duration_text_position = 150, 122 36 | end_duration_text_position = 515, 122 37 | 38 | # Draws 39 | draw_on_image = ImageDraw.Draw(track_background_image) 40 | draw_on_image.text(title_text_position, spotify_result.title, 'white', font=title_font) 41 | draw_on_image.text(artist_text_position, f'by {spotify_result.artist}', 'white', font=artist_font) 42 | draw_on_image.text(album_text_position, spotify_result.album, 'white', font=album_font) 43 | draw_on_image.text(start_duration_text_position, '0:00', 'white', font=start_duration_font) 44 | draw_on_image.text(end_duration_text_position, 45 | f"{dateutil.parser.parse(str(spotify_result.duration)).strftime('%M:%S')}", 46 | 'white', font=end_duration_font) 47 | 48 | # Background colour 49 | album_color = album_image.getpixel((250, 100)) 50 | background_image_color = Image.new('RGBA', track_background_image.size, album_color) 51 | background_image_color.paste(track_background_image, (0, 0), track_background_image) 52 | 53 | # Resize 54 | album_image_resize = album_image.resize((140, 160)) 55 | background_image_color.paste(album_image_resize, (0, 0), album_image_resize) 56 | 57 | # Save image 58 | background_image_color.convert('RGB').save('spotify.jpg', 'JPEG') 59 | 60 | await ctx.send(file=discord.File('spotify.jpg')) 61 | 62 | 63 | def setup(bot): 64 | bot.add_cog(Spotify(bot)) 65 | -------------------------------------------------------------------------------- /commands/ticket.py: -------------------------------------------------------------------------------- 1 | import json 2 | import discord 3 | from random import randint 4 | from discord.ext import commands 5 | 6 | 7 | class Ticket(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | 11 | @commands.command() 12 | @commands.has_permissions(administrator=True) 13 | async def createticket(self, ctx, *args): 14 | format_args = list(args) 15 | 16 | guild_id = ctx.message.guild.id 17 | channel_id = int(format_args[0].strip('<').strip('>').replace('#', '')) 18 | title = ' '.join(format_args[1:]) 19 | 20 | with open('.\\databases\\ticket.json', 'r') as file: 21 | ticket_data = json.load(file) 22 | new_ticket = str(guild_id) 23 | 24 | # Update existing ticket 25 | if new_ticket in ticket_data: 26 | ticket_data[new_ticket] += [channel_id] 27 | with open('.\\databases\\ticket.json', 'w') as update_ticket_data: 28 | json.dump(ticket_data, update_ticket_data, indent=4) 29 | 30 | # Add new ticket 31 | else: 32 | ticket_data[new_ticket] = [channel_id] 33 | with open('.\\databases\\ticket.json', 'w') as new_ticket_data: 34 | json.dump(ticket_data, new_ticket_data, indent=4) 35 | 36 | # Create new embed with reaction 37 | ticket_embed = discord.Embed(colour=randint(0, 0xffffff)) 38 | ticket_embed.set_thumbnail( 39 | url=f'https://cdn.discordapp.com/icons/{guild_id}/{ctx.message.guild.icon}.png') 40 | 41 | ticket_embed.add_field(name=f'Welcome To {ctx.message.guild} Server', value=f'{title}') 42 | send_ticket_embed = await self.bot.get_channel(channel_id).send(embed=ticket_embed) 43 | 44 | await send_ticket_embed.add_reaction(u'\U0001F3AB') 45 | 46 | @commands.command() 47 | @commands.has_permissions(administrator=True) 48 | async def closeticket(self, ctx, mentioned_user): 49 | mentioned_role = mentioned_user.strip('<@&>') 50 | get_mentioned_role = [items.name for items in ctx.message.author.guild.roles if f'{items.id}' in 51 | f'{mentioned_role}'] 52 | get_role = discord.utils.get(ctx.message.author.guild.roles, name=f'{get_mentioned_role[0]}') 53 | 54 | await get_role.delete(reason='Removed by command') 55 | await ctx.message.channel.delete(reason=None) 56 | 57 | @commands.Cog.listener() 58 | async def on_raw_reaction_add(self, payload): 59 | if payload.member.id != self.bot.user.id: 60 | with open('.\\databases\\ticket.json', 'r') as file: 61 | ticket_data = json.load(file) 62 | 63 | channel_id = list(ticket_data.values()) 64 | user_channel_id = payload.channel_id 65 | 66 | for items in channel_id: 67 | if user_channel_id in items: 68 | # Get guild and roles 69 | find_guild = discord.utils.find(lambda guild: guild.id == payload.guild_id, self.bot.guilds) 70 | guild_roles = discord.utils.get(find_guild.roles, name=f'{payload.member.name}') 71 | 72 | if guild_roles is None: 73 | # Create new role 74 | permissions = discord.Permissions(send_messages=True, read_messages=True) 75 | await find_guild.create_role(name=f'{payload.member.name}', permissions=permissions) 76 | 77 | # Assign new role 78 | new_user_role = discord.utils.get(find_guild.roles, name=f'{payload.member.name}') 79 | await payload.member.add_roles(new_user_role, reason=None, atomic=True) 80 | 81 | # Overwrite role permissions 82 | admin_role = discord.utils.get(find_guild.roles, name='Admin') 83 | 84 | overwrites = { 85 | find_guild.default_role: discord.PermissionOverwrite(read_messages=False), 86 | new_user_role: discord.PermissionOverwrite(read_messages=True), 87 | admin_role: discord.PermissionOverwrite(read_messages=True) 88 | } 89 | 90 | # Create new channel 91 | create_channel = await find_guild.create_text_channel( 92 | u'\U0001F4CB-{}'.format(new_user_role), overwrites=overwrites) 93 | 94 | await create_channel.send( 95 | f'{new_user_role.mention} Your ticket has been created! Please wait for ' 96 | f'{admin_role.mention} to response.') 97 | 98 | 99 | def setup(bot): 100 | bot.add_cog(Ticket(bot)) 101 | -------------------------------------------------------------------------------- /commands/utility.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | 3 | 4 | class Utility(commands.Cog): 5 | def __init__(self, bot): 6 | self.bot = bot 7 | 8 | @commands.command() 9 | async def ping(self, ctx): 10 | await ctx.send('pong!') 11 | 12 | 13 | def setup(bot): 14 | bot.add_cog(Utility(bot)) 15 | -------------------------------------------------------------------------------- /commands/voice_leaderboard.py: -------------------------------------------------------------------------------- 1 | import json 2 | import discord 3 | import datetime 4 | from tabulate import tabulate 5 | from discord.ext import commands 6 | from PIL import Image, ImageFont, ImageDraw 7 | 8 | 9 | class VoiceLeaderboard(commands.Cog): 10 | def __init__(self, bot): 11 | self.bot = bot 12 | 13 | @commands.Cog.listener() 14 | async def on_voice_state_update(self, member, before, after): 15 | with open('.\\databases\\voice_leaderboard.json', 'r') as file: 16 | voice_data = json.load(file) 17 | new_user = str(member.id) 18 | 19 | # Update existing user 20 | if new_user in voice_data: 21 | voice_leave_time = datetime.datetime.now().time().strftime('%H:%M:%S') 22 | voice_join_time = voice_data[new_user] 23 | 24 | calculate_time = ( 25 | datetime.datetime.strptime(voice_leave_time, '%H:%M:%S') - datetime.datetime.strptime( 26 | voice_join_time, '%H:%M:%S')) 27 | 28 | voice_data[new_user] = f'{calculate_time}' 29 | 30 | with open('.\\databases\\voice_leaderboard.json', 'w') as update_user_data: 31 | json.dump(voice_data, update_user_data, indent=4) 32 | 33 | # Add new user 34 | else: 35 | new_voice_join_time = datetime.datetime.now().time().strftime('%H:%M:%S') 36 | voice_data[new_user] = new_voice_join_time 37 | 38 | with open('.\\databases\\voice_leaderboard.json', 'w') as new_user_data: 39 | json.dump(voice_data, new_user_data, indent=4) 40 | 41 | @commands.command() 42 | async def voice(self, ctx): 43 | with open('.\\databases\\voice_leaderboard.json', 'r') as file: 44 | voice_data = json.load(file) 45 | 46 | user_ids = list(voice_data.keys()) 47 | user_time_spents = list(voice_data.values()) 48 | 49 | new_leaderboard = [] 50 | 51 | for index, user_id in enumerate(user_ids, 1): 52 | new_leaderboard.append([user_id, user_time_spents[index - 1]]) 53 | 54 | # Sort leaderboard order by user time spent 55 | new_leaderboard.sort(key=lambda items: items[1], reverse=True) 56 | 57 | user_rank_column = [] 58 | user_name_column = [] 59 | user_time_spent_column = [] 60 | 61 | # User rank 62 | for rank_index, rank_value in enumerate(new_leaderboard[:10]): 63 | user_rank_column.append([rank_index + 1]) 64 | 65 | # User name 66 | for name_index, name_value in enumerate(new_leaderboard[:10]): 67 | user_name_column.append([await self.bot.fetch_user(int(name_value[0]))]) 68 | 69 | # User time spend 70 | for time_spent_index, time_spent_value in enumerate(new_leaderboard[:10]): 71 | user_time_spent_column.append([time_spent_value[1]]) 72 | 73 | # Append column to table 74 | user_rank_table = tabulate(user_rank_column, tablefmt='plain', headers=['#\n'], numalign='left') 75 | user_name_table = tabulate(user_name_column, tablefmt='plain', headers=['Name\n'], numalign='left') 76 | user_time_spent_table = tabulate(user_time_spent_column, tablefmt='plain', headers=['Time Spent\n'], 77 | numalign='left') 78 | 79 | # Image 80 | image_template = Image.open('.\\assets\\voice_leaderboard_template.png') 81 | 82 | # Font 83 | font = ImageFont.truetype('theboldfont.ttf', 14) 84 | 85 | # Positions 86 | rank_text_position = 30, 50 87 | name_text_position = 80, 50 88 | rank_time_spent_text_position = 330, 50 89 | 90 | # Draws 91 | draw_on_image = ImageDraw.Draw(image_template) 92 | draw_on_image.text(rank_text_position, user_rank_table, 'white', font=font) 93 | draw_on_image.text(name_text_position, user_name_table, 'white', font=font) 94 | draw_on_image.text(rank_time_spent_text_position, user_time_spent_table, 'white', font=font) 95 | 96 | # Save image 97 | image_template.convert('RGB').save('voice_leaderboard.jpg', 'JPEG') 98 | 99 | await ctx.send(file=discord.File('voice_leaderboard.jpg')) 100 | 101 | 102 | def setup(bot): 103 | bot.add_cog(VoiceLeaderboard(bot)) 104 | -------------------------------------------------------------------------------- /commands/voice_member_count.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | from discord.ext import commands, tasks 4 | 5 | 6 | class VoiceMemberCount(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.Cog.listener() 11 | async def on_ready(self): 12 | self.voice_channel_update.start() 13 | 14 | @commands.Cog.listener() 15 | async def on_voice_state_update(self, member, before, after): 16 | member_id = str(member.id) 17 | 18 | try: 19 | with open('./databases/voice_member_count.json', 'r') as file: 20 | voice_member_data = json.load(file) 21 | new_voice_member_channel = str(member.voice.channel.id) 22 | new_voice_member_id = str(member.id) 23 | 24 | # Update existing voice data 25 | if new_voice_member_channel in voice_member_data: 26 | for item in list(voice_member_data.values()): 27 | if new_voice_member_id not in item: 28 | voice_member_data[new_voice_member_channel] += [new_voice_member_id] 29 | with open('./databases/voice_member_count.json', 'w') as update_voice_member_data: 30 | json.dump(voice_member_data, update_voice_member_data, indent=4) 31 | 32 | time.sleep(1) 33 | 34 | # Add new voice data 35 | else: 36 | voice_member_data[new_voice_member_channel] = [new_voice_member_id] 37 | with open('./databases/voice_member_count.json', 'w') as new_voice_member_data: 38 | json.dump(voice_member_data, new_voice_member_data, indent=4) 39 | 40 | time.sleep(1) 41 | 42 | except AttributeError: 43 | # Remove voice data 44 | for remove_keys, remove_values in voice_member_data.items(): 45 | if member_id in remove_values: 46 | remove_values.remove(member_id) 47 | 48 | with open('./databases/voice_member_count.json', 'w') as remove_voice_member_data: 49 | json.dump(voice_member_data, remove_voice_member_data, indent=4) 50 | 51 | @tasks.loop(minutes=10) 52 | async def voice_channel_update(self): 53 | # Update all voice channel stats every 10 minutes 54 | with open('./databases/voice_member_count.json', 'r') as file: 55 | voice_member_data = json.load(file) 56 | 57 | for routine_update_keys, routine_update_values in voice_member_data.items(): 58 | if not routine_update_values: 59 | reset_channel = self.bot.get_channel(int(routine_update_keys)) 60 | await reset_channel.edit(name=u'\U0001F449 \u0020 0') 61 | else: 62 | update_channel = self.bot.get_channel(int(routine_update_keys)) 63 | await update_channel.edit(name=u'\U0001F449 \u0020 {}'.format(len(routine_update_values))) 64 | 65 | 66 | def setup(bot): 67 | bot.add_cog(VoiceMemberCount(bot)) 68 | -------------------------------------------------------------------------------- /databases/chat_leaderboard.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /databases/global_chat.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /databases/karma.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /databases/moderation.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /databases/poll.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /databases/scheduler.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /databases/source_code.py: -------------------------------------------------------------------------------- 1 | print('Hello World!') 2 | -------------------------------------------------------------------------------- /databases/ticket.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /databases/voice_leaderboard.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /databases/voice_member_count.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import discord 3 | import logging 4 | from dotenv import load_dotenv 5 | from discord.ext import commands 6 | 7 | 8 | class TutorialBot(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | # Greetings 13 | @commands.Cog.listener() 14 | async def on_ready(self): 15 | print(f'Logged in as {self.bot.user} ({self.bot.user.id})') 16 | 17 | # Reconnect 18 | @commands.Cog.listener() 19 | async def on_resumed(self): 20 | print('Bot has reconnected!') 21 | 22 | # Error Handlers 23 | @commands.Cog.listener() 24 | async def on_command_error(self, ctx, error): 25 | # Uncomment line 26 for printing debug 26 | # await ctx.send(error) 27 | 28 | # Unknown command 29 | if isinstance(error, commands.CommandNotFound): 30 | await ctx.send('Invalid Command!') 31 | 32 | # Bot does not have permission 33 | elif isinstance(error, commands.MissingPermissions): 34 | await ctx.send('Bot Permission Missing!') 35 | 36 | 37 | # Gateway intents 38 | intents = discord.Intents.default() 39 | intents.members = True 40 | intents.presences = True 41 | 42 | # Bot prefix 43 | bot = commands.Bot(command_prefix=commands.when_mentioned_or('!'), 44 | description='A Simple Tutorial Bot', intents=intents) 45 | 46 | # Logging 47 | logger = logging.getLogger('discord') 48 | logger.setLevel(logging.DEBUG) 49 | handler = logging.FileHandler(filename='discord.log', encoding='utf-8', mode='w') 50 | handler.setFormatter(logging.Formatter('%(asctime)s:%(levelname)s:%(name)s: %(message)s')) 51 | logger.addHandler(handler) 52 | 53 | # Loading data from .env file 54 | load_dotenv() 55 | token = os.getenv('TOKEN') 56 | 57 | if __name__ == '__main__': 58 | # Load extension 59 | for filename in os.listdir('./commands'): 60 | if filename.endswith('.py'): 61 | bot.load_extension(f'commands.{filename[: -3]}') 62 | 63 | bot.add_cog(TutorialBot(bot)) 64 | bot.run(token, reconnect=True) 65 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp==3.7.3 2 | appdirs==1.4.4 3 | async-timeout==3.0.1 4 | attrs==20.3.0 5 | black==20.8b1 6 | cachetools==4.2.1 7 | certifi==2020.12.5 8 | chardet==3.0.4 9 | click==7.1.2 10 | cycler==0.10.0 11 | discord.py==1.6.0 12 | google-api-core==1.26.3 13 | google-api-python-client==2.1.0 14 | google-auth==1.28.0 15 | google-auth-httplib2==0.1.0 16 | googleapis-common-protos==1.53.0 17 | httplib2==0.19.1 18 | idna==2.10 19 | kiwisolver==1.3.1 20 | matplotlib==3.3.4 21 | multidict==5.1.0 22 | mypy-extensions==0.4.3 23 | numpy==1.20.1 24 | packaging==20.9 25 | pandas==1.2.3 26 | pathspec==0.8.1 27 | Pillow==8.1.2 28 | protobuf==3.15.7 29 | pyasn1==0.4.8 30 | pyasn1-modules==0.2.8 31 | pyparsing==2.4.7 32 | python-dateutil==2.8.1 33 | python-dotenv==0.15.0 34 | pytz==2021.1 35 | regex==2020.11.13 36 | requests==2.25.1 37 | rsa==4.7.2 38 | six==1.15.0 39 | tabulate==0.8.7 40 | toml==0.10.2 41 | typed-ast==1.4.2 42 | typing-extensions==3.7.4.3 43 | uritemplate==3.0.1 44 | urllib3==1.26.3 45 | yarl==1.6.3 46 | -------------------------------------------------------------------------------- /theboldfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevStrikerTech/Discord.py-Bot-Series/09a0dd374d3994caffdfcec6dd4066d68ded9913/theboldfont.ttf --------------------------------------------------------------------------------