# -*- coding: utf-8 -*- import facebook import pprint from dateutil.parser import parse import re graphs = {} #user_infos = {} #albums = {} def get_url(config, url): match = re.match(r"^(https?://)?(?:\w+\.)?facebook.com/(?P<user>[^/]*)/?(?P<photos>photos)?(/.*?)?$", url) if match is None: return if match.group("photos"): return "/photos/" + match.group("user") return "/user/" + match.group("user") def get_api(access_token): global graphs if access_token not in graphs: graphs[access_token] = facebook.GraphAPI(access_token) return graphs[access_token] def get_user_info(graph, user): #global user_infos user_infos = {} if user not in user_infos: user_infos[user] = graph.get_object(user + "?fields=name,username,about,description,link") #user_infos[user] = graph.get_object(user + "?fields=name,about,link") if "username" not in user_infos and "name" in user_infos: user_infos["username"] = user_infos["name"] return user_infos[user] def get_feed_info(user_info, config): username = user_info.get("username", user_info.get("name", None)) if not config["author_username"] and "name" in user_info and len(user_info["name"]) > 0: user = user_info["name"] else: user = username if "about" in user_info: description = user_info["about"] elif "description" in user_info: description = user_info["description"] else: description = user + "'s facebook" return { "title": user, "description": description, "author": username, "url": user_info["link"], "social": True, "entries": [] } def get_albumid_from_link(link): return re.match(r".*facebook\.com/[^/]*/photos/a\.([0-9]*).*", link).group(1) def generate_photo_entries(graph, config, user_info): #global albums albums = {} entries = [] photos_api = graph.get_connections(user_info['id'], 'photos/uploaded?limit=100&fields=link,name,updated_time,images') photos = photos_api["data"] unnamed_ids = {} username = user_info.get("username", user_info.get("name", None)) for photo in photos: albumid = get_albumid_from_link(photo["link"]) if albumid not in albums: albums[albumid] = graph.get_object(albumid + "?fields=name,created_time") album = albums[albumid] if albumid not in unnamed_ids: unnamed_ids[albumid] = {} album_unnamed = unnamed_ids[albumid] albumname = album["name"] albumdate = parse(album["created_time"]) albumdatestr = str(albumdate.year)[-2:] + str(albumdate.month).zfill(2) + str(albumdate.day).zfill(2) albumdatestr += ":" albumdatestr += str(albumdate.hour).zfill(2) + str(albumdate.minute).zfill(2) + str(albumdate.second).zfill(2) newalbumname = "(" + albumdatestr + ") " + albumname date = parse(photo["updated_time"]) image = photo["images"][0]["source"] if "name" in photo: caption = photo["name"] media_caption = caption else: caption = "" if photo["updated_time"] in album_unnamed: unnamed_id = album_unnamed[photo["updated_time"]] else: album_unnamed[photo["updated_time"]] = 0 unnamed_id = 0 media_caption = "unnamed " + str(unnamed_id) album_unnamed[photo["updated_time"]] += 1 entries.append({ "url": photo["link"], "caption": caption, "media_caption": media_caption, "date": date, "album": newalbumname, "author": username, "images": [image], "videos": [] }) return entries def generate_post_entries(graph, config, user_info): entries = [] posts_api = graph.get_connections(user_info['id'], 'posts?limit=100&fields=id,message,created_time,updated_time,permalink_url,picture') posts = posts_api["data"] username = user_info.get("username", user_info.get("name", None)) for post in posts: if "message" in post: caption = post["message"] else: caption = "" media_caption = "[POST] " + caption url = None # post["permalink_url"] if "permalink_url" not in post or not url or len(url) <= 0: url = "https://www.facebook.com/" + username + "/posts/" + post["id"].split("_")[1] else: url = post["permalink_url"] date = parse(post["created_time"]) updated_date = parse(post["updated_time"]) images = [] if "picture" in post: images = [post["picture"]] entries.append({ "url": url, "author": username, "caption": caption, "media_caption": media_caption, "date": date, "updated_date": updated_date, "images": images, "videos": [], "album": "Posts" }) return entries def process(server, config, path): if path.startswith("/access_app"): access_token = facebook.GraphAPI().get_app_access_token( config["app_id"], config["app_secret"] ) server.send_response(200, "OK") server.end_headers() server.wfile.write(bytes(access_token, "UTF-8")) return True if path.startswith("/access_2"): if "code" not in config: server.send_response(500, "ISE") server.end_headers() server.wfile.write(bytes("Need code", "UTF-8")) return True server.send_response(200, "OK") server.end_headers() token = facebook.GraphAPI().get_access_token_from_code( config["code"], config["redirect_url"], config["app_id"], config["app_secret"] ) server.wfile.write(bytes(token["access_token"], "UTF-8")) return True if path.startswith("/access"): url = "https://www.facebook.com/v2.8/dialog/oauth?client_id=%s&redirect_uri=%s" % ( config["app_id"], config["redirect_url"] ) server.send_response(301, "Moved") server.send_header("Location", url) server.end_headers() return True graph = get_api(config["access_token"]) if not graph: return None if path.startswith("/user/"): user = path[len("/user/"):] user_info = get_user_info(graph, user) feed = get_feed_info(user_info, config) feed["entries"] = generate_post_entries(graph, config, user_info) feed["entries"].extend(generate_photo_entries(graph, config, user_info)) return ("social", feed) if path.startswith("/photos/"): user = path[len("/photos/"):] user_info = get_user_info(graph, user) feed = get_feed_info(user_info, config) feed["entries"] = generate_photo_entries(graph, config, user_info) return ("social", feed) elif path.startswith("/posts/"): user = path[len("/posts/"):] user_info = get_user_info(graph, user) feed = get_feed_info(user_info, config) feed["entries"] = generate_post_entries(graph, config, user_info) return ("social", feed) def generate_user(server, config, user): graph = get_api(config["access_token"]) if not graph: return None user_info = get_user_info(graph, user) feed = get_feed_info(user_info, config) feed["entries"] = [] if config["posts"]: feed["entries"].extend(generate_post_entries(graph, config, user_info)) if config["photos"]: feed["entries"].extend(generate_photo_entries(graph, config, user_info)) return ("social", feed) def generate_access_app(server, config, path): if not config["app_id"] or not config["app_secret"]: server.send_response(500, "Error") server.end_headers() server.wfile.write(bytes("app_id or app_secret not set", "UTF-8")) return True access_token = facebook.GraphAPI().get_app_access_token( config["app_id"], config["app_secret"] ) server.send_response(200, "OK") server.end_headers() server.wfile.write(bytes(access_token, "UTF-8")) return True infos = [{ "name": "facebook", "display_name": "Facebook", "endpoints": { "user": { "name": "User's feed (photos and posts)", "process": generate_user }, "access_app": { "name": "Generate access token for app", "process": generate_access_app } }, "config": { "author_username": { "name": "Author = Username", "description": "Set the author's name to be their username", "value": False }, "access_token": { "name": "Access Token", "description": "Facebook Access Token", "value": "" }, "app_id": { "name": "App ID", "description": "Facebook App ID", "value": "" }, "app_secret": { "name": "App Secret", "description": "Facebook App Secret", "value": "" }, "photos": { "name": "Generate photos", "value": True }, "posts": { "name": "Generate posts", "value": True }, "redirect_url": { "name": "Redirect URL", "description": "Facebook App Redirect URL", "value": "" # /f/facebook/access_2 } }, "get_url": get_url, "process": process }]