Start version control for Frokostbot
Including a roadmap, which also acts as sort of a TODO. At this point, the MVP is successfully running.
This commit is contained in:
commit
8d49e24ed3
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.env
|
||||||
|
frokostbot.sh
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
browser-cookie3==0.17.0
|
||||||
|
requests==2.28.2
|
20
roadmap.md
Normal file
20
roadmap.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Frokostbot Roadmap
|
||||||
|
|
||||||
|
## Release an MVP that post today's menu to Slack.
|
||||||
|
Requirements:
|
||||||
|
[x] Extract menu from SharePoint.
|
||||||
|
[x] Process menu to improve presentation with emojis.
|
||||||
|
[x] Post menu to Slack.
|
||||||
|
|
||||||
|
## Improve the post by including a photo of the menu.
|
||||||
|
Requirements:
|
||||||
|
[ ] Ask Google Translate to translate menu from Danish to English.
|
||||||
|
[ ] Ask Crayion to generate images of the menu in English.
|
||||||
|
[ ] Include a random image from the Crayion results as an attachment in
|
||||||
|
the Slack post.
|
||||||
|
|
||||||
|
## Improve the post with an extended description.
|
||||||
|
Requirements:
|
||||||
|
[ ] Ask ChatGPT (or similar service) to provide a ~50 word, mouthwatering
|
||||||
|
presentation of today's menu.
|
||||||
|
[ ] Include the presentation in the Slack post.
|
171
slack.py
Normal file
171
slack.py
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
"""
|
||||||
|
Post today's menu to Slack.
|
||||||
|
|
||||||
|
This is done by parsing the JSON data from SharePoint.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import random
|
||||||
|
from urllib import error, parse, request
|
||||||
|
|
||||||
|
import browser_cookie3
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
# Load configuration.
|
||||||
|
try:
|
||||||
|
IS_LIVE = bool(os.environ.get("IS_LIVE"))
|
||||||
|
SLACK_ERROR_HOOK = os.environ["SLACK_ERROR_HOOK"]
|
||||||
|
SLACK_SUCCESS_HOOK = (
|
||||||
|
os.environ["SLACK_SUCCESS_HOOK"] if IS_LIVE else SLACK_ERROR_HOOK
|
||||||
|
)
|
||||||
|
SHAREPOINT_URL = os.environ["SHAREPOINT_URL"]
|
||||||
|
MENU_URL = os.environ["MENU_URL"]
|
||||||
|
except KeyError as e:
|
||||||
|
sys.stderr.write(f"Unable to load configuration for {e}.\n")
|
||||||
|
sys.exit(1)
|
||||||
|
sys.stdout.write("Configuration loaded.\n")
|
||||||
|
|
||||||
|
|
||||||
|
# Set up and execute request to SharePoint.
|
||||||
|
cookies = browser_cookie3.firefox(
|
||||||
|
domain_name="sharepoint.com"
|
||||||
|
)
|
||||||
|
headers = {
|
||||||
|
"Accept": "application/json;odata=verbose",
|
||||||
|
"Content-Type": "application/json;odata=verbose",
|
||||||
|
}
|
||||||
|
request_data = {
|
||||||
|
"parameters": {
|
||||||
|
"__metadata": {
|
||||||
|
"type": "SP.RenderListDataParameters"
|
||||||
|
},
|
||||||
|
"AddRequiredFields": True,
|
||||||
|
"AllowMultipleValueFilterForTaxonomyFields": True,
|
||||||
|
"FilterOutChannelFoldersInDefaultDocLib": True,
|
||||||
|
"RenderOptions": 5707271,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = requests.post(
|
||||||
|
SHAREPOINT_URL,
|
||||||
|
headers=headers,
|
||||||
|
cookies=cookies,
|
||||||
|
json=request_data,
|
||||||
|
)
|
||||||
|
data = response.json()
|
||||||
|
sys.stdout.write("Data retrieved from SharePoint.\n")
|
||||||
|
|
||||||
|
# Extract today's menu from the SharePoint data.
|
||||||
|
today = datetime.date.today().strftime("%d-%m-%Y")
|
||||||
|
menu = None
|
||||||
|
if "error" in data:
|
||||||
|
message = plain_message = f"Error: {data['error']['message']['value']}"
|
||||||
|
hook_url = SLACK_ERROR_HOOK
|
||||||
|
sys.stderr.write(f"SharePoint {message}\n")
|
||||||
|
else:
|
||||||
|
hook_url = SLACK_SUCCESS_HOOK
|
||||||
|
for entry in data["ListData"]["Row"]:
|
||||||
|
if entry.get("DAto") == today:
|
||||||
|
menu = entry
|
||||||
|
break
|
||||||
|
|
||||||
|
if menu is None:
|
||||||
|
sys.stderr.write("Unable to find the menu of today :(\n")
|
||||||
|
sys.exit(1)
|
||||||
|
menu = menu["Menutekst"]
|
||||||
|
sys.stdout.write(f"The menu of today ({today}) is: {menu}\n")
|
||||||
|
|
||||||
|
# Determine appropriate emojis for the menu.
|
||||||
|
emojis = []
|
||||||
|
for emoji, keywords in {
|
||||||
|
"flag-in": ["indisk", "indien"],
|
||||||
|
"hamburger": ["burger"],
|
||||||
|
"fish": ["fisk", "laks", "rødspætte", "sej "],
|
||||||
|
"shrimp": ["reje"],
|
||||||
|
"pig2": ["skinke", "gris"],
|
||||||
|
"cow": ["hakkebøf"],
|
||||||
|
"cow2": ["kalv", "okse"],
|
||||||
|
"chicken": ["kylling", "chicken"],
|
||||||
|
"turkey": ["kalkun"],
|
||||||
|
"rabbit2": [" hare"],
|
||||||
|
"rooster": ["coq au vin"],
|
||||||
|
"falafel": ["falafel"],
|
||||||
|
"hot_pepper": ["chili", "hot sauce"],
|
||||||
|
"onion": ["løg"],
|
||||||
|
"mango": ["mango"],
|
||||||
|
"mushroom": ["svampe", "kantarel", "champignon"],
|
||||||
|
"beans": ["bønne"],
|
||||||
|
"sandwich": ["sandwich"],
|
||||||
|
"stuffed_flatbread": ["pita"],
|
||||||
|
"pie": ["tærte"],
|
||||||
|
"hotdog": ["hotdog"],
|
||||||
|
"wine_glass": ["coq au vin"],
|
||||||
|
"bowl_with_spoon": ["suppe"],
|
||||||
|
"stew": ["gryde", "gullasch"],
|
||||||
|
"rice": [" ris", "ris "],
|
||||||
|
"ramen": ["nudler"],
|
||||||
|
"cloud": ["sky"],
|
||||||
|
"potato": ["kartoffel", "kartofler"],
|
||||||
|
"apple": ["æble"],
|
||||||
|
"baguette_bread": ["flute"],
|
||||||
|
"flag-dk": ["tillykke", "fødselsdag"],
|
||||||
|
"wave": ["farvel"],
|
||||||
|
"gift_heart": ["valentines"],
|
||||||
|
}.items():
|
||||||
|
for keyword in keywords:
|
||||||
|
if keyword in menu.lower():
|
||||||
|
emojis.append(emoji)
|
||||||
|
break
|
||||||
|
if emojis:
|
||||||
|
emojis = f":{': :'.join(emojis)}:"
|
||||||
|
sys.stdout.write(f"Emojis determined: {emojis}\n")
|
||||||
|
else:
|
||||||
|
emojis = ""
|
||||||
|
sys.stdout.write("No emojis determined.\n")
|
||||||
|
|
||||||
|
# Pick an introduction for the menu.
|
||||||
|
introduction = random.choice([
|
||||||
|
"Dagens menu er",
|
||||||
|
"I dag forkæler kantinen os med",
|
||||||
|
"Du kan godt glæde dig til senere! For vi skal have",
|
||||||
|
"Der bliver knoklet i køkkenet for at blive klar til at servere",
|
||||||
|
"Klokken 11:30 har kantinen fremtryllet en lækker omgang",
|
||||||
|
])
|
||||||
|
sys.stdout.write(f"Introduction picked: {introduction}\n")
|
||||||
|
|
||||||
|
# Compose message for Slack.
|
||||||
|
plain_message = f"{introduction} {menu}"
|
||||||
|
menu_url = MENU_URL
|
||||||
|
message = f"{introduction} {emojis} <{menu_url}|*{menu}*>".strip()
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"text": plain_message,
|
||||||
|
"blocks": [
|
||||||
|
{
|
||||||
|
"type": "section",
|
||||||
|
"text": {
|
||||||
|
"type": "mrkdwn",
|
||||||
|
"text": message,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
sys.stdout.write("Posting menu to Slack...\n")
|
||||||
|
hook = request.Request(
|
||||||
|
hook_url,
|
||||||
|
data=json.dumps(payload).encode("utf-8"),
|
||||||
|
headers={
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
method="POST",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
response = request.urlopen(hook)
|
||||||
|
except error.HTTPError as e:
|
||||||
|
sys.stderr.write(f"{e}\n")
|
||||||
|
sys.exit(1)
|
||||||
|
sys.stdout.write("Done :)\n")
|
Loading…
Reference in a new issue