diff --git a/.gitignore b/.gitignore index a2da7e1..df1d76e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ files/stats.json files/lost.json files/locked.json files/database.json +files/ufora_notifications.json .idea/ __pycache__ .env \ No newline at end of file diff --git a/cogs/launch.py b/cogs/launch.py index d0604ed..14568c7 100644 --- a/cogs/launch.py +++ b/cogs/launch.py @@ -8,6 +8,7 @@ import pytz import requests +# Temporarily disabled because of API (setup @ bottom) class Launch(commands.Cog): def __init__(self, client): self.client = client @@ -51,4 +52,5 @@ class Launch(commands.Cog): def setup(client): - client.add_cog(Launch(client)) + pass + # client.add_cog(Launch(client)) diff --git a/cogs/oneliners.py b/cogs/oneliners.py index 0c67f42..20bc822 100644 --- a/cogs/oneliners.py +++ b/cogs/oneliners.py @@ -6,6 +6,7 @@ from discord.ext import commands from enums.help_categories import Category from functions import checks, clap, mock, sunrise, timeFormatters import pytz +from requests import get import time import urllib.parse @@ -101,7 +102,7 @@ class Oneliners(commands.Cog): @commands.command(name="Todo", aliases=["List", "Td"]) @help.Category(category=Category.Didier) - async def todo(self, ctx, *args): + async def todo(self, ctx): await ctx.send("https://trello.com/b/PdtsAJea/didier-to-do-list") @commands.command(name="LMGTFY", aliases=["Dsfr"], usage="[Query]") @@ -130,7 +131,7 @@ class Oneliners(commands.Cog): await ctx.send("Shut, it already is.") @commands.command() - async def sc(self, ctx, *args): + async def sc(self, ctx): await ctx.send("http://take-a-screenshot.org/") @commands.command(aliases=["os", "sauce", "src"]) @@ -144,9 +145,19 @@ class Oneliners(commands.Cog): await ctx.send(":sunny:: **{}**\n:crescent_moon:: **{}**".format(s.sunrise(), s.sunset())) @commands.command(name="Tias", aliases=["TryIt"]) - async def tias(self, ctx, *args): + async def tias(self, ctx): await ctx.send("***Try it and see***") + @commands.command(name="Inspire") + @help.Category(Category.Other) + async def inspire(self, ctx): + image = get("http://inspirobot.me/api?generate=true") + + if image.status_code == 200: + await ctx.send(image.text) + else: + await ctx.send("Uh oh API down.") + def setup(client): client.add_cog(Oneliners(client)) diff --git a/cogs/tasks.py b/cogs/tasks.py index d38e14c..b44bcac 100644 --- a/cogs/tasks.py +++ b/cogs/tasks.py @@ -6,6 +6,7 @@ from functions import timeFormatters from functions.config import config from functions.database import currency, poke, prison, birthdays, stats from functions.scraping import getMatchweek +from functions import ufora_notifications import json import random import requests @@ -24,6 +25,7 @@ class Tasks(commands.Cog): self.updateMessageCounts.start() self.sendReminders.start() self.updateMatchweek.start() + self.uforaAnnouncements.start() @tasks.loop(hours=1.0) async def bankInterest(self): @@ -238,6 +240,24 @@ class Tasks(commands.Cog): async def beforeUpdateMatchweek(self): await self.client.wait_until_ready() + @tasks.loop(minutes=30.0) + async def uforaAnnouncements(self): + """ + Task that checks for new Ufora announcements every few minutes + """ + # Get new notifications + announcements = ufora_notifications.run() + + if announcements: + announcements_channel = self.client.get_channel(816724500136591380) + + for an in announcements: + await announcements_channel.send(embed=an.to_embed()) + + @uforaAnnouncements.before_loop + async def beforeUforaAnnouncements(self): + await self.client.wait_until_ready() + def getCurrentHour(self): return timeFormatters.dateTimeNow().hour diff --git a/data/constants.py b/data/constants.py index 0d3facf..8f8a9cb 100644 --- a/data/constants.py +++ b/data/constants.py @@ -14,11 +14,13 @@ botIDs = [ "728361496874057812" ] BugReports = "762668401960812554" +BotTesting = "679701786189103106" CallOfCode = "626699611192688641" CoCGeneral = "626699611813314561" DeZandbak = "728361030404538488" ErrorLogs = "762668505455132722" FeatureRequests = "762668473313787964" +ZandbakSpeeltuin = "769248992957038612" mods = { 626699611192688641: [384457911377854467, 171671190631481345], @@ -53,7 +55,12 @@ faq_channels = { 727876797458284584: "funcprog", 727876819264733244: "statprob", 727876836587208714: "sysprog", - 676713433567199232: "didier" + 676713433567199232: "didier", + 807566495550013460: "comparch", + 807567007355895838: "multimedia", + 807567261216538644: "sel1", + 807567345484169237: "webdev", + 807567387775336499: "wetrek" } diff --git a/data/embeds/__init__.py b/data/embeds/__init__.py new file mode 100644 index 0000000..55d7068 --- /dev/null +++ b/data/embeds/__init__.py @@ -0,0 +1 @@ +from .ufora import UforaNotification diff --git a/data/embeds/ufora.py b/data/embeds/ufora.py new file mode 100644 index 0000000..475110b --- /dev/null +++ b/data/embeds/ufora.py @@ -0,0 +1,95 @@ +from datetime import datetime, timedelta +from discord import Embed, Colour +from functions.stringFormatters import leadingZero as lz +from functions.timeFormatters import intToWeekday +import pytz +import re + + +class UforaNotification: + def __init__(self, content: dict, course, notif_id, course_id): + self._content: dict = content + self._course = course + self._notif_id, self._course_id = notif_id, course_id + self._view_url = self._create_url() + self._title = self._clean_content(self._content["title"]) + self._description = self._get_description() + self._published = self._get_published() + + def to_embed(self): + embed = Embed(colour=Colour.from_rgb(30, 100, 200)) + + embed.set_author(name=self._course) + embed.title = self._title + embed.url = self._view_url + embed.description = self._description + embed.set_footer(text=self._published) + + return embed + + def get_id(self): + return int(self._notif_id) if self._notif_id is not None else self._content["id"] + + def _create_url(self): + if self._notif_id is None or self._course_id is None: + return self._content["link"] + + return "https://ufora.ugent.be/d2l/le/news/{0}/{1}/view?ou={0}".format(self._course_id, self._notif_id) + + def _get_description(self): + desc = self._clean_content(self._content["summary"]) + + if len(desc) > 500: + return desc[:497] + "..." + + return desc + + def _clean_content(self, text: str): + # Dict with HTML & markdown tags to replace + html_table = { + # CHARACTERS: + "&": '&', + """: '"', + "apos;": "'", + ">": ">", + "<": "<", + # MARKDOWN SUPPORT: + "": "**", + "": "**", + "": "**", + "": "**", + "": "*", + "": "*", + "": "*", + "": "*", + "": "~~", + "": "~~", + "": "__", + "": "__", + # Represent paragraphs with newlines + "

": "\n", + "
": "\n", + "
": "\n", + "
": "\n" + } + + # Unescape HTML + for key, value in html_table.items(): + text = text.replace(key, value) + + # Remove HTML tags + return re.sub(r"<[^>]*>", "", text) + + def _get_published(self): + time_string = "%a, %d %b %Y %H:%M:%S %Z" + dt = datetime.strptime(self._content["published"], time_string)\ + .astimezone(pytz.timezone("Europe/Brussels")) + + # Apply timezone offset + dt = dt + timedelta(hours=dt.utcoffset().seconds//3600) + + return "{} {}/{}/{} om {}:{}:{}".format( + intToWeekday(dt.weekday()), + lz(dt.day), lz(dt.month), lz(dt.year), + lz(dt.hour), lz(dt.minute), lz(dt.second) + ) diff --git a/files/help.json b/files/help.json index 09f640e..3fe70ee 100644 --- a/files/help.json +++ b/files/help.json @@ -46,12 +46,12 @@ "hangman start": "Start een nieuwe Hangman game indien er nog geen bezig is. Indien je geen woord opgeeft, wordt er een willekeurig woord gekozen.\n**Indien je wel een woord opgeeft, werkt dit enkel in DM.**", "hangman guess": "Probeer het woord te raden.", "claim": "Claim [Aantal] Didier Dinks uit je profit.\nIndien je geen aantal opgeeft (of \"all\"), claim je alles, inclusief je investering.", + "inspire": "Generate quotes via [InspiroBot](https://inspirobot.me/).", "inventory": "Bekijk de items in jouw inventory.", "invest": "Investeer [Aantal] Didier Dinks in jouw Didier Bank om rente te vergaren.", "jpl": "Informatie over de Jupiler Pro League.", "jpl matches": "Bekijk de wedstrijden die gespeeld worden op [Week]. Default naar de huidige speeldag.", "jpl table": "De huidige stand van het klassement.", - "launch": "Tijdstip van de volgende SpaceX lancering.", "leaderboard": "Bekijk de Top 10 van [Categorie].\nIndien je geen categorie opgeeft krijg je een lijst van categorieën.", "les": "Bekijk het lessenrooster voor [Dag] in het [Jaargang]-de jaar.\nIndien je geen dag opgeeft, is dit standaard vandaag. De jaargang is standaard 2.\nLes Morgen/Overmorgen werkt ook.", "lmgtfy": "Stuur iemand een LMGTFY link wanneer ze je een domme vraag stellen in plaats van het zelf op te zoeken.\nQueries met spaties moeten **niet** tussen aanhalingstekens staan.", diff --git a/functions/timeFormatters.py b/functions/timeFormatters.py index 54c4663..b79dd74 100644 --- a/functions/timeFormatters.py +++ b/functions/timeFormatters.py @@ -1,8 +1,7 @@ import datetime -import time - import dateutil.relativedelta import pytz +import time def epochToDate(epochTimeStamp, strFormat="%d/%m/%Y om %H:%M:%S"): diff --git a/functions/ufora_notifications.py b/functions/ufora_notifications.py new file mode 100644 index 0000000..26a5419 --- /dev/null +++ b/functions/ufora_notifications.py @@ -0,0 +1,67 @@ +import re + +import feedparser +from data.embeds import UforaNotification +import json + + +course_urls = { + "Algoritmen en Datastructuren 2": "https://ufora.ugent.be/d2l/le/news/rss/222018/course?token=aehhv6utkf46t8cc102e0&ou=222018", + "Communicatienetwerken": "https://ufora.ugent.be/d2l/le/news/rss/221184/course?token=aehhv6utkf46t8cc102e0&ou=221184", + "Computerarchitectuur": "https://ufora.ugent.be/d2l/le/news/rss/228912/course?token=aehhv6utkf46t8cc102e0&ou=228912", + "Functioneel Programmeren": "https://ufora.ugent.be/d2l/le/news/rss/236396/course?token=aehhv6utkf46t8cc102e0&ou=236396", + "Multimedia": "https://ufora.ugent.be/d2l/le/news/rss/236949/course?token=aehhv6utkf46t8cc102e0&ou=236949", + "Software Engineering Lab 1": "https://ufora.ugent.be/d2l/le/news/rss/235800/course?token=aehhv6utkf46t8cc102e0&ou=235800", + "Statistiek en Probabiliteit": "https://ufora.ugent.be/d2l/le/news/rss/236398/course?token=aehhv6utkf46t8cc102e0&ou=236398", + "Systeemprogrammeren": "https://ufora.ugent.be/d2l/le/news/rss/222035/course?token=aehhv6utkf46t8cc102e0&ou=222035", + "Webdevelopment": "https://ufora.ugent.be/d2l/le/news/rss/223449/course?token=aehhv6utkf46t8cc102e0&ou=223449", + "Wetenschappelijk Rekenen": "https://ufora.ugent.be/d2l/le/news/rss/236404/course?token=aehhv6utkf46t8cc102e0&ou=236404" +} + + +def run(): + """ + Check for new notifications + """ + # List of new notifications + new_notifications = [] + + # List of old notifications + with open("files/ufora_notifications.json", "r") as fp: + notifications = json.load(fp) + + for course, url in course_urls.items(): + # Automatically append new/missing courses + if course not in notifications: + notifications[course] = [] + + # Get the updated feed + feed = feedparser.parse(url) + + # Filter out old notifications + feed = list(filter(lambda f: _parse_ids(f["id"])[0] not in notifications[course], feed.entries)) + + if feed: + for item in feed: + notif_id, course_id = _parse_ids(item["id"]) + new_notifications.append(UforaNotification(item, course, notif_id, course_id)) + + notifications[course].append(notif_id) + + # Update list of notifications + if new_notifications: + with open("files/ufora_notifications.json", "w") as fp: + json.dump(notifications, fp) + + return new_notifications + + +def _parse_ids(url: str): + match = re.search(r"[0-9]+-[0-9]+$", url) + + if not match: + return None, None + + spl = match[0].split("-") + + return spl[0], spl[1] diff --git a/ignored.md b/ignored.md index 483e923..9ce1bdf 100644 --- a/ignored.md +++ b/ignored.md @@ -102,4 +102,23 @@ Contains the stats to track for gambling games. Weren't made as a PSQL table bec Contains Didier's status message for when he logs in. Keep in mind that his activity is set to `Playing `. This was first used in Didier V1 to show whether or not he was in sandbox mode. - with your Didier Dinks. \ No newline at end of file + with your Didier Dinks. + +### files/ufora_notifications.json + +Stores ID's of all received Ufora notifications. + +```json +{ + "Algoritmen en Datastructuren 2": [], + "Communicatienetwerken": [], + "Computerarchitectuur": [], + "Functioneel Programmeren": [], + "Multimedia": [], + "Software Engineering Lab 1": [], + "Statistiek en Probabiliteit": [], + "Systeemprogrammeren": [], + "Webdevelopment": [], + "Wetenschappelijk Rekenen": [] +} +``` \ No newline at end of file