diff --git a/.gitignore b/.gitignore index aeaf289..6b7b046 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.json *.txt +*.png __pycache__ \ No newline at end of file diff --git a/api/__pycache__/__init__.cpython-37.pyc b/api/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 1c68e02..0000000 Binary files a/api/__pycache__/__init__.cpython-37.pyc and /dev/null differ diff --git a/api/__pycache__/inspirobot.cpython-37.pyc b/api/__pycache__/inspirobot.cpython-37.pyc deleted file mode 100644 index 3565d3c..0000000 Binary files a/api/__pycache__/inspirobot.cpython-37.pyc and /dev/null differ diff --git a/api/__pycache__/nasa.cpython-37.pyc b/api/__pycache__/nasa.cpython-37.pyc deleted file mode 100644 index 936abb5..0000000 Binary files a/api/__pycache__/nasa.cpython-37.pyc and /dev/null differ diff --git a/api/__pycache__/steam.cpython-37.pyc b/api/__pycache__/steam.cpython-37.pyc deleted file mode 100644 index d937f49..0000000 Binary files a/api/__pycache__/steam.cpython-37.pyc and /dev/null differ diff --git a/api/carbon.py b/api/carbon.py new file mode 100644 index 0000000..4ccd697 --- /dev/null +++ b/api/carbon.py @@ -0,0 +1,81 @@ +import requests +import numpy as np +import matplotlib.pyplot as plt +import datetime +from util import logging + +def current_level() -> []: + url = "https://api.carbonintensity.org.uk/intensity" + response = requests.get(url) + + if not response.ok: + logging.error(f"Failed to access Carbon API: {response.status_code} [{url}]") + + data = response.json()["data"][0] + return [data["from"], data["intensity"]["forecast"], data["intensity"]["actual"], data["intensity"]["index"]] + +def level_at(date: str) -> []: + url = f"https://api.carbonintensity.org.uk/intensity/date/{date}" + response = requests.get(url) + + if not response.ok: + logging.error(f"Failed to access Carbon API: {response.status_code} [{url}]") + return [] + + if len(response.json()["data"]) == 0: + return [] + + data = response.json()["data"][0] + return [data["from"], data["intensity"]["forecast"], data["intensity"]["actual"], data["intensity"]["index"]] + +def level_from_to(start: str, stop: str) -> str: + url = f"https://api.carbonintensity.org.uk/intensity/{start}/{stop}" + response = requests.get(url) + + if not response.ok: + logging.error(f"Failed to access Carbon API: {response.status_code} [{url}]") + return "" + + if len(response.json()["data"]) == 0: + return [] + + data = response.json()["data"] + + x = [] + measured = [] + indices = [] + + for i in range(len(data)): + intensity = data[i]["intensity"] + x.append(datetime.datetime.strptime(data[i]["from"], "%Y-%m-%dT%H:%MZ")) + measured.append(intensity["actual"]) + indices.append(intensity["index"]) + + curr_color = indices[0] + curr_color_index = 0 + for i in range(len(measured)): + if indices[i] != curr_color: + color = "" + if curr_color == "very low": + color = "#5bcc32" + if curr_color == "low": + color = "#87cc32" + if curr_color == "moderate": + color = "#ccc932" + if curr_color == "high": + color = "#cc8e32" + if curr_color == "very high": + color = "#cc4f32" + + plt.plot(x[curr_color_index:i+1], measured[curr_color_index:i+1], color) + curr_color_index = i + curr_color = indices[i] + + plt.xlabel("Time & Date") + plt.ylabel("Carbon Intensity [gCO2/kWh]") + plt.xticks(rotation=45) + plt.gcf().subplots_adjust(bottom=0.2) + plt.grid(b=True) + plt.savefig("plot.png") + plt.clf() + return "plot.png" \ No newline at end of file diff --git a/api/quote.py b/api/quote.py new file mode 100644 index 0000000..5323c9b --- /dev/null +++ b/api/quote.py @@ -0,0 +1,14 @@ +import requests +import random +from util import logging + +def fetch_quote() -> tuple: + url = "https://favqs.com/api/qotd" + response = requests.get(url) + + if not response.ok: + logging.error(f"Failed to access Quotes API: {response.status_code} [{url}]") + return () + + data = response.json() + return (data["quote"]["body"], data["quote"]["author"]) \ No newline at end of file diff --git a/api/thesaurus.py b/api/thesaurus.py new file mode 100644 index 0000000..b82e972 --- /dev/null +++ b/api/thesaurus.py @@ -0,0 +1,72 @@ +import requests +import re, random +import nltk +import threading, queue +from util import logging, config + +key = config.settings["api_keys"]["thesaurus"] +url = f"http://thesaurus.altervista.org/thesaurus/v1?key={key}&language=en_US&output=json&word=" + +def start_thread(target, args): + q = queue.Queue() + def wrapper(): + q.put(target(args)) + t = threading.Thread(target=wrapper) + t.start() + return q + +def thesaurufy_sentence(sentence): + symbols = nltk.word_tokenize(sentence) + tags = nltk.pos_tag(symbols) + + if len(symbols) == 0: + return "" + + queues = [] + for i in range(0, len(symbols)): + queues.append(start_thread(target = handle_token, args=(tags[i]))) + + for i in range(0, len(symbols)): + symbols[i] = queues[i].get() + + return "".join(symbols) + +def handle_token(args): + token = args + if not token[0].isalpha(): + return token[0] + + if token[1] != "NN" and token[1] != "VB" and token[1] != "VBG" and token[1] != "VBP" and token[1] != "JJ" and token[1] != "RB": + return (" " + token[0]) + + response = requests.get(url + token[0]) + if not response.ok: + # logging.warning(f"Thesaurus API returned {response.status_code} ({url + token[0]})") + return (" " + token[0]) + + responses = response.json()["response"] + condition = "" + if token[1] == "JJ": + condition = "(adj)" + elif token[1] == "RB": + condition = "(adv)" + elif token[1] == "NN": + condition = "(noun)" + else: + condition = "(verb)" + + try: + words = random.choice([a for a in responses if a["list"]["category"] == condition])["list"]["synonyms"].split("|") + except: + return (" " + token[0]) + + # print(words) + word = words[random.randint(0, len(words) - 1)] + + if "(" in word: + if "antonym" in word.split("(")[1].lower(): + return (" " + token[0]) + + word = word.split("(")[0][:-1] + + return (" " + word) \ No newline at end of file diff --git a/api/translation.py b/api/translation.py new file mode 100644 index 0000000..2459db7 --- /dev/null +++ b/api/translation.py @@ -0,0 +1,50 @@ +''' +This file provides the functionality for +the translation function, by first converting the +country/language name to an ISO369-1 language identifier +and then translating it using the Yandex Translation API +''' +import requests, urllib.parse +from util import logging, config +import pycountry + +api_key = config.settings["api_keys"]["yandex"] +langs = pycountry.languages + +def name_to_ISO(name: str) -> str: + alpha = "" + for language in langs: + if language.name.lower() == name.lower(): + try: + alpha = language.alpha_2 + break + except: + logging.warning(f"Tried to get alpha2 code of unknown language {language}") + + return alpha + +def ISO_to_name(ISO: str) -> str: + name = "" + for language in langs: + try: + if language.alpha_2.lower() == ISO.lower(): + try: + name = language.name + break + except: + logging.warning(f"Tried to get name of unknown language code {ISO}") + except: + # i dont care tbh + pass + + return name + +def translate(text: str, lang: str) -> (str, str): + url_encoded_text = urllib.parse.quote(text) + url = f"https://translate.yandex.net/api/v1.5/tr.json/translate?key={api_key}&text={url_encoded_text}&lang={lang}" + + response = requests.get(url) + if not response.ok: + logging.error(f"Failed to contact Yandex API: {response.status_code}") + return "" + return (response.json()["text"][0], response.json()["lang"]) \ No newline at end of file diff --git a/bot.py b/bot.py index b31d6b5..f04233c 100644 --- a/bot.py +++ b/bot.py @@ -23,7 +23,7 @@ logging.info(f"Total {total}, Failed {failed}\n") client = commands.Bot(command_prefix=config.settings["prefix"], case_insensitive=True) -@client.command() +@client.command(name="load", description="Loads a cog", usage="load ") @commands.is_owner() async def load(ctx : commands.Context, extension : str): try: @@ -32,7 +32,7 @@ async def load(ctx : commands.Context, extension : str): except: await ctx.message.add_reaction("πŸ‘Ž") -@client.command() +@client.command(name="unload", description="Unoads a cog", usage="unload ") @commands.is_owner() async def unload(ctx : commands.Context, extension : str): try: @@ -41,7 +41,7 @@ async def unload(ctx : commands.Context, extension : str): except: await ctx.message.add_reaction("πŸ‘Ž") -@client.command() +@client.command(name="reload", description="Reoads a cog", usage="reload ") @commands.is_owner() async def reload(ctx : commands.Context, extension : str): try: @@ -59,6 +59,20 @@ async def on_ready(): total = 0 failed = 0 # Load all cogs on startup + for filename in os.listdir("./cogs"): + if filename.endswith(".py"): + total += 1 + cog = f"cogs.{filename[:-3]}" + + try: + client.load_extension(cog) + logging.info(f"Trying {cog}.....Success!") + except Exception as e: + logging.info(f"Trying {cog}.....Failed!") + logging.error(str(e)) + failed += 1 + + # Load "fun" cogs for filename in os.listdir("./cogs/fun"): if filename.endswith(".py"): diff --git a/cogs/api/__pycache__/inspirobot.cpython-37.pyc b/cogs/api/__pycache__/inspirobot.cpython-37.pyc deleted file mode 100644 index 8f6d07a..0000000 Binary files a/cogs/api/__pycache__/inspirobot.cpython-37.pyc and /dev/null differ diff --git a/cogs/api/__pycache__/nasa.cpython-37.pyc b/cogs/api/__pycache__/nasa.cpython-37.pyc deleted file mode 100644 index d6fd5c8..0000000 Binary files a/cogs/api/__pycache__/nasa.cpython-37.pyc and /dev/null differ diff --git a/cogs/api/__pycache__/steam.cpython-37.pyc b/cogs/api/__pycache__/steam.cpython-37.pyc deleted file mode 100644 index 15d5fa1..0000000 Binary files a/cogs/api/__pycache__/steam.cpython-37.pyc and /dev/null differ diff --git a/cogs/api/carbon.py b/cogs/api/carbon.py new file mode 100644 index 0000000..ed67982 --- /dev/null +++ b/cogs/api/carbon.py @@ -0,0 +1,40 @@ +import discord +from discord.ext import commands +from api import carbon +from util import embed + +class Carbon(commands.Cog): + + def __init__(self, client: discord.Client): + self.client = client + + @commands.group(name="carbon", usage="carbon [subcommand]", description="Can accumulate data about carbon levels in the UK") + @commands.cooldown(1, 5) + async def carbon(self, ctx): + pass + + @carbon.command(name="now", usage="carbon now", description="Gets the latest available carbon intensity data") + async def now(self, ctx): + data = carbon.current_level() + await ctx.send(embed=embed.make_embed_fields_footer("Carbon intensity in the UK", f"The current carbon intensity is considered **{data[3]}**.", f"{data[0]} | All values in gCO2/kWh", ("Measured", data[2]), ("Predicted", data[1]))) + + @carbon.command(name="at", usage="carbon at ", description="Gets the carbon levels at the given date (YYYY-MM-DD)") + async def at(self, ctx: discord.Client, date: str): + data = carbon.level_at(date) + if len(data) == 0: + await ctx.send(embed=embed.make_error_embed(f"There is no data available for {date}.")) + else: + await ctx.send(embed=embed.make_embed_fields_footer("Carbon intensity in the UK", f"The carbon intensity for that date is considered **{data[3]}**.", f"{data[0]} | All values in gCO2/kWh", ("Measured", data[2]), ("Predicted", data[1]))) + + @carbon.command(name="during", usage="carbon during ", description="Creates a diagram about carbon levels in the given time period (YYYY-MM-DD)") + @commands.cooldown(1, 25) + async def during(self, ctx: discord.Client, start: str, stop: str): + path = carbon.level_from_to(start, stop) + if path == "": + await ctx.send(embed=embed.make_error_embed(f"There is no data available for the given time period.")) + else: + data = embed.make_embed_image("Carbon intensity in the UK", path) + await ctx.send(embed=data[0], file=data[1]) + +def setup(client: discord.Client): + client.add_cog(Carbon(client)) \ No newline at end of file diff --git a/cogs/api/inspirobot.py b/cogs/api/inspirobot.py index c533b41..3dc3b0d 100644 --- a/cogs/api/inspirobot.py +++ b/cogs/api/inspirobot.py @@ -14,7 +14,7 @@ class Inspirobot(commands.Cog): def __init__(self, client : discord.Client): self.client = client - @commands.command(name="Inspirobot", description="Sends a randomly generated inspirational quote", aliases=["inspiration", "inspiro"]) + @commands.command(name="inspirobot", description="Sends a randomly generated inspirational quote", usage="inspirobot", aliases=["inspiration", "inspiro", "insp"]) @commands.cooldown(1, 5) async def inspirobot(self, ctx : commands.Context): image = inspirobot.get_inspirational_quote() @@ -25,5 +25,11 @@ class Inspirobot(commands.Cog): embed.set_image(url=image) await ctx.send(embed=embed) + @commands.command(name="test", dscription="Who cares tbh") + @commands.cooldown(1, 3) + async def test(self, ctx): + await self.client.get_channel(621378664977793024).send("Test") + await ctx.send(self.client.name) + def setup(client : discord.Client): - client.add_cog(Inspirobot(client)) \ No newline at end of file + client.add_cog(Inspirobot(client)) diff --git a/cogs/api/nasa.py b/cogs/api/nasa.py index 4875ef4..0ce4322 100644 --- a/cogs/api/nasa.py +++ b/cogs/api/nasa.py @@ -12,7 +12,7 @@ class Nasa(commands.Cog): def __init__(self, client: discord.Client): self.client = client - @commands.command(name="APOD", description="Posts NASA's picture of the day.") + @commands.command(name="apod", description="Posts NASA's picture of the day.", usage="apod") @commands.cooldown(1, 30) async def apod(self, ctx: commands.Context): url = nasa.image_of_the_day() diff --git a/cogs/api/quote.py b/cogs/api/quote.py new file mode 100644 index 0000000..8e66342 --- /dev/null +++ b/cogs/api/quote.py @@ -0,0 +1,24 @@ +import discord +from discord.ext import commands +from util import embed, config +from api import quote + +class Quote(commands.Cog): + + def __init__(self, client: discord.Client): + self.client = client + + @commands.command(name="quote", description="Sends a random quote", usage="quote", aliases=["q"]) + @commands.cooldown(1, 2) + async def quote(self, ctx: commands.Context): + please_kill_me = quote.fetch_quote() + if len(please_kill_me) == 0: + await ctx.send(embed=embed.make_error_embed("Something went wrong while fetching a random quote")) + return + + end_my_life = discord.Embed(title=f'"{please_kill_me[0]}"', colour=int(config.settings["color"], 16)) + end_my_life.set_footer(text=please_kill_me[1]) + await ctx.send(embed=end_my_life) + +def setup(client: discord.Client): + client.add_cog(Quote(client)) \ No newline at end of file diff --git a/cogs/api/steam.py b/cogs/api/steam.py index 24a301d..5849076 100644 --- a/cogs/api/steam.py +++ b/cogs/api/steam.py @@ -11,7 +11,7 @@ class Steam(commands.Cog): def __init__(self, client: discord.Client): self.client = client - @commands.command(name="SteamLevel", description="Finds the steam level of a user", usage="SteamLevel ", aliases=["level"]) + @commands.command(name="steamlevel", description="Finds the steam level of a user", usage="steamlevel ", aliases=["level"]) @commands.cooldown(1, 2) async def SteamLevel(self, ctx: commands.Context, vanity_url: str): level = steam.get_steam_level(vanity_url) diff --git a/cogs/api/thesaurus.py b/cogs/api/thesaurus.py new file mode 100644 index 0000000..ffc81f5 --- /dev/null +++ b/cogs/api/thesaurus.py @@ -0,0 +1,18 @@ +import discord +from discord.ext import commands +from api import thesaurus + +class Thesaurus(commands.Cog): + + def __init__(self, client): + self.client = client + + @commands.command(name="thesaurus", description="Makes you smarter.", usage="thesaurus ", aliases=["saurus", "thes"]) + @commands.cooldown(1, 3) + async def thesaurus(self, ctx, *message): + loading = await ctx.send("Thinking...") + saurus = thesaurus.thesaurufy_sentence(" ".join(message)) + await loading.edit(content=saurus) + +def setup(client): + client.add_cog(Thesaurus(client)) \ No newline at end of file diff --git a/cogs/api/translation.py b/cogs/api/translation.py new file mode 100644 index 0000000..a380696 --- /dev/null +++ b/cogs/api/translation.py @@ -0,0 +1,32 @@ +import discord +from discord.ext import commands +from util import checking, embed +from api import translation + +class Translation(commands.Cog): + + def __init__(self, client: discord.Client): + self.client = client + + @commands.command(name="translate", description="Translates the given text", usage="translate ", aliases=["tl"]) + @commands.check(checking.is_author_bot) + async def translate(self, ctx: commands.Context, language: str, *message: str): + # Get language code + code = translation.name_to_ISO(language) + text = ' '.join(message) + if code == "": + await ctx.send(embed=embed.make_error_embed(f"There is no language named **{language}**.")) + return + + response = translation.translate(text, code) + if len(response) == 0: + await ctx.send(embed=embed.make_error_embed(f"The translation API doesn't support **{language}**.")) + return + translated = response[0] + direction = response[1].split("-") + _from = translation.ISO_to_name(direction[0]) + _to = translation.ISO_to_name(direction[1]) + await ctx.send(embed=embed.make_embed_field(f"{_from} -> {_to}", None, text, translated)) + +def setup(client: discord.Client): + client.add_cog(Translation(client)) \ No newline at end of file diff --git a/cogs/fun/__pycache__/autoconvert.cpython-37.pyc b/cogs/fun/__pycache__/autoconvert.cpython-37.pyc deleted file mode 100644 index 0119e37..0000000 Binary files a/cogs/fun/__pycache__/autoconvert.cpython-37.pyc and /dev/null differ diff --git a/cogs/fun/__pycache__/coinflip.cpython-37.pyc b/cogs/fun/__pycache__/coinflip.cpython-37.pyc deleted file mode 100644 index 4b1c09d..0000000 Binary files a/cogs/fun/__pycache__/coinflip.cpython-37.pyc and /dev/null differ diff --git a/cogs/fun/__pycache__/reactions.cpython-37.pyc b/cogs/fun/__pycache__/reactions.cpython-37.pyc deleted file mode 100644 index 1187acf..0000000 Binary files a/cogs/fun/__pycache__/reactions.cpython-37.pyc and /dev/null differ diff --git a/cogs/fun/autoconvert.py b/cogs/fun/autoconvert.py index bf6bf37..abb0a5a 100644 --- a/cogs/fun/autoconvert.py +++ b/cogs/fun/autoconvert.py @@ -65,12 +65,12 @@ class AutoConvert(commands.Cog): for currency in self.currencies: if currency == element[1]: continue - currency_string += f"{self.convert_currency(element[1], currency, element[0])}{currency}, " - embed.add_field(name=str(element[0])+element[1], value=currency_string[:-2]) + currency_string += f"{currency}{self.convert_currency(element[1], currency, element[0])}, " + embed.add_field(name=element[1]+str(element[0]), value=currency_string[:-2]) if not empty: await message.channel.send(embed=embed) def setup(client: discord.Client): - client.add_cog(AutoConvert(client)) \ No newline at end of file + client.add_cog(AutoConvert(client)) diff --git a/cogs/fun/coinflip.py b/cogs/fun/coinflip.py index 9a510e0..ebc0ea7 100644 --- a/cogs/fun/coinflip.py +++ b/cogs/fun/coinflip.py @@ -12,7 +12,7 @@ class Coinflip(commands.Cog): def __init__(self, client: discord.Client): self.client = client - @commands.command(name="coinflip", description="Flips a coin and reacts with the result", aliases=["coin", "flip"]) + @commands.command(name="coinflip", description="Flips a coin and reacts with the result", usage="coin", aliases=["coin", "flip"]) async def coinflip(self, ctx: commands.Context): if random.randint(0, 1) == 0: await ctx.message.add_reaction("πŸŒ‘") diff --git a/cogs/fun/encryption.py b/cogs/fun/encryption.py new file mode 100644 index 0000000..fa91450 --- /dev/null +++ b/cogs/fun/encryption.py @@ -0,0 +1,34 @@ +import discord +from discord.ext import commands +from util import embed + +class Encryption(commands.Cog): + def __init__(self, client: discord.Client): + self.client = client + + @commands.command(name="rot", description="Applies the ROT encryption", usage="rot [number] [message]") + @commands.cooldown(1, 2) + async def rot(self, ctx: commands.Context, shift: str, *message: str): + if not shift.isnumeric(): + await ctx.send(embed=embed.make_error_embed(f"{shift} is not a number.")) + return + + shift = int(shift) + if (shift < 1) or (shift > 25): + await ctx.send(embed=embed.make_error_embed("That's too much rotation. Stop it.")) + return + + old_message = list(' '.join(message)) + for i in range(0, len(old_message)): + if not old_message[i].isalpha(): + continue + + if old_message[i].isupper(): + old_message[i] = chr(ord('A') + ((ord(old_message[i]) + shift - ord('A')) % 26)) + else: + old_message[i] = chr(ord('a') + ((ord(old_message[i]) + shift - ord('a')) % 26)) + + await ctx.send(''.join(old_message)) + +def setup(client: discord.Client): + client.add_cog(Encryption(client)) \ No newline at end of file diff --git a/cogs/fun/math.py b/cogs/fun/math.py new file mode 100644 index 0000000..6c92e54 --- /dev/null +++ b/cogs/fun/math.py @@ -0,0 +1,20 @@ +import discord +from discord.ext import commands +from sympy import preview +from util import embed + +class Math(commands.Cog): + + def __init__(self, client: discord.Client): + self.client = client + + @commands.command(name="latex", description="Transforms a latex(math!) expression to an image", usage="latex ", aliases=["lat", "tex", "l"]) + @commands.cooldown(1, 5) + async def latex(self, ctx: commands.Context, *expr: str): + preview(f"$${' '.join(expr)}$$", viewer='file', filename='latex.png', euler=False) + result = embed.make_embed_image("LaTeX", "latex.png") + await ctx.send(embed=result[0], file=result[1]) + + +def setup(client: discord.Client): + client.add_cog(Math(client)) \ No newline at end of file diff --git a/cogs/fun/reactions.py b/cogs/fun/reactions.py index 4764ccb..4010144 100644 --- a/cogs/fun/reactions.py +++ b/cogs/fun/reactions.py @@ -29,9 +29,9 @@ class Reactions(commands.Cog): await message.add_reaction("πŸ‡¨πŸ‡³") if "america" in message.content.lower() or " usa" in message.content.lower(): - await message.add_reaction("πŸ‡¬") - await message.add_reaction("πŸ‡¦") - await message.add_reaction("πŸ‡Ύ") + await message.add_reaction("πŸ‡ΊπŸ‡Έ") + await message.add_reaction("πŸ”«") + await message.add_reaction("πŸ¦…") if "extremejoy" in message.content.lower(): diff --git a/cogs/fun/responses.py b/cogs/fun/responses.py new file mode 100644 index 0000000..1fd2261 --- /dev/null +++ b/cogs/fun/responses.py @@ -0,0 +1,32 @@ +import discord +from discord.ext import commands + +class Responses(commands.Cog): + + def __init__(self, client): + self.client = client + + @commands.Cog.listener() + async def on_message(self, message: discord.Message): + if message.author == self.client.user: + return + + # @applesauce + if ("<@!568271450176356352>" in message.content) and ("<@!657709911337074698>" in message.content): + await message.channel.send(f"Stop pinging us {message.author.mention} <:pinged:451198700832817202>") + return + + """ + if message.author.id == 478006431589728259: + if message.content == "<@&380535423233032193> NUMBERS REEEEEEEE": + await message.channel.send(f"<@&380535423233032193> {message.author.mention} REEEEEEEE") + + if message.content == "<@&380535423233032193> <:nockgun:352019166288543745> πŸ”’": + await message.channel.send(f"<@&380535423233032193> <:nockgun:352019166288543745> {message.author.mention}") + + if message.content == "<@&380535423233032193> is NOT for numbers": + await message.channel.send(f"<@&380535423233032193> ***IS*** for numbers") + """ + +def setup(client): + client.add_cog(Responses(client)) \ No newline at end of file diff --git a/cogs/fun/safety.py b/cogs/fun/safety.py new file mode 100644 index 0000000..2261316 --- /dev/null +++ b/cogs/fun/safety.py @@ -0,0 +1,35 @@ +import discord +from discord.ext import commands +from util import embed, logging +import time + +class Safety(commands.Cog): + def __init__(self, client): + self.client = client + self.msg_buffer = [".", ".", ".", ".", ".", "."] + self.ping = "<@&380535423233032193>" + self.channel = 439466964625391637 + self.last_action = 0 + + @commands.Cog.listener() + async def on_message(self, message): + if message.channel.id != self.channel: + return + + if (time.time() - self.last_action) < 10: + return + + + if self.ping in message.content: + for msg in self.msg_buffer: + if self.ping in msg: + # Ping not adequately spaced + await message.channel.send(embed=embed.make_embed("Coronavirus Safety Information", "Due to the ongoing pandemic, please remember to space your NaughtyStep pings at least six messages apart.\n Thank you.")) + self.last_action = time.time() + break + + self.msg_buffer.pop(0) + self.msg_buffer.append(message.content) + +def setup(client): + client.add_cog(Safety(client)) \ No newline at end of file diff --git a/cogs/fun/schedule.py b/cogs/fun/schedule.py new file mode 100644 index 0000000..cbc0808 --- /dev/null +++ b/cogs/fun/schedule.py @@ -0,0 +1,51 @@ +import discord +import time +import asyncio +from discord.ext import commands +from datetime import datetime, timedelta +from random import randrange +from util import logging + +def ceil_dt(dt, delta): + return dt + (datetime.min - dt) % delta + +def random_date(start, end): + """ + This function will return a random datetime between two datetime + objects. + """ + delta = end - start + int_delta = (delta.days * 24 * 60 * 60) + delta.seconds + random_second = randrange(int_delta) + return start + timedelta(seconds=random_second) + +class Schedule(commands.Cog): + + def __init__(self, client: discord.Client): + self.client = client + self.threads = 0 + self.THREAD_LIMIT = 20 + + async def call_event(self, sleep_time: int, name: str, user: discord.User, channel: discord.TextChannel): + await asyncio.sleep(sleep_time) + await channel.send(f"{user.mention} Your event \"{name}\" starts now!") + self.threads -= 1 + + @commands.command(name="schedule", description="Schedules an event", usage="schedule ", aliases=["s"]) + @commands.cooldown(1, 3) + async def schedule(self, ctx: commands.Context, *name: str): + date = ceil_dt(random_date(datetime.now(), datetime.now() + timedelta(days=7)), timedelta(minutes=15)) + await ctx.send(f"I scheduled \"{' '.join(name)}\" for **{date.strftime('%d.%m.%Y %H:%M CET')}**") + + if self.threads <= self.THREAD_LIMIT: + seconds = (date - datetime.now()).seconds + await self.call_event(seconds, ' '.join(name), ctx.message.author, ctx.message.channel) + self.threads += 1 + logging.info(f"Scheduled new event in {seconds} seconds. Current events: {self.threads}") + else: + logging.warning("Schedule: Event limit reached. Async function was not called.") + + + +def setup(client: discord.Client): + client.add_cog(Schedule(client)) diff --git a/cogs/fun/wolfram.py b/cogs/fun/wolfram.py new file mode 100644 index 0000000..dcc6725 --- /dev/null +++ b/cogs/fun/wolfram.py @@ -0,0 +1,26 @@ +import discord +from discord.ext import commands +from wolframclient.evaluation import WolframLanguageSession +from wolframclient.language import wl, wlexpr +from util import embed + +class WolframAlpha(commands.Cog): + def __init__(self, client): + self.client = client + self.session = WolframLanguageSession() + + def __del__(self): + self.session.terminate() + + @commands.command(name="WolframLanguage", description="Evaluates WolframLanguage expressions (Not to be confused with WolframAlpha!)", usage="WolframLanguage ", aliases=["wl"]) + async def WolframLanguage(self, ctx, *, expr): + msg = await ctx.send(embed=embed.make_embed("WolframLanguage", "Thinking...")) + evaluate = self.session.evaluate(expr) + + self.session.evaluate(wl.Export("eval.png", evaluate, "PNG")) + await msg.delete() + data = embed.make_embed_image("Result", "eval.png") + await ctx.send(embed=data[0], file=data[1]) + +def setup(client): + client.add_cog(WolframAlpha(client)) \ No newline at end of file diff --git a/cogs/help.py b/cogs/help.py new file mode 100644 index 0000000..257a67a --- /dev/null +++ b/cogs/help.py @@ -0,0 +1,106 @@ +import discord +from discord.ext import commands +from util import config, embed, logging + +class SetupHelp(commands.Cog): + def __init__(self, client: discord.Client): + self.client = client + self._original_help_command = client.help_command + client.help_command = Help() + client.help_command.cog = self + +class Help(commands.MinimalHelpCommand): + async def command_not_found(self, string: str): + await self.context.send(embed=embed.make_error_embed("Command not found")) + + async def subcommand_not_found(self, command, string): + await self.context.send(embed=embed.make_error_embed("Command not found")) + + async def send_cog_help(self, cog): + await self.context.send(embed=embed.make_error_embed("Command not found")) + + async def send_group_help(self, group): + try: + subcmds = "" + if group.commands != []: + for command in group.commands: + subcmds += "`" + command.name + "`, " + subcmds = subcmds[:-2] + else: + subcmds = "`None`" + + alias = "" + if group.aliases != []: + for i in range(len(group.aliases)): + if i == len(group.aliases) - 1: + alias = alias + '`' + group.aliases[i] + '`' + else: + alias = alias + '`' + group.aliases[i] + '`' + ', ' + else: + alias = "`None`" + + await self.context.send(embed=embed.make_embed_fields_ninl(group.name, group.description, ("Usage", f"`{config.settings['prefix']}{group.usage}`"), ("Aliases", alias), ("Subcommands", subcmds))) + except Exception as e: + logging.error(str(e)) + await self.context.send(embed=embed.make_error_embed("Command not found")) + + async def send_command_help(self, command): + try: + alias = "" + if command.aliases != []: + for i in range(len(command.aliases)): + if i == len(command.aliases) - 1: + alias = alias + '`' + command.aliases[i] + '`' + else: + alias = alias + '`' + command.aliases[i] + '`' + ', ' + else: + alias = "`None`" + + command_name = command.name + if command.parent != None: + command_name += f" ({command.parent})" + await self.context.send(embed=embed.make_embed_fields_ninl(command_name, command.description, ("Usage", f"`{config.settings['prefix']}{command.usage}`"), ("Aliases", alias))) + except Exception as e: + logging.error(str(e)) + await self.context.send(embed=embed.make_error_embed("Command not found")) + + + async def send_bot_help(self, mapping): + # get list of commands + cmds = [] + prefix = config.settings['prefix'] + + for cog, cog_commands in mapping.items(): + cmds = cmds + cog_commands + cog = cog # Just so that the warning goes away + + newCmds = [] + for item in cmds: + newCmds.append(str(item)) + newCmds = sorted(newCmds) + + finalCmds = [] + for item in newCmds: + try: + finalCmds.append(item) + except: + pass + + cmdString = "" + if len(finalCmds) != 0: + for i in range(len(finalCmds)): + if i == len(finalCmds)-1: + cmdString = cmdString + '`' + finalCmds[i] + '`' + else: + cmdString = cmdString + '`' + finalCmds[i] + '`' + ', ' + + + if cmdString != "": + await self.context.send(embed=embed.make_embed_field('Help', f'To get further information about a command use `{prefix}help `', "Commands", cmdString, inline=False)) + else: + await self.context.send(embed=embed.make_error_embed("No commands found.")) + + + +def setup(client: discord.Client): + client.add_cog(SetupHelp(client)) \ No newline at end of file diff --git a/data/count.py b/data/count.py new file mode 100644 index 0000000..0796d91 --- /dev/null +++ b/data/count.py @@ -0,0 +1,5 @@ +count=0 +with open('naughty_step.txt', 'r') as file: + for line in file: + count += len(line) +print(count) diff --git a/logs/log.txt b/logs/log.txt deleted file mode 100644 index 4d17b31..0000000 --- a/logs/log.txt +++ /dev/null @@ -1,20 +0,0 @@ -[08.01.2020 02:25:57] [INFO] Starting up... - -[08.01.2020 02:25:57] [INFO] --- Loading configs --- - -[08.01.2020 02:25:57] [INFO] Trying config.json.....Success! -[08.01.2020 02:25:57] [INFO] Finished loading configs. -[08.01.2020 02:25:57] [INFO] Total 1, Failed 0 - -[08.01.2020 02:26:00] [INFO] --- Loading cogs --- - -[08.01.2020 02:26:00] [INFO] Trying cogs.fun.autoconvert.....Success! -[08.01.2020 02:26:00] [INFO] Trying cogs.fun.coinflip.....Success! -[08.01.2020 02:26:00] [INFO] Trying cogs.fun.reactions.....Success! -[08.01.2020 02:26:00] [INFO] Trying cogs.api.inspirobot.....Success! -[08.01.2020 02:26:00] [INFO] Trying cogs.api.nasa.....Success! -[08.01.2020 02:26:00] [INFO] Trying cogs.api.steam.....Success! -[08.01.2020 02:26:00] [INFO] Finished loading cogs. -[08.01.2020 02:26:00] [INFO] Total 6, Failed 0 - -[08.01.2020 02:26:00] [INFO] Deep Blue finished loading. diff --git a/screenlog.0 b/screenlog.0 new file mode 100644 index 0000000..439cc6c --- /dev/null +++ b/screenlog.0 @@ -0,0 +1,3 @@ +root@h2864412:~/DeepBlue# cd screscreen -X +Please specify a command. +root@h2864412:~/DeepBlue# \ No newline at end of file diff --git a/util/__pycache__/__init__.cpython-37.pyc b/util/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 725ee60..0000000 Binary files a/util/__pycache__/__init__.cpython-37.pyc and /dev/null differ diff --git a/util/__pycache__/checking.cpython-37.pyc b/util/__pycache__/checking.cpython-37.pyc deleted file mode 100644 index 2afad87..0000000 Binary files a/util/__pycache__/checking.cpython-37.pyc and /dev/null differ diff --git a/util/__pycache__/config.cpython-37.pyc b/util/__pycache__/config.cpython-37.pyc deleted file mode 100644 index b6f9706..0000000 Binary files a/util/__pycache__/config.cpython-37.pyc and /dev/null differ diff --git a/util/__pycache__/logging.cpython-37.pyc b/util/__pycache__/logging.cpython-37.pyc deleted file mode 100644 index b200eb2..0000000 Binary files a/util/__pycache__/logging.cpython-37.pyc and /dev/null differ diff --git a/util/checking.py b/util/checking.py index 043c7ec..0d03dc6 100644 --- a/util/checking.py +++ b/util/checking.py @@ -8,6 +8,4 @@ from discord.ext import commands from util import logging def is_author_bot(ctx : commands.Context) -> bool: - if ctx.message.author.bot: - return False - return True \ No newline at end of file + return (not ctx.author.bot) \ No newline at end of file diff --git a/util/embed.py b/util/embed.py new file mode 100644 index 0000000..cfdd8d9 --- /dev/null +++ b/util/embed.py @@ -0,0 +1,41 @@ +import discord +from util import config + +def make_error_embed(message: str) -> discord.Embed: + embed = discord.Embed(title="Error", description=message, colour=int(config.settings["err_color"], 16)) + return embed + +def make_embed(title: str, desc: str) -> discord.Embed: + embed = discord.Embed(title=title, description=desc, colour=int(config.settings["color"], 16)) + return embed + +def make_embed_field(title: str, desc: str, field_name: str, field_val: str, inline: bool = True) -> discord.Embed: + embed = discord.Embed(title=title, description=desc, colour=int(config.settings["color"], 16)) + embed.add_field(name=field_name, value=field_val, inline=inline) + return embed + +def make_embed_fields(title: str, desc: str, *fields: tuple) -> discord.Embed: + embed = discord.Embed(title=title, description=desc, colour=int(config.settings["color"], 16)) + for name, value in fields: + embed.add_field(name=name, value=value) + return embed + +def make_embed_fields_footer(title: str, desc: str, footer: str, *fields: tuple) -> discord.Embed: + embed = discord.Embed(title=title, description=desc, colour=int(config.settings["color"], 16)) + embed.set_footer(text=footer) + for name, value in fields: + embed.add_field(name=name, value=value) + return embed + +def make_embed_fields_ninl(title: str, desc: str, *fields: tuple) -> discord.Embed: + embed = discord.Embed(title=title, description=desc, colour=int(config.settings["color"], 16), inline=False) + for name, value in fields: + embed.add_field(name=name, value=value, inline=False) + return embed + +def make_embed_image(title: str, path: str) -> (discord.Embed, discord.File): + embed = discord.Embed(title=title, colour=int(config.settings["color"], 16)) + attachment = discord.File(path, filename="image.png") + embed.set_image(url='attachment://image.png') + + return (embed, attachment) \ No newline at end of file