From 33fa1f13d822a7463a51334b5782ce41eee94cda Mon Sep 17 00:00:00 2001 From: Richard Chien Date: Thu, 12 Jan 2017 21:59:54 +0800 Subject: [PATCH] Rewrite weather commands --- command.py | 7 +- commands/simpletools.py | 4 +- commands/weather.py | 191 ++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 1 + 4 files changed, 199 insertions(+), 4 deletions(-) create mode 100644 commands/weather.py diff --git a/command.py b/command.py index ce2c4543..7d4b269f 100644 --- a/command.py +++ b/command.py @@ -294,8 +294,11 @@ hub = CommandHub() def split_args(maxsplit=0): def decorator(func): - def wrapper(args_text, *args, **kwargs): - args_list = list(filter(lambda arg: arg, re.split('|'.join(_command_args_seps), args_text, maxsplit))) + def wrapper(argument, *args, **kwargs): + if isinstance(argument, (list, tuple)): + args_list = list(argument) + else: + args_list = list(filter(lambda arg: arg, re.split('|'.join(_command_args_seps), argument, maxsplit))) return func(args_list, *args, **kwargs) return wrapper diff --git a/commands/simpletools.py b/commands/simpletools.py index 3e7d804d..e043a1d0 100644 --- a/commands/simpletools.py +++ b/commands/simpletools.py @@ -65,8 +65,8 @@ def short_url(args_text, ctx_msg): core.echo('生成失败,可能因为链接格式错误或服务器连接不上', ctx_msg) -@cr.register('weather') -@cr.register('天气', '查天气') +# @cr.register('weather') +# @cr.register('天气', '查天气') def weather(args_text, ctx_msg): city = args_text.strip() if not city: diff --git a/commands/weather.py b/commands/weather.py new file mode 100644 index 00000000..00c83e2b --- /dev/null +++ b/commands/weather.py @@ -0,0 +1,191 @@ +import os +from datetime import datetime + +import requests +import jieba + +from command import CommandRegistry, split_args +from commands import core +from little_shit import get_source +from interactive import * + +__registry__ = cr = CommandRegistry() + +_api_key = os.environ.get('HEWEATHER_API_KEY') +_base_api_url = 'https://free-api.heweather.com/v5' +_search_api_url = _base_api_url + '/search' +_forecast_api_url = _base_api_url + '/forecast' +_now_api_url = _base_api_url + '/now' +_suggestion_api_url = _base_api_url + '/suggestion' + +_cmd_weather = 'weather.weather' +_cmd_suggestion = 'weather.suggestion' + +_weekday_string = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] + + +@cr.register('weather') +@cr.register('天气', '查天气', '天气预报', '查天气预报') +@split_args() +def weather(args, ctx_msg, allow_interactive=True): + source = get_source(ctx_msg) + if allow_interactive and (len(args) < 1 or not args[0].startswith('CN') or has_session(source, _cmd_weather)): + # Be interactive + return _do_interactively(_cmd_weather, weather, args, ctx_msg, source) + + city_id = args[0] + session = requests.Session() + params = {'city': city_id, 'key': _api_key} + text = '' + + # Get real-time weather + data_now = session.get(_now_api_url, params=params).json() + if data_now and 'HeWeather5' in data_now and data_now['HeWeather5'][0].get('status') == 'ok': + now = data_now['HeWeather5'][0]['now'] + text += '实时:\n%s,气温%s˚C,体感温度%s˚C,%s%s级,能见度%skm' \ + % (now['cond']['txt'], now['tmp'], now['fl'], now['wind']['dir'], now['wind']['sc'], now['vis']) + + # Get forecast + data_forecast = session.get(_forecast_api_url, params=params).json() + if data_forecast and 'HeWeather5' in data_forecast and data_forecast['HeWeather5'][0].get('status') == 'ok': + daily_forecast = data_forecast['HeWeather5'][0]['daily_forecast'] + text += '\n\n预报:\n' + + for forecast in daily_forecast: + d = datetime.strptime(forecast['date'], '%Y-%m-%d') + text += '%d月%d日%s,' % (d.month, d.day, _weekday_string[d.weekday()]) + + cond_d = forecast['cond']['txt_d'] + cond_n = forecast['cond']['txt_n'] + text += cond_d + ('转' + cond_n if cond_d != cond_n else '') + ',' + + text += forecast['tmp']['min'] + '~' + forecast['tmp']['max'] + '°C,' + text += forecast['wind']['dir'] + forecast['wind']['sc'] + '级,' + text += '降雨概率%s%%' % forecast['pop'] + text += '\n' + + text = text.rstrip() + if text: + core.echo(text, ctx_msg) + else: + core.echo('查询失败了,请稍后再试哦~', ctx_msg) + + +@cr.register('suggestion') +@cr.register('生活指数', '生活建议', '天气建议') +@split_args() +def suggestion(args, ctx_msg, allow_interactive=True): + source = get_source(ctx_msg) + if allow_interactive and (len(args) < 1 or not args[0].startswith('CN') or has_session(source, _cmd_suggestion)): + # Be interactive + return _do_interactively(_cmd_suggestion, suggestion, args, ctx_msg, source) + + city_id = args[0] + session = requests.Session() + params = {'city': city_id, 'key': _api_key} + text = '' + + # Get suggestion + data_suggestion = session.get(_suggestion_api_url, params=params).json() + if data_suggestion and 'HeWeather5' in data_suggestion and data_suggestion['HeWeather5'][0].get('status') == 'ok': + data = data_suggestion['HeWeather5'][0]['suggestion'] + text += '生活指数:\n\n' \ + '舒适度:%s\n\n' \ + '洗车指数:%s\n\n' \ + '穿衣指数:%s\n\n' \ + '感冒指数:%s\n\n' \ + '运动指数:%s\n\n' \ + '旅游指数:%s\n\n' \ + '紫外线指数:%s' \ + % tuple([data[k]['txt'] for k in ('comf', 'cw', 'drsg', 'flu', 'sport', 'trav', 'uv')]) + + if text: + core.echo(text, ctx_msg) + else: + core.echo('查询失败了,请稍后再试哦~', ctx_msg) + + +_state_machines = {} + + +def _do_interactively(command_name, func, args, ctx_msg, source): + def ask_for_city(s, a, c): + if len(a) > 0: + if search_city(s, a, c): + return True + else: + core.echo('你要查询哪个城市呢?', c) + s.state += 1 + + def search_city(s, a, c): + if len(a) < 1: + core.echo('你输入的城市不正确哦,请重新发送命令~', c) + return True + + prov = None + city = a[0] + # Try to split province and city if possible + tmp = jieba.lcut(city) + if len(tmp) == 2: + prov, city = tmp + + resp = requests.get(_search_api_url, params={ + 'city': city, + 'key': _api_key + }) + data = resp.json() + if resp.status_code == 200 and data and 'HeWeather5' in data: + city_list = data['HeWeather5'] + if city_list[0].get('status') != 'ok': + core.echo('没有找到你输入的城市哦,请重新发送命令~', c) + return True + + if prov: + city_list = list(filter(lambda c: c['basic']['prov'] == prov, city_list)) + + s.data['city_list'] = city_list + + if len(city_list) == 1: + # Directly choose the first one + choose_city(s, ['1'], c) + return True + + # Here comes more than one city with the same name + core.echo( + '找到 %d 个重名城市,请选择你要查询的那个,发送它的序号:\n\n' % len(city_list) + + '\n'.join( + [str(i + 1) + '. ' + c['basic']['prov'] + c['basic']['city'] for i, c in enumerate(city_list)] + ), + c + ) + + s.state += 1 + + def choose_city(s, a, c): + if len(a) != 1 or not a[0].isdigit(): + core.echo('你输入的序号不正确哦,请重新发送命令~', c) + return True + + choice = int(a[0]) - 1 # Should be from 0 to len(city_list) - 1 + city_list = s.data['city_list'] + if choice < 0 or choice >= len(city_list): + core.echo('你输入的序号超出范围了,请重新发送命令~', c) + return True + + city_id = city_list[choice]['basic']['id'] + # sess.data['func']([city_id], c, allow_interactive=False) + func([city_id], c, allow_interactive=False) + return True + + if command_name not in _state_machines: + _state_machines[command_name] = ( + ask_for_city, # 0 + search_city, # 1 + choose_city # 2 + ) + + sess = get_session(source, command_name) + sess.data['func'] = func + if _state_machines[command_name][sess.state](sess, args, ctx_msg): + # Done + remove_session(source, command_name) diff --git a/docker-compose.yml b/docker-compose.yml index 4ec8b02e..2db45361 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,6 +50,7 @@ services: - BAIDU_SPEECH_API_KEY=YOUR_API_KEY - BAIDU_SPEECH_SECRET_KEY=YOUR_SECRET_KEY - BING_SPEECH_API_KEY=YOUR_API_KEY + - HEWEATHER_API_KEY=YOUR_API_KEY restart: always networks: my-net: