#!/usr/bin/env python3 # # Copyright 2009 Facebook # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import asyncio import os.path import tornado.auth import tornado.escape import tornado.httpserver import tornado.options import tornado.web from tornado.options import define, options define("port", default=8888, help="run on the given port", type=int) define("facebook_api_key", help="your Facebook application API key", type=str) define("facebook_secret", help="your Facebook application secret", type=str) class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/", MainHandler), (r"/auth/login", AuthLoginHandler), (r"/auth/logout", AuthLogoutHandler), ] settings = dict( cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__", login_url="/auth/login", template_path=os.path.join(os.path.dirname(__file__), "templates"), static_path=os.path.join(os.path.dirname(__file__), "static"), xsrf_cookies=True, facebook_api_key=options.facebook_api_key, facebook_secret=options.facebook_secret, ui_modules={"Post": PostModule}, debug=True, autoescape=None, ) tornado.web.Application.__init__(self, handlers, **settings) class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): user_json = self.get_secure_cookie("fbdemo_user") if not user_json: return None return tornado.escape.json_decode(user_json) class MainHandler(BaseHandler, tornado.auth.FacebookGraphMixin): @tornado.web.authenticated async def get(self): stream = await self.facebook_request( "/me/home", self._on_stream, access_token=self.current_user["access_token"] ) if stream is None: # Session may have expired self.redirect("/auth/login") return self.render("stream.html", stream=stream) class AuthLoginHandler(BaseHandler, tornado.auth.FacebookGraphMixin): async def get(self): my_url = ( self.request.protocol + "://" + self.request.host + "/auth/login?next=" + tornado.escape.url_escape(self.get_argument("next", "/")) ) if self.get_argument("code", False): user = await self.get_authenticated_user( redirect_uri=my_url, client_id=self.settings["facebook_api_key"], client_secret=self.settings["facebook_secret"], code=self.get_argument("code"), ) self.set_secure_cookie("fbdemo_user", tornado.escape.json_encode(user)) self.redirect(self.get_argument("next", "/")) return self.authorize_redirect( redirect_uri=my_url, client_id=self.settings["facebook_api_key"], extra_params={"scope": "user_posts"}, ) class AuthLogoutHandler(BaseHandler, tornado.auth.FacebookGraphMixin): def get(self): self.clear_cookie("fbdemo_user") self.redirect(self.get_argument("next", "/")) class PostModule(tornado.web.UIModule): def render(self, post): return self.render_string("modules/post.html", post=post) async def main(): tornado.options.parse_command_line() if not (options.facebook_api_key and options.facebook_secret): print("--facebook_api_key and --facebook_secret must be set") return http_server = tornado.httpserver.HTTPServer(Application()) http_server.listen(options.port) await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main())