MensaBot - A Slack Bot For Lunch Information
Everyday at work around noon the question of where to get lunch comes up. Normally, we choose between different restataurants in the vicinity of the office. One exception is the HU Mensa (university cafeteria). Despite being really cheap the food quality there varies a lot and it really depends on the daily menu whether a visit is worthwhile.
To tackle this issue I decided to spent another IT Open Space putting together a little script that will help us in the future.
Poking around with the official iOS Mensa app I figured out there is an RSS-Feed that provides the weekly menu in a machine readable format. It was time to extract the desired data. It would have been nicer if the feed was in a proper format like XML but they decided to push HTML to the application. Therefore, I had to extract everything I needed using XPath and regular expressions:
import json
import re
import feedparser as fp
from datetime import datetime
from lxml import etree, html
from flask import Flask, jsonify, Response, request
COLORS = {'rot': 'danger', 'orange': 'warning', 'gruen': 'good'}
def get_mensa_food():
f = fp.parse('http://www.studentenwerk-berlin.de/speiseplan/rss/hu_nord/tag/kurz/0')
tree = html.fromstring(f['entries'][0]['summary'])
food_items = tree.xpath('//*[contains(concat( " ", @class, " " ), concat( " ", "mensa_day_speise_name", " " ))]')
food_prices = tree.xpath('//*[contains(concat( " ", @class, " " ), concat( " ", "mensa_day_speise_preis", " " ))]')
food_list = [{'color': COLORS[x[0].xpath('./a/@href')[0].split('_')[1]],
'text': (re.sub('\d{2,}\w*\d*', '', x[0].text_content().strip())).rstrip() + ' '
+ x[1].text_content().rstrip()} for x in list(zip(food_items, food_prices))]
t = 'Today'
if datetime.now().hour >= 16:
t = 'Tomorrow'
if datetime.now().weekday() == 4:
t = 'Monday'
payload = {"response_type": "in_channel",
"text": '{} at Mensa'.format(t),
'attachments': food_list}
resp = Response(response=json.dumps(payload, ensure_ascii=False),
status=200, mimetype="application/json")
return resp
The script fetches the feed data, gets the food items and some additional information like prices and nutritional values. If the script is executed after 4 p.m. it will show the food offering for the next day. On Friday after 4 p.m. it will return the menu for the upcoming monday.
After I got the script working I decided to wrap it with a little Flask application so I could trigger it from within Slack:
@app.route('/mensa', methods=['GET'])
def get_mensa_food():
.
.
.
if __name__ == '__main__':
app.run()
I deployed the app on a free Heroku dyno and created a Slack Slash Command that would respond to /mensa
. The result would look like the following:
One caveat with the free Heroku solution though is that Heroku puts their (non-paid) dynos to sleep after a certain time of inactivity. The result is that ususally the first call to the API will result in a timeout. With a paid dyno this would not a be an issue.
The whole code is also on Github