Example #1
0
class MainLogic(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

    def __init__(self):
        self.ob = Observer()
        self.timer = RepeatedTimer(28800, self.ob.ob_all)
        self.cmd_list = {
            "help": self._help,
            "status": self._status,
            "list": self._list,
            "delete": self._delete,
            "admin-status": self._admin_status,
            "admin-list": self._admin_list,
            "admin-run": self._admin_run
        }
        self.ob.init_multi_thread()

    def __del__(self):
        self.timer.stop()

    def handle_msg(self, msg):
        # print(msg.source, msg.content, msg.create_time)
        content = msg.content
        reply = ""

        if self._is_URL(content):
            reply = self._handle_URL(msg)
        elif self._is_email(content):
            reply = self._handle_email(msg)
        elif self._is_cmd(content):
            reply = self._handle_cmd(msg)
        else:  # invalid msg
            reply = "听不懂你在说啥!\n--------------\n观察目标暂时只接受微信公众号文章。\n不要有无意义的空格、分号、换行符等。\n请回复「help」查看规则"

        if reply_too_long(reply):
            reply = _trim_reply(reply)
        return reply

    def handle_event(self, evt):
        if self._is_subscribe_event(evt):
            reply = "欢迎关注,回复[help]查看基础指引。\n机器人功能仍在施工中,见谅"
        else:
            reply = "Sorry, can not handle this for now"
        return reply

    def _handle_URL(self, msg):
        open_id = msg.source
        URL = self._trim_URL(msg.content)

        db = DbOperator()
        success, result = db.find_my_article(open_id)
        if success:
            for item in result:
                if URL == item['URL']:
                    reply = "目标已在观察列表中:" + URL
                    return reply

        success = self.ob.ob_this_one(URL, open_id)
        if not success:
            reply = "目标初次访问异常,无法进行备份。若确定是误判,请联系管理员:[email protected]"
        else:
            reply = "收到!开始观察目标!"
        # 判断一下用户是否绑定邮箱,没绑定的话提供警告
        success, _ = db.find_user(open_id)
        if not success:
            reply = reply + "\n--------------\n【警告】你的账号未绑定邮箱,无法收到备份及通知。请回复[help]查看规则"
        return reply

    def _handle_email(self, msg):
        open_id = msg.source
        email = msg.content
        user = (open_id, email)
        send_flag = False
        db = DbOperator()
        success, _ = db.find_user(open_id)
        if success:  # 已有记录,更新绑定关系
            result = db.update_user(user)
        else:  # 无记录,添加绑定关系
            result = db.add_user(user)
            send_flag = send_user_check_email(email)
        if result:
            reply = "你的账号成功绑定邮箱:" + email
            if send_flag:
                reply += """\n--------
你是初次绑定邮箱,我发了一封确认邮件给您
请检查邮箱,确认能收到我的通知邮件
(注意垃圾箱!)"""
        else:
            # db 操作失败的日志 db 那边会记录
            reply = "绑定邮箱:" + email + "失败!请联系管理员处理:[email protected]"
        return reply

    def _handle_cmd(self, msg):
        reply = "处理命令出错,无法提取出有效命令"
        string = msg.content
        try:
            argv = string.split()
            cmd = argv[0].lower()
        except IndexError:
            logger.warning("_handle_cmd parse string fail: " + string)
            return reply

        reply = self.cmd_list[cmd](msg)
        return reply

    def _help(self, msg):
        return HELP_MSG

    def _status(self, msg):
        open_id = msg.source
        db = DbOperator()
        success, user = db.find_user(open_id)
        if not success:
            logger.info("_status can't find user:"******"没有找到邮箱绑定记录,请重新绑定"
        email = user['email']
        return "你的账号现在绑定邮箱为:" + email

    def _list(self, msg):
        open_id = msg.source
        db = DbOperator()
        success, article_list = db.find_my_article(open_id)
        if not success:
            logger.info("_list no record by user:"******"没有正在观察中的记录"
        reply = "现有" + str(len(article_list)) + "条记录观察中\n-------\n"
        URL_list = []
        for item in article_list:
            URL_list.append(
                str(item['article_id']) + " " + str(item['title']) + " " +
                item['URL'])
        reply = reply + "\n\n".join(URL_list)
        return reply

    def _admin_status(self, msg):
        user = msg.source
        if user not in ADMIN_LIST:
            return "非管理员账号,无法执行admin命令"

        db = DbOperator()
        reply = analyze_article_status(db)
        reply += "\n-------\n"
        reply += analyze_user_status(db)
        reply += "\n-------\n"
        qsize = self.ob.get_cur_checker_q_size()
        reply += "checker队列中的条目数量:" + str(qsize)
        reply += "\n-------\n"
        qsize = self.ob.get_cur_mail_q_size()
        reply += "mail队列中的条目数量:" + str(qsize)
        reply += "\n-------\n"
        space_info = tools.total_used_space(DEFAULT_PATH)
        reply += space_info
        reply += "\n-------\n"
        reply += "version:" + APP_VER
        return reply

    def _admin_list(self, msg):
        user = msg.source
        if user not in ADMIN_LIST:
            return "非管理员账号,无法执行admin命令"
        db = DbOperator()
        success, result = db.fetch_all_article()
        if not success:
            logger.info("_admin_list fetch 0")
            return "admin-list 查询失败,结果为空"

        output = []
        for item in result:
            output.append(item['article_id'])
            output.append(item['URL'])
            output.append(item['open_id'])
            output.append(item['backup_addr'])
            # output.append(item['start_date']) # 这个有bug,先注释掉
            output.append(item['status'])
            output.append('--------')
        return "\n".join(map(str, output))

    def _admin_run(self, msg):
        user = msg.source
        if user not in ADMIN_LIST:
            return "非管理员账号,无法执行admin命令"
        if self.ob.ob_all():
            return "已执行一次全局观察"
        else:
            return "全局观察执行中出错,请检查日志"

    def _delete(self, msg):
        string = msg.content
        try:
            argv = string.split()
            article_id = int(argv[1])
        except IndexError:
            logger.exception("_delete parse string fail: " + string)
            return "delete 失败,不是合法的输入,请检查格式"

        db = DbOperator()
        success, result = db.find_article(article_id)
        if not success:
            logger.debug("_delete fetch nothing: " + string)
            return "试图删除编号为" + str(article_id) + "的文章,但未找到"
        user = msg.source
        if result['open_id'] == user:  # 确实是本人的观察目标
            if update_article_status(article_id,
                                     False,
                                     optionals={
                                         "reason": REASON_DELETE_BY_USER
                                     }):
                return "成功删除观察目标:" + str(article_id)
            else:
                logger.error("_delete process fail: " + string)
                return "您的输入合法,但后台处理失败,请联系管理员"
        else:
            logger.info(user + " try to delete article: " + str(article_id))
            return str(article_id) + " 不是您的观察目标,无法删除"

    def _is_URL(self, string):
        # 特殊处理,暂时只接受微信文章地址
        if string.startswith('https://mp.weixin.qq.com/s'):
            #合法性校验
            if (' ' not in string) and (len(string) < 2000):
                return True
        return False

    def _is_email(self, string):
        return EMAIL_RULE.match(string)

    def _is_cmd(self, string):
        try:
            argv = string.split()
            header = argv[0].lower()
        except IndexError:
            return False

        if header in self.cmd_list:
            return True
        else:
            return False

    def _trim_URL(self, URL):
        if "&chksm=" in URL:
            try:
                short_ver = URL.split("&chksm=")[0]
            except IndexError:
                logger.warning("_trim_URL fail: " + URL)
                return URL
            else:
                return short_ver
        else:
            return URL

    def _is_subscribe_event(self, msg):
        if msg.event == "subscribe":
            return True
        return False