Skip to content

Commit

Permalink
Merge pull request #624 from TotallyNotRobots/openweathermap
Browse files Browse the repository at this point in the history
Replace DarkSky with OpenWeatherMap
  • Loading branch information
linuxdaemon authored Apr 10, 2023
2 parents a261736 + 5441e5c commit 56c9c70
Show file tree
Hide file tree
Showing 8 changed files with 478 additions and 152 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add spam protection in herald.py
- Add config reload hooks
### Changed
- Replace DarkSky with OpenWeatherMap
- Updated wine.json (Vault108)
- Refactor tests to remove dependency on mock library
- Change link_announcer.py to only warn on connection errors
Expand Down
2 changes: 1 addition & 1 deletion config.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
}
},
"api_keys": {
"openweathermap": "",
"tvdb": "",
"bing_azure": "",
"wolframalpha": "",
Expand All @@ -114,7 +115,6 @@
"imgur_client_secret": "",
"spotify_client_id": "",
"spotify_client_secret": "",
"darksky": "",
"rdio_key": "",
"rdio_secret": "",
"google_dev_key": "",
Expand Down
5 changes: 5 additions & 0 deletions docs/user/openweathermap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Open Weather Map
The weather.py plugin uses the OpenWeatherMap OneCall 3.0 API.

Once you have an OpenWeatherMap API account, simply insert the API key in the config.json under api_keys -> openweathermap.

143 changes: 78 additions & 65 deletions plugins/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
from typing import List, Optional, Tuple

import googlemaps
from forecastiopy.ForecastIO import ForecastIO
import pyowm
from pyowm import OWM
from googlemaps.exceptions import ApiError
from pyowm.weatherapi25.weather import Weather
from sqlalchemy import Column, PrimaryKeyConstraint, String, Table

from cloudbot import hook
from cloudbot.util import colors, database, web
from cloudbot.util import colors, database

Api = Optional[googlemaps.Client]


class PluginData:
maps_api = None # type: Api
owm_api: Optional[OWM] = None


data = PluginData()
Expand Down Expand Up @@ -147,6 +150,15 @@ def create_maps_api(bot):
data.maps_api = None


@hook.on_start()
def create_owm_api(bot):
owm_key = bot.config.get_api_key("openweathermap")
if owm_key:
data.owm_api = OWM(owm_key, pyowm.owm.cfg.get_default_config())
else:
data.owm_api = None


def get_location(nick):
"""looks in location_cache for a saved location"""
location = [row[1] for row in location_cache if nick.lower() == row[0]]
Expand All @@ -160,16 +172,18 @@ def check_and_parse(event, db):
"""
Check for the API keys and parse the location from user input
"""
ds_key = event.bot.config.get_api_key("darksky")
if not ds_key:
return None, "This command requires a DarkSky API key."

if not data.maps_api:
return (
None,
"This command requires a Google Developers Console API key.",
)

if not data.owm_api:
return (
None,
"This command requires a OpenWeatherMap API key.",
)

# If no input try the db
if not event.text:
location = get_location(event.nick)
Expand All @@ -193,14 +207,15 @@ def check_and_parse(event, db):
except LocationNotFound as e:
return None, str(e)

fio = ForecastIO(
ds_key,
units=ForecastIO.UNITS_US,
latitude=location_data["lat"],
longitude=location_data["lng"],
owm_api = data.owm_api
wm = owm_api.weather_manager()
conditions = wm.one_call(
location_data['lat'],
location_data['lng'],
exclude="minutely,hourly"
)

return (location_data, fio), None
return (location_data, conditions), None


@hook.command("weather", "we", autohelp=False)
Expand All @@ -210,27 +225,30 @@ def weather(reply, db, triggered_prefix, event):
if not res:
return err

location_data, fio = res

daily_conditions = fio.get_daily()["data"]
current = fio.get_currently()
location_data, owm = res
daily_conditions: List[Weather] = owm.forecast_daily
current: Weather = owm.current
today = daily_conditions[0]
wind_speed = current["windSpeed"]
today_high = today["temperatureHigh"]
today_low = today["temperatureLow"]
current.update(
name="Current",
wind_direction=bearing_to_card(current["windBearing"]),
wind_speed_mph=wind_speed,
wind_speed_kph=mph_to_kph(wind_speed),
summary=current["summary"].rstrip("."),
temp_f=round_temp(current["temperature"]),
temp_c=round_temp(convert_f2c(current["temperature"])),
temp_high_f=round_temp(today_high),
temp_high_c=round_temp(convert_f2c(today_high)),
temp_low_f=round_temp(today_low),
temp_low_c=round_temp(convert_f2c(today_low)),
)
wind_mph = current.wind('miles_hour')
wind_speed = wind_mph['speed']
today_temp = today.temperature('fahrenheit')
today_high = today_temp['max']
today_low = today_temp['min']
current_temperature = current.temperature('fahrenheit')['temp']
current_data = {
'name': "Current",
'wind_direction': bearing_to_card(wind_mph['deg']),
'wind_speed_mph': wind_speed,
'wind_speed_kph': mph_to_kph(wind_speed),
'summary': current.status,
'temp_f': round_temp(current_temperature),
'temp_c': round_temp(convert_f2c(current_temperature)),
'temp_high_f': round_temp(today_high),
'temp_high_c': round_temp(convert_f2c(today_high)),
'temp_low_f': round_temp(today_low),
'temp_low_c': round_temp(convert_f2c(today_low)),
'humidity': current.humidity / 100,
}

parts = [
("Current", "{summary}, {temp_f}F/{temp_c}C"),
Expand All @@ -248,22 +266,14 @@ def weather(reply, db, triggered_prefix, event):
for part in parts
)

url = web.try_shorten(
"https://darksky.net/forecast/{lat:.3f},{lng:.3f}".format_map(
location_data
)
)

reply(
colors.parse(
"{current_str} -- "
"{place} - "
"$(ul){url}$(clear) "
"($(i)To get a forecast, use {cmd_prefix}fc$(i))"
).format(
place=location_data["address"],
current_str=current_str.format_map(current),
url=url,
current_str=current_str.format_map(current_data),
cmd_prefix=triggered_prefix,
)
)
Expand All @@ -278,31 +288,41 @@ def forecast(reply, db, event):
if not res:
return err

location_data, fio = res
location_data, owm = res

daily_conditions = fio.get_daily()["data"]
one_call = owm
daily_conditions = one_call.forecast_daily
today, tomorrow, *three_days = daily_conditions[:5]

today["name"] = "Today"
tomorrow["name"] = "Tomorrow"

for day_fc in (today, tomorrow):
wind_speed = day_fc["windSpeed"]
today_data = {
'data': today,
}
tomorrow_data = {
'data': tomorrow
}
three_days_data = [{'data': d} for d in three_days]
today_data["name"] = "Today"
tomorrow_data["name"] = "Tomorrow"

for day_fc in (today_data, tomorrow_data):
wind_speed = day_fc['data'].wind('miles_hour')
day_fc.update(
wind_direction=bearing_to_card(day_fc["windBearing"]),
wind_speed_mph=wind_speed,
wind_speed_kph=mph_to_kph(wind_speed),
summary=day_fc["summary"].rstrip("."),
wind_direction=bearing_to_card(wind_speed['deg']),
wind_speed_mph=wind_speed['speed'],
wind_speed_kph=mph_to_kph(wind_speed['speed']),
summary=day_fc['data'].status,
)

for fc_data in (today, tomorrow, *three_days):
high = fc_data["temperatureHigh"]
low = fc_data["temperatureLow"]
for fc_data in (today_data, tomorrow_data, *three_days_data):
temp = fc_data['data'].temperature('fahrenheit')
high = temp['max']
low = temp['min']
fc_data.update(
temp_high_f=round_temp(high),
temp_high_c=round_temp(convert_f2c(high)),
temp_low_f=round_temp(low),
temp_low_c=round_temp(convert_f2c(low)),
humidity=fc_data['data'].humidity / 100,
)

parts = [
Expand All @@ -319,20 +339,13 @@ def forecast(reply, db, event):
"{}: {}".format(part[0], part[1]) for part in parts
)

url = web.try_shorten(
"https://darksky.net/forecast/{lat:.3f},{lng:.3f}".format_map(
location_data
)
)

out_format = "{today_str} | {tomorrow_str} -- {place} - $(ul){url}$(clear)"
out_format = "{today_str} | {tomorrow_str} -- {place}"

reply(
colors.parse(out_format).format(
today_str=day_str.format_map(today),
tomorrow_str=day_str.format_map(tomorrow),
today_str=day_str.format_map(today_data),
tomorrow_str=day_str.format_map(tomorrow_data),
place=location_data["address"],
url=url,
)
)
return None
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ beautifulsoup4 == 4.10.0
chardet == 4.0.0
cleverwrap == 0.3.0.2
feedparser == 6.0.8
forecastiopy == 0.22
googlemaps == 4.5.3
imgurpython == 1.1.7
isodate == 0.6.0
Expand All @@ -14,6 +13,7 @@ multidict == 5.2.0
nltk == 3.6.6
psutil == 5.8.0
py-irclib == 0.3.0
pyowm == 3.3.0
requests == 2.27.1
tweepy == 3.10.0
urllib3 == 1.26.7
Expand Down
Loading

0 comments on commit 56c9c70

Please sign in to comment.