def sendRequest(self, method=MethodEnum.GET, url=None):
        self.method = method
        self.requestUrl = url
        requestHeaders = self.getHeader()
        requestBody = self.requestBody
        if method == MethodEnum.POST:
            self.__response = requests.post(url=url,
                                            data=requestBody,
                                            headers=requestHeaders,
                                            timeout=self.timeout)
        elif method == MethodEnum.GET:
            self.__response = requests.get(url=url,
                                           params=requestBody,
                                           headers=requestHeaders,
                                           timeout=self.timeout)
        elif method == MethodEnum.PUT:
            self.__response = requests.put(url=url,
                                           data=requestBody,
                                           headers=requestHeaders,
                                           timeout=self.timeout)
        elif method == MethodEnum.DELETE:
            self.__response = requests.delete(url=url,
                                              data=requestBody,
                                              headers=requestHeaders,
                                              timeout=self.timeout)

        msg = '发送{}请求:{}, headers:{}, data:{}, 响应码:{}, 响应body:{}'.format(
            method.value, url, requestHeaders, requestBody, self.statusCode,
            self.responseBody)
        logger.info(msg)
        print(msg)
示例#2
0
    def send(self):
        self.msg['Subject'] = self.title
        self.msg['From'] = self.sender
        self.msg['To'] = self.receiver

        # 邮件正文
        if self.message:
            self.msg.attach(MIMEText(self.message))

        # 添加附件,支持多个附件(传入list),或者单个附件(传入str)
        if self.files:
            if isinstance(self.files, list):
                for f in self.files:
                    self._attach_file(f)
            elif isinstance(self.files, str):
                self._attach_file(self.files)

        # 连接服务器并发送
        try:
            smtp_server = smtplib.SMTP(self.server)  # 连接sever
        except (gaierror and error) as e:
            logger.exception('发送邮件失败,无法连接到SMTP服务器,检查网络以及SMTP服务器. %s', e)
        else:
            try:
                smtp_server.login(self.sender, self.password)  # 登录
            except smtplib.SMTPAuthenticationError as e:
                logger.exception('用户名密码验证失败!%s', e)
            else:
                smtp_server.sendmail(self.sender, self.receiver.split(';'),
                                     self.msg.as_string())  # 发送邮件
                logger.info('发送邮件"{0}"成功! 收件人:{1}。如果没有收到邮件,请检查垃圾箱,'
                            '同时检查收件人地址是否正确'.format(self.title, self.receiver))
            finally:
                smtp_server.quit()  # 断开连接
示例#3
0
def localize(url: str, filename: str, failed_times: int = 0) -> Optional[str]:
    """本地化图片存储在语料库图片文件夹

    Args:
        url (str): 要下载的url
        filename (str): 下载后存储的文件名称
        failed_times (int, optional): 初始失败次数. Defaults to 0.

    Returns:
        Optional[str]: 成功下载会返回下载后的文件储存路径,否则返回None
    """

    searchfile = CORPUS_IMAGES_PATH.glob(filename.split('.')[0] + ".*")
    for f in searchfile:
        fp = f
        logger.debug(f'File [{filename}] has localized with {fp}')
        return fp.name
    fp = CORPUS_IMAGES_PATH / filename
    try:
        urlretrieve(url, fp)
        realpath = fp.with_suffix('.' + what(fp))  # 修复文件为真正的后缀
        fp.rename(realpath)
        logger.info(f'Localize image [{filename}] with path: {realpath.name}')
        return realpath.name
    except Exception as err:
        failed_times += 1
        logger.warning(
            f'Download file [{url}] error {failed_times} times: {err}')
        if failed_times < 6:
            return localize(url, filename, failed_times=failed_times)
        else:
            logger.error(
                f'Can not download file [{url}] with filename[{fp}]: {err}')
            return None
示例#4
0
 def _attach_file(self, att_file):
     """将单个文件添加到附件列表中"""
     att = MIMEText(open('%s' % att_file, 'rb').read(), 'plain', 'utf-8')
     att["Content-Type"] = 'application/octet-stream'
     file_name = re.split(r'[\\|/]', att_file)
     att["Content-Disposition"] = 'attachment; filename="%s"' % file_name[-1]
     self.msg.attach(att)
     logger.info('attach file {}'.format(att_file))
示例#5
0
async def abbo_confirmation_handle(bot: Bot, event: Event):
    if event.raw_message.strip() == '确认':
        uid = event.get_user_id()
        User_Blocker(uid).add_block(1)
        logger.info(f'将用户 {uid} 加入屏蔽列表,reason:1')
        await add_black_by_user.finish('好的,我不会再响应您的消息~')
    else:
        await add_black_by_user.finish('未输入确认信息,我仍然会响应您的消息~')
 def wrapped_function(*args, **kwargs):
     if func.__doc__:
         desc = func.__doc__
     else:
         desc = func.__name__
     log_string = "【{}】开始执行".format(desc)
     print(log_string)
     logger.info(log_string)
     return func(*args, **kwargs)
示例#7
0
async def ban_user(bot: Bot, event: Event):
    user_id = event.user_id
    if user_id in SUPERUSERS:
        await anti_abuse.finish('啊!你不可以这样说我!')
    else:
        User_Blocker(user_id).add_block(2)
        logger.info(f'因消息 "{event.raw_message}" 将用户 {user_id} 加入屏蔽列表,reason:2')
        await anti_abuse.finish('早苗好伤心!暂时不要理你了(╯°□°)╯︵( .o.)')


# TODO:群主加入黑名单权限,输入群号和群成员,然后验证群主身份与成员
# TODO:群内检测滥用屏蔽
示例#8
0
async def get_a(bot: Bot, event: MessageEvent, state: T_State):
    question = state["question"]
    if "answer" in state:
        answer = state["answer"]
    else:
        answer = Message(event.raw_message)
        # 不可以at自己
        for seg in answer:
            if seg.type == 'at' and seg.data["qq"] == str(event.self_id):
                await learn.finish('我为什么要at我自己?不要这样啦,会有bug的::>_<::')
        answer = await msg2str(answer, localize_=True, bot=bot)

    if len(question) > 3000 or len(answer) > 3000:
        await learn.finish(f'内容太长的对话{BOTNAME}记不住的说>﹏<')
    if answer:
        logger.debug(f'Current answer is [{answer}]')
        source = event.group_id if event.message_type == "group" and 'selflearn' not in state else 0
        public = 0 if state["force_priv"] else state["public"]
        creator = event.user_id if 'selflearn' not in state else event.self_id
        result = insertone(question, answer, 70, creator, source, public)
        if isinstance(result, tuple):
            await learn.finish(f'记录已被用户{result[0]}在{result[1]}时创建')
        else:
            logger.info(
                f'Insert record to corpus :\nquestion:[{question}]\nanswer:[{answer}]\npublic:{public}\ncreator:{creator}\nsource:{source}'
            )

            dlmt = DailyNumberLimiter(uid=event.user_id,
                                      func_name='学习',
                                      max_num=6)
            if dlmt.check(close_conn=False):
                if dlmt.count <= 3:
                    exp_mu, fund_mu = 5, 10
                else:
                    exp_mu, fund_mu = 2, 3
                exp = cgauss(exp_mu, 1, 1)
                fund = cgauss(fund_mu, 1, 1)
                user = UserLevel(event.user_id)
                await user.expup(exp, bot, event)
                user.turnover(fund)
                msg = f'对话已记录, 赠送您{exp}exp 和 {fund}金币作为谢礼~'
                dlmt.increase()
            else:
                dlmt.conn.close()
                msg = '对话已记录'
            if state["force_priv"]:
                msg += "\n(消息中含at信息,将强制设置公开性为群内限定)"
            msg += "\n﹟ 当前对话相对出现率默认设置为70,如需设置出现率可直接输入0-100范围内数字,否则可忽视本条说明"
            preprob[event.user_id] = result
            await learn.finish(msg)
    else:
        await learn.finish(reply_header(event, '这条词语好像记不住耶,要不联系主人试试?'))
示例#9
0
    def __init__(self, uid: int, func_name: str, max_num: int):
        """

        Args:
            uid (int): 用户ID
            func_name (str): 服务名
            max_num (int): 最大调用次数
        """
        self.conn = QbotDB()  # 注意没有使用上下文管理,要手动commit()

        # 如果没有func_name列增加三个相关列
        if func_name not in self.__class__.func_name_ls:
            logger.debug(
                f'A new func {func_name} will be add in table calltimes')
            self.__class__.func_name_ls.append(func_name)
            self.conn.update(
                f"ALTER TABLE calltimes  ADD {func_name}_day DATE, ADD {func_name}_count INT DEFAULT 0, ADD {func_name}_total INT DEFAULT 0;"
            )
            self.conn.update(
                f"UPDATE calltimes SET {func_name}_day = CURDATE();")
            logger.info(f'Add func_name: {func_name} to table calltimes')
            self.conn.commit()

        result = self.conn.queryone(
            f'select {func_name}_day, {func_name}_count, {func_name}_total from calltimes where qq_number=%s;',
            (uid, ))  # 暂时没发现列可以通过传参方式替换的方法,只能动态拼装

        if result:
            self.last_call, self.count, self.total = result
            # 可能之前是调用其他功能时自动创建的记录所以当前功能的最后调用时间是null
            if self.last_call is None:
                self.conn.update(
                    f"UPDATE calltimes SET {func_name}_day = CURDATE() WHERE qq_number=%s;",
                    (uid, ))
                self.conn.commit()
                self.last_call = date.today()
        else:
            # 如果没有用户记录在相关列上增加用户记录并设置为初始值
            self.conn.insert(
                f"INSERT INTO calltimes (qq_number, {func_name}_day, {func_name}_count, {func_name}_total) "
                "VALUES(%s, CURDATE(), 0, 0)", (uid, ))
            self.conn.commit()
            self.last_call, self.count, self.total = date.today(), 0, 0

        self.uid = uid
        self.func_name = func_name
        self.max_num = max_num
示例#10
0
async def parse_qa(bot: Bot, event: MessageEvent, state: T_State):
    # 退出指令
    if str(event.message) in CANCEL_EXPRESSION:
        await learn.finish('已退出当前对话')
    # if f'[CQ:at,qq={event.self_id}]' in event.raw_message:
    #     await learn.finish('我为什么要at我自己?不要这样啦,会有bug的::>_<::')
    for seg in Message(event.raw_message):
        if seg.type == "at" and seg.data["qq"] != str(event.self_id):
            # 强制非公开
            if state["public"]:
                state["force_priv"] = True
                logger.info('Got at info, force set public to 0')
        # 不能存入消息的格式
        if seg.type not in ALLOW_SEGMENT:
            if seg.type == 'reply':
                await learn.finish('请不要学习带有回复上文消息的内容,会引发定位错误')
            else:
                await learn.finish('接收的消息不在可学习范围内')
示例#11
0
    def find_element_by_xpath(self, xpath):
        """
        Finds an element by xpath.

        :Args:
         - xpath - The xpath locator of the element to find.

        :Returns:
         - WebElement - the element if it was found

        :Raises:
         - NoSuchElementException - if the element wasn't found

        :Usage:
            element = driver.find_element_by_xpath('//div/td[1]')
        """
        loggerinfo = self.find_element(by=By.XPATH, value=xpath)
        logger.info("Element is finded:" + xpath)
        return self.find_element(by=By.XPATH, value=xpath)
示例#12
0
    def __init__(self, *records) -> None:
        """
        传入记录列表,通常为查询数据库之后将结果分别字符串化后的列表
        """

        self.rcd_ls = defaultdict(str)
        container = 0
        cruprint = 1
        for record in records:  # 每页容器大小为9,带有图片的记录为3,其它为1,循环为分页内容分配记录,容器大小满时分配下一页
            if container >= 9:
                cruprint += 1
                container = 0
            cost = 3 if '[CQ:image,' in record else 1
            self.rcd_ls[cruprint] += record
            container += cost
        total = len(self.rcd_ls)
        logger.info(f'Generate records list with {total} page(s)')
        if total > 10:
            logger.info(f'Too many records to show, clip to 10 pages')
            while total > 10:
                self.rcd_ls.popitem()
                total -= 1
        self.crupg = 1
        self.pagebar = PagingBar(total)
示例#13
0
    def run(self, yaml_name=webConfig.LOGIN_SUCCESS, case_name=None):
        yaml_path = find_file(WebConfig.yaml_path, yaml_name + '.yaml')
        yaml_data = YamlReader(yaml_path).data
        self.scenarios = yaml_data[0].get('scenarios')
        self.setCase(case_name)
        requests = self.getRequests()

        if self.browser is None:
            self.openDefaultBrowser()

        img = None
        msg = '【%s】场景开始执行' % self.getCaseName()
        print(msg)
        logger.info(msg)
        for request in requests:
            msg = 'label:【%s】' % request.get('label')
            print(msg)
            logger.info(msg)
            for action in request.get('actions'):
                logger.info('action:%s' % action)
                if action == 'getScreenshot()':
                    time.sleep(1)
                    self.success_img.append(
                        self.browser.driver.get_screenshot_as_base64())
                    continue
                try:
                    msg = self.doAction(action)
                    if yaml_name != webConfig.LOGIN_SUCCESS:
                        print(msg)
                    logger.info(msg)
                except Exception as e:
                    print(str(e))
                    logger.error(str(e))
                    img = self.browser.driver.get_screenshot_as_base64()
                    self.imgs.append(img)
                    return False

        result = True if img is None else False
        return result
示例#14
0
async def kicked_remind(bot: Bot, event: GroupDecreaseNoticeEvent):
    msg = f'被 {event.operator_id} 踢出群 {event.group_id}'
    logger.info(msg)
    for su in SUPERUSERS:
        await bot.send_private_msg(user_id=su, message=msg)
示例#15
0
    def test_001_login(self):
        """APP登录测试"""
        time.sleep(8)
        # ele_earn_money = iniHelper("login", "earn_money")
        # ele_update_version_icon = iniHelper("login", "update_version")
        # ele_close_update_version = iniHelper("login", "close_update_version")
        """1、引导页面:点击[赚钱]"""
        # BaseAction(self.driver).find_element("id", ele_earn_money).click()
        """2、关闭新版本推送通知"""
        # update_icon = BaseAction(self.driver).find_element("id", ele_update_version_icon)
        # if update_icon is not None:
        #     self.driver.tap([(843, 606)])
        #     BaseAction(self.driver).find_element("class_name", ele_close_update_version).click()
        #     time.sleep(3)
        # else:
        #     log.info("no push new version update !!!")
        """3、点击右下角图标:账户"""
        self.driver.tap([(912, 1855)])
        ele_account = iniHelper("login", "account_text")
        BaseAction(self.driver).find_element("class_name", ele_account).click()
        """4、进入登录页面,使用用户名和密码登录"""
        ele_login_phone = iniHelper("login", "username")
        ele_login_pwd = iniHelper("login", "password")
        ele_login_submit = iniHelper("login", "login_submit")
        data_login_phone = iniHelper("input_testdata", "login_phone")
        data_login_pwd = iniHelper("input_testdata", "login_pwd")
        BaseAction(self.driver).find_element("id", ele_login_phone).click()
        BaseAction(self.driver).find_element("id", ele_login_phone).clear()
        BaseAction(self.driver).find_element(
            "id", ele_login_phone).send_keys(data_login_phone)
        BaseAction(self.driver).find_element("id", ele_login_pwd).click()
        BaseAction(self.driver).find_element("id", ele_login_pwd).clear()
        BaseAction(self.driver).find_element(
            "id", ele_login_pwd).send_keys(data_login_pwd)
        BaseAction(self.driver).find_element("id", ele_login_submit).click()
        time.sleep(5)
        """5、登录完成,查看是否登录成功"""
        ele_account = iniHelper("login", "account_icon")
        BaseAction(self.driver).find_element("id", ele_account).click()
        is_login_account = iniHelper("login", "is_login_icon")
        if is_login_account is not None:
            log.info("login is success !!!")
        else:
            log.info("login is failed !!!")

    # def test_002_invest(self):
        """6、购买青盈"""
        ele_invest_icon = iniHelper("invest_product", "invest_icon")
        BaseAction(self.driver).find_element("id", ele_invest_icon).click()
        headers = {
            "X-Auth-Token":
            test_app_login(iniHelper("input_testdata", "login_phone"),
                           iniHelper("input_testdata", "login_pwd"))
        }
        amount_result = requests.post(url=url.query_qy_amount,
                                      headers=headers,
                                      verify=False).json()
        """6.1、查询青盈剩余可投金额,金额大于200,则立即购买青盈,否则调用青盈开标接口进行开标成功后再购买"""
        if Decimal(amount_result["data"]["xqy"]["data"]["totalAmount"]).quantize(Decimal("0.00")) >= \
                Decimal(200.00).quantize(Decimal("0.00")):
            ele_qy_investpage = iniHelper("invest_product",
                                          "investpage_qy_ele")
            ele_qyinfo_buy = iniHelper("invest_product", "qyinfo_buy_ele")
            ele_confirm_buy_qy = iniHelper("invest_product", "confirm_buy_qy")
            BaseAction(self.driver).find_element("id",
                                                 ele_qy_investpage).click()
            BaseAction(self.driver).find_element("id", ele_qyinfo_buy).click()
            BaseAction(self.driver).find_element(
                "class_name", ele_confirm_buy_qy).send_keys(
                    iniHelper("input_testdata", "buy_qy_amount"))
            os.popen("adb shell input tap " + str(644.0) + " " + str(1263.0))
            ele_return_account = iniHelper("invest_product", "return_account")
            BaseAction(self.driver).find_element("class_name",
                                                 ele_return_account).click()
            self.driver.tap([(231, 1103)])
            BaseAction(self.driver).find_element("class_name",
                                                 ele_return_account).click()
            log.info("auto_buy qy success!!!")
            log.info("auto_qya success and return account page success!!!")
        else:
            """6.2、青盈剩余可投小于200调用青盈开标接口"""
            ele_qy_open_phone = iniHelper("open_qy_loan", "phone")
            ele_qy_open_amount = iniHelper("open_qy_loan", "amount")
            open_result = qy_amount_open(ele_qy_open_phone, ele_qy_open_amount)
            time.sleep(5)
            if str(open_result) == 'SUCCESS':
                log.info("That is a new qy_loan created successfully!!!")
                ele_qy_investpage = iniHelper("invest_product",
                                              "investpage_qy_ele")
                ele_qyinfo_buy = iniHelper("invest_product", "qyinfo_buy_ele")
                ele_confirm_buy_qy = iniHelper("invest_product",
                                               "confirm_buy_qy")
                BaseAction(self.driver).find_element(
                    "id", ele_qy_investpage).click()
                BaseAction(self.driver).find_element("id",
                                                     ele_qyinfo_buy).click()
                BaseAction(self.driver).find_element(
                    "class_name", ele_confirm_buy_qy).send_keys(
                        iniHelper("input_testdata", "buy_qy_amount"))
                os.popen("adb shell input tap " + str(644.0) + " " +
                         str(1263.0))
                self.driver.find_element_by_class_name(
                    "android.view.View").click()
                ele_return_account = iniHelper("invest_product",
                                               "return_account")
                BaseAction(self.driver).find_element(
                    "class_name", ele_return_account).click()
                self.driver.tap([(231, 1103)])
                BaseAction(self.driver).find_element(
                    "class_name", ele_return_account).click()
                log.info("auto_buy qy success!!!")
                log.info("auto_qya success and return account page success!!!")
            else:
                log.info("That is a new qy_loan created failed!!!")
示例#16
0
class DailyNumberLimiter:
    """使用此类限制每个用户单个功能的调用量
    """

    # 查询所有信息列表
    with QbotDB() as conn:
        # 查询数据库中所有存在的功能的名称存入列表中
        _count_tuple = conn.queryall(
            "SELECT COLUMN_NAME FROM information_schema.COLUMNS "
            "WHERE TABLE_SCHEMA = 'qbotdb' AND TABLE_NAME = 'calltimes' AND column_name like '%%_count';"
        )
        func_name_ls = list(map(lambda x: x[0].split('_')[0], _count_tuple))
        logger.info(f'当前数据库内功能限制列表:{str(func_name_ls)}')
        del _count_tuple

    def __init__(self, uid: int, func_name: str, max_num: int):
        """

        Args:
            uid (int): 用户ID
            func_name (str): 服务名
            max_num (int): 最大调用次数
        """
        self.conn = QbotDB()  # 注意没有使用上下文管理,要手动commit()

        # 如果没有func_name列增加三个相关列
        if func_name not in self.__class__.func_name_ls:
            logger.debug(
                f'A new func {func_name} will be add in table calltimes')
            self.__class__.func_name_ls.append(func_name)
            self.conn.update(
                f"ALTER TABLE calltimes  ADD {func_name}_day DATE, ADD {func_name}_count INT DEFAULT 0, ADD {func_name}_total INT DEFAULT 0;"
            )
            self.conn.update(
                f"UPDATE calltimes SET {func_name}_day = CURDATE();")
            logger.info(f'Add func_name: {func_name} to table calltimes')
            self.conn.commit()

        result = self.conn.queryone(
            f'select {func_name}_day, {func_name}_count, {func_name}_total from calltimes where qq_number=%s;',
            (uid, ))  # 暂时没发现列可以通过传参方式替换的方法,只能动态拼装

        if result:
            self.last_call, self.count, self.total = result
            # 可能之前是调用其他功能时自动创建的记录所以当前功能的最后调用时间是null
            if self.last_call is None:
                self.conn.update(
                    f"UPDATE calltimes SET {func_name}_day = CURDATE() WHERE qq_number=%s;",
                    (uid, ))
                self.conn.commit()
                self.last_call = date.today()
        else:
            # 如果没有用户记录在相关列上增加用户记录并设置为初始值
            self.conn.insert(
                f"INSERT INTO calltimes (qq_number, {func_name}_day, {func_name}_count, {func_name}_total) "
                "VALUES(%s, CURDATE(), 0, 0)", (uid, ))
            self.conn.commit()
            self.last_call, self.count, self.total = date.today(), 0, 0

        self.uid = uid
        self.func_name = func_name
        self.max_num = max_num

    def check(self, close_conn: bool = True) -> bool:
        """检查是否已超过今日最大调用量

        Args:
            close_conn (bool, optional): 是否在检查之后直接关闭连接. Defaults to True.

        Returns:
            bool: 次数小于最大调用量时为True
        """
        if self.last_call < date.today():
            self.count = 0
            self.conn.update(
                f'UPDATE calltimes SET {self.func_name}_count=0, {self.func_name}_day=CURDATE() WHERE qq_number=%s',
                (self.uid, ))

        if not self.conn.q:
            self.conn.commit()
        if close_conn:
            self.conn.close()
        return self.count < self.max_num

    def increase(self, num: int = 1):
        """增加调用量记录

        Args:
            num (int, optional): 增加的次数. Defaults to 1.
        """
        self.count += num
        self.total += num
        self.conn.update(
            f"UPDATE calltimes SET {self.func_name}_count={self.func_name}_count+1, {self.func_name}_total={self.func_name}_total+1 WHERE qq_number=%s",
            (self.uid, ))
        self.conn.commit()
        self.conn.close()
示例#17
0
async def send_lolicon(bot: Bot, event: MessageEvent, state: T_State):

    gid = event.group_id if event.message_type=='group' else 0
    if gid:
        if str(gid) not in sl_settings:
            await setu.finish('''先设置本群sl再使用此功能吧
[设置sl 最小sl-最大sl]
例如:设置sl 0-4
────────────
sl说明:
大概可以解释成本群能接收的工口程度,sl越高的图被人看见越会触发社死事件
※ r18权限已改为sl, 当最大sl为5时即为开启r18权限,sl0-4级别仅适用于美图,色图会自动忽略
最低sl0:不含任何ero要素,纯陶冶情操,也有一部分风景图
最高sl5: 就是R18了
中间的等级依次过渡''')

        max_sl = sl_settings[str(gid)]['max_sl']
        min_sl = sl_settings[str(gid)]['min_sl']
        restricted = True if max_sl < 5 else False  # r18是否在本群受限
    else:
        restricted = False

    # 限制条件优先度:r18,5张最大数,等级限制数量,频率,资金,由于要检测参数只好先把个别参数解析混入条款中了
    uid = event.user_id

    # r18限制条款,顺便解析了r18
    r18_call = state["_matched_dict"]['r18_call'] or state["_matched_dict"]['r18_call2']
    if r18_call and restricted:
        await setu.finish(reply_header(event, f'当前群内最大sl为{max_sl},已禁止R18内容'))

    # 5张最大数量限制条款,顺便解析了num
    if state["_matched_dict"]['num']:
        num = cn2an(state["_matched_dict"]['num'].replace('两', '二'), 'smart')
    elif state["_matched_dict"]['num2']:
        num = cn2an(state["_matched_dict"]['num2'].replace('两', '二'), 'smart')
    else:
        num = 1

    if num > 5:
        await setu.finish(reply_header(event, '一次最多只能要5张'))
    elif num == 0:
        await setu.finish(reply_header(event, '你好奇怪的要求'))
    elif num < 0:
        await setu.finish(reply_header(event, f'好的,你现在欠大家{-num}张涩图,快发吧'))  # TODO: 想想办法把负数给提取出来

    # 等级限制数量条款,注册了用户信息
    userinfo = UserLevel(uid)
    if userinfo.level < num:
        if userinfo.level > 0:
            await setu.finish(f'您当前等级为{userinfo.level},最多一次要{userinfo.level}张')
        elif num > 1:
            await setu.finish(reply_header(event, '啊这..0级用户一次只能叫一张哦,使用[签到]或者学习对话可以提升等级~'))

    # 频率限制条款,注册了频率限制器
    flmt = FreqLimiter(uid, 'setu')
    if not flmt.check():
        refuse = f'你冲得太快了,请{ceil(flmt.left_time())}秒后再冲'  # 不用round主要是防止出现'还有0秒'的不科学情况
        if userinfo.level == 0:
            refuse += ',提升等级可以加快装填哦~'
        await setu.finish(reply_header(event, refuse))
    cd = cd_step(userinfo.level, 480)  # 冷却时间
    flmt.start_cd(cd)  # 先进行冷却,防止连续呼叫击穿频率装甲,要是没返回图的话解掉

    # 资金限制条款,注册了每日次数限制器
    cost = num * 3
    dlmt = DailyNumberLimiter(uid, '色图', 3)
    in_free = True if event.message_type == 'private' and event.sub_type == 'friend'\
            else dlmt.check(close_conn=False)  # 来自好友的对话不消耗金币

    if userinfo.fund < cost and not in_free:
        if userinfo.fund > 0:
            refuse = choice((f'你还剩{userinfo.fund}块钱啦,要饭也不至于这么穷吧!', f'你只剩{userinfo.fund}块钱了,要不考虑援交一下赚点钱?'))
        elif userinfo.level == 0 and userinfo.fund == 0:
            refuse = '每天有三次免费次数哦,使用[签到]领取资金来获得更多使用次数吧~'
        else:
            refuse = '你已经穷得裤子都穿不起了,到底是做了什么呀?!'
        dlmt.conn.close()  # 确认直接结束不会增加调用次数了,直接返还链接
        flmt.start_cd(0)
        await setu.finish(reply_header(event, refuse))

    kwd = state["_matched_dict"]['kwd'] or ''

    if r18_call:
        r18 = 1 if r18_call in ('r18', 'R18') else 0      
    else:
        if event.message_type == 'group':
            if max_sl < 5:
                r18 = 0
            elif min_sl == 5:
                r18 = 1
            elif min_sl < 5:
                r18 = 2
        else:
            r18 = 2
    
    msg = MessageSegment.reply(id_=event.message_id) if event.message_type == 'group' else MessageSegment.text('') # 由于当前私聊回复有bug所以只在群里设置信息开始为回复消息

    # 有搜索条件,从本地库中提
    if kwd:
        kwds = tuple(kwdrex.split(kwd))
        success, result = get_setu(gid, kwds, num, r18)
        if not success:
            flmt.start_cd(0)
            dlmt.conn.close()
            await setu.finish(reply_header(event, result))

        count = result['count']  # 返回数量,每次处理过后自减1
        miss_count = 0  # 丢失数量
        for data in result['data']:
            if data is None:
                miss_count += 1
                continue
            img : Path = data['file']
            logger.debug(f'当前处理本地图片{img.name}')
            info = f"{data['title']}\n画师:{data['author']}\nPID:{data['source']}\n"
            try:
                im_b64 = Image_Handler(img).save2b64()
            except OSError as err:
                miss_count += 1
                logger.error(f'File {img} may be damaged: {err}')
                continue
            except UnidentifiedImageError as imgerr:
                miss_count += 1
                logger.error(f'failed to open local file: {img}: {imgerr}')
                continue
            msg += MessageSegment.text(info) + MessageSegment.image(im_b64)
            if count > 1:
                msg += MessageSegment.text('\n=====================\n')
                count -= 1
            elif result['count'] < num:
                msg += MessageSegment.text(f'\n=====================\n没搜到{num}张,只搜到这些了')

    # 无搜索条件,链接API,5次错误退出并反馈错误
    else:
        logger.debug('Start getting lolicon API')
        failed_time = 0
        while failed_time < 5:
            try:
                result = await get_lolicon(kwd, r18, num)
                break
            except BaseException as e:
                failed_time += 1
                logger.exception(f"connect api faild {failed_time} time(s)\n{e}")
        else:
            logger.error(f'多次链接API失败,当前参数: kwd: [{kwd}], num: {num}, r18: {r18}')
            dlmt.conn.close()
            flmt.start_cd(0)
            await setu.finish('链接API失败, 若多次失败请反馈给维护组', at_sender=True)
        logger.debug('Receive lolicon API data!')

        # 处理数据
        if result['code'] == 0:
            count = result['count']  # 返回数量,每次处理过后自减1
            untreated_ls = []  # 未处理数据列表,遇到本地库中没有的数据要加入这个列表做并发下载
            miss_count = 0  # 丢失数量
            for data in result['data']:
                pid = data['pid']
                p = data['p']
                name = f'{pid}_p{p}'
                # 按 色图备份路径->美图原文件路径 顺序查找本地图,遇到没有本地路径的等待并发下载处理
                imgbkup = [f for f in Path(SETUPATH).glob(f'{name}.[jp][pn]*g')]
                if imgbkup:
                    img = imgbkup[0]
                else:
                    imgorg = [f for f in (Path(MEITUPATH)/'origin_info').rglob(f'{name}.[jp][pn]*g')]
                    if imgorg:
                        img = imgorg[0]
                    else:
                        untreated_ls.append(data)
                        continue
                logger.debug(f'当前处理本地图片{name}')
                info = f"{data['title']}\n画师:{data['author']}\nPID:{name}\n"
                try:
                    im_b64 = Image_Handler(img).save2b64()
                except UnidentifiedImageError as imgerr:
                    miss_count += 1
                    logger.error(f'failed to open local file: {img}: {imgerr}')
                    continue
                msg += MessageSegment.text(info) + MessageSegment.image(im_b64)
                if count > 1:
                    msg += MessageSegment.text('\n=====================\n')
                    count -= 1
                elif result['count'] < num:
                    msg += MessageSegment.text(f'\n=====================\n没搜到{num}张,只搜到这些了')
                
            # 对未处理过的数据进行并发下载,只下载1200做临时使用
            async with httpx.AsyncClient() as client:
                task_ls = []
                for imgurl in [d['url'] for d in untreated_ls]:
                    task_ls.append(client.get(get_1200(imgurl), timeout=120))
                imgs = await gather(*task_ls, return_exceptions=True)
                
            for i, data in enumerate(untreated_ls):
                if isinstance(imgs[i], BaseException):
                    miss_count += 1
                    logger.exception(data)
                    continue
                if imgs[i].status_code != httpx.codes.OK:
                    miss_count += 1
                    logger.error(f'Got unsuccessful status_code [{imgs[i].status_code}] when visit url: {imgs[i].url}')
                    continue
                pid = data['pid']
                p = data['p']
                name = f'{pid}_p{p}'
                info = f"{data['title']}\n画师:{data['author']}\nPID:{name}\n"
                logger.debug(f'当前处理网络图片{name}')
                try:
                    im_b64 = Image_Handler(imgs[i].content).save2b64()
                except BaseException as err:
                    logger.error(f"Error with handle {name}, url: [{data['url']}]\n{err}")
                    miss_count += 1
                    continue

                msg += MessageSegment.text(info) + MessageSegment.image(im_b64)
                if count > 1:
                    msg += MessageSegment.text('\n=====================\n')
                    count -= 1
                elif result['count'] < num:
                    msg += MessageSegment.text(f'\n=====================\n没搜到{num}张,只搜到这些了')
            if miss_count > 0 and num > 1:
                msg += MessageSegment.text(f'\n有{miss_count}张图丢掉了,{BOTNAME}也不知道丢到哪里去了T_T')
            elif miss_count == 1:
                msg += MessageSegment.text(f'{BOTNAME}拿来了图片但是弄丢了呜呜T_T')
        else:
            flmt.start_cd(0)
            dlmt.conn.close()
            await setu.finish(msg + MessageSegment.text('获取涩图失败,请稍后再试'))

    try:
        await setu.send(msg)
    except NetworkError as err:
        logger.error(f'Maybe callout error happend: {err}')
    except AdapterException as err:
        logger.error(f"Some Unkown error: {err}")

    if miss_count < result['count']:
        if not in_free:
            cost = (result['count'] - miss_count) * 3  # 返回数量可能少于调用量,并且要减去miss的数量
            userinfo.turnover(-cost)  # 如果超过每天三次的免费次数则扣除相应资金
        dlmt.increase()  # 调用量加一
    else:
        flmt.start_cd(0)  # 一张没得到也刷新CD
        dlmt.conn.close()

    # 下载原始图片做本地备份
    if not kwd:
        async with httpx.AsyncClient() as bakeuper:
            backup_ls = []
            json_ls = []
            for info in untreated_ls:
                url = info['url']
                json_data = {
                    'pid': info['pid'],
                    'p': info['p'],
                    'uid': info['uid'],
                    'title': info['title'],
                    'author': info['author'],
                    'url': url,
                    'r18': info['r18'],
                    'tags': info['tags']
                }
                backup_ls.append(bakeuper.get(url, timeout=500))
                json_ls.append(json_data)
            origims = await gather(*backup_ls, return_exceptions=True)
            for i, im in enumerate(origims):
                if isinstance(im, BaseException):
                    logger.exception(im)
                    continue
                if im.status_code != httpx.codes.OK:
                    logger.error(f'Got unsuccessful status_code [{im.status_code}] when visit url: {im.url}')
                    continue
                imgfp = Path(SETUPATH)/(str(json_ls[i]['pid']) + '_p' + str(json_ls[i]['p']) + '.' + json_ls[i]['url'].split('.')[-1])
                jsonfp = Path(SETUPATH)/(str(json_ls[i]['pid']) + '_p' + str(json_ls[i]['p']) + '.json')
                try:
                    with imgfp.open('wb') as f:
                        f.write(im.content)
                    logger.info(f'Downloaded image {imgfp.absolute()}')
                except BaseException as e:
                    logger.exception(e)
                with jsonfp.open('w', encoding='utf-8') as j:
                    json.dump(json_ls[i], j, ensure_ascii=False, escape_forward_slashes=False, indent=4)
                    logger.info(f'Generated json {jsonfp.absolute()}')
                increase_setu(**json_ls[i])