def render_episodes(episodes, variables): """Render cards given a list of episodes""" if not len(episodes): return [Message(u'沒有更多的集數可以看囉 :(')] m = Message() for ep in episodes: b = Message.Bubble(ep.drama_name + u'第 %d 集' % ep.serial_number, image_url=CacheImage(ep.image_url), subtitle=ep.description) b.add_button( Message.Button(Message.ButtonType.WEB_URL, u'帶我去看', url=TrackedURL(ep.link, 'WatchButton'), variables=variables)) b.add_button( Message.Button(Message.ButtonType.POSTBACK, u'我想看前幾集', payload=EventPayload( 'GET_HISTORY', { 'drama_id': ep.drama_id, 'from_episode': ep.serial_number, 'backward': True, }))) m.add_bubble(b) m.add_quick_reply(Message.QuickReply(Message.QuickReplyType.TEXT, u'第1集')) m.add_quick_reply(Message.QuickReply(Message.QuickReplyType.TEXT, u'熱門韓劇')) m.add_quick_reply(Message.QuickReply(Message.QuickReplyType.TEXT, u'熱門日劇')) m.add_quick_reply(Message.QuickReply(Message.QuickReplyType.TEXT, u'熱門台劇')) m.add_quick_reply(Message.QuickReply(Message.QuickReplyType.TEXT, u'熱門陸劇')) m.add_quick_reply(Message.QuickReply(Message.QuickReplyType.TEXT, u'通知設定')) return [m]
def render_cards(news_info, news): """Render cards given a list of new entries.""" if not len(news): return [Message(u'找不到你要的新聞喔!')] m = Message() for n in news: image_url = (n.image_url if n.image_url != '' else get_default_image(n.source)) b = Message.Bubble(n.title, image_url=image_url, subtitle=n.description) b.add_button(Message.Button( Message.ButtonType.POSTBACK, u'在這讀', payload=EventPayload('GET_CONTENT', { 'entry_link': n.link, 'char_offset': 0, 'pic_index': 1, }, False))) b.add_button(Message.Button( Message.ButtonType.WEB_URL, u'去網站讀', url=n.link)) b.add_button(Message.Button(Message.ButtonType.ELEMENT_SHARE)) m.add_bubble(b) add_quick_reply_keywords(news_info, m, True) return [m]
def run(content_config, env, variables): """ content_config schema: Platform dependent message: { 'text': 'blablabla', 'choices': [ { 'title': 'option 1', 'payload': '1', 'acceptable_inputs': ['opt 1'] }, ... ] } """ platform_type = env['platform_type'].value m = Message(buttons_text=content_config['text'], variables=variables) for i, choice in enumerate(content_config['choices']): # Facebook only support 3 buttons if i == 3 and platform_type == SupportedPlatform.Facebook: break b = Message.Button(Message.ButtonType.POSTBACK, choice['title'], payload=TextPayload(choice['payload']), acceptable_inputs=choice['acceptable_inputs'], variables=variables) m.add_button(b) return [m]
def run(unused_content_config, unused_env, unused_variables): m = Message() b = Message.Bubble('Tap postback') b.add_button( Message.Button(Message.ButtonType.POSTBACK, 'Postback', payload=TextPayload('PAYLOAD_TEXT'))) m.add_bubble(b) return [m]
def run(content_config, unused_env, variables): """ content_config schema: { "type": "random or query", "term": "query term if type is query", "max_count": 5, // max number of images to return "auth": { "client_id": "imgur client_id", "client_secret": "imgur client_secret" } } """ client = imgurpython.ImgurClient(content_config['auth']['client_id'], content_config['auth']['client_secret']) if content_config['type'] == 'random': images = [ x for x in client.gallery_random() if isinstance(x, GalleryImage) ] else: term = Resolve(content_config['term'], variables) images = [ x for x in client.gallery_search(term) if isinstance(x, GalleryImage) ] images = random.sample(images, int(content_config['max_count'])) m = Message() for i in images: c = Message.Bubble(i.title, i.link[:-4], i.link, i.description) c.add_button( Message.Button(Message.ButtonType.WEB_URL, 'Source', url=i.link[:-4])) c.add_button( Message.Button(Message.ButtonType.POSTBACK, 'Like', payload=TextPayload('like %s' % i.link))) m.add_bubble(c) return [Message('Here are the images you requested'), m]
def render_dramas(dramas): """Render cards given a list of dramas""" if not len(dramas): return Message(u'找不到你要的劇喔!') m = Message() for d in dramas: b = Message.Bubble(d.name, image_url=CacheImage(d.image_url), subtitle=d.description) if d.subscribed: b.add_button( Message.Button(Message.ButtonType.POSTBACK, u'取消追蹤', payload=EventPayload('UNSUBSCRIBE', { 'drama_id': d.id, }, False))) else: b.add_button( Message.Button(Message.ButtonType.POSTBACK, u'追蹤我', payload=EventPayload('SUBSCRIBE', { 'drama_id': d.id, }, False))) b.add_button( Message.Button(Message.ButtonType.POSTBACK, u'我想看前幾集', payload=EventPayload( 'GET_HISTORY', { 'drama_id': d.id, 'from_episode': 0, 'backward': True, }))) b.add_button(Message.Button(Message.ButtonType.ELEMENT_SHARE)) m.add_bubble(b) return m
def run(unused_content_config, env, unused_variables): user_input = UserInput() msgs = [] # The output messages. # output limit and map size size = (500, 260) if env['platform_type'] == SupportedPlatform.Line: max_count = 3 else: max_count = 5 address = u'' # str. The address user entered. filters_str = u'' # str. The filter used this time. geocoder = GoogleMapsPlaceAPI(api_key=GOOGLE_STATIC_MAP_API_KEY) more_data = user_input.Get('more_data') latlng = Memory.Get('latlng', None) # tuple. User's current location. cached_transaction = Memory.Get('cached_transaction', None) next_data_index = Memory.Get('next_data_index', max_count) if more_data: # Yes, user clicks 'more data', dump data from cache. if not cached_transaction: return [Message(u'請重新輸入地址或條件。')] trans = cached_transaction[next_data_index:next_data_index + max_count] Memory.Set('next_data_index', next_data_index + max_count) if not trans: return [Message(u'沒有更多物件了,請重新輸入地址或條件。')] else: # Nope. User is not entering 'more data'. reset = user_input.Get('reset') location = user_input.Get('location') if reset: latlng = Memory.Get('latlng', None) rules = None # User uploaded current location. elif location and 'coordinates' in location: latlng = (location['coordinates']['lat'], location['coordinates']['long']) rules = Deser(Memory.Get('rules', None)) if rules: rules.AddPureSortingRules(latlng) else: # User entered either an address or criteria; or both. query = user_input.Get('query') rules = twrealprice_rule.Rules.Create() query = rules.ParseQuery(query) address = query.strip() _LOG.info('User query: address=[%s] filters:[%s]', address, rules.filters) if address: if latlng: center = {'lat': latlng[0], 'long': latlng[1]} else: center = None geo_results = geocoder.query_top_n(n=3, address=address, language='zh_TW', region='TW', bounds=[[[20.0, 118.0], [26.0, 123.0]]], center=center) if not geo_results: if rules.filters: msgs.append( Message((u'我不認識這個地址:[%s], 但是我認' u'識條件:[%s]. 請修改一下地址。') % (address, ' '.join(rules.filters)))) address = u'' else: return [Message(u'我不認識這個地址:[%s]' % address)] if len(geo_results) == 1: latlng = (geo_results[0]['location'][0], geo_results[0]['location'][1]) if len(geo_results) > 1: m = Message(buttons_text=u'你指的是以下哪一個地址呢?') for r in geo_results: m.add_button( Message.Button(Message.ButtonType.POSTBACK, r['address'], payload=LocationPayload( r['location'], False))) # If user also entered criteria, save it for later query. if rules.filters: Memory.Set('rules', cPickle.dumps(rules)) # Postpone the AddPureSortingRules until we get latlng. return [m] if not rules.filters: # If user didn't enter filter, use the previous one. rules = Deser(Memory.Get('rules', None)) if rules: _LOG.info('User prev rules: %s', rules.filters) if rules: rules.AddPureSortingRules(latlng) next_data_index = max_count if not latlng: return [Message(u'我還不知道你想查的地方是哪裡。請輸入地址或是送出你的位置。')] Memory.Set('rules', cPickle.dumps(rules)) Memory.Set('latlng', latlng) Memory.Set('next_data_index', next_data_index) twrealprice = TwRealPrice() try: trans = twrealprice.nearby_transaction(max_count, latlng, rules) except IOError: return [Message(u'資料庫找不到,趕快回報給粉絲頁管理員,謝謝。')] if rules and rules.filters: filters_str = u'篩選條件: 「%s」。' % u'」「'.join(rules.filters) if not trans: msg = Message(u'對不起,找不到成交行情喔!試試別的地址或條件。') msgs.append(msg) return msgs # Construct main result cards. main_map = GoogleStaticMapAPIRequestBuilder(GOOGLE_STATIC_MAP_API_KEY, size) style = MarkerStyle() main_map.add_marker(latlng, **style.next()) for s in trans: lat = float(s['latlng'][0]) lng = float(s['latlng'][1]) main_map.add_marker((lat, lng), **style.next()) subtitle = (filters_str + u'搜尋結果僅供參考,詳細完整實價登錄資料,以內政部公佈為準。') msg = Message() b = Message.Bubble(u'%s附近的成交行情' % address, image_url=main_map.build_url(), subtitle=subtitle) msg.add_bubble(b) msgs.append(msg) # At this point, 'trans' must have data to show. i = -1 for s in trans: # In this loop, the strings are concatenated in utf-8 first because the # transaction data come with utf-8 encoded. try: def UnitPrice(s): try: return '%.2f萬' % (float(s['單價每平方公尺']) * twrealprice_rule.M2_PER_PING / 10000) except ValueError: return '--.--' lines = [ ' '.join([ '%d萬' % (int(s['總價元']) / 10000), '(%s * %.2f坪 + %d萬)' % (UnitPrice(s), float(s['建物移轉總面積平方公尺']) / twrealprice_rule.M2_PER_PING, int(s['車位總價元']) / 10000), '%s(%s)' % ( s['建物型態'].split('(')[0], Age(s['建築完成年月']), ), ]).decode('utf-8'), ' '.join([ '%s/共%s' % (s['移轉層次'].replace('層', '樓'), s['總樓層數'].replace('層', '樓')), StreetOnly(s), ]).decode('utf-8'), ' '.join([ RocSlash(s['交易年月日']) + '成交', '%s房%s廳%s衛' % (s['建物現況格局-房'], s['建物現況格局-廳'], s['建物現況格局-衛']), '地坪%.2f' % (float(s['土地移轉總面積平方公尺']) / twrealprice_rule.M2_PER_PING), '車位%.2f坪' % (float(s['車位移轉總面積平方公尺']) / twrealprice_rule.M2_PER_PING), s['車位類別'], s['主要用途'], '備註: ' + s['備註'] if s['備註'] else '', ]).decode('utf-8'), ] if env['platform_type'] == SupportedPlatform.Line: title = lines[0] subtitle = lines[1] + lines[2] else: title = lines[0] + lines[1] subtitle = lines[2] i += 1 msg = Message() msg.add_bubble( Message.Bubble((u'%c: ' % (i + ord('A'))) + title, subtitle=subtitle)) msgs.append(msg) except ValueError: traceback.print_exc() continue return msgs
def run(content_config, unused_env, variables): """ content_config schema: Platform dependent message: { 'send_payload_to_current_node': False, 'api_key': 'google geocoding api key', 'query_term': 'query term', 'language': 'zh_TW', 'region': 'tw', 'bounds': [ {'lat': 23.816576, 'long': 119.781068}, {'lat': 25.314444, 'long': 122.053702} ], 'center': {'lat': 23.816576, 'long': 122.053702} } """ query_term = Resolve(content_config['query_term'], variables) # Remove stop words query_term = re.sub(STOP_WORDS, '', query_term) cfg = content_config api = GoogleMapsPlaceAPI(content_config['api_key']) py_bounds = [] for b in cfg.get('bounds', []): py_bounds.append([(b[0]['lat'], b[0]['long']), (b[1]['lat'], b[1]['long'])]) results = api.query_top_n(3, query_term, cfg['language'], cfg['region'], py_bounds, cfg.get('center', None)) in_currrent = content_config['send_payload_to_current_node'] if not results: m = Message(u'對不起,我找不到這個地址, 請重新輸入 >_<') elif len(results) == 1: m = Message(u'你指的是「%s」嗎?' % results[0]['address']) m.add_quick_reply( Message.QuickReply(Message.QuickReplyType.TEXT, u'是', payload=LocationPayload(results[0]['location'], in_currrent), acceptable_inputs=[u'^對', '(?i)y', '(?i)ok'])) m.add_quick_reply( Message.QuickReply(Message.QuickReplyType.TEXT, u'否', payload=EventPayload('WRONG_ADDRESS', None, in_currrent), acceptable_inputs=[u'不', '(?i)n'])) else: m = Message(buttons_text=u'你指的是以下哪一個地址呢?') for r in results: m.add_button( Message.Button(Message.ButtonType.POSTBACK, r['address'], payload=LocationPayload(r['location'], in_currrent))) return [m]
def run(content_config, unused_env, variables): """ content_config schema: { "bot_admins": { "platform_user_ident_1": ["bot_id_1", "bot_id_2" ..], ... } } """ user = g.user bots = content_config['bot_admins'].get(user.platform_user_ident) if not bots: return [] bot = Memory.Get('bot') if not bot: msgs = [ Message(u'嗨 {{user.first_name}},你想要操作哪隻機器人呢?', variables=variables) ] page = 1 m = Message() msgs.append(m) bubble = Message.Bubble('第 %d 頁' % page) for bot_id in bots: bot = Bot.get_by(id=bot_id, single=True) if not bot: continue bubble.add_button( Message.Button(Message.ButtonType.POSTBACK, title=bot.name, payload=EventPayload('SELECT_BOT', bot_id))) if len(bubble.buttons) == 3: m.add_bubble(bubble) page += 1 bubble = Message.Bubble(u'第 %d 頁' % page) if len(bubble.buttons): m.add_bubble(bubble) msgs[-1].add_quick_reply( Message.QuickReply(Message.QuickReplyType.TEXT, u'放棄', payload=EventPayload('CONTROL_FLOW', 'reset'), acceptable_inputs=['(?i)giveup', '(?i)reset'])) return msgs operation = Memory.Get('operation') if not operation: m = Message(buttons_text=u'你想要執行什麼動作呢?') m.add_button( Message.Button(Message.ButtonType.POSTBACK, title=u'廣播訊息', payload=EventPayload('SELECT_OP', 'broadcast'))) m.add_button( Message.Button(Message.ButtonType.POSTBACK, title=u'放棄', payload=EventPayload('CONTROL_FLOW', 'reset'))) return [m] if operation == 'broadcast': broadcast_message = Memory.Get('broadcast_message') status = Memory.Get('status') if status == 'input_broadcast_message': m = Message(u'你可以繼續輸入下一則訊息:') m.add_quick_reply( Message.QuickReply( Message.QuickReplyType.TEXT, u'完成', payload=EventPayload('MESSAGE_INPUT', 'done'), acceptable_inputs=[u'好了', u'(?i)done', '(?i)y'])) m.add_quick_reply( Message.QuickReply( Message.QuickReplyType.TEXT, u'重來', payload=EventPayload('MESSAGE_INPUT', 'restart'), acceptable_inputs=[u'好了', u'(?i)restart', '(?i)cancel'])) m.add_quick_reply( Message.QuickReply( Message.QuickReplyType.TEXT, u'放棄', payload=EventPayload('CONTROL_FLOW', 'reset'), acceptable_inputs=['(?i)giveup', '(?i)reset'])) return [m] elif not broadcast_message: Memory.Set('status', 'input_broadcast_message') return [Message(u'請輸入你要廣播的訊息:')] elif status == 'preview_message': msgs = [Message(u'請確認你要廣播的訊息:')] for raw_msg in broadcast_message: msgs.append(Message.FromDict(raw_msg)) m = msgs[-1] m.add_quick_reply( Message.QuickReply(Message.QuickReplyType.TEXT, u'確認', payload=EventPayload( 'CONFIRM_MESSAGE', True), acceptable_inputs=[u'是', '(?i)y', '(?i)ok'])) m.add_quick_reply( Message.QuickReply( Message.QuickReplyType.TEXT, u'取消', payload=EventPayload('CONFIRM_MESSAGE', False), acceptable_inputs=[u'是', '(?i)no', '(?i)cancel'])) return msgs return [Message(u'錯誤的操作')]
def run(content_config, unused_env, variables): """ content_config schema: { "api_key": "api_key", "send_payload_to_current_node": true, "location": "location variables", "max_count": 3 "distance_threshold": 0.050, "display_weather": true } """ location = Resolve(content_config['location'], variables) if 'coordinates' not in location: return [Message('不正確的輸入,請重新輸入')] c = (location['coordinates']['lat'], location['coordinates']['long']) youbike = YoubikeInfo() k = content_config.get('max_count', 5) size = (500, 260) stations = youbike.find_knn(k, c, content_config['distance_threshold']) if not stations: return [Message('對不起,這裡附近沒有 Ubike 站喔!')] m = GoogleStaticMapAPIRequestBuilder(content_config['api_key'], size) m.add_marker(c, 'purple') for s in stations: m.add_marker((float(s['lat']), float(s['lng']))) msgs = [] # Construct main result cards. msg = Message() b = Message.Bubble(u'附近的 Ubike 站點', image_url=m.build_url(), subtitle=u'以下是最近的 %d 個站點' % k) best = stations[0] for s in stations: if s['sbi'] > 0: best = s break best_gps_coord = (float(best['lat']), float(best['lng'])) b.add_button(Message.Button(Message.ButtonType.WEB_URL, u'帶我去', url=m.build_navigation_url(best_gps_coord))) to_current = content_config['send_payload_to_current_node'] b.add_button(Message.Button(Message.ButtonType.POSTBACK, u'再次查詢', payload=LocationPayload(c, to_current))) msg.add_bubble(b) for s in stations: m.clear_markers() m.add_marker(c, 'purple') gps_coord = (float(s['lat']), float(s['lng'])) m.add_marker(gps_coord, color='red') sbi, bemp = int(s['sbi']), int(s['bemp']) subtitle = u'剩餘數量: %d\n空位數量: %d\n' % (sbi, bemp) if sbi < 5: subtitle += (u'預計進車時間: %d 分鐘\n' % youbike.average_waiting_time( str(s['sno']), youbike.Direction.In)) if bemp < 5: subtitle += (u'預計出位時間:%d 分鐘\n' % youbike.average_waiting_time( str(s['sno']), youbike.Direction.Out)) b = Message.Bubble(s['ar'], image_url=m.build_url(), subtitle=subtitle) b.add_button(Message.Button(Message.ButtonType.WEB_URL, u'地圖導航', url=m.build_navigation_url(gps_coord))) msg.add_bubble(b) msgs.append(msg) if content_config['display_weather'] and 'weather' in best: # See http://openweathermap.org/weather-conditions # for weather codes. weather = best['weather'] code = best['weather']['weather_code'] temp = weather['temp'] time = GetUserTime() if code / 100 == 3 or code in [500, 501, 520]: msgs.append(Message(u'提醒你,現在外面天雨路滑,騎車小心!' u'記得帶傘或穿件雨衣唷')) elif (code / 100 == 2 or code in [502, 503, 504, 511, 521, 522, 531, 901, 902]): msgs.append(Message(u'現在外面大雨滂沱,不如叫個 Uber 吧!')) elif temp >= 30 and time.hour >= 10 and time.hour <= 16: msgs.append(Message(u'提醒你,現在外面天氣炎熱 (攝氏 %.1f 度) ,' u'記得做好防曬唷' % temp)) return msgs
def run_get_content(news_info, variables, limit): try: event = variables['event'] content, char_offset, total_length = news_info.get_content( event.value['entry_link'], event.value['char_offset'], limit) src, alt, pic_index = news_info.get_picture( event.value['entry_link'], event.value['pic_index']) except KeyError: return [Message(u'抱歉,有些東西出錯了,請再試一次!')] content_ended = char_offset == -1 picture_ended = pic_index == -1 all_ended = content_ended and picture_ended has_picture = src and alt has_content = content != '' progress = float(char_offset) / total_length * 100 \ if char_offset >= 0 else 100.0 msgs = [] if has_content and not all_ended: m = Message(buttons_text=content) m.add_button(Message.Button( Message.ButtonType.POSTBACK, u'繼續讀 ({:.0f}%)'.format(progress), payload=EventPayload('GET_CONTENT', { 'entry_link': event.value['entry_link'], 'char_offset': char_offset, 'pic_index': pic_index, }, False))) m.add_button(Message.Button( Message.ButtonType.WEB_URL, u'去網站讀', url=event.value['entry_link'])) msgs.append(m) elif has_content: msgs.append(Message(content)) if has_picture: src_msg = Message(image_url=src) if alt.strip(): alt_msg = Message() b = Message.Bubble(alt, subtitle=' ') # Only append button to the last message, so only append if # content is empty if not has_content and not all_ended: b.add_button(Message.Button( Message.ButtonType.POSTBACK, u'繼續看圖 (文字已結束)', payload=EventPayload('GET_CONTENT', { 'entry_link': event.value['entry_link'], 'char_offset': char_offset, 'pic_index': pic_index, }, False))) b.add_button(Message.Button( Message.ButtonType.WEB_URL, u'去網站讀', url=event.value['entry_link'])) alt_msg.add_bubble(b) msgs = [src_msg, alt_msg] + msgs else: msgs = [src_msg] + msgs if all_ended: m = Message(u'這則新聞讀完囉!') add_quick_reply_keywords(news_info, m, True) msgs.append(m) return msgs
def run(content_config, unused_env, variables): """ content_config schema: { "query_url": "http://ecshweb.pchome.com.tw/search/v3.3/all/results?" "q=%s&page=1&sort=rnk/dc", "term": "{{matches#1}}", "max_count": 7, "list": "prods", "attributes": { "title": "${{price}}: {{name}}", "image": "http://a.ecimg.tw/{{picB}}", "item_url": "http://24h.pchome.com.tw/{{Id}}", "subtitle": "{{describe}}", "buttons": [ { "type": "web_url", "title": "商品頁面", "url": "http://24h.pchome.com.tw/{{Id}}" } ] } } """ url = content_config['query_url'] timeout = content_config.get('timeout_secs', DEFAULT_TIMEOUT_SECS) if 'term' in content_config: term = Resolve(content_config['term'], variables) url = url % urllib.quote(term.encode('utf-8')) try: content = json.loads(urllib2.urlopen(url, timeout=timeout).read()) except Exception: return [] max_count = content_config['max_count'] attributes = content_config['attributes'] elements = content.get(content_config['list'], []) if len(elements) > max_count: elements = elements[:max_count] m = Message() for element in elements: var = dict(variables, **element) bub = Message.Bubble(title=attributes['title'], item_url=attributes.get('item_url', None), image_url=attributes['image'], subtitle=attributes.get('subtitle', None), variables=var) for b in attributes.get('buttons', []): payload = b.get('payload', None) bub.add_button( Message.Button( b_type=Message.ButtonType(b['type']), title=b['title'], url=b.get('url', None), payload=TextPayload(payload) if payload else None, variables=var)) m.add_bubble(bub) return [m]