This is an old revision of the document!
Table of Contents
WeatherBot
This is a simple bot script, written in Python and based on the PyPump pump.io toolkit. It looks at the inbox of a pump.io account (e.g. https://hub.polari.us/weather) and looks for new major messages. If the message contains a viable “City,Region” string, the weather information for that location will be returned by the bot as a reply to the original inbox message.
This script uses the http://openweathermap.org/api OpenWeatherMap API.
The logo is derived from two OpenClipArt.org images: http://openclipart.org/detail/191072/blue-robot-by-scout-191072 (Blue Robot, by Scout http://openclipart.org/user-detail/Scout), and http://openclipart.org/detail/190891/clouds-by-arking-190891 (Clouds by Arking http://openclipart.org/user-detail/arking)
License
Copyright 2014, Stephen Jacob Sekula https://hub.polari.us/steve
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.
External Software Dependencies
- LYNX (for converting HTML to plain text - some pump clients don't send messages in HTML, some do… this saved me having to write my own converter)
Source Code
#!/usr/bin/env python # Originally authored by Stephen Sekula (https://hub.polari.us/steve) # Distributed under an Apache 2.0 license # For more information ,see # http://polari.us/dokuwiki/doku.php?id=weatherbot#license # import re import pycurl import cStringIO import json import sys import time import subprocess import os from pypump import PyPump, Client from pypump.models.image import Image from pypump.models.collection import Collection from pypump.models.collection import Public from pypump.models.person import Person from pypump.exception import PyPumpException # city,region search pattern citypattern = re.compile('.*?([A-Za-z]+,.*)',re.DOTALL) # Place the credentials below for the pump.io account # These can be obtained by using: http://polari.us/dokuwiki/doku.php?id=navierstokes#a_simple_program_to_authenticate_pypump_against_your_pumpio_instance client_credentials = [] client_tokens = [] # Webfinger for your account webfinger = "me@example.com" # Important: define a log file that contains a list of activities to which # we have already responded. logfile_name = "weatherbot.activity.log" def simple_verifier(url): print 'Go to: ' + url return raw_input('Verifier: ') # they will get a code back def HTMLToText( html_text ): pid = os.getpid() htmlfile = open('/tmp/%d_msg.html' % (pid),'w') try: htmlfile.write( html_text ) except UnicodeEncodeError: htmlfile.write( unicodedata.normalize('NFKD', html_text).encode('ascii','ignore') ) pass htmlfile.close() try: ascii_text = subprocess.check_output(["lynx", "--dump", "-width 2048", "-nolist", "/tmp/%d_msg.html" % (pid)]) except subprocess.CalledProcessError: print "There was a problem trying to call lynx - make sure it is installed correctly." return html_text return ascii_text def reply(activity,my_reply): try: activity.obj.comment(my_reply) except PyPumpException: print " ... PyPumpException - ERROR - I will try to get this request next time!" return False pass return True client = Client( webfinger, name="PyPump", type="native", key=client_credentials[0], # client key secret=client_credentials[1] # client secret ) # archive of activities I have already processed process_log = open(logfile_name,'a+') while 1==1: pump = PyPump( client=client, token=client_tokens[0], # the token key secret=client_tokens[1], # the token secret verifier_callback=simple_verifier ) my_inbox = pump.me.inbox for activity in my_inbox.major[:100]: author = None to = None cc = None content = None id = None try: author = activity.obj.author to = getattr(activity, "to", []) cc = getattr(activity, "cc", []) content = activity.obj.content id = activity.obj.id except AttributeError: continue if content == None: continue for recipient in to: if isinstance(recipient, Person): if recipient.webfinger == pump.client.webfinger: # check to see if we already handled this request do_i_respond = True process_log.seek(0) for processed_id in process_log: processed_id = processed_id.rstrip() if activity.obj.id == processed_id: do_i_respond = False pass pass if not do_i_respond: continue # handle this content text_content = HTMLToText(content) search_result = citypattern.match(text_content) if search_result: print "%s sent a weather request, which I will now process..." % (activity.obj.author) cityrequest = search_result.group(1) # clean up poor formatting in content cityrequest = cityrequest.replace(", ", ",") # send city information to openweathermap.org curl_tries = 0 curl_success = False curl_buffer = cStringIO.StringIO() while curl_success == False and curl_tries < 5: c = pycurl.Curl() c.setopt(c.URL, 'http://api.openweathermap.org/data/2.5/weather?q=%s' % (cityrequest)) c.setopt(c.WRITEFUNCTION, curl_buffer.write) try: c.perform() except pycurl.error: curl_tries += 1 time.sleep(10) pass curl_success = True c.close() if curl_success == False: response = "<p>There was a problem getting the response from OpenWeatherMaps.org. It could be them; or, it could be that you did not give me a valid \"City,Region\". Please try again.</p>" my_reply = pump.Comment(response) success = reply(activity,my_reply) if success: process_log.write(activity.obj.id+"\n") print " ... user did not send a valid City,Region pair or there is a problem with OpenWeatherMaps.org." else: continue pass # Try loading the data from the JSON try: weather_data = json.loads(curl_buffer.getvalue()) curl_buffer.close() except ValueError: response = "<p>There was a problem getting the response from OpenWeatherMaps.org. It could be them; or, it could be that you did not give me a valid \"City,Region\" pair from you. Please try again.</p>" my_reply = pump.Comment(response) success = reply(activity,my_reply) curl_buffer.close() process_log.write(activity.obj.id+"\n") print " ... your request resulted in a bad response by the OpenWeatherMap.org API. Check it for funny business. Sorry!" continue try: humidity = weather_data['main']['humidity'] temp_kelvin = weather_data['main']['temp'] temp_celsius = temp_kelvin - 273.15 temp_farenheit = temp_celsius*1.8 + 32.0 wind_speed_kmh = weather_data['wind']['speed'] wind_speed_mph = wind_speed_kmh/0.6214 response = "" response += "<p>The weather in %s is currently: </p>\n" % weather_data["name"] response += "<p><img src=\"http://openweathermap.org/img/w/%s.png\"/></p>" % (weather_data['weather'][0]['icon']) response += "<ul>\n" response += "<li> Temperature: %0.1fC (%0.1fF) </li>\n" % (temp_celsius, temp_farenheit) response += "<li> Humidity: %0.1f%% </li> \n" % (humidity) response += "<li> Wind Speed: %.1fkm/h (%.1fmph) </li>\n" % (wind_speed_kmh,wind_speed_mph) response += "<li> Conditions: %s - %s</li> \n" % (weather_data['weather'][0]['main'], weather_data['weather'][0]['description']) response += "</ul>\n" my_reply = pump.Comment(response) my_reply.cc = cc try: activity.obj.comment(my_reply) process_log.write(activity.obj.id+"\n") print " ... successfully handled the request and responded" except PyPumpException: print " ... PyPumpException - ERROR - I will try to get this request next time!" continue except KeyError: response = "<p>There was a problem getting the response from OpenWeatherMaps.org. It could be them; or, it could be that you did not give me a valid \"City,Region\" string. Please try again.</p>" my_reply = pump.Comment(response) success = reply(activity,my_reply) curl_buffer.close() process_log.write(activity.obj.id+"\n") print " ... got bad data from the API, it seems. Letting the user know." continue pass pass pass pass pass pass print "Sleeping until next cycle..." time.sleep(30) process_log.close()