- Portiere Weather- und Forecast-Plugins

- convert_timestamp() und round() in utilites
- Bugfixes und kleinere Änderungen
This commit is contained in:
Andreas Bielawski 2016-06-15 01:16:27 +02:00
parent 5523700615
commit b176c2099b
5 changed files with 372 additions and 49 deletions

View File

@ -60,7 +60,7 @@ end
function creds_manager:add_creds(var, key) function creds_manager:add_creds(var, key)
print('Saving credential for '..var..' to redis hash '..hash) print('Saving credential for '..var..' to redis hash '..hash)
redis:hset(hash, var, key) redis:hset(hash, var, key)
reload_creds() creds_manager:reload_creds()
return 'Gespeichert!' return 'Gespeichert!'
end end
@ -68,7 +68,7 @@ function creds_manager:del_creds(var)
if redis:hexists(hash, var) == true then if redis:hexists(hash, var) == true then
print('Deleting credential for '..var..' from redis hash '..hash) print('Deleting credential for '..var..' from redis hash '..hash)
redis:hdel(hash, var) redis:hdel(hash, var)
reload_creds() creds_manager:reload_creds()
return 'Key von "'..var..'" erfolgreich gelöscht!' return 'Key von "'..var..'" erfolgreich gelöscht!'
else else
return 'Du hast keine Logininformationen für diese Variable eingespeichert.' return 'Du hast keine Logininformationen für diese Variable eingespeichert.'
@ -80,7 +80,7 @@ function creds_manager:rename_creds(var, newvar)
local key = redis:hget(hash, var) local key = redis:hget(hash, var)
if redis:hsetnx(hash, newvar, key) == true then if redis:hsetnx(hash, newvar, key) == true then
redis:hdel(hash, var) redis:hdel(hash, var)
reload_creds() creds_manager:reload_creds()
return '"'..var..'" erfolgreich zu "'..newvar..'" umbenannt.' return '"'..var..'" erfolgreich zu "'..newvar..'" umbenannt.'
else else
return "Variable konnte nicht umbenannt werden: Zielvariable existiert bereits." return "Variable konnte nicht umbenannt werden: Zielvariable existiert bereits."

222
otouto/plugins/forecast.lua Normal file
View File

@ -0,0 +1,222 @@
local forecast = {}
local HTTPS = require('ssl.https')
local URL = require('socket.url')
local JSON = require('dkjson')
local utilities = require('otouto.utilities')
local bindings = require('otouto.bindings')
local redis = (loadfile "./otouto/redis.lua")()
function forecast:init(config)
if not cred_data.forecastio_apikey then
print('Missing config value: forecastio_apikey.')
print('weather.lua will not be enabled.')
return
elseif not cred_data.google_apikey then
print('Missing config value: google_apikey.')
print('weather.lua will not be enabled.')
return
end
forecast.triggers = {
"^(/f)$",
"^(/f) (.*)$",
"^(/fh)$",
"^(/fh) (.*)$",
"^(/forecast)$",
"^(/forecast) (.*)$",
"^(/forecasth)$",
"^(/forecasth) (.*)$"
}
forecast.doc = [[*
]]..config.cmd_pat..[[f*: Wettervorhersage für deinen Wohnort _(/location set <Ort>)_
*]]..config.cmd_pat..[[f* _<Ort>_: Wettervorhersage für diesen Ort
*]]..config.cmd_pat..[[fh*: 24-Stunden-Wettervorhersage für deine Stadt _(/location set [Ort]_
*]]..config.cmd_pat..[[fh* _<Ort>_: 24-Stunden-Wettervorhersage für diesen Ort
]]
end
forecast.command = 'forecast'
local BASE_URL = "https://api.forecast.io/forecast"
local apikey = cred_data.forecastio_apikey
local google_apikey = cred_data.google_apikey
function get_city_name(lat, lng)
local city = redis:hget('telegram:cache:weather:pretty_names', lat..','..lng)
if city then return city end
local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=political&language=de&key='..google_apikey
local res, code = HTTPS.request(url)
if code ~= 200 then return 'Unbekannte Stadt' end
local data = JSON.decode(res).results[1]
local city = data.formatted_address
print('Setting '..lat..','..lng..' in redis hash telegram:cache:weather:pretty_names to "'..city..'"')
redis:hset('telegram:cache:weather:pretty_names', lat..','..lng, city)
return city
end
function get_condition_symbol(weather, n)
if weather.data[n].icon == 'clear-day' then
return '☀️'
elseif weather.data[n].icon == 'clear-night' then
return '🌙'
elseif weather.data[n].icon == 'rain' then
return '☔️'
elseif weather.data[n].icon == 'snow' then
return '❄️'
elseif weather.data[n].icon == 'sleet' then
return '🌨'
elseif weather.data[n].icon == 'wind' then
return '💨'
elseif weather.data[n].icon == 'fog' then
return '🌫'
elseif weather.data[n].icon == 'cloudy' then
return '☁️☁️'
elseif weather.data[n].icon == 'partly-cloudy-day' then
return '🌤'
elseif weather.data[n].icon == 'partly-cloudy-night' then
return '🌙☁️'
else
return ''
end
end
function get_temp(weather, n, hourly)
if hourly then
local temperature = string.gsub(round(weather.data[n].temperature, 1), "%.", ",")
local condition = weather.data[n].summary
return temperature..'°C | '..get_condition_symbol(weather, n)..' '..condition
else
local day = string.gsub(round(weather.data[n].temperatureMax, 1), "%.", ",")
local night = string.gsub(round(weather.data[n].temperatureMin, 1), "%.", ",")
local condition = weather.data[n].summary
return '☀️ '..day..'°C | 🌙 '..night..'°C | '..get_condition_symbol(weather, n)..' '..condition
end
end
function forecast:get_forecast(lat, lng)
print('Finde Wetter in '..lat..', '..lng)
local text = redis:get('telegram:cache:forecast:'..lat..','..lng)
if text then print('...aus dem Cache..') return text end
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,hourly,alerts,flags'
local response_body = {}
local request_constructor = {
url = url,
method = "GET",
sink = ltn12.sink.table(response_body)
}
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
if not ok then return nil end
local data = JSON.decode(table.concat(response_body))
local ttl = string.sub(response_headers["cache-control"], 9)
local weather = data.daily
local city = get_city_name(lat, lng)
local header = '*Vorhersage für '..city..':*\n_'..weather.summary..'_\n'
local text = '*Heute:* '..get_temp(weather, 1)
local text = text..'\n*Morgen:* '..get_temp(weather, 2)
for day in pairs(weather.data) do
if day > 2 then
text = text..'\n*'..convert_timestamp(weather.data[day].time, '%a, %d.%m')..'*: '..get_temp(weather, day)
end
end
local text = string.gsub(text, "Mon", "Mo")
local text = string.gsub(text, "Tue", "Di")
local text = string.gsub(text, "Wed", "Mi")
local text = string.gsub(text, "Thu", "Do")
local text = string.gsub(text, "Fri", "Fr")
local text = string.gsub(text, "Sat", "Sa")
local text = string.gsub(text, "Sun", "So")
cache_data('forecast', lat..','..lng, header..text, tonumber(ttl), 'key')
return header..text
end
function forecast:get_forecast_hourly(lat, lng)
print('Finde stündliches Wetter in '..lat..', '..lng)
local text = redis:get('telegram:cache:forecast:'..lat..','..lng..':hourly')
if text then print('...aus dem Cache..') return text end
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=currently,minutely,daily,alerts,flags'
local response_body = {}
local request_constructor = {
url = url,
method = "GET",
sink = ltn12.sink.table(response_body)
}
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
if not ok then return nil end
local data = JSON.decode(table.concat(response_body))
local ttl = string.sub(response_headers["cache-control"], 9)
local weather = data.hourly
local city = get_city_name(lat, lng)
local header = '*24-Stunden-Vorhersage für '..city..':*\n_'..weather.summary..'_'
local text = ""
for hour in pairs(weather.data) do
if hour < 26 then
text = text..'\n*'..convert_timestamp(weather.data[hour].time, '%H:%M Uhr')..'* | '..get_temp(weather, hour, true)
end
end
cache_data('forecast', lat..','..lng..':hourly', header..text, tonumber(ttl), 'key')
return header..text
end
function forecast:action(msg, config, matches)
local user_id = msg.from.id
local city = get_location(user_id)
if matches[2] then
city = matches[2]
else
local set_location = get_location(user_id)
if not set_location then
city = 'Berlin, Deutschland'
else
city = set_location
end
end
local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
if not lat and not lng then
print('Koordinaten nicht eingespeichert, frage Google...')
coords = utilities.get_coords(city, config)
lat = coords.lat
lng = coords.lon
end
if not lat and not lng then
utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
return
end
redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
if matches[1] == '/forecasth' or matches[1] == '/fh' then
text = forecast:get_forecast_hourly(lat, lng)
else
text = forecast:get_forecast(lat, lng)
end
if not text then
text = '*Konnte die Wettervorhersage für diese Stadt nicht bekommen.*'
end
utilities.send_reply(self, msg, text, true)
end
return forecast

View File

@ -1,63 +1,150 @@
local weather = {} local weather = {}
local HTTP = require('socket.http') local HTTPS = require('ssl.https')
local URL = require('socket.url')
local JSON = require('dkjson') local JSON = require('dkjson')
local utilities = require('otouto.utilities') local utilities = require('otouto.utilities')
local bindings = require('otouto.bindings')
local redis = (loadfile "./otouto/redis.lua")()
function weather:init(config) function weather:init(config)
if not config.owm_api_key then if not cred_data.forecastio_apikey then
print('Missing config value: owm_api_key.') print('Missing config value: forecastio_apikey.')
print('weather.lua will not be enabled.')
return
elseif not cred_data.google_apikey then
print('Missing config value: google_apikey.')
print('weather.lua will not be enabled.') print('weather.lua will not be enabled.')
return return
end end
weather.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('weather', true).table weather.triggers = {
weather.doc = [[``` "^/wetter$",
]]..config.cmd_pat..[[weather <location> "^/wetter (.*)$",
Returns the current weather conditions for a given location. "^/w$",
```]] "^/w (.*)$"
}
weather.doc = [[*
]]..config.cmd_pat..[[wetter*: Wetter für deinen Wohnort _(/location set [Ort])_
*]]..config.cmd_pat..[[wetter* _<Ort>_: Wetter für diesen Ort
]]
end end
weather.command = 'weather <location>' weather.command = 'wetter'
function weather:action(msg, config) local BASE_URL = "https://api.forecast.io/forecast"
local apikey = cred_data.forecastio_apikey
local google_apikey = cred_data.google_apikey
local input = utilities.input(msg.text) function get_city_name(lat, lng)
if not input then local city = redis:hget('telegram:cache:weather:pretty_names', lat..','..lng)
if msg.reply_to_message and msg.reply_to_message.text then if city then return city end
input = msg.reply_to_message.text local url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng='..lat..','..lng..'&result_type=political&language=de&key='..google_apikey
else local res, code = HTTPS.request(url)
utilities.send_message(self, msg.chat.id, weather.doc, true, msg.message_id, true) if code ~= 200 then return 'Unbekannte Stadt' end
return local data = JSON.decode(res).results[1]
end local city = data.formatted_address
print('Setting '..lat..','..lng..' in redis hash telegram:cache:weather:pretty_names to "'..city..'"')
redis:hset('telegram:cache:weather:pretty_names', lat..','..lng, city)
return city
end
function weather:get_weather(lat, lng)
print('Finde Wetter in '..lat..', '..lng)
local text = redis:get('telegram:cache:weather:'..lat..','..lng)
if text then print('...aus dem Cache') return text end
local url = BASE_URL..'/'..apikey..'/'..lat..','..lng..'?lang=de&units=si&exclude=minutely,hourly,daily,alerts,flags'
local response_body = {}
local request_constructor = {
url = url,
method = "GET",
sink = ltn12.sink.table(response_body)
}
local ok, response_code, response_headers, response_status_line = HTTPS.request(request_constructor)
if not ok then return nil end
local data = JSON.decode(table.concat(response_body))
local ttl = string.sub(response_headers["cache-control"], 9)
local weather = data.currently
local city = get_city_name(lat, lng)
local temperature = string.gsub(round(weather.temperature, 1), "%.", ",")
local feelslike = string.gsub(round(weather.apparentTemperature, 1), "%.", ",")
local temp = '*Wetter in '..city..':*\n'..temperature..' °C'
local conditions = ' | '..weather.summary
if weather.icon == 'clear-day' then
conditions = conditions..' ☀️'
elseif weather.icon == 'clear-night' then
conditions = conditions..' 🌙'
elseif weather.icon == 'rain' then
conditions = conditions..' ☔️'
elseif weather.icon == 'snow' then
conditions = conditions..' ❄️'
elseif weather.icon == 'sleet' then
conditions = conditions..' 🌨'
elseif weather.icon == 'wind' then
conditions = conditions..' 💨'
elseif weather.icon == 'fog' then
conditions = conditions..' 🌫'
elseif weather.icon == 'cloudy' then
conditions = conditions..' ☁️☁️'
elseif weather.icon == 'partly-cloudy-day' then
conditions = conditions..' 🌤'
elseif weather.icon == 'partly-cloudy-night' then
conditions = conditions..' 🌙☁️'
else
conditions = conditions..''
end
local windspeed = ' | 💨 '..string.gsub(round(weather.windSpeed, 1), "%.", ",")..' m/s'
local text = temp..conditions..windspeed
if temperature ~= feelslike then
text = text..'\n(gefühlt: '..feelslike..' °C)'
end
cache_data('weather', lat..','..lng, text, tonumber(ttl), 'key')
return text
end
function weather:action(msg, config, matches)
local user_id = msg.from.id
if matches[1] ~= '/wetter' and matches[1] ~= '/w' then
city = matches[1]
else
local set_location = get_location(user_id)
if not set_location then
city = 'Berlin, Deutschland'
else
city = set_location
end end
end
local coords = utilities.get_coords(input, config) local lat = redis:hget('telegram:cache:weather:'..string.lower(city), 'lat')
if type(coords) == 'string' then local lng = redis:hget('telegram:cache:weather:'..string.lower(city), 'lng')
utilities.send_reply(self, msg, coords) if not lat and not lng then
return print('Koordinaten nicht eingespeichert, frage Google...')
end coords = utilities.get_coords(city, config)
lat = coords.lat
lng = coords.lon
end
local url = 'http://api.openweathermap.org/data/2.5/weather?APPID=' .. config.owm_api_key .. '&lat=' .. coords.lat .. '&lon=' .. coords.lon if not lat and not lng then
utilities.send_reply(self, msg, '*Diesen Ort gibt es nicht!*', true)
return
end
local jstr, res = HTTP.request(url) redis:hset('telegram:cache:weather:'..string.lower(city), 'lat', lat)
if res ~= 200 then redis:hset('telegram:cache:weather:'..string.lower(city), 'lng', lng)
utilities.send_reply(self, msg, config.errors.connection)
return
end
local jdat = JSON.decode(jstr)
if jdat.cod ~= 200 then
utilities.send_reply(self, msg, 'Error: City not found.')
return
end
local celsius = string.format('%.2f', jdat.main.temp - 273.15)
local fahrenheit = string.format('%.2f', celsius * (9/5) + 32)
local output = '`' .. celsius .. '°C | ' .. fahrenheit .. '°F, ' .. jdat.weather[1].description .. '.`'
utilities.send_reply(self, msg, output, true)
local text = weather:get_weather(lat, lng)
if not text then
text = 'Konnte das Wetter von dieser Stadt nicht bekommen.'
end
utilities.send_reply(self, msg, text, true)
end end
return weather return weather

View File

@ -8,10 +8,10 @@ local utilities = require('otouto.utilities')
wikipedia.command = 'wiki <Begriff>' wikipedia.command = 'wiki <Begriff>'
function wikipedia:init(config) function wikipedia:init(config)
wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true):t('w', true).table wikipedia.triggers = utilities.triggers(self.info.username, config.cmd_pat):t('wikipedia', true):t('wiki', true).table
wikipedia.doc = [[* wikipedia.doc = [[*
]]..config.cmd_pat..[[wiki* _<Begriff>_: Gibt Wikipedia-Artikel aus ]]..config.cmd_pat..[[wiki* _<Begriff>_: Gibt Wikipedia-Artikel aus
Aliase: ]]..config.cmd_pat..[[w, ]]..config.cmd_pat..[[wikipedia]] Alias: ]]..config.cmd_pat..[[wikipedia]]
end end
local get_title = function(search) local get_title = function(search)

View File

@ -240,6 +240,12 @@ function run_command(str)
return result return result
end end
function convert_timestamp(timestamp, format)
local converted_date = run_command('date -d @'..timestamp..' +"'..format..'"')
local converted_date = string.gsub(converted_date, '%\n', '')
return converted_date
end
function string.starts(String, Start) function string.starts(String, Start)
return Start == string.sub(String,1,string.len(Start)) return Start == string.sub(String,1,string.len(Start))
end end
@ -659,6 +665,14 @@ function tablelength(T)
return count return count
end end
function round(num, idp)
if idp and idp>0 then
local mult = 10^idp
return math.floor(num * mult + 0.5) / mult
end
return math.floor(num + 0.5)
end
function comma_value(amount) function comma_value(amount)
local formatted = amount local formatted = amount
while true do while true do