示例#1
0
    def __init__(self):
        # First time, read from bot and clear them.
        self.singleton = {k: Memory.Get(k, None) for k in self.KEYS}
        _LOG.info('User input: %r', self.singleton)

        for k in self.KEYS:
            Memory.Set(k, None)
示例#2
0
    def nearby_transaction(self, max_count, latlng, rules):
        """Returns transaction data near 'latlng' sorted by 'rules'.

        Args:
          max_count: int.
          latlng: tuple.
          rules: Rules. None: no criteria entered so far.

        Returns:
          list of transaction data.
        """

        db_files = [
            '/var/lib/twrealprice/twrealprice.db',
            '/var/lib/bb8/apps/twrealprice/resource/twrealprice.db',
        ]

        start_time = time.time()

        for db_file in db_files:
            try:
                conn = sqlite3.connect(db_file)
                break
            except sqlite3.OperationalError:
                _LOG.warn('Database file is not found: %s', db_file)
        else:
            raise IOError('Cannot find database in the following pathes: %s' %
                          ', '.join(db_files))

        nearby = []
        try:
            lat_center, lng_center = LatLngIndex(latlng[0], latlng[1])
        except ValueError:
            traceback.print_exc()
            return []

        for lat_in, lng_in in GenNearbyIndex(lat_center, lng_center, 1):
            for row in conn.execute('select tran_pickle from trans ' +
                                    'where lat_in = %d and lng_in = %d' %
                                    (lat_in, lng_in)):
                nearby += cPickle.loads(str(row[0]))

        conn.close()
        end_time = time.time()
        _LOG.info('DB search (%d data): %.6f sec ...', len(nearby),
                  end_time - start_time)

        for s in nearby:
            s['AGE'] = Age(s['建築完成年月'])[:-3]  # remove 年

        if not rules:
            rules = twrealprice_rule.Rules()
            rules.AddPureSortingRules(latlng)
        rules.CountScore(nearby)
        rules.Sort()

        Memory.Set('cached_transaction', rules.Top(MAX_CACHE_TRANS))
        return rules.Top(max_count)
示例#3
0
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
示例#4
0
文件: default.py 项目: aitjcize/bb8
def run(parser_config, user_input, as_root):
    """
    parser_config schema:
    {
       "links": [{
           "rule": {
               "type": "regexp",
               "params": ["^action1-[0-9]$", "action[23]-1"],
               "collect_as": {
                 "key": "keyname",
                 "value": "{{matches#1}}  # if omit, this will be {{text}}
               },
               "memory_set": {
                 "key": "keyname",
                 "value": "{{matches#1}}  # if omit, this will be {{text}}
               },
           },
           "end_node_id": 0,
           "ack_message": "action1 activated"
       }, {
           "rule": {
               "type": "location",
               "params": null
           },
           "end_node_id": 1,
           "ack_message": "action2 activated"
       }]
    }
    """
    collect = {}

    for link in parser_config['links']:
        r_type = link['rule']['type']

        def ret(link, variables, collect):
            end_node_id = link.get('end_node_id', None)
            if end_node_id:
                end_node_id = Render(end_node_id, variables)
            ack_msg = link.get('ack_message', None)
            return ParseResult(end_node_id, ack_msg, variables, collect)

        collect_as = link['rule'].get('collect_as')
        memory_set = link['rule'].get('memory_set')
        settings_set = link['rule'].get('settings_set')

        if r_type == 'regexp' and user_input.text:
            for param in link['rule']['params']:
                m = re.search(unicode(param), user_input.text)
                if m:
                    new_vars = {
                        'text': user_input.text,
                        'matches': [user_input.text] + list(m.groups())
                    }
                    if collect_as:
                        collect[collect_as['key']] = Render(
                            collect_as.get('value', '{{text}}'), new_vars)

                    if memory_set:
                        value = memory_set.get('value', '{{text}}')
                        if (isinstance(value, unicode)
                                or isinstance(value, str)):
                            value = Render(value, new_vars)
                        Memory.Set(memory_set['key'], value)

                    if settings_set:
                        value = settings_set.get('value', '{{text}}')
                        if (isinstance(value, unicode)
                                or isinstance(value, str)):
                            value = Render(value, new_vars)
                        Settings.Set(settings_set['key'], value)

                    return ret(link, new_vars, collect)
        elif r_type == 'location' and user_input.location:
            if collect_as:
                collect[collect_as['key']] = user_input.location
            if memory_set:
                Memory.Set(memory_set['key'], user_input.location)
            if settings_set:
                Settings.Set(settings_set['key'], user_input.location)
            return ret(link, {'location': user_input.location}, collect)
        elif r_type == 'event' and user_input.event:
            for param in link['rule']['params']:
                if re.search(param, user_input.event.key):
                    new_vars = {
                        'key': user_input.event.key,
                        'value': user_input.event.value
                    }
                    if memory_set:
                        value = memory_set.get('value', '{{text}}')
                        if (isinstance(value, unicode)
                                or isinstance(value, str)):
                            value = Render(value, new_vars)
                        Memory.Set(memory_set['key'], value)

                    if settings_set:
                        value = settings_set.get('value', '{{text}}')
                        if (isinstance(value, unicode)
                                or isinstance(value, str)):
                            value = Render(value, new_vars)
                        Settings.Set(settings_set['key'], value)

                    return ret(link, {'event': user_input.event}, {})
        elif r_type == 'sticker' and user_input.sticker:
            for param in link['rule']['params']:
                if re.search(param, user_input.sticker):
                    return ret(link, {'sticker': user_input.sticker}, {})

    if as_root:
        return ParseResult(errored=True)

    on_error = parser_config.get('on_error')
    if on_error:
        collect_as = on_error.get('collect_as')
        if collect_as:
            value = collect_as.get('value', '{{text}}')
            collect[collect_as['key']] = Render(value,
                                                {'text': user_input.text})

        variables = {'text': user_input.text}
        return ParseResult(Render(on_error['end_node_id'], variables),
                           on_error.get('ack_message'),
                           variables,
                           collect,
                           errored=True)

    return ParseResult(None,
                       'Invalid input, please re-enter',
                       {'text': user_input.text},
                       collect,
                       errored=True)
示例#5
0
文件: composeai.py 项目: aitjcize/bb8
def run(parser_config, user_input, unused_as_root):
    """
    {
      "links": [
        {
          "action_ident": "continue",
          "end_node_id": null,
          "ack_message": ""
        },
        {
          "action_ident": "done",
          "end_node_id": "[[root]]",
          "ack_message": ""
        }
      ]
    }
    """
    if user_input.text:
        if u'重設' in user_input.text or 'reset' in user_input.text.lower():
            Memory.Clear()
            return ParseResult(ack_message=u'讓我們重新開始吧!')

    if user_input.event:
        event = user_input.event
        if event.key == 'CONTROL_FLOW':
            if event.value == 'reset':
                Memory.Clear()
                return ParseResult(parser_config['done'], ack_message=u'放棄操作')
        elif event.key == 'SELECT_BOT':
            Memory.Set('bot', event.value)
        elif event.key == 'SELECT_OP':
            Memory.Set('operation', event.value)
        elif event.key == 'MESSAGE_INPUT':
            if event.value == 'done':
                Memory.Set('status', 'preview_message')
            elif event.value == 'restart':
                Memory.Set('broadcast_message', None)
                Memory.Set('status', None)
        elif event.key == 'CONFIRM_MESSAGE':
            if event.value:
                bot = Bot.get_by(id=Memory.Get('bot'), single=True)
                if not bot:
                    return ParseResult(ack_message=u'發生錯誤,找不到對應的' u'機器人')
                msgs = [
                    Message.FromDict(raw_msg)
                    for raw_msg in Memory.Get('broadcast_message')
                ]
                BroadcastMessage(bot, msgs)
                Memory.Clear()
                return ParseResult(parser_config['done'],
                                   ack_message=u'您的訊息已送出!')
            else:
                Memory.Set('broadcast_message', None)

    status = Memory.Get('status')
    if status == 'input_broadcast_message':
        broadcast_message = Memory.Get('broadcast_message') or []
        broadcast_message.append(user_input.raw_message)
        Memory.Set('broadcast_message', broadcast_message)

    return ParseResult(skip_content_module=False)
示例#6
0
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'錯誤的操作')]
示例#7
0
文件: drama.py 项目: aitjcize/bb8
def run(content_config, unused_env, variables):
    drama_info = DramaInfo()
    n_items = content_config.get('n_items', DEFAULT_N_ITEMS)
    user_id = GetUserId()

    def append_categories_to_quick_reply(m):
        m.add_quick_reply(
            Message.QuickReply(Message.QuickReplyType.TEXT, u'荼蘼'))
        m.add_quick_reply(
            Message.QuickReply(Message.QuickReplyType.TEXT, u'The K2 第十集'))
        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]

    if content_config['mode'] == 'subscribe':
        event = variables['event']
        drama_id = event.value['drama_id']
        Memory.Set('last_query_drama_id', drama_id)
        drama_info.subscribe(user_id, drama_id)

        episodes = drama_info.get_history(drama_id=drama_id,
                                          from_episode=0,
                                          backward=True)
        return ([
            Message(u'謝謝您的追蹤,'
                    u'我們會在有更新的時候通知您!'),
            Message(u'在等待的同時,'
                    u'您可以先看看之前的集數喲!')
        ] + render_episodes(episodes, variables))

    if content_config['mode'] == 'unsubscribe':
        event = variables['event']
        drama_id = event.value['drama_id']
        drama_info.unsubscribe(user_id, drama_id)

        return [Message(u'已成功取消訂閱')]

    if content_config['mode'] == 'get_history':
        event = variables['event']
        drama_id = event.value['drama_id']
        Memory.Set('last_query_drama_id', drama_id)
        from_episode = event.value['from_episode']
        backward = event.value['backward']
        episodes = drama_info.get_history(drama_id=drama_id,
                                          from_episode=from_episode,
                                          backward=backward)

        return render_episodes(episodes, variables)

    if content_config['mode'] == 'prompt':
        m = Message(u'你比較喜歡以下的什麼劇呢?')
        append_categories_to_quick_reply(m)
        return [m]

    country = content_config['mode'].replace('trending_', '')
    if content_config['mode'] == 'search':
        query_term = Resolve(content_config['query_term'], variables)
        dramas = drama_info.search(user_id, query_term, n_items)
        if dramas:
            if len(dramas) == 1:
                Memory.Set('last_query_drama_id', dramas[0].id)
            m = render_dramas(dramas)
            append_categories_to_quick_reply(m)
            return [m]
        m = Message(u'找不到耶!你可以換個關鍵字或試試熱門的類別:')
        append_categories_to_quick_reply(m)
        return [m]

    if content_config['mode'] == 'search_episode':
        if len(variables['matches']) == 3:
            dramas = drama_info.search(user_id, variables['matches'][1], 1)
            if dramas:
                Memory.Set('last_query_drama_id', dramas[0].id)

        drama_id = Memory.Get('last_query_drama_id')
        if drama_id:
            try:
                try:
                    serial_number = int(
                        Resolve(content_config['episode'], variables))
                except ValueError:
                    serial_number = convert_to_arabic_numbers(
                        Resolve(content_config['episode'], variables))
                episode = drama_info.get_episode(drama_id, serial_number)
            except Exception:
                return [Message('沒有這一集喔')]
            return render_episodes([episode], variables)
        else:
            return [Message('請先告訴我你要查的劇名')]

    m = render_dramas(drama_info.get_trending(user_id, country=country))
    append_categories_to_quick_reply(m)
    return [m]