Jake.codes

fbFoodAlert

Friday, November 21, 2014

Get a custom menu of only the foods you love from the Facebook Culinary Team

Edit the FOODS variable with an easily matchable name of the food you are interested in consuming, and when it’s found in that day’s Facebook Page post, it will appear in a nicely formatted Markdown list.

Compatibilty

Todos

Changelog

#!/usr/local/bin/python

# fbFoodAlert.py
# created by Jake Bilbrey <bilbrey@fb.com>
#
#
# Get a custom menu of only the foods
# you love from the Facebook Culinary Team
#
# Edit the `FOODS` variable with an easily
# matchable name of the food you are interested
# in consuming, and when it's found in that day's
# Facebook Page post, it will appear in a nicely
# formatted Markdown list.
#
#
# Compatibilty
#
# * Your computer with Python 2.7
#   * You may need to run `pip install requests` from Terminal.
#   * Use it with:
#       * Alfred
#       * Automator
#       * Keyboard Maestro
#       * TextExpander (set `OSXAPP` to "textexpander")
#
# * Your iPhone or iPad via Pythonista
#   https://itunes.apple.com/us/app/pythonista/id528579881?mt=8
#   * Use it with:
#      * Drafts 4
#      * Safari: pythonista://fbFoodAlert?action=run
#
#
# TODO:
#
# * Sweet Stop updates on Monday for the week
#
# * Maybe you want to eat a nice food no matter
#   where it is served. Why limit to one place?
#
# * Sweet Stop and Boba shouldn't prevent you
#   from being reminded that the only lunch you
#   want is a salad from Harvest

from datetime import date
import operator
import os
import re
import requests
import sys
import urllib
import webbrowser

#
# Configuration
#

# Your Auth Token
# https://developers.facebook.com/tools/explorer
# (use an app token so it doesn't expire like crazy)
TOKEN = ""

# Facebook Culinary Team page ID
ID = "80518126820"

# Today's Date
TODAY = date.today().isoformat()

# Send Output To
# This is so the same code can be used anywhere
OSXAPP = "nvalt"
IOSAPP = "drafts"

# Resturants
RESTURANTS = [
    # (matchable name, actual name)
    ("livin", "Living the Dream"),
    ("epic", "Cafe Epic"),
    ("burger shack", "Burger Shack"),
    ("bbq shack", "BBQ Shack"),
    ("naan stop", "Naan Stop"),
    ("zeke", "Senor Zekes"),
    ("big tony", "Big Tony"),
    ("noodle bar", "Noodle Bar"),
    ("nacho royale", "Teddys Nacho Royale"),
    ("harvest", "Harvest"),
    ("sweet stop", "Sweet Stop"),
    ("ice cream & froyo", "Sweet Stop"),
]

# Meals
MEALS = [
    # should be matchable
    "breakfast",
    "lunch",
    "super food",
    "dinner",
    "boba",
]

# Foods
FOODS = [
    # (food, resturant match, meal match)
    # leave meal "" for any
    ("brown sugar", "noodle bar", "boba"),
    ("burrito", "zeke", "breakfast"),
    ("chocolate cookie", "epic", ""),
    ("chocolate", "noodle bar", "boba"),
    ("cookies & cream", "sweet stop", ""),
    ("heatstroke club", "zeke", ""),
    ("lava cake", "epic", ""),
    ("meatloaf", "bbq shack", ""),
    ("oreo", "livin", ""),
    ("pomegranate", "epic", ""),
    ("pulled pork", "epic", ""),
    ("refried beans", "epic", ""),
    ("refried beans", "livin", ""),
    ("sandwich", "zeke", "breakfast"),
    ("sea salted caramel", "sweet stop", ""),
    ("shrimp fried rice", "epic", ""),
    ("strawberry field", "epic", ""),
    ("watermelon", "noodle bar", "boba"),
    ("fruit smoothie", "epic", ""),
    ("pork sandwich", "zeke", ""),
]


#
# Functions
#


# Get Posts from Page ID
def getPosts(id, token):
    response = requests.request(
        "GET",
        "https://graph.facebook.com/v2.2/" + id + "/posts?fields=message",
        params={
            "access_token": token
        }
    )
    results = response.json()

    if 'error' not in results:
        results = results['data']

    return results


# Get Resturant from Message
def getResturant(message):
    resturant = ""
    for resturantMatch, resturantName in RESTURANTS:
        if resturantMatch not in message.lower():
            continue

        resturant = resturantName
        break

    return resturant


# Get Meal from Message
def getMeal(message):
    meal = ""
    for mealMatch in MEALS:
        if mealMatch not in message.lower():
            continue

        meal = mealMatch
        break

    return meal


# Get Foods from Message
def getFoods(message, postid):
    food = []
    for foodMatch, resturantMatch, mealMatch in FOODS:
        if foodMatch not in message.lower():
            continue
        if resturantMatch not in message.lower():
            continue
        if not mealMatch == "":
            if mealMatch not in message.lower():
                continue
        else:
            mealMatch = getMeal(message)
        match = (
            getLine(foodMatch, message),
            getResturant(resturantMatch),
            mealMatch,
            postid
        )
        food.append(match)
    return food


# Get Match's Line
def getLine(food, message):
    line = re.search("^(.*)?" + food + "(.*)?$", message, re.I | re.M)
    line = line.group(0)
    line = line.replace("*", "").replace("~", "")
    line = line.strip()
    return line


# Print Results
def formatResults(output):
    formattedOutput = "# fbFoodAlert "
    formattedOutput += date.today().strftime('%b %d %Y') + "\n\n"

    if len(output) == 0:
        formattedOutput += "Nothing today, go to Harvest"
        return formattedOutput

    # Sort by Meal
    output = sorted(output, key=operator.itemgetter(2))
    # Sort by Resturant
    output = sorted(output, key=operator.itemgetter(1))

    lastResturantAndMeal = ""
    for match, resturant, meal, postid in output:
        currentResturantAndMeal = resturant
        if not meal == "":
            currentResturantAndMeal += ", " + meal.title()

        if not lastResturantAndMeal == currentResturantAndMeal:
            if not lastResturantAndMeal == "":
                formattedOutput += "\n\n"
            formattedOutput += "## [" + currentResturantAndMeal + "]"
            formattedOutput += "(https://www.facebook.com/" + postid + ")\n\n"
        formattedOutput += "* " + match.title() + "\n"
        lastResturantAndMeal = currentResturantAndMeal

    return formattedOutput.strip()


# Output In
def outputIn(thing, output):
    if thing == "drafts":
        webbrowser.open(
            "drafts4://x-callback-url/create?text=" +
            urllib.quote(output.encode('utf-8'))
        )
    elif thing == "nvalt":
        webbrowser.open(
            "nvalt://make/?txt=" +
            urllib.quote(output.encode('utf-8'))
        )
    elif thing == "textexpander":
        sys.stdout.write(output)
    else:
        print output


#
# Main
#
def main():
    posts = getPosts(ID, TOKEN)

    if 'error' not in posts:
        output = []
        for post in posts:
            if not post["created_time"].startswith(TODAY):
                continue
            if 'message' not in post:
                continue
            results = getFoods(post['message'], post['id'])
            for result in results:
                output.append(result)

        if os.uname()[4][0] == "i":
            outputIn(IOSAPP, formatResults(output))
        else:
            outputIn(OSXAPP, formatResults(output))
    else:
        print 'Error: ' + posts['error']['message']


if __name__ == '__main__':
    main()