├── .idea ├── .gitignore ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── setuweb.iml └── vcs.xml ├── README.md ├── __init__.py ├── blueprint.py ├── bot.py ├── config.json ├── static └── jquery-3.5.1.min.js └── templates ├── acgmx.html ├── index.html └── result.html /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/setuweb.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hoshinobot 在线setu插件 2 | ## 简介 3 | 本插件可调用lolicon api或acgmx api查找setu并可发送到bot所在群中\ 4 | 支持在线查找以及群内调用\ 5 | 在线使用地址为http://bot地址/setu 6 | 可通过setuhelp指令获取\ 7 | 群内使用方法见指令说明 8 | 支持私聊(需魔改Hoshinobot源码)\ 9 | 私聊不撤回,不检测r18\ 10 | 佛系编程 想要什么功能建议自己改 11 | 12 | ### Hoshinobot魔改 13 | 14 | 1. 在priv.py中找到不允许私聊的注释(第78行)将False改为True 15 | 2. 找到msghandler.py中第8行的if event.detail_type != 'group':\ 16 | 下一行的return改为pass 或者删除整个if块(第8行, 第9行) 17 | 18 | ## 安装方法 19 | 1. 在hoshino/modules文件夹下使用git clone本项目 20 | 2. 在config.json中添加(可添加多个)lolicon的apikey() 21 | 3. 在config.json中添加acgmx的apikey() 22 | 4. (可选)修改config.json中的proxy(代理) 23 | 5. 在hoshino/config/\_\_bot\_\_.py中添加本插件 24 | 25 | ## 指令说明 26 | |指令|功能| 27 | |---|---| 28 | |setuhelp|查看帮助| 29 | |setpsw|设置本群密码| 30 | |setuallow|允许当前群发送setu| 31 | |setuforbid|禁止当前群发送setu| 32 | |r18allow|允许当前群发送r18setu| 33 | |r18forbid|禁止当前群发送r18setu| 34 | |withdrawon|开启当前群撤回| 35 | |withdrawoff|关闭当前群撤回| 36 | |setwithdraw|时间设置撤回时间(秒)| 37 | |setpsw|设置当前群的web端密码| 38 | |antishielding(0关\\类型1\\2\\3)|查看或设置反和谐类型| 39 | |forwardon|开启消息转发模式| 40 | |forwardoff|关闭消息转发模式| 41 | |正则表达式|群内涩图| 42 | 43 | 正则表达式: ^[来发给]\(?P.*)?[份点张幅]\(?Puids(\d\s?)*\|)?\(?Ptags(.*\s?)*\|)?的?\(?P[Rr]18)?\(?P.*)?[涩瑟色]图$ 44 | 45 | 注意使用uids或tags需要以|结束 46 | 47 | 各种群内开关只有管理员以上及主人才能使用\ 48 | r18选项仅对lolicon api有效\ 49 | 群内撤回默认关闭\ 50 | 撤回时间为全局设置 仅主人可以设置\ 51 | 撤回时间默认60秒 设为0为全局关闭撤回\ 52 | 本插件图片发送前下载至资源文件夹中img/setuweb\ 53 | 若图片已存在不下载直接发送\ 54 | 若有多个lolicon apikey 失败时将自动切换\ 55 | 若全部失败 显示最后一个的失败信息 56 | 57 | ## config说明 58 | |key|value|类型| 59 | |---|---|---| 60 | |~~apikey~~(已不使用)|~~lolicon.app的apikey~~|~~字符串列表~~| 61 | |size|群内触发时使用的size|枚举(original, regular, small, thumb, mini)| 62 | |proxy|lolicon.app的代理|字符串| 63 | |acgmx_token|acgmx的token|字符串| 64 | |group_api|群内触发setu所用的api|字符串("lolicon"/"acgmx")| 65 | |withdraw|撤回时间 0为全局关闭|整数| 66 | |antishielding|反和谐类型0关1随机像素2旋转3混合|整数(0\\1\\2\\3)| 67 | |url|自定义页面url代替说明中的http://ip:port/|字符串| 68 | |forward|是否启用卡片消息|布尔| 69 | 70 | group_api为acg_mx时只能获取单张随机setu 71 | 72 | # 提问的智慧 73 | 74 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from nonebot import get_bot 2 | from hoshino import R 3 | from .blueprint import bp 4 | import os 5 | try: 6 | import ujson as json 7 | except ImportError: 8 | import json 9 | 10 | path = R.img('setuweb/').path 11 | 12 | if not os.path.exists(path): 13 | os.makedirs(path) 14 | 15 | 16 | bot = get_bot() 17 | app = bot.server_app 18 | app.register_blueprint(bp, url_prefix='/setu') # 注册蓝图 19 | -------------------------------------------------------------------------------- /blueprint.py: -------------------------------------------------------------------------------- 1 | from .bot import get_groups, send_to_group, send_to_group_acgmx, group_psw 2 | from quart import Blueprint, render_template, request 3 | import aiohttp 4 | import os 5 | import asyncio 6 | 7 | try: 8 | import ujson as json 9 | except ImportError: 10 | import json 11 | 12 | curr_dir = os.path.dirname(__file__) 13 | bp = Blueprint('setu', 'setuweb', static_folder=f'{curr_dir}/static', template_folder=f'{curr_dir}/templates') 14 | config = json.load(open(f'{curr_dir}/config.json', 'r')) 15 | 16 | 17 | @bp.route('/', methods=['GET']) 18 | async def setuindex(): 19 | return await render_template('index.html') 20 | 21 | 22 | @bp.route('/result', methods=['POST']) 23 | async def seturesult(): 24 | form = await request.form 25 | groups = await get_groups() 26 | keyword = form['keyword'] 27 | uids = form['uids'].strip() 28 | if uids != '': 29 | uids = uids.split(' ') 30 | try: 31 | uids = [int(uid) for uid in uids] 32 | except ValueError: 33 | uids = [] 34 | else: 35 | uids = [] 36 | 37 | tags = form['tags'].strip() 38 | if tags != '': 39 | tags = tags.split(' ') 40 | else: 41 | tags = [] 42 | result = {} 43 | code = -1 44 | # for apikey in apikeys: 45 | # if keyword == '': 46 | # params = { 47 | # 'apikey': apikey, 48 | # 'proxy': config['proxy'], 49 | # 'r18': form['r18'], 50 | # 'num': form['num'], 51 | # 'size': form['size'], 52 | # } 53 | # else: 54 | # params = { 55 | # 'apikey': config['apikey'], 56 | # 'proxy': config['proxy'], 57 | # 'keyword': keyword, 58 | # 'r18': form['r18'], 59 | # 'num': form['num'], 60 | # 'size': form['size'], 61 | # } 62 | datas = { 63 | 'proxy': config['proxy'], 64 | 'r18': form['r18'], 65 | 'num': form['num'], 66 | 'size': form['size'] 67 | } 68 | if keyword != '': 69 | datas['keyword'] = keyword 70 | if uids: 71 | datas['uid'] = uids 72 | if tags: 73 | datas['tag'] = tags 74 | async with aiohttp.ClientSession(raise_for_status=True) as session: 75 | async with session.post('https://api.lolicon.app/setu/v2', json=datas) as rq: 76 | result = await rq.read() 77 | result = json.loads(result) 78 | err = result['error'] 79 | data = result['data'] 80 | return await render_template('result.html', 81 | err=err, 82 | data=data, 83 | size=form['size'], 84 | noresult=len(data) == 0, 85 | groups=groups) 86 | 87 | 88 | @bp.route('/send', methods=['POST']) 89 | async def send(): 90 | form = await request.form 91 | group_id = form['group_id'] 92 | psw = form['psw'] 93 | try: 94 | password = group_psw[group_id] 95 | except KeyError: 96 | return '请先在群内设置密码' 97 | if psw != password: 98 | return '密码错误' 99 | group_id = int(group_id) 100 | r18 = form['r18'] == 'True' # 前端获取的是字符串 比较判断 101 | url = form['url'] 102 | pid = form['pid'] 103 | p = form['p'] 104 | ori_url = f'https://www.pixiv.net/artworks/{pid}' 105 | title = form['title'] 106 | author = form['author'] 107 | result = await send_to_group(group_id, url, pid, p, title, author, ori_url, r18) 108 | return result 109 | 110 | 111 | @bp.route('/acgmxsend', methods=['POST']) 112 | async def acgmxsend(): 113 | form = await request.form 114 | group_id = form['group_id'] 115 | psw = form['psw'] 116 | try: 117 | password = group_psw[group_id] 118 | except KeyError: 119 | return '请先在群内设置密码' 120 | if psw != password: 121 | return '密码错误' 122 | pid = form['pid'] 123 | p = form['p'] 124 | url = form['url'] 125 | ori_url = f'https://www.pixiv.net/artworks/{pid}' 126 | title = form['title'] 127 | author = form['author'] 128 | group_id = int(group_id) 129 | result = await send_to_group_acgmx(group_id, url, pid, p, title, author, ori_url, config['acgmx_token']) 130 | return result 131 | 132 | 133 | @bp.route('/acgmx', methods=['GET']) 134 | async def acgmx(): 135 | groups = await get_groups() 136 | headers = { 137 | 'token': config['acgmx_token'], 138 | 'referer': 'https://www.acgmx.com/' 139 | } 140 | url = 'https://api.acgmx.com/public/setu' 141 | async with aiohttp.ClientSession(headers=headers) as session: 142 | async with session.get(url) as res: 143 | res = await res.read() 144 | res = json.loads(res) 145 | img_url = res['data']['large'] 146 | pid = res['data']['illust'] 147 | pageCount = res['data']['pageCount'] 148 | xRestrict = res['data']['xRestrict'] 149 | restrict = res['data']['restrict'] 150 | title = res['data']['title'] 151 | author = res['data']['user']['name'] 152 | uid = res['data']['user']['id'] 153 | img_type = img_url.split('.')[-1] 154 | if pageCount == '1': 155 | pixiv_cat_url = f'https://pixiv.cat/{pid}.{img_type}' 156 | else: 157 | pixiv_cat_url = f'https://pixiv.cat/{pid}-{xRestrict}.{img_type}' 158 | return await render_template('acgmx.html', 159 | img_url=img_url, 160 | pixiv_cat_url=pixiv_cat_url, 161 | pid=pid, 162 | p=restrict, 163 | title=title, 164 | author=author, 165 | uid=uid, 166 | groups=groups) 167 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | from aiohttp import ClientConnectorError, ClientPayloadError 2 | from aiocqhttp.exceptions import ActionFailed 3 | from nonebot import get_bot 4 | from hoshino import Service, R 5 | from hoshino.typing import HoshinoBot, CQEvent 6 | from hoshino.priv import check_priv, ADMIN, SUPERUSER 7 | import requests 8 | import os 9 | import aiohttp 10 | import asyncio 11 | from PIL import Image 12 | import random 13 | 14 | try: 15 | import ujson as json 16 | except ImportError: 17 | import json 18 | 19 | allow_path = os.path.dirname(__file__) + '/allowed_groups.json' 20 | if not os.path.exists(allow_path): 21 | allowed_groups = {} 22 | json.dump(allowed_groups, open(allow_path, 'w'), indent=4) 23 | else: 24 | allowed_groups = json.load(open(allow_path, 'r')) 25 | 26 | r18_path = os.path.dirname(__file__) + '/r18_groups.json' 27 | if not os.path.exists(r18_path): 28 | r18_groups = {} 29 | json.dump(r18_groups, open(r18_path, 'w'), indent=4) 30 | else: 31 | r18_groups = json.load(open(r18_path, 'r')) 32 | 33 | withdraw_path = os.path.dirname(__file__) + '/withdraw_groups.json' 34 | if not os.path.exists(withdraw_path): 35 | withdraw_groups = {} 36 | json.dump(withdraw_groups, open(withdraw_path, 'w'), indent=4) 37 | else: 38 | withdraw_groups = json.load(open(withdraw_path, 'r')) 39 | 40 | psw_path = os.path.dirname(__file__) + '/group_psw.json' 41 | if not os.path.exists(psw_path): 42 | group_psw = {} 43 | json.dump(group_psw, open(psw_path, 'w'), indent=4) 44 | else: 45 | group_psw = json.load(open(psw_path, 'r')) 46 | 47 | sv = Service('setuweb') 48 | 49 | bot = get_bot() 50 | port = bot.config.PORT 51 | curr_dir = os.path.dirname(__file__) 52 | config = json.load(open(f'{curr_dir}/config.json', 'r')) 53 | if config['url'] != '': 54 | setu_url = config['url'] 55 | else: 56 | try: 57 | ip = json.loads(requests.get('https://jsonip.com/').text)['ip'] 58 | except requests.exceptions.ConnectionError: 59 | ip = 'ip获取失败' 60 | if port == 80: 61 | setu_url = f'http://{ip}/setu/' 62 | else: 63 | setu_url = f'http://{ip}:{port}/setu/' 64 | 65 | help_str = f'''本插件为在线setu插件 66 | 进入{setu_url}开始使用本插件 67 | 输入setpsw+密码来设置本群web端发送密码 68 | 输入setuallow允许本群发送Setu 69 | 输入setuforbid禁止本群发送Setu 70 | 输入r18allow允许本群发送r18Setu 71 | 输入r18forbid禁止本群发送r18Setu 72 | 输入withdrawon开启本群撤回 73 | 输入withdrawoff关闭本群撤回 74 | 输入setwithdraw+时间设置撤回时间(秒) 0为全局关闭撤回 75 | 输入setpsw+密码设置本群web端密码 76 | 输入antishielding设置反和谐方式 77 | 输入forwardon开启转发消息模式 78 | 输入forwardoff关闭转发消息模式 79 | 输入来\\发\\给(数量)份\\点\\张\\幅(uids空格分隔的uid|)(tags空格分隔的tag|)(R\\r18)(关键字)\\涩\\瑟\\色图 在群内获取Setu 80 | (正则表达式^[来发给](?P.*)?[份点张幅](?Puids(\d\s?)*\|)?(?Ptags(.*\s?)*\|)?的?(?P[Rr]18)?(?P.*)?[涩瑟色]图$) 81 | 注意使用uids或tags需要以|结束''' 82 | 83 | 84 | @sv.on_fullmatch('setuhelp') 85 | async def setuhelp(bot: HoshinoBot, ev: CQEvent): 86 | await bot.send(ev, help_str) 87 | 88 | 89 | @sv.on_fullmatch('setuallow') 90 | async def setuallow(bot: HoshinoBot, ev: CQEvent): 91 | if ev['message_type'] != 'group': 92 | return 93 | if not check_priv(ev, ADMIN): 94 | await bot.send(ev, f'管理员以上才能使用') 95 | return 96 | group_id = ev.group_id 97 | allowed_groups[str(group_id)] = True 98 | json.dump(allowed_groups, open(allow_path, 'w'), indent=4) 99 | await bot.send(ev, f'已允许{group_id}') 100 | 101 | 102 | @sv.on_fullmatch('setuforbid') 103 | async def setuforbid(bot: HoshinoBot, ev: CQEvent): 104 | if ev['message_type'] != 'group': 105 | return 106 | if not check_priv(ev, ADMIN): 107 | await bot.send(ev, f'管理员以上才能使用') 108 | return 109 | group_id = ev.group_id 110 | allowed_groups[str(group_id)] = False 111 | json.dump(allowed_groups, open(allow_path, 'w'), indent=4) 112 | await bot.send(ev, f'已禁止{group_id}') 113 | 114 | 115 | @sv.on_fullmatch('r18allow') 116 | async def setuallow(bot: HoshinoBot, ev: CQEvent): 117 | if ev['message_type'] != 'group': 118 | return 119 | if not check_priv(ev, ADMIN): 120 | await bot.send(ev, f'管理员以上才能使用') 121 | return 122 | group_id = ev.group_id 123 | r18_groups[str(group_id)] = True 124 | json.dump(r18_groups, open(r18_path, 'w'), indent=4) 125 | await bot.send(ev, f'已允许{group_id}r18') 126 | 127 | 128 | @sv.on_fullmatch('r18forbid') 129 | async def setuforbid(bot: HoshinoBot, ev: CQEvent): 130 | if ev['message_type'] != 'group': 131 | return 132 | if not check_priv(ev, ADMIN): 133 | await bot.send(ev, f'管理员以上才能使用') 134 | return 135 | group_id = ev.group_id 136 | r18_groups[str(group_id)] = False 137 | json.dump(r18_groups, open(r18_path, 'w'), indent=4) 138 | await bot.send(ev, f'已禁止{group_id}r18') 139 | 140 | 141 | @sv.on_fullmatch('withdrawon') 142 | async def withdrawon(bot: HoshinoBot, ev: CQEvent): 143 | if ev['message_type'] != 'group': 144 | return 145 | if not check_priv(ev, ADMIN): 146 | await bot.send(ev, f'管理员以上才能使用') 147 | return 148 | group_id = ev.group_id 149 | withdraw_groups[str(group_id)] = True 150 | json.dump(withdraw_groups, open(withdraw_path, 'w'), indent=4) 151 | await bot.send(ev, f'已开启{group_id}撤回') 152 | 153 | 154 | @sv.on_fullmatch('withdrawoff') 155 | async def withdrawoff(bot: HoshinoBot, ev: CQEvent): 156 | if ev['message_type'] != 'group': 157 | return 158 | if not check_priv(ev, ADMIN): 159 | await bot.send(ev, f'管理员以上才能使用') 160 | return 161 | group_id = ev.group_id 162 | withdraw_groups[str(group_id)] = False 163 | json.dump(withdraw_groups, open(withdraw_path, 'w'), indent=4) 164 | await bot.send(ev, f'已关闭{group_id}撤回') 165 | 166 | 167 | @sv.on_fullmatch('forwardon') 168 | async def withdrawon(bot: HoshinoBot, ev: CQEvent): 169 | if not check_priv(ev, SUPERUSER): 170 | await bot.send(ev, f'管理员以上才能使用') 171 | return 172 | config['forward'] = True 173 | json.dump(config, open(f'{curr_dir}/config.json', 'w'), indent=4) 174 | await bot.send(ev, f'已开启卡片消息模式') 175 | 176 | 177 | @sv.on_fullmatch('forwardoff') 178 | async def withdrawoff(bot: HoshinoBot, ev: CQEvent): 179 | if not check_priv(ev, SUPERUSER): 180 | await bot.send(ev, f'管理员以上才能使用') 181 | return 182 | config['forward'] = False 183 | json.dump(config, open(f'{curr_dir}/config.json', 'w'), indent=4) 184 | await bot.send(ev, f'已关闭卡片消息模式') 185 | 186 | 187 | @sv.on_prefix('setwithdraw') 188 | async def setwithdraw(bot: HoshinoBot, ev: CQEvent): 189 | if not check_priv(ev, SUPERUSER): 190 | await bot.send(ev, f'机器人主人才能使用') 191 | return 192 | time = ev.message.extract_plain_text() 193 | try: 194 | time = int(time) 195 | except ValueError: 196 | await bot.send(ev, '请输入setwithdraw+数字') 197 | return 198 | if time < 0: 199 | await bot.send(ev, '请输入setwithdraw+大于0的数字') 200 | return 201 | config['withdraw'] = time 202 | json.dump(config, open(f'{curr_dir}/config.json', 'w'), indent=4) 203 | await bot.send(ev, f'已设置撤回时间为{time}秒') 204 | 205 | 206 | @sv.on_prefix('setpsw') 207 | async def setpsw(bot: HoshinoBot, ev: CQEvent): 208 | if ev['message_type'] != 'group': 209 | return 210 | if not check_priv(ev, ADMIN): 211 | await bot.send(ev, f'管理员以上才能使用') 212 | return 213 | psw = ev.message.extract_plain_text().strip() 214 | group_id = ev.group_id 215 | group_psw[str(group_id)] = psw 216 | json.dump(group_psw, open(psw_path, 'w'), indent=4) 217 | if psw == '': 218 | await bot.send(ev, f'已设置群{group_id}密码为空') 219 | else: 220 | await bot.send(ev, f'已设置群{group_id}密码为{psw}') 221 | 222 | 223 | @sv.on_prefix('antishielding') 224 | async def withdrawon(bot: HoshinoBot, ev: CQEvent): 225 | if not check_priv(ev, SUPERUSER): 226 | await bot.send(ev, f'机器人主人才能使用') 227 | return 228 | msg = ev.message.extract_plain_text().strip() 229 | if msg == '': 230 | status = '关闭' if config['antishielding'] == 0 else '开启' 231 | antitype = '错误' 232 | if config['antishielding'] == 1: 233 | antitype = '随机像素' 234 | elif config['antishielding'] == 2: 235 | antitype = '旋转' 236 | elif config['antishielding'] == 3: 237 | antitype = '混合' 238 | if config['antishielding'] == 0: 239 | await bot.send(ev, f'当前反和谐{status}') 240 | else: 241 | await bot.send(ev, f'当前反和谐{status},类型为{antitype}') 242 | elif msg not in ['0', '1', '2', '3']: 243 | await bot.send(ev, '请输入正确的数字') 244 | else: 245 | config['antishielding'] = int(msg) 246 | json.dump(config, open(f'{curr_dir}/config.json', 'w'), indent=4) 247 | await bot.send(ev, f'已将反和谐设为{msg}') 248 | 249 | 250 | @sv.on_rex( 251 | r'^[来发给](?P.*)?[份点张幅](?Puids(\d\s?)*\|)?(?Ptags(.*\s?)*\|)?的?(?P[Rr]18)?(?P.*)?[涩瑟色]图$') 252 | async def group_setu(bot: HoshinoBot, ev: CQEvent): 253 | if ev['message_type'] == 'group': 254 | group_id = ev.group_id 255 | try: 256 | if not allowed_groups[str(group_id)]: 257 | await bot.send_group_msg(group_id=group_id, message='此群不允许发送Setu!') 258 | return 259 | except KeyError: 260 | await bot.send_group_msg(group_id=group_id, message='此群不允许发送Setu!') 261 | return 262 | api = config['group_api'] 263 | 264 | if api == 'lolicon': 265 | num_convert = {'零': 0, '一': 1, '二': 2, '两': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10} 266 | num_convert_big = {'壹': 1, '贰': 2, '叁': 3, '肆': 4, '伍': 5, '陆': 6, '柒': 7, '捌': 8, '玖': 9, '拾': 10} 267 | num = ev['match'].group('count') 268 | if num in num_convert: 269 | num = num_convert[num] 270 | if num in num_convert_big: 271 | num = num_convert_big[num] 272 | try: 273 | num = int(num) 274 | except ValueError: 275 | if num == '': 276 | num = 1 277 | else: 278 | await bot.send(ev, '请输入正确的数字') 279 | return 280 | else: 281 | if num > 10: 282 | num = 10 283 | elif num < 1: 284 | await bot.send(ev, '请输入大于或等于1的数字') 285 | return 286 | uids = ev['match'].group('uids') 287 | if uids is not None and uids != '': 288 | uids = uids.strip()[4:-1].split(' ') 289 | try: 290 | uids = [int(uid) for uid in uids] 291 | except ValueError: 292 | await bot.send(ev, '请输入正确的数字uid') 293 | return 294 | tags = ev['match'].group('tags') 295 | if tags is not None and tags != '': 296 | tags = tags.strip()[4:-1].split(' ') 297 | r18 = int(ev['match'].group('r18') is not None) 298 | if ev['message_type'] == 'group': 299 | if r18 == 1: 300 | try: 301 | if not r18_groups[str(group_id)]: 302 | await bot.send_group_msg(group_id=group_id, message='此群不允许发送r18Setu!') 303 | return 304 | except KeyError: 305 | await bot.send_group_msg(group_id=group_id, message='此群不允许发送r18Setu!') 306 | return 307 | keyword = ev['match'].group('keyword') 308 | # apikeys = config['apikey'] 309 | size = config['size'] 310 | result = {} 311 | # for apikey in apikeys: 312 | # if keyword == '': 313 | # params = { 314 | # 'apikey': apikey, 315 | # 'proxy': config['proxy'], 316 | # 'r18': r18, 317 | # 'num': num, 318 | # 'size': size, 319 | # } 320 | # else: 321 | # params = { 322 | # 'apikey': config['apikey'], 323 | # 'proxy': config['proxy'], 324 | # 'keyword': keyword, 325 | # 'r18': r18, 326 | # 'num': num, 327 | # 'size': size, 328 | # } 329 | # async with aiohttp.ClientSession() as session: 330 | # async with session.get('https://api.lolicon.app/setu/', params=params) as rq: 331 | # result = await rq.read() 332 | # result = json.loads(result) 333 | # code = result['code'] 334 | # if code == 0 or code == 404: 335 | # break 336 | 337 | datas = { 338 | 'proxy': config['proxy'], 339 | 'r18': r18, 340 | 'num': num, 341 | 'size': size, 342 | } 343 | headers = {'content-type': 'application/json'} 344 | if keyword != '': 345 | datas['keyword'] = keyword 346 | if uids is not None: 347 | datas['uid'] = uids 348 | if tags is not None: 349 | datas['tag'] = tags 350 | async with aiohttp.ClientSession(raise_for_status=True) as session: 351 | async with session.post('https://api.lolicon.app/setu/v2', data=json.dumps(datas), headers=headers) as rq: 352 | result = await rq.read() 353 | result = json.loads(result) 354 | err = result['error'] 355 | if err != '': 356 | await bot.send(ev, err) 357 | return 358 | elif len(result['data']) == 0: 359 | await bot.send(ev, '找不到符合条件的Setu') 360 | return 361 | else: 362 | # sending = [] 363 | # data = result['data'] 364 | # for setu in data: 365 | # urls = setu['urls'] 366 | # pid = setu['pid'] 367 | # p = setu['p'] 368 | # title = setu['title'] 369 | # author = setu['author'] 370 | # ori_url = f'https://www.pixiv.net/artworks/{pid}' 371 | # if ev['message_type'] == 'group': 372 | # sending.append(send_to_group(ev.group_id, urls[size], pid, p, title, author, ori_url, bool(r18))) 373 | # else: 374 | # sending.append(send_to_private(ev.user_id, urls[size], pid, p, title, author, ori_url)) 375 | # await asyncio.gather(*sending) 376 | # 旧单独发送 377 | groupsending = [] 378 | sending = [] 379 | data = result['data'] 380 | for setu in data: 381 | urls = setu['urls'] 382 | pid = setu['pid'] 383 | p = setu['p'] 384 | title = setu['title'] 385 | author = setu['author'] 386 | ori_url = f'https://www.pixiv.net/artworks/{pid}' 387 | if ev['message_type'] == 'group': 388 | groupsending.append({'group_id': ev.group_id, 389 | 'url': urls[size], 390 | 'pid': pid, 391 | 'p': p, 392 | 'title': title, 393 | 'author': author, 394 | 'ori_url': ori_url, 395 | 'r18': bool(r18)}) 396 | else: 397 | sending.append(send_to_private(ev.user_id, urls[size], pid, p, title, author, ori_url)) 398 | if ev['message_type'] == 'group': 399 | await send_list_to_group(*groupsending) 400 | else: 401 | await asyncio.gather(*sending) 402 | 403 | 404 | 405 | elif config['group_api'] == 'acgmx': # 个人未使用 未维护 406 | await bot.send_group_msg(group_id=group_id, message='获取中') 407 | headers = { 408 | 'token': config['acgmx_token'], 409 | 'referer': 'https://www.acgmx.com/' 410 | } 411 | url = 'https://api.acgmx.com/public/setu' 412 | async with aiohttp.ClientSession(headers=headers, raise_for_status=True) as session: 413 | async with session.get(url) as res: 414 | res = await res.read() 415 | res = json.loads(res) 416 | img_url = res['data']['large'] 417 | pid = res['data']['illust'] 418 | restrict = res['data']['restrict'] 419 | title = res['data']['title'] 420 | author = res['data']['user']['name'] 421 | ori_url = f'https://www.pixiv.net/artworks/{pid}' 422 | if ev['message_type'] == 'group': 423 | await send_to_group_acgmx(ev.group_id, img_url, pid, restrict, title, author, ori_url, 424 | config['acgmx_token']) 425 | else: 426 | await send_to_private_acgmx(ev.user_id, url, pid, restrict, title, author, ori_url, config['acgmx_token']) 427 | 428 | 429 | async def down_img(url: str): 430 | print(f'downloading from {url}') 431 | filename = url.split('/')[-1] 432 | path = R.img('setuweb/').path 433 | try: 434 | async with aiohttp.ClientSession(raise_for_status=True) as session: 435 | res = await session.get(url) 436 | f = open(f'{path}/{filename}', 'wb') 437 | res = await res.read() 438 | f.write(res) 439 | return True 440 | except ClientConnectorError: 441 | print('ClientConnectError') 442 | return False 443 | except ClientPayloadError: 444 | print('ClientPayloadError') 445 | return False 446 | 447 | 448 | async def down_acgmx_img(url: str, token: str): 449 | headers = { 450 | 'token': token, 451 | 'referer': 'https://www.acgmx.com/' 452 | } 453 | print(f'downloading from {url}') 454 | filename = url.split('/')[-1] 455 | path = R.img('setuweb/').path 456 | try: 457 | async with aiohttp.ClientSession(headers=headers, raise_for_status=True) as session: 458 | res = await session.get(url) 459 | f = open(f'{path}/{filename}', 'wb') 460 | res = await res.read() 461 | f.write(res) 462 | return True 463 | except ClientConnectorError: 464 | return False 465 | 466 | 467 | def format_msg(url: str, pid: str, p: str, title: str, author: str, ori_url: str): 468 | # filename = url.split('/')[-1] 469 | # img = R.img(f'setuweb/{filename}').cqcode 470 | msg = f'pid: {pid} p{p}\n标题: {title}\n作者: {author}\n原地址: {ori_url}\n{url}' 471 | return msg 472 | 473 | 474 | async def send_to_group(group_id: int, url: str, pid: str, p: str, title: str, author: str, ori_url: str, r18: bool): 475 | try: 476 | if not allowed_groups[str(group_id)]: 477 | return '此群不允许发送!' 478 | except KeyError: 479 | return '此群不允许发送!' 480 | if r18: 481 | try: 482 | if not r18_groups[str(group_id)]: 483 | return '此群不允许发送r18Setu!' 484 | except KeyError: 485 | return '此群不允许发送r18Setu!' 486 | filename = url.split('/')[-1] 487 | msg = format_msg(url, pid, p, title, author, ori_url) 488 | if config['forward']: 489 | msg += '\n' 490 | data = { 491 | "type": "node", 492 | "data": { 493 | "name": '小冰', 494 | "uin": '2854196306', 495 | "content": msg 496 | } 497 | } 498 | msg_result = await bot.send_group_forward_msg(group_id=group_id, messages=data) 499 | else: 500 | await bot.send_group_msg(group_id=group_id, message=msg) 501 | downres = True 502 | if not os.path.exists(R.img(f'setuweb/{filename}').path): 503 | downres = await down_img(url) 504 | if not downres: 505 | return f'涩图下载失败\nurl:{url}' 506 | filename = url.split('/')[-1] 507 | if config['antishielding']: 508 | path = R.img(f'setuweb/{filename}').path 509 | await imgAntiShielding(path) 510 | imgtype = path.split('.')[-1] 511 | imgname = path.split('\\')[-1].split('.')[-2] 512 | filename = f'{imgname}_anti.{imgtype}' 513 | img = R.img(f'setuweb/{filename}').cqcode 514 | if config['forward']: 515 | msg += img 516 | data = { 517 | "type": "node", 518 | "data": { 519 | "name": '小冰', 520 | "uin": '2854196306', 521 | "content": msg 522 | } 523 | } 524 | try: 525 | await bot.delete_msg(message_id=msg_result['message_id']) 526 | except ActionFailed: 527 | await bot.send_group_msg(group_id=group_id, message='信息撤回失败') 528 | result = await bot.send_group_forward_msg(group_id=group_id, messages=data) 529 | else: 530 | result = await bot.send_group_msg(group_id=group_id, message=img) 531 | withdraw = int(config['withdraw']) 532 | ifwithdraw = True 533 | try: 534 | if not withdraw_groups[str(group_id)]: 535 | ifwithdraw = False 536 | except KeyError: 537 | ifwithdraw = False 538 | if ifwithdraw and withdraw and withdraw > 0: 539 | print(f'{withdraw}秒后撤回') 540 | await asyncio.sleep(withdraw) 541 | await bot.delete_msg(message_id=result['message_id']) 542 | return f'已发送到群{group_id}并撤回!' 543 | return f'已发送到群{group_id}!' 544 | 545 | 546 | async def send_list_to_group(*args): 547 | try: 548 | if not allowed_groups[str(args[0]['group_id'])]: 549 | return '此群不允许发送!' 550 | except KeyError: 551 | return '此群不允许发送!' 552 | if args[0]['r18']: 553 | try: 554 | if not r18_groups[str(args[0]['group_id'])]: 555 | return '此群不允许发送r18Setu!' 556 | except KeyError: 557 | return '此群不允许发送r18Setu!' 558 | await bot.send_group_msg(group_id=args[0]['group_id'], message='Setu下载中,请等待若干时间。') 559 | # 涩图下载开始 560 | 561 | data = [] 562 | for i in args: 563 | msg = '' 564 | downres = True 565 | filename = i['url'].split('/')[-1] 566 | url = i['url'] 567 | if not os.path.exists(R.img(f'setuweb/{filename}').path): 568 | downres = await down_img(url) 569 | 570 | if not downres: 571 | img = '此图片下载失败' 572 | else: 573 | filename = url.split('/')[-1] 574 | if config['antishielding']: 575 | path = R.img(f'setuweb/{filename}').path 576 | await imgAntiShielding(path) 577 | imgtype = path.split('.')[-1] 578 | imgname = path.split('\\')[-1].split('.')[-2] 579 | filename = f'{imgname}_anti.{imgtype}' 580 | img = R.img(f'setuweb/{filename}').cqcode 581 | if config['forward']: 582 | filename = i['url'].split('/')[-1] 583 | msg += format_msg(i['url'], i['pid'], i['p'], i['title'], i['author'], i['ori_url']) + '\n' 584 | msg += img + '\n' 585 | data.append({ 586 | "type": "node", 587 | "data": { 588 | "name": '小冰', 589 | "uin": '2854196306', 590 | "content": msg 591 | } 592 | }) 593 | else: 594 | msg += format_msg(i['url'], i['pid'], i['p'], i['title'], i['author'], i['ori_url']) + '\n' 595 | msg += img + '\n' 596 | result = await bot.send_group_msg(group_id=args[0]['group_id'], message=msg) 597 | withdraw = int(config['withdraw']) 598 | ifwithdraw = True 599 | group_id = args[0]['group_id'] 600 | try: 601 | if not withdraw_groups[str(group_id)]: 602 | ifwithdraw = False 603 | except KeyError: 604 | ifwithdraw = False 605 | if ifwithdraw and withdraw and withdraw > 0: 606 | print(f'{withdraw}秒后撤回') 607 | await asyncio.sleep(withdraw) 608 | await bot.delete_msg(message_id=result['message_id']) 609 | 610 | if config['forward']: 611 | result = await bot.send_group_forward_msg(group_id=args[0]['group_id'], messages=data) 612 | 613 | withdraw = int(config['withdraw']) 614 | ifwithdraw = True 615 | group_id = args[0]['group_id'] 616 | try: 617 | if not withdraw_groups[str(group_id)]: 618 | ifwithdraw = False 619 | except KeyError: 620 | ifwithdraw = False 621 | if ifwithdraw and withdraw and withdraw > 0: 622 | print(f'{withdraw}秒后撤回') 623 | await asyncio.sleep(withdraw) 624 | await bot.delete_msg(message_id=result['message_id']) 625 | return f'已发送到群{group_id}并撤回!' 626 | return f'已发送到群{group_id}!' 627 | 628 | 629 | async def send_to_group_acgmx(group_id: int, url: str, pid: str, p: str, title: str, author: str, ori_url: str, 630 | token: str): 631 | try: 632 | if not allowed_groups[str(group_id)]: 633 | return '此群不允许发送!' 634 | except KeyError: 635 | return '此群不允许发送!' 636 | else: 637 | msg = format_msg(url, pid, p, title, author, ori_url) 638 | if config['forward']: 639 | msg += '\n' 640 | data = { 641 | "type": "node", 642 | "data": { 643 | "name": '小冰', 644 | "uin": '2854196306', 645 | "content": msg 646 | } 647 | } 648 | msg_result = await bot.send_group_msg(group_id=group_id, messages=data) 649 | else: 650 | await bot.send_group_msg(group_id=group_id, message=msg) 651 | filename = url.split('/')[-1] 652 | downres = True 653 | if not os.path.exists(R.img(f'setuweb/{filename}').path): 654 | downres = await down_acgmx_img(url, token) 655 | if not downres: 656 | return f'涩图下载失败\nurl:{url}' 657 | if config['antishielding']: 658 | path = R.img(f'setuweb/{filename}').path 659 | await imgAntiShielding(path) 660 | imgtype = path.split('.')[-1] 661 | imgname = path.split('\\')[-1].split('.')[-2] 662 | filename = f'{imgname}_anti.{imgtype}' 663 | img = R.img(f'setuweb/{filename}').cqcode 664 | if config['forward']: 665 | msg += img 666 | data = { 667 | "type": "node", 668 | "data": { 669 | "name": '小冰', 670 | "uin": '2854196306', 671 | "content": msg 672 | } 673 | } 674 | try: 675 | await bot.delete_msg(message_id=msg_result['message_id']) 676 | except ActionFailed: 677 | await bot.send_group_msg(group_id=group_id, message='信息撤回失败') 678 | result = await bot.send_group_forward_msg(group_id=group_id, messages=data) 679 | else: 680 | result = await bot.send_group_msg(group_id=group_id, message=img) 681 | withdraw = int(config['withdraw']) 682 | ifwithdraw = True 683 | try: 684 | if not withdraw_groups[str(group_id)]: 685 | ifwithdraw = False 686 | except KeyError: 687 | ifwithdraw = False 688 | if ifwithdraw and withdraw and withdraw > 0: 689 | print(f'{withdraw}秒后撤回') 690 | await asyncio.sleep(withdraw) 691 | await bot.delete_msg(message_id=result['message_id']) 692 | return f'已发送到群{group_id}并撤回!' 693 | return f'已发送到群{group_id}!' 694 | 695 | 696 | async def send_to_private(user_id: int, url: str, pid: str, p: str, title: str, author: str, ori_url: str): 697 | msg = format_msg(url, pid, p, title, author, ori_url) 698 | await bot.send_private_msg(user_id=user_id, message=msg) 699 | filename = url.split('/')[-1] 700 | if not os.path.exists(R.img(f'setuweb/{filename}').path): 701 | await down_img(url) 702 | if config['antishielding']: 703 | path = R.img(f'setuweb/{filename}').path 704 | await imgAntiShielding(path) 705 | imgtype = path.split('.')[-1] 706 | imgname = path.split('\\')[-1].split('.')[-2] 707 | filename = f'{imgname}_anti.{imgtype}' 708 | img = R.img(f'setuweb/{filename}').cqcode 709 | await bot.send_private_msg(user_id=user_id, message=img) 710 | 711 | 712 | async def send_to_private_acgmx(user_id: int, url: str, pid: str, p: str, title: str, author: str, ori_url: str, 713 | token: str): 714 | msg = format_msg(url, pid, p, title, author, ori_url) 715 | await bot.send_private_msg(user_id=user_id, message=msg) 716 | filename = url.split('/')[-1] 717 | if not os.path.exists(R.img(f'setuweb/{filename}').path): 718 | await down_acgmx_img(url, token) 719 | if config['antishielding']: 720 | path = R.img(f'setuweb/{filename}').path 721 | await imgAntiShielding(path) 722 | imgtype = path.split('.')[-1] 723 | imgname = path.split('\\')[-1].split('.')[-2] 724 | filename = f'{imgname}_anti.{imgtype}' 725 | img = R.img(f'setuweb/{filename}').cqcode 726 | await bot.send_private_msg(user_id=user_id, message=img) 727 | 728 | 729 | async def imgAntiShielding(path): 730 | image = Image.open(path) 731 | imgtype = path.split('.')[-1] 732 | imgname = path.split('\\')[-1].split('.')[-2] 733 | path_anti = os.path.dirname(path) + f'\\{imgname}_anti.{imgtype}' 734 | w, h = image.size 735 | pixels = [ 736 | [0, 0], 737 | [w - 1, 0], 738 | [0, h - 1], 739 | [w - 1, h - 1] 740 | ] 741 | if config['antishielding'] == 1: # 随机像素 742 | for p in pixels: 743 | image.putpixel((p[0], p[1]), 744 | (int(random.random() * 255), 745 | int(random.random() * 255), 746 | int(random.random() * 255))) 747 | image.save(path_anti) 748 | elif config['antishielding'] == 2: # 旋转 749 | image = image.rotate(90, expand=True) 750 | image.save(path_anti) 751 | 752 | elif config['antishielding'] == 3: # 混合 753 | for p in pixels: 754 | image.putpixel((p[0], p[1]), 755 | (int(random.random() * 255), 756 | int(random.random() * 255), 757 | int(random.random() * 255))) 758 | image.rotate(90) 759 | image.save(path_anti) 760 | 761 | 762 | def get_groups(): 763 | groups = bot.get_group_list() 764 | return groups 765 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "size": "regular", 3 | "proxy": "i.pixiv.cat", 4 | "acgmx_token": "", 5 | "group_api": "lolicon", 6 | "withdraw": 60, 7 | "antishielding": 0, 8 | "url": "", 9 | "forward" : false 10 | } -------------------------------------------------------------------------------- /static/jquery-3.5.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 2 | 3 | 4 | 5 | Setu! 6 | 7 | 8 | 9 | pid: {{pid}} p{{p}}
10 | uid: {{uid}}
11 | 标题: {{title}}
12 | 作者: {{author}}
13 | 原地址: https://www.pixiv.net/artworks/{{pid}}
14 |
15 | 16 | 17 | 18 | 19 | 20 | 25 | 群密码: 26 |
27 |
28 | 原图:
29 | 30 | 31 | 50 | 51 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Setu! 6 | 7 | 8 | 9 | 13 |
14 | lolicon.app
15 |
16 | 关键字:
17 | uids(使用空格分割):
18 | tags(使用空格分隔):
19 | r18: 20 | 否 21 | 是 22 | 混合
23 | 数量:
24 | 尺寸: 25 | original 26 | regular 27 | small 28 | thumb 29 | mini
30 |
31 |
32 |
33 | 39 | 40 | 52 | -------------------------------------------------------------------------------- /templates/result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Setu! 6 | 7 | 8 | 9 | {% if err != '' %} 10 | {{err}} 11 | {% else %} 12 | {% if noresult %} 13 | 找不到符合条件的的Setu 14 | {% endif %} 15 | {% for setu in data %} 16 | pid: {{setu['pid']}} p{{setu['p']}}
17 | uid: {{setu['uid']}}
18 | r18: {% if setu['r18'] %}是{% else %}否{% endif %}
19 | 标题: {{setu['title']}}
20 | 作者: {{setu['author']}}
21 | 原地址: https://www.pixiv.net/artworks/{{setu['pid']}}
22 | 链接: {{setu['urls'][size]}}
23 | r18: 24 | {% if setu['r18'] %} 25 | 是 26 | {% else %} 27 | 否 28 | {% endif %} 29 |
30 | 图片尺寸: {{setu['height']}}*{{setu['width']}}
31 | Tags: 32 | {% for tag in setu['tags'] %} 33 | {{tag}} 34 | {% endfor %} 35 |
36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 48 | 群密码: 49 |
50 |
51 |
52 | {% endfor %} 53 | {% endif %} 54 | 73 | 74 | --------------------------------------------------------------------------------