🏡 index : ~doyle/dave.git

# -*- coding: utf-8 -*-
"""Get the weather from an address using forecast.io."""
from __future__ import division
import dave.modules
import requests
import dave.config
import pickle
from urllib.parse import quote_plus
import arrow
from dave.models import Location
import socket


@dave.module.help("Syntax: weather [location]. Get the forecast in the specified location.")
@dave.module.command(["weather"], "?( .*)?$")
@dave.module.priority(dave.module.Priority.HIGHEST)
@dave.module.ratelimit(1, 5)
def weather(bot, args, sender, source):
    location = args[0]

    db_location = dave.config.session.query(Location).filter(Location.nick.ilike(sender)).first()

    if not location:
        if db_location:
            location = db_location.location
        else:
            return
    else:
        location = location.strip()

        if db_location:
            db_location.location = location
        else:
            db_location = Location(nick=sender, location=location)
            dave.config.session.add(db_location)

    geocode = get_location(location)

    if not geocode or not geocode["results"]:
        return

    formatted_address = get_address(geocode)
    timezone = get_timezone(geocode)
    weather = get_weather(geocode)

    bot.reply(source, sender, u"{}: {}°C/{}°F (feels like {}°C/{}°F), {}. Wind: {}mph/{}km/h, humidity: {}% | Sun: {} - {} {}".format(
        formatted_address,
        int(round(weather["currently"]["temperature"])),
        int(round(weather["currently"]["temperature"] * (9 / 5) + 32)),
        int(round(weather["currently"]["apparentTemperature"])),
        int(round(weather["currently"]["apparentTemperature"] * (9 / 5) + 32)),
        weather["currently"]["summary"],
        int(round(weather["currently"]["windSpeed"])),
        int(round(weather["currently"]["windSpeed"] * 1.60934)),
        int(round(weather["currently"]["humidity"] * 100)),
        arrow.get(weather["daily"]["data"][0]["sunriseTime"]).to(timezone["zoneName"] or "UTC").strftime("%H:%M").lower(),
        arrow.get(weather["daily"]["data"][0]["sunsetTime"]).to(timezone["zoneName"] or "UTC").strftime("%H:%M").lower(),
        timezone["abbreviation"] or "UTC"
    ))


def get_location(location):
    """Get the lat/long of the location the user gave us."""
    key = "location:{}".format(location.lower())

    if not dave.config.redis.exists(key):
        r = requests.get("https://maps.googleapis.com/maps/api/geocode/json?address={}&key={}".format(
            quote_plus(location),
            dave.config.config["api_keys"]["google_maps"]
        ))

        geocode = r.json()

        dave.config.redis.set(key, pickle.dumps(geocode))
    else:
        geocode = pickle.loads(dave.config.redis.get(key))

    return geocode


def get_address(geocode):
    """Get a nicely formatted location to return."""
    formatted_address = []

    has_locality = False

    for component in geocode["results"][0]["address_components"]:
        if "locality" in component["types"]:
            has_locality = True
            formatted_address.append(component["short_name"])

        if "country" in component["types"]:
            formatted_address.append(component["short_name"])

    if not has_locality:
        for component in geocode["results"][0]["address_components"]:
            if "postal_town" in component["types"]:
                formatted_address.insert(0, component["short_name"])

    if len(formatted_address) < 2:
        formatted_address = [a["short_name"] for a in geocode["results"][0]["address_components"]]

    return ", ".join(formatted_address)


def get_timezone(geocode):
    """Get the timezone of the location the user gave us"""
    key = "timezone:{}_{}".format(geocode["results"][0]["geometry"]["location"]["lat"],
                                  geocode["results"][0]["geometry"]["location"]["lng"])

    if not dave.config.redis.exists(key):
        r = requests.get("http://api.timezonedb.com/?key={}&lat={}&lng={}&format=json".format(
            dave.config.config["api_keys"]["timezonedb"],
            geocode["results"][0]["geometry"]["location"]["lat"],
            geocode["results"][0]["geometry"]["location"]["lng"]
        ))

        timezone = r.json()

        dave.config.redis.set(key, pickle.dumps(timezone))
    else:
        timezone = pickle.loads(dave.config.redis.get(key))

    return timezone


def get_weather(geocode):
    """Get the weather for the location the user gave us"""
    key = "weather:{}_{}".format(geocode["results"][0]["geometry"]["location"]["lat"],
                                 geocode["results"][0]["geometry"]["location"]["lng"])

    if not dave.config.redis.exists(key):
        r = requests.get("https://api.forecast.io/forecast/{}/{},{}?units=uk2".format(
            dave.config.config["api_keys"]["forecast.io"],
            geocode["results"][0]["geometry"]["location"]["lat"],
            geocode["results"][0]["geometry"]["location"]["lng"]
        ))

        json = r.json()

        dave.config.redis.setex(key, 300, pickle.dumps(json))
    else:
        json = pickle.loads(dave.config.redis.get(key))

    return json