weatherbot
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
weatherbot [2014/08/23 17:30] – [Source Code] sekula | weatherbot [2014/08/31 03:23] (current) – [Talking to WeatherBot] sekula | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== WeatherBot ====== | ====== 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:// | + | {{ : |
This script uses the [[http:// | This script uses the [[http:// | ||
+ | |||
+ | The logo is derived from two OpenClipArt.org images: [[http:// | ||
===== License ===== | ===== License ===== | ||
Line 15: | Line 17: | ||
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. | 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 ===== | ||
+ | |||
+ | * PyPump [[https:// | ||
+ | * 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) | ||
+ | |||
+ | ===== Bug Reporting and Feature Requests ===== | ||
+ | |||
+ | * [[community: | ||
+ | |||
===== Source Code ===== | ===== Source Code ===== | ||
Line 24: | Line 36: | ||
# Distributed under an Apache 2.0 license | # Distributed under an Apache 2.0 license | ||
# For more information ,see | # For more information ,see | ||
- | # http:// | ||
- | # | ||
import re | import re | ||
Line 33: | Line 43: | ||
import sys | import sys | ||
import time | import time | ||
- | + | import subprocess | |
+ | import os | ||
+ | import unicodedata | ||
+ | |||
+ | from datetime import datetime | ||
from pypump import PyPump, Client | from pypump import PyPump, Client | ||
from pypump.models.image import Image | from pypump.models.image import Image | ||
Line 43: | Line 58: | ||
# city,region search pattern | # city,region search pattern | ||
- | citypattern = re.compile(' | + | citypattern = re.compile(' |
+ | |||
+ | tripletpattern = re.compile(' | ||
+ | doubletpattern = re.compile(' | ||
+ | |||
+ | for_tripletpattern = re.compile(' | ||
+ | in_tripletpattern = re.compile(' | ||
+ | |||
+ | for_doubletpattern = re.compile(' | ||
+ | in_doubletpattern = re.compile(' | ||
# Place the credentials below for the pump.io account | # Place the credentials below for the pump.io account | ||
# These can be obtained by using: http:// | # These can be obtained by using: http:// | ||
- | client_credentials = [] | + | client_credentials = [' |
- | client_tokens = [] | + | client_tokens = [' |
# Webfinger for your account | # Webfinger for your account | ||
- | webfinger = "me@example.com" | + | webfinger = "name@example.com" |
# Important: define a log file that contains a list of activities to which | # Important: define a log file that contains a list of activities to which | ||
Line 61: | Line 86: | ||
print 'Go to: ' + url | print 'Go to: ' + url | ||
return raw_input(' | return raw_input(' | ||
+ | |||
+ | def HTMLToText( html_text ): | ||
+ | pid = os.getpid() | ||
+ | |||
+ | htmlfile = open('/ | ||
+ | try: | ||
+ | htmlfile.write( html_text ) | ||
+ | except UnicodeEncodeError: | ||
+ | htmlfile.write( unicodedata.normalize(' | ||
+ | pass | ||
+ | |||
+ | htmlfile.close() | ||
+ | | ||
+ | try: | ||
+ | ascii_text = subprocess.check_output([" | ||
+ | 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, | def reply(activity, | ||
Line 71: | Line 117: | ||
| | ||
return True | return True | ||
+ | |||
+ | def KelvinToCelsius(temp_kelvin): | ||
+ | return temp_kelvin - 273.15 | ||
+ | |||
+ | def CelsiusToFarenheit(temp_celsius): | ||
+ | return temp_celsius*1.8 + 32.0 | ||
+ | |||
+ | def FarenheitToCelsius(temp_farenheit): | ||
+ | return (temp_farenheit - 32.0)/1.8 | ||
+ | |||
+ | def KPHToMPH(speed_kph): | ||
+ | return speed_kph/ | ||
+ | |||
+ | def current_conditions(weather_data): | ||
+ | humidity = weather_data[' | ||
+ | | ||
+ | temp_kelvin = weather_data[' | ||
+ | temp_celsius = KelvinToCelsius(temp_kelvin) | ||
+ | temp_farenheit = CelsiusToFarenheit(temp_celsius) | ||
+ | | ||
+ | wind_speed_kmh = weather_data[' | ||
+ | wind_speed_mph = KPHToMPH(wind_speed_kmh) | ||
+ | | ||
+ | heat_index_farenheit = -42.379 + 2.04901523*temp_farenheit + 10.14333127*humidity - .22475541*temp_farenheit*humidity - .00683783*temp_farenheit*temp_farenheit - .05481717*humidity*humidity + .00122874*temp_farenheit*temp_farenheit*humidity + .00085282*temp_farenheit*humidity*humidity - .00000199*temp_farenheit*temp_farenheit*humidity*humidity | ||
+ | heat_index_celsius = FarenheitToCelsius(heat_index_farenheit) | ||
+ | | ||
+ | response = "" | ||
+ | | ||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "</ | ||
+ | |||
+ | |||
+ | return response | ||
+ | |||
+ | def forecast(weather_data): | ||
+ | list_of_data = weather_data[' | ||
+ | response = "" | ||
+ | |||
+ | response += "< | ||
+ | response += "< | ||
+ | response += "< | ||
+ | for moment in list_of_data: | ||
+ | dt = datetime.fromtimestamp(moment[" | ||
+ | timestamp = dt.strftime(' | ||
+ | temp_celsius = KelvinToCelsius(moment[" | ||
+ | temp_farenheit = CelsiusToFarenheit(temp_celsius) | ||
+ | humidity = moment[" | ||
+ | description = "%s - %s" % (moment[" | ||
+ | icon = " | ||
+ | response += "< | ||
+ | response += "</ | ||
+ | |||
+ | return response | ||
Line 133: | Line 239: | ||
# handle this content | # handle this content | ||
| | ||
- | | + | |
- | | + | |
- | | + | query = -1 |
+ | if text_content.find(' | ||
+ | | ||
+ | | ||
+ | query = 0 | ||
+ | elif text_content.find(' | ||
+ | query = 1 | ||
+ | pass | ||
+ | else: | ||
+ | # default to current conditions | ||
+ | query = 0 | ||
+ | |||
+ | |||
+ | search_result = in_tripletpattern.match(text_content) | ||
+ | if not search_result: | ||
+ | | ||
+ | search_result = for_tripletpattern.match(text_content) | ||
+ | if not search_result: | ||
+ | print " | ||
+ | search_result = tripletpattern.match(text_content) | ||
+ | |||
+ | if not search_result: | ||
+ | print " ... Search failed for pure triplet" | ||
+ | search_result = in_doubletpattern.match(text_content) | ||
+ | if not search_result: | ||
+ | print " ... Search failed for in + doublet" | ||
+ | search_result = for_doubletpattern.match(text_content) | ||
+ | if not search_result: | ||
+ | print " ... Search failed for for + doublet" | ||
+ | search_result = doubletpattern.match(text_content) | ||
+ | if not search_result: | ||
+ | print " ... Search failed for pure doublet" | ||
+ | pass | ||
+ | if query >= 0 and search_result: | ||
cityrequest = search_result.group(1) | cityrequest = search_result.group(1) | ||
+ | |||
+ | query_type = " | ||
+ | if query == 1: | ||
+ | query_type = " | ||
+ | pass | ||
+ | | ||
+ | author = "" | ||
+ | if type(activity.obj.author) == Person: | ||
+ | author = activity.obj.author.display_name | ||
+ | elif type(activity.obj.author) == unicode: | ||
+ | author = unicodedata.normalize(' | ||
+ | else: | ||
+ | author = activity.obj.author | ||
+ | pass | ||
+ | | ||
+ | print "%s sent a weather request for the %s for %s, which I will now process..." | ||
| | ||
# clean up poor formatting in content | # clean up poor formatting in content | ||
Line 152: | Line 307: | ||
| | ||
c = pycurl.Curl() | c = pycurl.Curl() | ||
- | c.setopt(c.URL, | + | |
+ | # current conditions | ||
+ | | ||
+ | elif query == 1: | ||
+ | # forecast | ||
+ | c.setopt(c.URL, | ||
+ | pass | ||
c.setopt(c.WRITEFUNCTION, | c.setopt(c.WRITEFUNCTION, | ||
Line 198: | Line 359: | ||
try: | try: | ||
- | | + | |
+ | response = current_conditions(weather_data) | ||
+ | elif query == 1: | ||
+ | response = forecast(weather_data) | ||
+ | |||
+ | pass | ||
- | temp_kelvin = weather_data[' | ||
- | temp_celsius = temp_kelvin - 273.15 | ||
- | temp_farenheit = temp_celsius*1.8 + 32.0 | ||
- | | ||
- | wind_speed_kmh = weather_data[' | ||
- | wind_speed_mph = wind_speed_kmh/ | ||
- | | ||
- | response = "" | ||
- | | ||
- | response += "< | ||
- | response += "< | ||
- | response += "< | ||
- | response += "< | ||
- | response += "< | ||
- | response += "< | ||
- | response += "< | ||
- | response += "</ | ||
- | | ||
my_reply = pump.Comment(response) | my_reply = pump.Comment(response) | ||
my_reply.cc = cc | my_reply.cc = cc | ||
+ | my_reply.cc.append( pump.Public ) | ||
+ | | ||
try: | try: | ||
activity.obj.comment(my_reply) | activity.obj.comment(my_reply) | ||
Line 227: | Line 377: | ||
print " | print " | ||
continue | continue | ||
+ | |||
except KeyError: | except KeyError: | ||
response = "< | response = "< | ||
Line 236: | Line 387: | ||
print " | print " | ||
continue | continue | ||
- | | ||
pass | pass | ||
pass | pass | ||
Line 248: | Line 398: | ||
process_log.close() | process_log.close() | ||
| | ||
+ | |||
</ | </ | ||
+ | ===== Talking to WeatherBot ===== | ||
+ | |||
+ | WeatherBot (e.g. [[https:// | ||
+ | |||
+ | < | ||
+ | New York,NY,US | ||
+ | or | ||
+ | Stockholm, | ||
+ | </ | ||
+ | |||
+ | results in the original behavior - you get the current conditions. | ||
+ | |||
+ | Sending any of these: | ||
+ | |||
+ | < | ||
+ | Current conditions in Jefferson City,MO,US | ||
+ | Currently Uppsala,SE | ||
+ | Current in London,UK | ||
+ | Conditions for Moscow,RU | ||
+ | etc. | ||
+ | </ | ||
+ | |||
+ | also results in the current conditions. | ||
+ | |||
+ | But sending any of these: | ||
+ | |||
+ | < | ||
+ | Forecast for London,UK | ||
+ | Forecast in Dallas, | ||
+ | </ | ||
+ | |||
+ | will return a table of forecast data for the city in question, going out 3 days. This is just a first attempt to do simple request processing based on language, and, yes, it's only in English for now. But now you can at least get a forecast! | ||
+ | |||
+ | {{ : | ||
weatherbot.1408815003.txt.gz · Last modified: 2014/08/23 17:30 by sekula