Merge pull request #42 from stijndcl/ufora_notifications

Ufora notifications, Inspire
pull/45/head
Stijn De Clercq 2021-03-03 18:49:48 +01:00 committed by GitHub
commit 2bc949de83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 231 additions and 9 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@ files/stats.json
files/lost.json files/lost.json
files/locked.json files/locked.json
files/database.json files/database.json
files/ufora_notifications.json
.idea/ .idea/
__pycache__ __pycache__
.env .env

View File

@ -8,6 +8,7 @@ import pytz
import requests import requests
# Temporarily disabled because of API (setup @ bottom)
class Launch(commands.Cog): class Launch(commands.Cog):
def __init__(self, client): def __init__(self, client):
self.client = client self.client = client
@ -51,4 +52,5 @@ class Launch(commands.Cog):
def setup(client): def setup(client):
client.add_cog(Launch(client)) pass
# client.add_cog(Launch(client))

View File

@ -6,6 +6,7 @@ from discord.ext import commands
from enums.help_categories import Category from enums.help_categories import Category
from functions import checks, clap, mock, sunrise, timeFormatters from functions import checks, clap, mock, sunrise, timeFormatters
import pytz import pytz
from requests import get
import time import time
import urllib.parse import urllib.parse
@ -101,7 +102,7 @@ class Oneliners(commands.Cog):
@commands.command(name="Todo", aliases=["List", "Td"]) @commands.command(name="Todo", aliases=["List", "Td"])
@help.Category(category=Category.Didier) @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") await ctx.send("https://trello.com/b/PdtsAJea/didier-to-do-list")
@commands.command(name="LMGTFY", aliases=["Dsfr"], usage="[Query]") @commands.command(name="LMGTFY", aliases=["Dsfr"], usage="[Query]")
@ -130,7 +131,7 @@ class Oneliners(commands.Cog):
await ctx.send("Shut, it already is.") await ctx.send("Shut, it already is.")
@commands.command() @commands.command()
async def sc(self, ctx, *args): async def sc(self, ctx):
await ctx.send("http://take-a-screenshot.org/") await ctx.send("http://take-a-screenshot.org/")
@commands.command(aliases=["os", "sauce", "src"]) @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())) await ctx.send(":sunny:: **{}**\n:crescent_moon:: **{}**".format(s.sunrise(), s.sunset()))
@commands.command(name="Tias", aliases=["TryIt"]) @commands.command(name="Tias", aliases=["TryIt"])
async def tias(self, ctx, *args): async def tias(self, ctx):
await ctx.send("***Try it and see***") 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): def setup(client):
client.add_cog(Oneliners(client)) client.add_cog(Oneliners(client))

View File

@ -6,6 +6,7 @@ from functions import timeFormatters
from functions.config import config from functions.config import config
from functions.database import currency, poke, prison, birthdays, stats from functions.database import currency, poke, prison, birthdays, stats
from functions.scraping import getMatchweek from functions.scraping import getMatchweek
from functions import ufora_notifications
import json import json
import random import random
import requests import requests
@ -24,6 +25,7 @@ class Tasks(commands.Cog):
self.updateMessageCounts.start() self.updateMessageCounts.start()
self.sendReminders.start() self.sendReminders.start()
self.updateMatchweek.start() self.updateMatchweek.start()
self.uforaAnnouncements.start()
@tasks.loop(hours=1.0) @tasks.loop(hours=1.0)
async def bankInterest(self): async def bankInterest(self):
@ -238,6 +240,24 @@ class Tasks(commands.Cog):
async def beforeUpdateMatchweek(self): async def beforeUpdateMatchweek(self):
await self.client.wait_until_ready() 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): def getCurrentHour(self):
return timeFormatters.dateTimeNow().hour return timeFormatters.dateTimeNow().hour

View File

@ -14,11 +14,13 @@ botIDs = [
"728361496874057812" "728361496874057812"
] ]
BugReports = "762668401960812554" BugReports = "762668401960812554"
BotTesting = "679701786189103106"
CallOfCode = "626699611192688641" CallOfCode = "626699611192688641"
CoCGeneral = "626699611813314561" CoCGeneral = "626699611813314561"
DeZandbak = "728361030404538488" DeZandbak = "728361030404538488"
ErrorLogs = "762668505455132722" ErrorLogs = "762668505455132722"
FeatureRequests = "762668473313787964" FeatureRequests = "762668473313787964"
ZandbakSpeeltuin = "769248992957038612"
mods = { mods = {
626699611192688641: [384457911377854467, 171671190631481345], 626699611192688641: [384457911377854467, 171671190631481345],
@ -53,7 +55,12 @@ faq_channels = {
727876797458284584: "funcprog", 727876797458284584: "funcprog",
727876819264733244: "statprob", 727876819264733244: "statprob",
727876836587208714: "sysprog", 727876836587208714: "sysprog",
676713433567199232: "didier" 676713433567199232: "didier",
807566495550013460: "comparch",
807567007355895838: "multimedia",
807567261216538644: "sel1",
807567345484169237: "webdev",
807567387775336499: "wetrek"
} }

View File

@ -0,0 +1 @@
from .ufora import UforaNotification

View File

@ -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;": "'",
">": ">",
"&lt;": "<",
# MARKDOWN SUPPORT:
"<b>": "**",
"</b>": "**",
"<strong>": "**",
"</strong>": "**",
"<i>": "*",
"</i>": "*",
"<em>": "*",
"</em>": "*",
"<del>": "~~",
"</del>": "~~",
"<ins>": "__",
"</ins>": "__",
# Represent paragraphs with newlines
"</p>": "\n",
"<br>": "\n",
"<br/>": "\n",
"<br />": "\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)
)

View File

@ -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 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.", "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.", "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.", "inventory": "Bekijk de items in jouw inventory.",
"invest": "Investeer [Aantal] Didier Dinks in jouw Didier Bank om rente te vergaren.", "invest": "Investeer [Aantal] Didier Dinks in jouw Didier Bank om rente te vergaren.",
"jpl": "Informatie over de Jupiler Pro League.", "jpl": "Informatie over de Jupiler Pro League.",
"jpl matches": "Bekijk de wedstrijden die gespeeld worden op [Week]. Default naar de huidige speeldag.", "jpl matches": "Bekijk de wedstrijden die gespeeld worden op [Week]. Default naar de huidige speeldag.",
"jpl table": "De huidige stand van het klassement.", "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.", "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.", "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.", "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.",

View File

@ -1,8 +1,7 @@
import datetime import datetime
import time
import dateutil.relativedelta import dateutil.relativedelta
import pytz import pytz
import time
def epochToDate(epochTimeStamp, strFormat="%d/%m/%Y om %H:%M:%S"): def epochToDate(epochTimeStamp, strFormat="%d/%m/%Y om %H:%M:%S"):

View File

@ -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]

View File

@ -103,3 +103,22 @@ 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. 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. 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": []
}
```