Working with Google Appengine and a basic Twitter Bot

By now, some of you who follow me on Twitter will probably know that I made a Twitter Bot called Twendly that tweets the current trends on an hourly basis. For various reasons, mostly because I thought it would be fun, I decided to do this using Googles AppEngine. AppEngine only support Python at the moment, but if you are familiar with VB, C# or any modern programming langauge, you’ll find it a breeze to pick up. The AppEngine also has some very good (if basic) help on getting started. This post assumes that you’ve signed up with AppEngine and have completed all of the getting started tutorials. There are some complete Python Twitter libraries out there, but I couldn’t find one already optimised for App Engine, so I just decided to do the following with help from various web sites and the Twitter API guide.Two small tips that will help with the tutorials:

  1. On windows, I found that AppEngine assumes you have put your application under the AppEngine directory (C:Program FilesGoogleApp_Engine). If you create it anywhere else (e.g. C:myapp) then the correct command to start the local AppEngine is c:myapp
  2. To make starting the server easier, just create a .bat file and put that command in it. Then put a shortcut to the .bat file on your desktop. I also did the same for the application upload command as well update c:myapp

It’s trivial, but it will save you a lot of time remembering commands and looking for a DOS / CMD prompt in Windows.OK, so you’ve now successfully created you first application in AppEngine and want to do something useful. Well here’s a full Python script, incorporating Django templates on how to create a basic Twitter Bot.from google.appengine.ext.webapp import templatefrom google.appengine.ext import webappfrom google.appengine.ext.webapp.util import run_wsgi_appfrom google.appengine.api import urlfetch_DEBUG = True # When this is true, some things go to the screen as text and templates aren't always generated. Error message are more verbose._DEBUG_TWITTERBOT = True # When in debug mode I post to a private twitter account instead of my bot## User name constants_TWITTERBOT_USER = "mytwitterbot" # The username you use in twitter to login to = "mydevtwitterbot" # # The username you use in twitter to login to your "dev" twitter account = "mytwitterbotpwd" # I'm lazy, I use the same password for both accounts, you probably shouldn't do this!## URLS used for Twitter API calls - stored as constants for easy update later._TWIT_UPDATE = ""# I use a base handler and put my Twitter post agent in here so I can call it from any classes that derive this.class BaseRequestHandler(webapp.RequestHandler):  def generate(self, template_name, template_values={}):    # You'll see we are using Django    directory = os.path.dirname(__file__)    path = os.path.join(directory, 'templates', template_name)    self.response.out.write(template.render(path, template_values, debug=_DEBUG))  def TwitterPost(self, msg, username):      password = _TWITTERBOT_PWD      form_fields = {        "status": msg,        "source": "twendly"  # If you register your bot with Twitter, this is how posts know to show "from Twendly" with a link back to the Twendly site.        }      authheader =  "Basic %s" % base64.encodestring('%s:%s' % (username, password))      base64string = base64.encodestring('%s:%s' % (username, password))[:-1]      authheader =  "Basic %s" % base64string      payload = urllib.urlencode(form_fields)      # Note the URL is using HTTPS which is supported by AppEngine so the call should be secured.      url = _TWIT_UPDATE      result = urlfetch.fetch(url=url,payload=payload,method=urlfetch.POST, headers={"Authorization": authheader})      if int(result.status_code) != 200:        # You could get fancy and put some error handling in here if you're inclined.        return int(result.status_code)      return 200class MainPage(BaseRequestHandler):  def get(self):    title = 'Welcome page of your App'    self.generate('index.html', template_values={'title': title})class TwitterBot(BaseRequestHandler):    def get(self):        # Do some stuff - what ever it is you want to tweet about!        msg = "My cool tweet"        If _DEBUG_TWITTER_BOT == true:            username = _TWITTERBOTDEV_USER        else:            username = _TWITTERBOT_USER        response = self.TwitterPost(msg, username)        if response != 200:            status = "Whoops. Twitter was down or your code stuffed up"        else:            status = "Hooray! Tweeted: %s " % msg        self.generate('bot.html', template_values={'status': status})application = webapp.WSGIApplication(                                     [('/', MainPage),                                      ('/twitterbot', twitterbot)],                                     debug=True)def main():  run_wsgi_app(application)if __name__ == "__main__":  main()

You should be able to simply copy the above code and complete the following:

  1. Create the new application ID in Google AppEngine (original in tutorials was “greeting” from memory — make a more meaningful one).
  2. Setup the twitter accounts you need (you’ll need separate mail accounts for each). I always keep my dev one locked so it’s not on public timeline.
  3. Create the basic Django templates, index.html and bot.html — they don’t need to be fancy. They need to be in a sub-directory of your main app called templates. (e.g. C:myapptemplates).
  4. Update your app.yaml file with the ID name and version number (probably 1).
  5. Make the bot do something useful for you — pass messages as parameters or something similar, open a URL and fetch a value (the weather?), you name it, your own Twitter bot to follow.

That should be it, you’ll be able to test it locally and then once you’re happy — push it up to AppEngine and test it out.NB — Remember Twitter won’t let you post exactly the same message twice, so make sure that you change the message each time you test it.In the next post I will talk about ways and means of automating the bot you’ve now created (AppEngine doesn’t have a scheduler), and a couple of pitfalls I discovered along the way.