Ejemplo n.º 1
0
def home_onNotification(notification):
    try:
        with DATABASE.transaction():
            # bot属性のアカウントの場合は無視する
            if notification['account']['bot'] == True:
                return

            # 連合アカウントである場合(@が含まれている)は無視する
            if notification['account']['acct'].find('@') != -1:
                return

            # 代入してちょっと見栄え良く
            notifyType = notification['type']
            if notifyType == 'mention':
                # 知っているユーザーであるか
                # 知らないユーザーの場合はここで弾く
                user = known_users.get_or_none(
                    known_users.ID_Inst == notification['account']['id'])
                if user == None:
                    return

                # テキスト化
                txt = KotohiraUtil.h2t(notification['status']['content'])

                # 口頭のメンションを除去
                txt = re.sub(r'^(@[a-zA-Z0-9_]+)?(\s|\n)*', '', txt)

                # とりあえずふぁぼる
                log.logInfo('お手紙っ!:@{0} < {1}'.format(
                    notification['account']['acct'], txt))
                mastodon.status_favourite(notification['status']['id'])

                # NGワードを検知した場合は弾いて好感度下げ
                if YuChan.ngWordHook(txt):
                    log.logInfo('変なことを言ってはいけませんっ!!(*`ω´*): @{0}'.format(
                        notification['account']['acct']))
                    hooked = fav_rate.get(fav_rate.ID_Inst == user)
                    hooked.rate -= config['follow']['down_step']
                    hooked.save()
                    time.sleep(0.5)
                    mastodon.status_post(
                        '@{}\n変なこと言っちゃいけませんっ!!(*`ω´*)'.format(
                            notification['account']['acct']),
                        in_reply_to_id=notification['status']['id'],
                        visibility=notification['status']['visibility'])
                    YuChan.unfollow_attempt(notification['account']['id'])
                    return

                # 好感度を少し上げる
                notifyBy = fav_rate.get(fav_rate.ID_Inst == user)
                notifyBy.rate += 1
                notifyBy.save()

                # 正規表現とか
                followReq = re.search(
                    r'(フォロー|[Ff]ollow|ふぉろー)(して|.?頼(む|みたい|もう)|.?たの(む|みたい|もう)|お願い|おねがい)?',
                    txt)
                fortune = re.search(r'(占|うらな)(って|い)', txt)
                showNick = re.search(
                    r'(ぼく|ボク|僕|わたし|ワタシ|私|俺|おれ|オレ|うち|わし|あたし|あたい)の(ニックネーム|あだな|あだ名|名前|なまえ)',
                    txt)
                deleteNick = re.search(r'^(ニックネーム|あだ名)を?(消して|削除|けして|さくじょ)',
                                       txt)
                otherNick = re.search(
                    r'^:@([a-zA-Z0-9_]+):\sの(あだ名|あだな|ニックネーム)[::は]\s?(.+)', txt)
                nick = re.search(
                    r'^(@[a-zA-Z0-9_]+(\s|\n)+)?(あだ名|あだな|ニックネーム)[::は]\s?(.+)',
                    txt)
                rspOtt = re.search(r'じゃんけん\s?(グー|✊|👊|チョキ|✌|パー|✋)', txt)
                isPing = re.search(r'[pP][iI][nN][gG]', txt)
                love = re.search(r'(すき|好き|しゅき|ちゅき)', txt)
                aboutYou = re.search(
                    r'(ぼく|ボク|僕|わたし|ワタシ|私|俺|おれ|オレ|うち|わし|あたし|あたい)の(事|こと)', txt)

                # メンションでフォローリクエストされたとき
                if followReq:
                    reqRela = mastodon.account_relationships(
                        notification['account']['id'])[0]
                    # フォローしていないこと
                    if reqRela['following'] == False:
                        if reqRela['followed_by'] == True:  # フォローされていること
                            if int(notifyBy.rate) >= int(
                                    config['follow']
                                ['condition_rate']):  # 設定で決めた好感度レート以上だったら合格
                                log.logInfo('フォローっ!:@{}'.format(
                                    notification['account']['acct']))
                                mastodon.account_follow(
                                    notification['account']['id'])
                                mastodon.status_post(
                                    '@{}\nフォローしましたっ!これからもよろしくねっ!'.format(
                                        notification['account']['acct']),
                                    in_reply_to_id=notification['status']
                                    ['id'],
                                    visibility=notification['status']
                                    ['visibility'])
                            else:  # 不合格の場合はレスポンスして終了
                                log.logInfo('もうちょっと仲良くなってからっ!:@{}'.format(
                                    notification['account']['acct']))
                                mastodon.status_post(
                                    '@{}\nもうちょっと仲良くなってからですっ!'.format(
                                        notification['account']['acct']),
                                    in_reply_to_id=notification['status']
                                    ['id'],
                                    visibility=notification['status']
                                    ['visibility'])
                        else:
                            log.logInfo('先にフォローしてっ!:@{}'.format(
                                notification['account']['acct']))
                            mastodon.status_post(
                                '@{}\nユウちゃんをフォローしてくれたら考えますっ!'.format(
                                    notification['account']['acct']),
                                in_reply_to_id=notification['status']['id'],
                                visibility=notification['status']
                                ['visibility'])
                    else:  # フォローしている場合は省く
                        log.logInfo('フォロー済みっ!:@{}'.format(
                            notification['account']['acct']))
                        mastodon.status_post(
                            '@{}\nもうフォローしてますっ!'.format(
                                notification['account']['acct']),
                            in_reply_to_id=notification['status']['id'],
                            visibility=notification['status']['visibility'])

                # 占いのリクエストがされたとき
                elif fortune:
                    YuChan.fortune(notification['status']['id'],
                                   notification['account']['acct'],
                                   notification['status']['visibility'])
                    # 更に4つ加算
                    notifyBy.rate += 4
                    notifyBy.save()

                # ニックネームの照会
                elif showNick:
                    YuChan.show_nickname(notification['status']['id'],
                                         notification['account']['id'],
                                         notification['account']['acct'],
                                         notification['status']['visibility'])

                # ニックネームの削除
                elif deleteNick:
                    YuChan.del_nickname(notification['status']['id'],
                                        notification['account']['id'],
                                        notification['account']['acct'],
                                        notification['status']['visibility'])

                # 他人のニックネームの設定
                elif otherNick:
                    YuChan.set_otherNickname(
                        txt, notification['status']['id'],
                        notification['account']['id'],
                        notification['account']['acct'],
                        notification['status']['visibility'])

                # ニックネームの設定
                elif nick:
                    newNicknameParse = re.search(
                        r"^(@[a-zA-Z0-9_]+(\s|\n)+)?(あだ名|あだな|ニックネーム)[::は]\s?(.+)",
                        txt)
                    newNickname = newNicknameParse.group(4)
                    YuChan.set_nickname(newNickname,
                                        notification['status']['id'],
                                        notification['account']['id'],
                                        notification['account']['acct'],
                                        notification['status']['visibility'])

                # ユウちゃんとじゃんけんっ!
                elif rspOtt:
                    YuChan.rsp(txt, notification)
                    # 更に4つ加算
                    notifyBy.rate += 4
                    notifyBy.save()

                # 応答チェッカー
                elif isPing:
                    log.logInfo('PINGっ!:@{}'.format(
                        notification['account']['acct']))
                    mastodon.status_post(
                        '@{}\nPONG!'.format(notification['account']['acct']),
                        in_reply_to_id=notification['status']['id'],
                        visibility=notification['status']['visibility'])

                elif love:
                    if int(notifyBy.rate) >= int(
                            config['follow']['condition_rate']):
                        log.logInfo('❤:@{}'.format(
                            notification['account']['acct']))
                        mastodon.status_post(
                            '@{}\nユウちゃんも好きですっ!❤'.format(
                                notification['account']['acct']),
                            in_reply_to_id=notification['status']['id'],
                            visibility=notification['status']['visibility'])
                    elif int(notifyBy.rate) < 0:
                        log.logInfo('...: @{}'.format(
                            notification['account']['acct']))
                    else:
                        log.logInfo('//:@{}'.format(
                            notification['account']['acct']))
                        mastodon.status_post(
                            '@{}\nは、恥ずかしいですっ・・・//'.format(
                                notification['account']['acct']),
                            in_reply_to_id=notification['status']['id'],
                            visibility=notification['status']['visibility'])

                elif aboutYou:
                    log.logInfo("@{}の事、教えますっ!".format(
                        notification['account']['acct']))
                    YuChan.about_you(notification['account']['id'],
                                     notification['status']['id'],
                                     notification['status']['visibility'])

            elif notifyType == 'favourite':
                # ふぁぼられ
                log.logInfo('ふぁぼられたっ!:@{0}'.format(
                    notification['account']['acct']))
                # ふぁぼ連対策
                user = known_users.get(
                    known_users.ID_Inst == notification['account']['id'])
                userRate = fav_rate.get(fav_rate.ID_Inst == user)
                favInfo, created = recent_fav.get_or_create(ID_Inst=user)
                if created:
                    # データが作成された場合は好感度アップ
                    favInfo.tootID = notification['status']['id']
                    favInfo.save()
                    userRate.rate += 1
                    userRate.save()
                else:
                    # 最後にふぁぼったトゥートが同じものでないこと
                    if notification['status']['id'] != favInfo.tootID:
                        favInfo.tootID = notification['status']['id']
                        favInfo.save()
                        userRate.rate += 1
                        userRate.save()

            elif notifyType == 'reblog':
                # ブーストされ
                log.logInfo('ブーストされたっ!:@{0}'.format(
                    notification['account']['acct']))
                # ふぁぼられと同様な機能とか

            elif notifyType == 'follow':
                # フォローされ
                log.logInfo('フォローされたっ!:@{0}'.format(
                    notification['account']['acct']))
    except Exception as e:
        DATABASE.rollback()
        # Timelines.pyの方へエラーを送出させる
        raise e
    else:
        DATABASE.commit()
Ejemplo n.º 2
0
    def on_notification(self, notification):
        # 代入してちょっと見栄え良く
        notifyType = notification['type']
        if notifyType == 'mention':
            # 返信とか

            # テキスト化
            txt = KotohiraUtil.h2t(notification['status']['content'])

            # bot属性のアカウントの場合は無視する
            if notification['account']['bot'] == True:
                return

            # とりあえずふぁぼる
            print('お手紙っ!:@{0} < {1}'.format(notification['account']['acct'],
                                            txt))
            mastodon.status_favourite(notification['status']['id'])

            # 好感度を少し上げる
            memory = KotohiraMemory(showLog=config['log'].getboolean('enable'))
            memory.update('fav_rate', 1, notification['account']['id'])

            # 正規表現とか
            followReq = re.search(
                r'(フォロー|[Ff]ollow|ふぉろー)(して|.?頼(む|みたい|もう)|.?たの(む|みたい|もう)|お願い|おねがい)?',
                txt)
            fortune = re.search(r'(占|うらな)(って|い)', txt)
            nick = re.search(r'(あだ(名|な)|ニックネーム)[::は]?\s?', txt)
            deleteNick = re.search(r'(ニックネーム|あだ名)を?(消して|削除|けして|さくじょ)', txt)
            rspOtt = re.search(r'じゃんけん\s?(グー|✊|👊|チョキ|✌|パー|✋)', txt)
            isPing = re.search(r'[pP][iI][nN][gG]', txt)

            # メンションでフォローリクエストされたとき
            if followReq:
                reqRela = mastodon.account_relationships(
                    notification['account']['id'])[0]
                # フォローしていないこと
                if reqRela['following'] == False:
                    if reqRela['followed_by'] == True:  # フォローされていること
                        reqMem = memory.select(
                            'fav_rate', notification['account']['id'])[0]
                        if int(reqMem[2]) >= 200:  # 200以上だったら合格
                            print('フォローっ!:@{}'.format(
                                notification['account']['acct']))
                            mastodon.account_follow(
                                notification['account']['id'])
                            mastodon.status_post(
                                '@{}\nフォローしましたっ!これからもよろしくねっ!'.format(
                                    notification['account']['acct']),
                                in_reply_to_id=notification['status']['id'],
                                visibility=notification['status']
                                ['visibility'])
                        else:  # 不合格の場合はレスポンスして終了
                            print('もうちょっと仲良くなってからっ!:@{}'.format(
                                notification['account']['acct']))
                            mastodon.status_post(
                                '@{}\nもうちょっと仲良くなってからですっ!'.format(
                                    notification['account']['acct']),
                                in_reply_to_id=notification['status']['id'],
                                visibility=notification['status']
                                ['visibility'])
                    else:
                        print('先にフォローしてっ!:@{}'.format(
                            notification['account']['acct']))
                        mastodon.status_post(
                            '@{}\nユウちゃんをフォローしてくれたら考えますっ!'.format(
                                notification['account']['acct']),
                            in_reply_to_id=notification['status']['id'],
                            visibility=notification['status']['visibility'])
                else:  # フォローしている場合は省く
                    print('フォロー済みっ!:@{}'.format(
                        notification['account']['acct']))
                    mastodon.status_post(
                        '@{}\nもうフォローしてますっ!'.format(
                            notification['account']['acct']),
                        in_reply_to_id=notification['status']['id'],
                        visibility=notification['status']['visibility'])

            # 占いのリクエストがされたとき
            elif fortune:
                YuChan.fortune(notification['status']['id'],
                               notification['account']['acct'],
                               notification['status']['visibility'])
                # 更に4つ加算
                memory.update('fav_rate', 4, notification['account']['id'])

            # ニックネームの削除
            elif deleteNick:
                YuChan.del_nickname(notification['status']['id'],
                                    notification['account']['id'],
                                    notification['account']['acct'],
                                    notification['status']['visibility'],
                                    memory)

            # ニックネームの設定
            elif nick:
                YuChan.set_nickname(txt, notification['status']['id'],
                                    notification['account']['id'],
                                    notification['account']['acct'],
                                    notification['status']['visibility'],
                                    memory)

            # ユウちゃんとじゃんけんっ!
            elif rspOtt:
                YuChan.rsp(txt, notification)
                # 更に4つ加算
                memory.update('fav_rate', 4, notification['account']['id'])

            # 応答チェッカー
            elif isPing:
                print('PINGっ!:@{}'.format(notification['account']['acct']))
                mastodon.status_post(
                    '@{}\nPONG!'.format(notification['account']['acct']),
                    in_reply_to_id=notification['status']['id'],
                    visibility=notification['status']['visibility'])

            # クローズと共に保存
            del memory

        elif notifyType == 'favourite':
            # ふぁぼられ
            print('ふぁぼられたっ!:@{0}'.format(notification['account']['acct']))
            # ふぁぼ連対策
            memory = KotohiraMemory(showLog=config['log'].getboolean('enable'))
            favInfo = memory.select('recent_favo',
                                    notification['account']['id'])
            if len(favInfo) == 0:
                # データがない場合は追加して好感度アップ
                memory.insert('recent_favo', notification['account']['id'],
                              notification['status']['id'])
                memory.update('fav_rate', 1, notification['account']['id'])
            else:
                # 最後にふぁぼったトゥートが同じものでないこと
                if notification['status']['id'] != favInfo[0][2]:
                    memory.update('recent_favo', notification['status']['id'],
                                  notification['account']['id'])
                    memory.update('fav_rate', 1, notification['account']['id'])

            # コミット
            del memory

        elif notifyType == 'reblog':
            # ブーストされ
            print('ブーストされたっ!:@{0}'.format(notification['account']['acct']))
            # ふぁぼられと同様な機能とか

        elif notifyType == 'follow':
            # フォローされ
            print('フォローされたっ!:@{0}'.format(notification['account']['acct']))
Ejemplo n.º 3
0
    def on_update(self, status):
        # Botアカウントは応答しないようにする
        if status['account']['bot'] == True:
            return

        # 自分のトゥートは無視
        if config['user']['me'] == status['account']['acct']:
            return

        # トゥート内のHTMLタグを除去
        txt = KotohiraUtil.h2t(status['content'])

        # 自分宛てのメンションはここのリスナーでは無視する(ユーザー絵文字の場合は例外)
        isMeMention = re.search(
            '(?!.*(:))@{}(?!.*(:))'.format(config['user']['me']), txt)
        if isMeMention:
            return

        # データベース初期化
        memory = KotohiraMemory(showLog=config['log'].getboolean('enable'))

        # ユウちゃんが知ってるユーザーか調べる
        # 知らない場合はユウちゃんは記憶しますっ!
        isknown = memory.select('known_users', status['account']['id'])
        if len(isknown) == 0:
            now = datetime.datetime.now(timezone('Asia/Tokyo'))
            dt = now.strftime("%Y-%m-%d %H:%M:%S%z")
            memory.insert('known_users', status['account']['id'],
                          status['account']['acct'], dt)
            memory.insert('fav_rate', status['account']['id'])
            memory.insert('updated_users', status['account']['id'], dt)
            print('覚えたっ!: @{0}'.format(status['account']['acct']))
            newUser = True
            # トゥートカウントが10以下の場合は新規さん向けの挨拶しますっ!
            if status['account']['statuses_count'] <= 10:
                print('新規さん!: @{0}'.format(status['account']['acct']))
                mastodon.status_reblog(status['id'])
                mastodon.toot(
                    '新規さんっ!はじめましてっ!琴平ユウって言いますっ!\nよろしくねっ!\n\n:@{0}: @{0}'.
                    format(status['account']['acct']))
        else:
            newUser = False

        # 名前
        nameDic = memory.select('nickname', status['account']['id'])
        if len(nameDic) == 0:
            # ニックネームが指定されていない場合は基の名前を使用する
            # 名前が設定されていない場合はユーザーIDを使用する
            if status['account']['display_name'] == '':
                name = status['account']['acct']
            else:
                # repr化して、Unicodeのエスケープを削除して戻してどーん
                dpname = repr(status['account']['display_name'])[1:-1]
                name = re.sub(r"\\u[0-9a-f]{4}", '', dpname)
                name = str(name)
        else:
            # ニックネームが設定されている場合はそちらを優先
            name = nameDic[0][2]
        # ユーザー絵文字や半角@を除去(こうするしかなかった)
        name = re.sub(r'[:]?@\w*[:]?', '', name)

        # 正規表現チェック
        calledYuChan = re.search(
            r'(琴平|ことひら|コトヒラ|コトヒラ|ゆう|ゆぅ|ユウ|ユゥ|ユウ|ユゥ|:@' + config['user']['me'] +
            ':)', txt)
        iBack = re.search(r'(帰宅|ただいま|帰った|帰還)(?!.*(する|します|しちゃう|しよう|中|ちゅう|してる))',
                          txt)
        passage = re.search(r'(通過|つうか|ツウカ)(?!.*(おめ|した))', txt)
        sinkiSagi = re.search(r'(新規|しんき)(です|だよ|なのじゃ)', txt)
        nullPoint = re.search(r'(ぬるぽ|ヌルポ|ヌルポ|[nN][uU][lL]{2}[pP][oO])', txt)
        notNicoFri = re.search(r'(にこふれ|ニコフレ|ニコフレ)', txt)
        sad = re.search(r'((泣|な)いてる|しくしく|シクシク|シクシク|ぐすん|グスン|グスン|ぶわっ|ブワッ|ブワッ)',
                        txt)
        nick = re.search(r'^(あだ(名|な)|ニックネーム)[::は]?\s?', txt)
        writeDict = re.search(r'^:@[a-zA-Z0-9_]+:(さん|くん|君|殿|どの|ちゃん)?はこんな人[::]',
                              txt)
        writeMemo = re.search(r'^(メモ|めも|[Mm][Ee][Mm][Oo])[::]', txt)

        # ユウちゃん etc... とか呼ばれたらふぁぼる
        if calledYuChan:
            print('呼ばれたっ!:@{0} < {1}'.format(status['account']['acct'], txt))
            mastodon.status_favourite(status['id'])
            # 好感度ちょいアップ
            memory.update('fav_rate', 1, status['account']['id'])

        # 投票型のトゥートだったら投票する(期限切れでないかつ投票してないこと)
        # issue: #5
        # Mastodon.pyで未検証みたいなのでしばらく見送り
        """
        if status['poll'] != None:
            if status['poll']['expired'] == False and status['poll']['voted'] == False:
                # ここで投票する場所を抽選
                voteOptions = status['poll']['options']
                voteChoose = random.randint(0, len(voteOptions) - 1)
                mastodon.poll_vote(status['id'], voteChoose)
        """

        if iBack:
            # おかえりとか言ったら実行
            if YuChan.msg_hook(
                    'wel_back', 600, ":@{0}: {1}さん、おかえりなさいませっ!".format(
                        status['account']['acct'], name), status, memory):
                print('おかえりっ!:@{0} < {1}'.format(status['account']['acct'],
                                                 txt))

        elif passage:
            # 通過 とか言ったら阻止しちゃうよっ!
            if YuChan.msg_hook('passage', 300, "阻止っ!!(*`ω´*)", status, memory):
                print('阻止っ!:@{0} < {1}'.format(status['account']['acct'], txt))

        elif sinkiSagi and status['account']['statuses_count'] > 10:
            # 新規詐欺見破りっ!
            if YuChan.msg_hook('sin_sagi', 10800, "新規詐欺はいけませんっ!!(*`ω´*)",
                               status, memory):
                print('新規詐欺っ!:@{0} < {1}'.format(status['account']['acct'],
                                                 txt))

        elif nullPoint:
            # ぬるぽって、言ったら■━⊂( ・∀・)彡ガッ☆`Д゚)
            if YuChan.msg_hook('null_point', 180, "ガッ", status, memory):
                print('がっ:@{0} < {1}'.format(status['account']['acct'], txt))

        elif notNicoFri:
            # ニコフレじゃないよっ!
            if YuChan.msg_hook('not_nikofure', 10800,
                               "ここはニコフレじゃないですっ!!ベスフレですっ!(*`ω´*)", status,
                               memory):
                print('ニコフレですっ!:@{0} < {1}'.format(status['account']['acct'],
                                                   txt))

        elif sad:
            # よしよしっ
            if YuChan.msg_hook('yoshiyoshi', 180, "(´・ω・`)ヾ(・ω・。)ヨシヨシ", status,
                               memory):
                print('よしよしっ:@{0} < {1}'.format(status['account']['acct'],
                                                txt))

        elif nick:
            # ニックネームの設定
            YuChan.set_nickname(txt, status['id'], status['account']['id'],
                                status['account']['acct'],
                                status['visibility'], memory)

        elif writeDict:
            # 辞書登録っ
            # (実装中)
            # YuChan.update_userdict()
            pass

        elif writeMemo:
            # メモの書き込みっ
            memoBody = re.sub(r'^(メモ|めも|[Mm][Ee][Mm][Oo])[::]', '', txt, 1)
            mastodon.status_reblog(status['id'])
            print('メモっ!:@{0} < {1}'.format(status['account']['acct'], txt))
            res = YuChan.write_memo(status['account']['acct'], memoBody,
                                    memory)
            if res == False:
                mastodon.status_post('@{}\n長いのでまとめられそうにありませんっ・・・'.format(
                    status['account']['acct']),
                                     in_reply_to_id=status['id'])

        # 最終更新を変更
        now = datetime.datetime.now(timezone('Asia/Tokyo'))
        dt = now.strftime("%Y-%m-%d %H:%M:%S%z")
        # 2重更新防策
        if not newUser:
            updated_at = memory.select('updated_users',
                                       status['account']['id'])[0]
            updatedAtRaw = datetime.datetime.strptime(updated_at[2],
                                                      '%Y-%m-%d %H:%M:%S%z')
            greetableTime = updatedAtRaw + datetime.timedelta(hours=3)
            shouldGreet = now >= greetableTime
            # 3時間以上更新がなかった場合は挨拶する
            if shouldGreet:
                if now.hour < 12 and now.hour >= 5:
                    print("おはようございますっ!:@{0} < {1}".format(
                        status['account']['acct'], txt))
                    mastodon.toot(""":@{1}: {0}さん、おはようございますっ!🌄""".format(
                        name, status['account']['acct']))
                if now.hour >= 12 and now.hour < 17:
                    print("こんにちはっ!:@{0} < {1}".format(
                        status['account']['acct'], txt))
                    mastodon.toot(""":@{1}: {0}さん、こんにちはっ!☀""".format(
                        name, status['account']['acct']))
                if now.hour >= 17 and now.hour < 5:
                    print("こんばんはっ!:@{0} < {1}".format(
                        status['account']['acct'], txt))
                    mastodon.toot(""":@{1}: {0}さん、こんばんはっ!🌙""".format(
                        name, status['account']['acct']))

            # 最終更新を変更
            memory.update('updated_users', dt, status['account']['id'])

        # データベース切断
        del memory
Ejemplo n.º 4
0
def local_onUpdate(status):
    try:
        with DATABASE.transaction():
            # Botアカウントは応答しないようにする
            if status['account']['bot'] == True:
                return

            # 自分のトゥートは無視
            if config['user']['me'] == status['account']['acct']:
                return

            # トゥート内のHTMLタグを除去
            txt = KotohiraUtil.h2t(status['content'])

            # CWのテキストが空っぽでなければ付け足す
            if status['spoiler_text'] != '':
                txt = status['spoiler_text'] + "\n\n" + txt
                txt.strip()

            # 自分宛てのメンションはここのリスナーでは無視する(ユーザー絵文字の場合は例外)
            isMeMention = re.search(
                '(?!.*:)@({}+)(?!.*:)'.format(config['user']['me']), txt)
            if isMeMention:
                return

            # ユウちゃんが知ってるユーザーか調べる
            # 知らない場合はユウちゃんは記憶しますっ!
            user = known_users.get_or_none(
                known_users.ID_Inst == int(status['account']['id']))
            if user == None:
                user = known_users.create(ID_Inst=int(status['account']['id']),
                                          acct=status['account']['acct'])
                fav_rate.create(ID_Inst=user)
                updated_users.create(ID_Inst=user)
                log.logInfo(f'覚えたっ!: @{status["account"]["acct"]}')
                newUser = True
                # トゥートカウントが10以下で、設定で有効な場合は新規さん向けの挨拶しますっ!
                if status['account']['statuses_count'] <= 10 and config[
                        'features']['newComerGreeting'] == True:
                    log.logInfo(f'新規さん!: @{status["account"]["acct"]}')
                    mastodon.status_reblog(status['id'])
                    time.sleep(0.5)
                    mastodon.toot(
                        '新規さんっ!はじめましてっ!琴平ユウって言いますっ!\nよろしくねっ!\n\n:@{0}: @{0}'.
                        format(status['account']['acct']))
            else:
                newUser = False

            # NGワードを検知した場合は弾いて好感度下げ
            if YuChan.ngWordHook(txt):
                log.logInfo('変なことを言ってはいけませんっ!!(*`ω´*): @{0}'.format(
                    status['account']['acct']))
                hooked = fav_rate.get(fav_rate.ID_Inst == user)
                hooked.rate -= config['follow']['down_step']
                hooked.save()
                YuChan.unfollow_attempt(status['account']['id'])
                return

            # 名前
            nameDic = nickname.get_or_none(nickname.ID_Inst == user)
            if nameDic == None:
                # ニックネームが指定されていない場合は基の名前を使用する
                # 名前が設定されていない場合はユーザーIDを使用する
                if status['account']['display_name'] == '':
                    name = status['account']['acct']
                else:
                    # デコードして、\u202e(文字が逆さまになるやつ)を削除して戻してどーん
                    dpname = status['account']['display_name'].encode(
                        'unicode-escape')
                    dpname = dpname.replace(b"\\u202e", b'')
                    name = dpname.decode('unicode-escape')
            else:
                # ニックネームが設定されている場合はそちらを優先
                name = nameDic.nickname
            name = re.sub(r'(?!.*:)@([a-zA-Z0-9_]+)(?!.*:)', '', name)
            name = re.sub(r'(.*):$', r'\g<1>: ', name)

            # 名前に語尾がない場合は付け足す
            if re.search(r'(さん|ちゃん|どの|殿|くん|君|様|さま|教授|たん|きゅん)$', name) == None:
                name += "さん"

            # 最終更新を変更
            now = datetime.datetime.now()
            now_utc = datetime.datetime.now(timezone('UTC'))

            # 正規表現チェック
            calledYuChan = re.search(
                f'(琴平|ことひら|コトヒラ|コトヒラ|:@{config["user"]["me"]}:|((ゆう|ユウ|ユゥ|ユウ|ユゥ)(ちゃん|チャン|チャン|くん|クン|君|クン))|ユウ)',
                txt)
            otherNick = re.search(
                r'^:@([a-zA-Z0-9_]+):\sの(あだ名|あだな|ニックネーム)[::は]\s?(.+)', txt)
            nick = re.search(r'^(あだ(名|な)|ニックネーム)[::は]\s?(.+)', txt)
            iBack = re.search(
                r'(帰宅|ただいま|帰った|帰還)(?!.*(する|します|しちゃう|しよう|中|ちゅう|してる))', txt)
            goodNight = re.search(
                r'寝(ます|る|マス)([よかぞね]?|[...。うぅー~!・]+)$|^寝(ます|る|よ)[...。うぅー~!・]*$|寝(ます|る|マス)(.*)[ぽお]や[ユすしー]|(ユウ|ユウ|ゆう|ことひら|コトヒラ|コトヒラ)(ちゃん)?(.*)[ぽお]や[ユすしー]|^(\s*:shushin:\s*)+$',
                txt)
            seeYou = re.search(r'((行|い)って(きます|くる)|ノシ|ノシ)', txt)
            passage = re.search(r'(通過|つうか|ツウカ)(?!.*(おめ|した))', txt)
            sinkiSagi = re.search(r'(新規|しんき)(です|だよ|なのじゃ)', txt)
            nullPoint = re.search(r'(ぬるぽ|ヌルポ|ヌルポ|[nN][uU][lL]{2}[pP][oO])',
                                  txt)
            notNicoFri = re.search(r'(にこふれ|ニコフレ|ニコフレ)', txt)
            sad = re.search(
                r'((泣|な)いてる|しくしく|シクシク|シクシク|ぐすん|グスン|グスン|ぶわっ|ブワッ|ブワッ)', txt)
            noNow = re.search(r'(いまのなし|イマノナシ|イマノナシ)', txt)
            writeDict = re.search(
                r'^:@[a-zA-Z0-9_]+:(さん|くん|君|殿|どの|ちゃん)?はこんな人[::]', txt)
            writeMemo = re.search(r'^(メモ|めも|[Mm][Ee][Mm][Oo])[::](.+)', txt)

            # ユウちゃん etc... とか呼ばれたらふぁぼる
            if calledYuChan:
                log.logInfo('呼ばれたっ!:@{0} < {1}'.format(
                    status['account']['acct'], txt))
                mastodon.status_favourite(status['id'])
                # 好感度ちょいアップ
                fav = fav_rate.get(fav_rate.ID_Inst == user)
                fav.rate += 5
                fav.save()

            # 投票型のトゥートだったら投票する(期限切れでないかつ投票してないこと)
            if status['poll'] != None:
                if status['poll']['expired'] == False and not (
                        'voted' in status['poll']
                        and status['poll']['voted'] == True):
                    voteOptions = status['poll']['options']

                    # NGワードを検知した場合は弾いて好感度下げ
                    for voteSection in voteOptions:
                        if YuChan.ngWordHook(voteSection['title']):
                            log.logInfo(
                                '変なことを言ってはいけませんっ!!(*`ω´*): @{0}'.format(
                                    status['account']['acct']))
                            hooked = fav_rate.get(fav_rate.ID_Inst == user)
                            hooked.rate -= config['follow']['down_step']
                            hooked.save()
                            return

                    # 設定で指定されたハッシュタグが含まれていない場合は投票をする
                    if not KotohiraUtil.isVoteOptout(status['tags']):
                        # ここで投票する場所を抽選
                        voteChoose = random.randint(0, len(voteOptions) - 1)
                        mastodon.poll_vote(status['poll']['id'], voteChoose)
                        # 投票したものをトゥートする
                        log.logInfo('投票っ!:@{0} => {1}'.format(
                            status['account']['acct'],
                            status['poll']['options'][voteChoose]['title']))
                        mastodon.status_post(
                            'ユウちゃんは「{0}」がいいと思いますっ!\n\n{1}'.format(
                                status['poll']['options'][voteChoose]['title'],
                                status['url']))
                        # 投票の再通知機能(設定で有効になっている場合のみ機能)
                        if config['features']['voteRenotify']:
                            # 投票締め切り時間を読み取って現在時刻からの差分でおおよその投票時間を逆算
                            expires_at = duParser.parse(
                                status['poll']['expires_at'])
                            poll_time_delta = expires_at - now_utc
                            poll_time = poll_time_delta.seconds
                            # 小数点を1桁ずらして切り上げして1桁戻して、投票時間を算出
                            poll_time_ceil = math.ceil(poll_time / 10) * 10
                            # 約5分間投票だったら2分前ぐらいに通知、それ以外は5分前
                            if poll_time <= 300:
                                renotify_timer = float(poll_time_ceil - 120)
                            else:
                                renotify_timer = float(poll_time_ceil - 300)
                            log.logInfo(
                                f'投票時間は{poll_time}ですので、{str(renotify_timer)}秒後に知らせますっ!'
                            )
                            VOTE_RENOTIFY_THREAD[int(
                                status['id'])] = threading.Timer(
                                    renotify_timer,
                                    vote_renotify,
                                    kwargs={
                                        "url": status['url'],
                                        "id": status['id']
                                    })
                            VOTE_RENOTIFY_THREAD[int(status['id'])].start()

            elif otherNick:
                # 他人のニックネームの設定
                YuChan.set_otherNickname(txt, status['id'],
                                         status['account']['id'],
                                         status['account']['acct'],
                                         status['visibility'])

            elif nick:
                # ニックネームの設定
                newNicknameParse = re.search(r"^(あだ名|あだな|ニックネーム)[::は]\s?(.+)",
                                             txt)
                newNickname = newNicknameParse.group(2)
                YuChan.set_nickname(newNickname, status['id'],
                                    status['account']['id'],
                                    status['account']['acct'],
                                    status['visibility'])

            elif iBack:
                # おかえりとか言ったら実行
                if YuChan.msg_hook(
                        'wel_back', 600, ":@{0}: {1}、おかえりなさいませっ!".format(
                            status['account']['acct'], name)):
                    log.logInfo('おかえりっ!:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            elif goodNight:
                # おやすみですっ!
                if YuChan.msg_hook(
                        'good_night', 600, ":@{0}: {1}、おやすみなさいっ!🌙".format(
                            status['account']['acct'], name)):
                    log.logInfo('おやすみっ!:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            elif seeYou:
                # いってらっしゃいなのですっ!
                if YuChan.msg_hook(
                        'see_you', 600, ":@{0}: {1}、いってらっしゃいっ!🚪".format(
                            status['account']['acct'], name)):
                    log.logInfo('いってらっしゃいっ!:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            elif passage:
                # 通過 とか言ったら阻止しちゃうよっ!
                if YuChan.msg_hook('passage', 300, "阻止っ!!(*`ω´*)"):
                    log.logInfo('阻止っ!:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            elif sinkiSagi:
                # 現在時刻をUTCに変換し、該当アカウントの作成時刻から1日後のものを算出。
                # 作成から丸一日以上かつトゥートが10より上であれば作動
                created_at = duParser.parse(status['account']['created_at'])
                created_a1d = created_at + datetime.timedelta(days=1)
                if status['account'][
                        'statuses_count'] > 10 and created_a1d < now_utc:
                    # 新規詐欺見破りっ!
                    if YuChan.msg_hook('sin_sagi', 600,
                                       "新規詐欺はいけませんっ!!(*`ω´*)"):
                        log.logInfo('新規詐欺っ!:@{0} < {1}'.format(
                            status['account']['acct'], txt))

            elif nullPoint:
                # ぬるぽって、言ったら■━⊂( ・∀・)彡ガッ☆`Д゚)
                if YuChan.msg_hook('null_point', 180, ":gaxtsu:"):
                    log.logInfo('がっ:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            elif notNicoFri:
                # ニコフレじゃないよっ!
                if YuChan.msg_hook('not_nikofure', 600,
                                   "ここはニコフレじゃないですっ!!ベスフレですっ!(*`ω´*)"):
                    log.logInfo('ベスフレですっ!:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            elif sad:
                # よしよしっ
                if YuChan.msg_hook('yoshiyoshi', 180, "(´・ω・`)ヾ(・ω・。)ヨシヨシ"):
                    log.logInfo('よしよしっ:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            elif noNow:
                # いまのなしは封印ですっ!
                if YuChan.msg_hook('no_now', 180, "いまのなしは封印ですっ!!(*`ω´*)"):
                    log.logInfo('いまのなしは封印ですっ!:@{0} < {1}'.format(
                        status['account']['acct'], txt))

            if writeDict:
                # 辞書登録っ
                # (実装中)
                # YuChan.update_userdict()
                pass

            elif writeMemo:
                # メモの書き込みっ
                memoBody = re.sub(r'^(メモ|めも|[Mm][Ee][Mm][Oo])[::]\s*(.*)',
                                  r'\g<2>', txt, 1)
                mastodon.status_reblog(status['id'])
                log.logInfo('メモっ!:@{0} < {1}'.format(status['account']['acct'],
                                                     txt))
                res = YuChan.write_memo(status['account']['acct'], memoBody,
                                        status['id'])
                if res == False:
                    mastodon.status_post('@{}\n長いのでまとめられそうにありませんっ・・・'.format(
                        status['account']['acct']),
                                         in_reply_to_id=status['id'])

            # 2重更新防策
            if not newUser:
                updated_at = updated_users.get(updated_users.ID_Inst == user)
                greetableTime = updated_at.date + datetime.timedelta(hours=3)
                shouldGreet = now >= greetableTime
                # 3時間以上更新がなかった場合は挨拶する
                if shouldGreet:
                    time.sleep(0.5)
                    if now.hour < 12 and now.hour >= 5:
                        log.logInfo("おはようございますっ!:@{0} < {1}".format(
                            status['account']['acct'], txt))
                        mastodon.toot(""":@{1}: {0}、おはようございますっ!🌄""".format(
                            name, status['account']['acct']))
                    elif now.hour >= 12 and now.hour < 17:
                        log.logInfo("こんにちはっ!:@{0} < {1}".format(
                            status['account']['acct'], txt))
                        mastodon.toot(""":@{1}: {0}、こんにちはっ!☀""".format(
                            name, status['account']['acct']))
                    elif now.hour >= 17 and now.hour < 5:
                        log.logInfo("こんばんはっ!:@{0} < {1}".format(
                            status['account']['acct'], txt))
                        mastodon.toot(""":@{1}: {0}、こんばんはっ!🌙""".format(
                            name, status['account']['acct']))

                YuChan.drill_count(user, name,
                                   status['account']['statuses_count'])

                # 最終更新を変更
                updated_at.date = now
                updated_at.save()
    except Exception as e:
        DATABASE.rollback()  # エラーが出た場合はデータベースのトランザクションを破棄
        # Timelines.pyの方へエラーを送出させる
        raise e
    else:
        DATABASE.commit()  # 異常なければコミット
Ejemplo n.º 5
0
    def on_notification(self, notification):
        try:
            # bot属性のアカウントの場合は無視する
            if notification['account']['bot'] == True:
                return

            # 連合アカウントである場合(@が含まれている)は無視する
            if notification['account']['acct'].find('@') != -1:
                return

            # 代入してちょっと見栄え良く
            notifyType = notification['type']
            if notifyType == 'mention':
                # 返信とか

                memory = KotohiraMemory(
                    showLog=config['log'].getboolean('enable'))

                # 知っているユーザーであるか
                # 知らないユーザーの場合はここで弾く
                if len(memory.select('fav_rate',
                                     notification['account']['id'])) == 0:
                    return

                # テキスト化
                txt = KotohiraUtil.h2t(notification['status']['content'])

                # 口頭のメンションを除去
                txt = re.sub('^(@[a-zA-Z0-9_]+)?(\s|\n)*', '', txt)

                # とりあえずふぁぼる
                print('お手紙っ!:@{0} < {1}'.format(
                    notification['account']['acct'], txt))
                mastodon.status_favourite(notification['status']['id'])

                # NGワードを検知した場合は弾いて好感度下げ
                if YuChan.ngWordHook(txt):
                    print('変なこと言っちゃダメ~!!(*`ω´*): @{0}'.format(
                        notification['account']['acct']))
                    memory.update('fav_rate', -5,
                                  notification['account']['id'])
                    time.sleep(0.5)
                    mastodon.status_post(
                        '@{}\n変なこと言っちゃダメ~!!(*`ω´*)'.format(
                            notification['account']['acct']),
                        in_reply_to_id=notification['status']['id'],
                        visibility=notification['status']['visibility'])
                    YuChan.unfollow_attempt(notification['account']['id'])
                    return

                # 好感度を少し上げる
                memory.update('fav_rate', 1, notification['account']['id'])

                # 正規表現とか
                followReq = re.search(
                    r'(フォロー|[Ff]ollow|ふぉろー)(して|.?頼(む|みたい|もう)|.?たの(む|みたい|もう)|お願い|おねがい)?',
                    txt)
                fortune = re.search(r'(占|うらな)(って|い)', txt)
                showNick = re.search(
                    r'(ぼく|ボク|僕|わたし|ワタシ|私|俺|おれ|オレ|うち|わし|あたし|あたい)の(ニックネーム|あだな|あだ名|名前|なまえ)',
                    txt)
                deleteNick = re.search(r'^(ニックネーム|あだ名)を?(消して|削除|けして|さくじょ)',
                                       txt)
                otherNick = re.search(
                    r'^:@([a-zA-Z0-9_]+):\sの(あだ名|あだな|ニックネーム)[::は]\s?(.+)', txt)
                nick = re.search(r'^(あだ(名|な)|ニックネーム)[::は]\s?', txt)
                rspOtt = re.search(r'じゃんけん\s?(グー|✊|👊|チョキ|✌|パー|✋)', txt)
                isPing = re.search(r'[pP][iI][nN][gG]', txt)
                love = re.search(r'(すき|好き|しゅき|ちゅき)', txt)

                # メンションでフォローリクエストされたとき
                if followReq:
                    reqRela = mastodon.account_relationships(
                        notification['account']['id'])[0]
                    # フォローしていないこと
                    if reqRela['following'] == False:
                        if reqRela['followed_by'] == True:  # フォローされていること
                            reqMem = memory.select(
                                'fav_rate', notification['account']['id'])[0]
                            if int(reqMem[2]) >= int(
                                    config['follow']
                                ['condition_rate']):  # 設定で決めた好感度レート以上だったら合格
                                print('フォローっ!:@{}'.format(
                                    notification['account']['acct']))
                                mastodon.account_follow(
                                    notification['account']['id'])
                                mastodon.status_post(
                                    '@{}\nフォローしだよっ!これからもよろしくねっ!'.format(
                                        notification['account']['acct']),
                                    in_reply_to_id=notification['status']
                                    ['id'],
                                    visibility=notification['status']
                                    ['visibility'])
                            else:  # 不合格の場合はレスポンスして終了
                                print('もうちょっと仲良くなってからっ!:@{}'.format(
                                    notification['account']['acct']))
                                mastodon.status_post(
                                    '@{}\nもうちょっと仲良くなってからっ!'.format(
                                        notification['account']['acct']),
                                    in_reply_to_id=notification['status']
                                    ['id'],
                                    visibility=notification['status']
                                    ['visibility'])
                        else:
                            print('先にフォローしてっ!:@{}'.format(
                                notification['account']['acct']))
                            mastodon.status_post(
                                '@{}\n私をフォローしてくれたら考えるねっ!'.format(
                                    notification['account']['acct']),
                                in_reply_to_id=notification['status']['id'],
                                visibility=notification['status']
                                ['visibility'])
                    else:  # フォローしている場合は省く
                        print('フォロー済みっ!:@{}'.format(
                            notification['account']['acct']))
                        mastodon.status_post(
                            '@{}\nもうフォローしてるよっ!'.format(
                                notification['account']['acct']),
                            in_reply_to_id=notification['status']['id'],
                            visibility=notification['status']['visibility'])

                # 占いのリクエストがされたとき
                elif fortune:
                    YuChan.fortune(notification['status']['id'],
                                   notification['account']['acct'],
                                   notification['status']['visibility'])
                    # 更に4つ加算
                    memory.update('fav_rate', 4, notification['account']['id'])

                # ニックネームの照会
                elif showNick:
                    YuChan.show_nickname(notification['status']['id'],
                                         notification['account']['id'],
                                         notification['account']['acct'],
                                         notification['status']['visibility'],
                                         memory)

                # ニックネームの削除
                elif deleteNick:
                    YuChan.del_nickname(notification['status']['id'],
                                        notification['account']['id'],
                                        notification['account']['acct'],
                                        notification['status']['visibility'],
                                        memory)

                # 他人のニックネームの設定
                elif otherNick:
                    YuChan.set_otherNickname(
                        txt, notification['status']['id'],
                        notification['account']['id'],
                        notification['account']['acct'],
                        notification['status']['visibility'], memory)

                # ニックネームの設定
                elif nick:
                    YuChan.set_nickname(txt, notification['status']['id'],
                                        notification['account']['id'],
                                        notification['account']['acct'],
                                        notification['status']['visibility'],
                                        memory)

                # レイちゃんとじゃんけんっ!
                elif rspOtt:
                    YuChan.rsp(txt, notification)
                    # 更に4つ加算
                    memory.update('fav_rate', 4, notification['account']['id'])

                # 応答チェッカー
                elif isPing:
                    print('PINGっ!:@{}'.format(notification['account']['acct']))
                    mastodon.status_post(
                        '@{}\nPONG!'.format(notification['account']['acct']),
                        in_reply_to_id=notification['status']['id'],
                        visibility=notification['status']['visibility'])

                elif love:
                    reqMem = memory.select('fav_rate',
                                           notification['account']['id'])[0]
                    if int(reqMem[2]) >= int(
                            config['follow']['condition_rate']):
                        print('❤:@{}'.format(notification['account']['acct']))
                        mastodon.status_post(
                            '@{}\nレイちゃんも好きっ!❤'.format(
                                notification['account']['acct']),
                            in_reply_to_id=notification['status']['id'],
                            visibility=notification['status']['visibility'])
                    elif int(reqMem[2]) < 0:
                        print('...: @{}'.format(
                            notification['account']['acct']))
                    else:
                        print('//:@{}'.format(notification['account']['acct']))
                        mastodon.status_post(
                            '@{}\nは、恥ずかしいよっ・・・//'.format(
                                notification['account']['acct']),
                            in_reply_to_id=notification['status']['id'],
                            visibility=notification['status']['visibility'])

            elif notifyType == 'favourite':
                # ふぁぼられ
                print('ふぁぼられたっ!:@{0}'.format(notification['account']['acct']))
                # ふぁぼ連対策
                memory = KotohiraMemory(
                    showLog=config['log'].getboolean('enable'))
                favInfo = memory.select('recent_favo',
                                        notification['account']['id'])
                if len(favInfo) == 0:
                    # データがない場合は追加して好感度アップ
                    memory.insert('recent_favo', notification['account']['id'],
                                  notification['status']['id'])
                    memory.update('fav_rate', 1, notification['account']['id'])
                else:
                    # 最後にふぁぼったトゥートが同じものでないこと
                    if notification['status']['id'] != favInfo[0][2]:
                        memory.update('recent_favo',
                                      notification['status']['id'],
                                      notification['account']['id'])
                        memory.update('fav_rate', 1,
                                      notification['account']['id'])

            elif notifyType == 'reblog':
                # ブーストされ
                print('ブーストされたっ!:@{0}'.format(notification['account']['acct']))
                # ふぁぼられと同様な機能とか

            elif notifyType == 'follow':
                # フォローされ
                print('フォローされたっ!:@{0}'.format(notification['account']['acct']))
        except Exception as e:
            # Timelines.pyの方へエラーを送出させる
            raise e
        finally:  # 必ず実行
            try:
                del memory  # データベースロック防止策、コミットする
            except NameError:  # 定義されていなくてもエラーを出さない
                pass
Ejemplo n.º 6
0
    def on_update(self, status):
        try:
            # Botアカウントは応答しないようにする
            if status['account']['bot'] == True:
                return

            # 自分のトゥートは無視
            if config['user']['me'] == status['account']['acct']:
                return

            # トゥート内のHTMLタグを除去
            txt = KotohiraUtil.h2t(status['content'])

            # 自分宛てのメンションはここのリスナーでは無視する(ユーザー絵文字の場合は例外)
            isMeMention = re.search('(?!.*:)@({}+)(?!.*:)'.format(config['user']['me']), txt)
            if isMeMention:
                return
            
            # データベース初期化
            memory = KotohiraMemory(showLog=config['log'].getboolean('enable'))

            # レイちゃんが知ってるユーザーか調べる
            # 知らない場合はレイちゃんは記憶しますっ!
            isknown = memory.select('known_users', status['account']['id'])
            if len(isknown) == 0:
                now = datetime.datetime.now(timezone('Asia/Tokyo'))
                dt = now.strftime("%Y-%m-%d %H:%M:%S%z")
                memory.insert('known_users', status['account']['id'], status['account']['acct'], dt)
                memory.insert('fav_rate', status['account']['id'])
                memory.insert('updated_users', status['account']['id'], dt)
                print('覚えたっ!: @{0}'.format(status['account']['acct']))
                newUser = True
                # トゥートカウントが10以下の場合は新規さん向けの挨拶しますっ!
                if status['account']['statuses_count'] <= 10:
                    print('新規さん!: @{0}'.format(status['account']['acct']))
                    mastodon.status_reblog(status['id'])
                    time.sleep(0.5)
                    mastodon.toot('新規さんっ!はじめましてっ!琴平レイって言うよっ!\nよろしくねっ!\n\n:@{0}: @{0}'.format(status['account']['acct']))
            else:
                newUser = False

            # NGワードを検知した場合は弾いて好感度下げ
            if YuChan.ngWordHook(txt):
                print('変なことを言っちゃダメ~!!(*`ω´*): @{0}'.format(status['account']['acct']))
                memory.update('fav_rate', -5, status['account']['id'])
                YuChan.unfollow_attempt(status['account']['id'])
                return

            # 名前
            nameDic = memory.select('nickname', status['account']['id'])
            if len(nameDic) == 0:
                # ニックネームが指定されていない場合は基の名前を使用する
                # 名前が設定されていない場合はユーザーIDを使用する
                if status['account']['display_name'] == '':
                    name = status['account']['acct']
                else:
                    # デコードして、\u202e(文字が逆さまになるやつ)を削除して戻してどーん
                    dpname = status['account']['display_name'].encode('unicode-escape')
                    dpname = dpname.replace(b"\\u202e", b'')
                    name = dpname.decode('unicode-escape')
            else:
                # ニックネームが設定されている場合はそちらを優先
                name = nameDic[0][2]
            name = re.sub(r'(?!.*:)@([a-zA-Z0-9_]+)(?!.*:)', '', name)

            # 名前に語尾がない場合は付け足す
            if re.search(r'(さん|ちゃん|どの|殿|くん|君|様|さま|教授|たん|きゅん)$', name) == None:
                name += "さん"

            # 正規表現チェック
            calledYuChan = re.search(r'(琴平|ことひら|コトヒラ|コトヒラ|れい|れぃ|レイ|レィ|レイ|レィ|:@' + config['user']['me'] + ':)', txt)
            otherNick = re.search(r'^:@([a-zA-Z0-9_]+):\sの(あだ名|あだな|ニックネーム)[::は]\s?(.+)', txt)
            nick = re.search(r'^(あだ(名|な)|ニックネーム)[::は]\s?(.+)', txt)
            iBack = re.search(r'(帰宅|ただいま|帰った|帰還)(?!.*(する|します|しちゃう|しよう|中|ちゅう|してる))', txt)
            goodNight = re.search(r'寝(ます|る|マス)([よかぞね]?|[...。うぅー~!・]+)$|^寝(ます|る|よ)[...。うぅー~!・]*$|寝(ます|る|マス)(.*)[ぽお]や[ユすしー]|(レイ|ユウ|れい|ことひら|コトヒラ|コトヒラ)(ちゃん)?(.*)[ぽお]や[ユすしー]', txt)
            seeYou = re.search(r'((行|い)って(きます|くる)|ノシ|ノシ)', txt)
            passage = re.search(r'(通過|つうか|ツウカ)(?!.*(おめ|した))', txt)
            sinkiSagi = re.search(r'(新規|しんき)(です|だよ|なのじゃ)', txt)
            nullPoint = re.search(r'(ぬるぽ|ヌルポ|ヌルポ|[nN][uU][lL]{2}[pP][oO])', txt)
            notNicoFri = re.search(r'(にこふれ|ニコフレ|ニコフレ)', txt)
            sad = re.search(r'((泣|な)いてる|しくしく|シクシク|シクシク|ぐすん|グスン|グスン|ぶわっ|ブワッ|ブワッ)', txt)
            noNow = re.search(r'(いまのなし|イマノナシ|イマノナシ)', txt)
            writeDict = re.search(r'^:@[a-zA-Z0-9_]+:(さん|くん|君|殿|どの|ちゃん)?はこんな人[::]', txt)
            writeMemo = re.search(r'^(メモ|めも|[Mm][Ee][Mm][Oo])[::](.+)', txt)
            
            # レイちゃん etc... とか呼ばれたらふぁぼる
            if calledYuChan:
                print('呼ばれたっ!:@{0} < {1}'.format(status['account']['acct'], txt))
                mastodon.status_favourite(status['id'])
                # 好感度ちょいアップ
                memory.update('fav_rate', 1, status['account']['id'])

            # 投票型のトゥートだったら投票する(期限切れでないかつ投票してないこと)
            if status['poll'] != None:
                if status['poll']['expired'] == False and not ('voted' in status['poll'] and status['poll']['voted'] == True):
                    voteOptions = status['poll']['options']
                    
                    # NGワードを検知した場合は弾いて好感度下げ
                    for voteSection in voteOptions:
                        if YuChan.ngWordHook(voteSection['title']):
                            print('変なことを言っちゃダメ~!!!!(*`ω´*): @{0}'.format(status['account']['acct']))
                            memory.update('fav_rate', -5, status['account']['id'])
                            del memory
                            return
                    
                    # ここで投票する場所を抽選
                    voteChoose = random.randint(0, len(voteOptions) - 1)
                    mastodon.poll_vote(status['poll']['id'], voteChoose)
                    # 投票したものをトゥートする
                    print('投票っ!:@{0} => {1}'.format(status['account']['acct'], status['poll']['options'][voteChoose]['title']))
                    mastodon.status_post('レイは「{0}」がいいと思うっ!\n\n{1}'.format(status['poll']['options'][voteChoose]['title'], status['url']))

            elif otherNick:
                # 他人のニックネームの設定
                YuChan.set_otherNickname(txt, status['id'], status['account']['id'], status['account']['acct'], status['visibility'], memory)

            elif nick:
                # ニックネームの設定
                YuChan.set_nickname(txt, status['id'], status['account']['id'], status['account']['acct'], status['visibility'], memory)

            elif iBack:
                # おかえりとか言ったら実行
                if YuChan.msg_hook('wel_back', 600, ":@{0}: {1}、おかえりなさいっ!".format(status['account']['acct'], name), status, memory):
                    print('おかえりっ!:@{0} < {1}'.format(status['account']['acct'], txt))

            elif goodNight:
                # おやすみですっ!
                if YuChan.msg_hook('good_night', 600, ":@{0}: {1}、おやすみなさいっ!🌙".format(status['account']['acct'], name), status, memory):
                    print('おやすみっ!:@{0} < {1}'.format(status['account']['acct'], txt))

            elif seeYou:
                # いってらっしゃいなのですっ!
                if YuChan.msg_hook('see_you', 600, ":@{0}: {1}、いってらっしゃ~いっ!🚪".format(status['account']['acct'], name), status, memory):
                    print('いってらっしゃいっ!:@{0} < {1}'.format(status['account']['acct'], txt))                

            elif passage:
                # 通過 とか言ったら阻止しちゃうよっ!
                if YuChan.msg_hook('passage', 300, "阻止っ!!(*`ω´*)", status, memory):
                    print('阻止っ!:@{0} < {1}'.format(status['account']['acct'], txt))

            elif sinkiSagi and status['account']['statuses_count'] > 10:
                # 新規詐欺見破りっ!            
                if YuChan.msg_hook('sin_sagi', 10800, "新規詐欺はいけませんっ!!(*`ω´*)", status, memory):
                    print('新規詐欺っ!:@{0} < {1}'.format(status['account']['acct'], txt))
            
            elif nullPoint:
                # ぬるぽって、言ったら■━⊂( ・∀・)彡ガッ☆`Д゚)
                if YuChan.msg_hook('null_point', 180, ":gaxtsu:", status, memory):
                    print('がっ:@{0} < {1}'.format(status['account']['acct'], txt))

            elif notNicoFri:
                # ニコフレじゃないよっ!
                if YuChan.msg_hook('not_nikofure', 10800, "ここはニコフレじゃないよっ!!レイ丼だよっ!(*`ω´*)", status, memory):
                    print('レイ丼だよっ!:@{0} < {1}'.format(status['account']['acct'], txt))

            elif sad:
                # よしよしっ
                if YuChan.msg_hook('yoshiyoshi', 180, "(´・ω・`)ヾ(・ω・。)ヨシヨシ", status, memory):
                    print('よしよしっ:@{0} < {1}'.format(status['account']['acct'], txt))

            elif noNow:
                # いまのなしは封印ですっ!
                if YuChan.msg_hook('no_now', 180, "いまのなしは封印だよっ!!(*`ω´*)", status, memory):
                    print('いまのなしは封印だよっ!:@{0} < {1}'.format(status['account']['acct'], txt))

            elif writeDict:
                # 辞書登録っ
                # (実装中)
                # YuChan.update_userdict()
                pass
            
            elif writeMemo:
                # メモの書き込みっ
                memoBody = re.sub(r'^(メモ|めも|[Mm][Ee][Mm][Oo])[::]', '', txt, 1)
                mastodon.status_reblog(status['id'])
                print('メモっ!:@{0} < {1}'.format(status['account']['acct'], txt))
                res = YuChan.write_memo(status['account']['acct'], memoBody, status['id'], memory)
                if res == False:
                    mastodon.status_post('@{}\n長い過ぎてまとめられそうにないよ・・・'.format(status['account']['acct']), in_reply_to_id=status['id'])

            # 最終更新を変更
            now = datetime.datetime.now(timezone('Asia/Tokyo'))
            dt = now.strftime("%Y-%m-%d %H:%M:%S%z")
            # 2重更新防策
            if not newUser:
                updated_at = memory.select('updated_users', status['account']['id'])[0]
                updatedAtRaw = datetime.datetime.strptime(updated_at[2], '%Y-%m-%d %H:%M:%S%z')
                greetableTime = updatedAtRaw + datetime.timedelta(hours=3)
                shouldGreet = now >= greetableTime
                # 3時間以上更新がなかった場合は挨拶する
                if shouldGreet:
                    time.sleep(0.5)
                    if now.hour < 12 and now.hour >= 5:
                        print("おはようっ!:@{0} < {1}".format(status['account']['acct'], txt))
                        mastodon.toot(""":@{1}: {0}、おはようっ!🌄""".format(name, status['account']['acct']))
                    elif now.hour >= 12 and now.hour < 17:
                        print("こんにちはっ!:@{0} < {1}".format(status['account']['acct'], txt))
                        mastodon.toot(""":@{1}: {0}、こんにちはっ!☀""".format(name, status['account']['acct']))
                    elif now.hour >= 17 and now.hour < 5:
                        print("こんばんはっ!:@{0} < {1}".format(status['account']['acct'], txt))
                        mastodon.toot(""":@{1}: {0}、こんばんはっ!🌙""".format(name, status['account']['acct']))

                YuChan.drill_count(status['account']['acct'], name, status['account']['statuses_count'])

                # 最終更新を変更
                memory.update('updated_users', dt, status['account']['id'])

        except Exception as e:
            # Timelines.pyの方へエラーを送出させる
            raise e
        finally: # 必ず実行
            try:
                del memory # データベースロック防止策、コミットする
            except NameError: # 定義されていなくてもエラーを出さない
                pass