summaryrefslogtreecommitdiff
path: root/ATRI/plugins/setu
diff options
context:
space:
mode:
Diffstat (limited to 'ATRI/plugins/setu')
-rw-r--r--ATRI/plugins/setu/__init__.py8
-rw-r--r--ATRI/plugins/setu/modules/data_source.py178
-rw-r--r--ATRI/plugins/setu/modules/main_setu.py193
-rw-r--r--ATRI/plugins/setu/modules/scheduler.py19
-rw-r--r--ATRI/plugins/setu/modules/store.py138
5 files changed, 536 insertions, 0 deletions
diff --git a/ATRI/plugins/setu/__init__.py b/ATRI/plugins/setu/__init__.py
new file mode 100644
index 0000000..d9f1ffe
--- /dev/null
+++ b/ATRI/plugins/setu/__init__.py
@@ -0,0 +1,8 @@
+import nonebot
+from pathlib import Path
+
+
+_sub_plugins = set()
+
+_sub_plugins |= nonebot.load_plugins(
+ str((Path(__file__).parent / 'modules').resolve()))
diff --git a/ATRI/plugins/setu/modules/data_source.py b/ATRI/plugins/setu/modules/data_source.py
new file mode 100644
index 0000000..72e2270
--- /dev/null
+++ b/ATRI/plugins/setu/modules/data_source.py
@@ -0,0 +1,178 @@
+import os
+import json
+import string
+import aiosqlite
+from aiosqlite.core import Connection
+from pathlib import Path
+from random import sample, choice
+from aiohttp import ClientSession
+from nonebot.adapters.cqhttp.message import MessageSegment, Message
+
+from ATRI.log import logger as log
+from ATRI.config import NsfwCheck
+from ATRI.exceptions import RequestError, WriteError
+from ATRI.utils.request import get_bytes
+from ATRI.utils.img import compress_image
+
+
+TEMP_DIR: Path = Path('.') / 'ATRI' / 'data' / 'temp' / 'setu'
+SETU_DIR = Path('.') / 'ATRI' / 'data' / 'database' / 'setu'
+os.makedirs(TEMP_DIR, exist_ok=True)
+os.makedirs(SETU_DIR, exist_ok=True)
+NSFW_URL = f"http://{NsfwCheck.host}:{NsfwCheck.port}/?url="
+SIZE_REDUCE: bool = True
+
+
+class Hso:
+ @staticmethod
+ async def nsfw_check(url: str) -> float:
+ url = NSFW_URL + url
+ try:
+ data = json.loads(await get_bytes(url))
+ except RequestError:
+ raise RequestError('Request failed!')
+ return round(data['score'], 4)
+
+ @staticmethod
+ async def _comp_setu(url: str) -> str:
+ temp_id = ''.join(sample(string.ascii_letters + string.digits, 8))
+ file = TEMP_DIR / f'{temp_id}.png'
+
+ try:
+ async with ClientSession() as session:
+ async with session.get(url) as r:
+ data = await r.read()
+ except RequestError:
+ raise RequestError('Request img failed!')
+
+ try:
+ with open(file, 'wb') as r:
+ r.write(data)
+ except WriteError:
+ raise WriteError('Writing img failed!')
+
+ return compress_image(os.path.abspath(file))
+
+ @classmethod
+ async def setu(cls, data: dict) -> str:
+ pid = data['pid']
+ title = data['title']
+ if SIZE_REDUCE:
+ img = MessageSegment.image(
+ 'file:///' + await cls._comp_setu(data['url']), proxy=False)
+ else:
+ img = MessageSegment.image(data['url'], proxy=False)
+
+ msg = (
+ f"Pid: {pid}\n"
+ f"Title: {title}\n"
+ f"{img}"
+ )
+ return msg
+
+ @classmethod
+ async def acc_setu(cls, d: list) -> str:
+ data: dict = choice(d)
+
+ for i in data['tags']:
+ if i['name'] == "R-18":
+ return "太涩了不方便发w"
+
+ pid = data['id']
+ title = data['title']
+ try:
+ pic = data['meta_single_page']['original_image_url'] \
+ .replace('pximg.net', 'pixiv.cat')
+ except Exception:
+ pic = choice(data['meta_pages'])['original']['image_urls'] \
+ .replace('pximg.net', 'pixiv.cat')
+ if SIZE_REDUCE:
+ img = MessageSegment.image(
+ 'file:///' + await cls._comp_setu(pic), proxy=False)
+ else:
+ img = MessageSegment.image(pic, proxy=False)
+
+ msg = (
+ f"Pid: {pid}\n"
+ f"Title: {title}\n"
+ f"{img}"
+ )
+ return msg
+
+
+class SetuData:
+ SETU_DATA = SETU_DIR / 'setu.db'
+
+ @classmethod
+ async def _check_database(cls) -> bool:
+ if not cls.SETU_DATA.exists():
+ log.warning(f'未发现数据库\n-> {cls.SETU_DATA}\n将开始创建')
+ async with aiosqlite.connect(cls.SETU_DATA) as db:
+ cur = await db.cursor()
+ await cur.execute(
+ """
+ CREATE TABLE setu(
+ pid PID, title TITLE, tags TAGS,
+ user_id USER_ID, user_name USER_NAME,
+ user_account USER_ACCOUNT, url URL,
+ UNIQUE(
+ pid, title, tags, user_id,
+ user_name, user_account, url
+ )
+ );
+ """
+ )
+ await db.commit()
+ log.warning(f'...创建数据库\n-> {cls.SETU_DATA}\n完成!')
+ return True
+ return True
+
+ @classmethod
+ async def add_data(cls, d: dict) -> None:
+ data = (
+ d['pid'], d['title'], d['tags'], d['user_id'],
+ d['user_name'], d['user_account'], d['url']
+ )
+
+ check = await cls._check_database()
+ if check:
+ async with aiosqlite.connect(cls.SETU_DATA) as db:
+ await db.execute(
+ """
+ INSERT INTO setu(
+ pid, title, tags, user_id,
+ user_name, user_account, url
+ ) VALUES(
+ ?, ?, ?, ?, ?, ?, ?
+ );
+ """,
+ data
+ )
+ await db.commit()
+
+ @classmethod
+ async def del_data(cls, pid: int) -> None:
+ if not isinstance(pid, int): # 防注入
+ raise ValueError('Please provide int.')
+
+ check = await cls._check_database()
+ if check:
+ async with aiosqlite.connect(cls.SETU_DATA) as db:
+ await db.execute(f"DELETE FROM setu WHERE pid = {str(pid)};")
+ await db.commit()
+
+ @classmethod
+ async def count(cls):
+ check = await cls._check_database()
+ if check:
+ async with aiosqlite.connect(cls.SETU_DATA) as db:
+ async with db.execute("SELECT * FROM setu") as cursor:
+ return len(await cursor.fetchall()) # type: ignore
+
+ @classmethod
+ async def get_setu(cls):
+ check = await cls._check_database()
+ if check:
+ async with aiosqlite.connect(cls.SETU_DATA) as db:
+ async with db.execute("SELECT * FROM setu ORDER BY RANDOM() limit 1;") as cursor:
+ return await cursor.fetchall()
diff --git a/ATRI/plugins/setu/modules/main_setu.py b/ATRI/plugins/setu/modules/main_setu.py
new file mode 100644
index 0000000..649cd6a
--- /dev/null
+++ b/ATRI/plugins/setu/modules/main_setu.py
@@ -0,0 +1,193 @@
+import re
+import json
+from random import choice, random
+
+from nonebot.permission import SUPERUSER
+from nonebot.adapters.cqhttp import Bot, MessageEvent
+from nonebot.adapters.cqhttp.message import Message
+
+from ATRI.service import Service as sv
+from ATRI.rule import is_in_service
+from ATRI.utils.request import get_bytes, post_bytes
+from ATRI.utils.limit import is_too_exciting
+from ATRI.config import Setu, BotSelfConfig
+from ATRI.exceptions import RequestError
+
+from .data_source import Hso, SIZE_REDUCE, SetuData
+
+
+LOLICON_URL: str = "https://api.lolicon.app/setu/"
+PIXIV_URL:str = "https://api.kyomotoi.moe/api/pixiv/search?mode=exact_match_for_tags&word="
+R18_ENABLED: int = 0
+USE_LOCAL_DATA: bool = False
+MIX_LOCAL_DATA: bool = False
+
+
+setu = sv.on_regex(r"来[张点][色涩]图|[涩色]图来|想要[涩色]图|[涩色]图[Tt][Ii][Mm][Ee]",
+ rule=is_in_service('setu'))
+
+async def _setu(bot: Bot, event: MessageEvent) -> None:
+ user = event.user_id
+ check = is_too_exciting(user, 3, hours=1)
+ if not check:
+ return
+
+ await bot.send(event, "别急,在找了!")
+ params = {
+ "apikey": Setu.key,
+ "r18": str(R18_ENABLED),
+ "size1200": "true"
+ }
+ try:
+ data = json.loads(await post_bytes(LOLICON_URL, params))['data'][0]
+ except RequestError:
+ raise RequestError('Request failed!')
+
+ check = await Hso.nsfw_check(data['url'])
+ score = "{:.2%}".format(check, 4)
+
+ if not MIX_LOCAL_DATA:
+ if USE_LOCAL_DATA:
+ data = (await SetuData.get_setu())[0] # type: ignore
+ data = {
+ "pid": data[0],
+ "title": data[1],
+ "url": data[6]
+ }
+ if random() <= 0.1:
+ await bot.send(event, '我找到图了,但我发给主人了❤')
+ msg = await Hso.setu(data) + f"\n由用户({user})提供"
+ for sup in BotSelfConfig.superusers:
+ await bot.send_private_msg(user_id=sup, message=msg)
+ else:
+ await setu.finish(Message(await Hso.setu(data)))
+ else:
+ if check >= 0.9:
+ if random() <= 0.2:
+ repo = (
+ "我找到图了,但我发给主人了❤\n"
+ f"涩值:{score}"
+ )
+ await bot.send(event, repo)
+ msg = await Hso.setu(data) + f"\n由用户({user})提供,涩值:{score}"
+ for sup in BotSelfConfig.superusers:
+ await bot.send_private_msg(user_id=sup, message=msg)
+ else:
+ await setu.finish(Message(await Hso.setu(data)))
+ else:
+ if random() <= 0.1:
+ await bot.send(event, '我找到图了,但我发给主人了❤')
+ msg = await Hso.setu(data) + f"\n由用户({user})提供,涩值:{score}"
+ for sup in BotSelfConfig.superusers:
+ await bot.send_private_msg(user_id=sup, message=msg)
+ else:
+ await setu.finish(Message(await Hso.setu(data)))
+ else:
+ if random() <= 0.5:
+ if random() <= 0.1:
+ await bot.send(event, '我找到图了,但我发给主人了❤')
+ msg = await Hso.setu(data) + f"\n由用户({user})提供"
+ for sup in BotSelfConfig.superusers:
+ await bot.send_private_msg(user_id=sup, message=msg)
+ else:
+ await setu.finish(Message(await Hso.setu(data)))
+ else:
+ data = (await SetuData.get_setu())[0] # type: ignore
+ data = {
+ "pid": data[0],
+ "title": data[1],
+ "url": data[6]
+ }
+ if random() <= 0.1:
+ await bot.send(event, '我找到图了,但我发给主人了❤')
+ msg = await Hso.setu(data) + f"\n由用户({user})提供"
+ for sup in BotSelfConfig.superusers:
+ await bot.send_private_msg(user_id=sup, message=msg)
+ else:
+ await setu.finish(Message(await Hso.setu(data)))
+
+
+key_setu = sv.on_regex(r"来[点张](.*?)的[涩色🐍]图", rule=is_in_service('setu'))
+
+@key_setu.handle()
+async def _key_setu(bot: Bot, event: MessageEvent) -> None:
+ user = event.user_id
+ check = is_too_exciting(user, 10, hours=1)
+ if not check:
+ await setu.finish('休息一下吧❤')
+
+ await bot.send(event, "别急,在找了!")
+ msg = str(event.message).strip()
+ tag = re.findall(r"来[点张](.*?)的?[涩色🐍]图", msg)[0]
+ URL = PIXIV_URL + tag
+
+ try:
+ data = json.loads(await get_bytes(URL))['illusts']
+ except RequestError:
+ raise RequestError('Request msg failed!')
+
+ if random() <= 0.1:
+ await bot.send(event, '我找到图了,但我发给主人了❤')
+ msg = await Hso.acc_setu(data) + f"\n由用户({user})提供"
+ for sup in BotSelfConfig.superusers:
+ await bot.send_private_msg(user_id=sup, message=msg)
+ else:
+ await setu.finish(Message(await Hso.acc_setu(data)))
+
+
+setu_config = sv.on_command(cmd='涩图设置', permission=SUPERUSER)
+
+@setu_config.handle()
+async def _setu_config(bot: Bot, event: MessageEvent) -> None:
+ global R18_ENABLED, SIZE_REDUCE, USE_LOCAL_DATA, MIX_LOCAL_DATA
+ msg = str(event.message).split(' ')
+ if msg[0] == "":
+ repo = (
+ "可用设置如下:\n"
+ "启用/禁用r18\n"
+ "启用/禁用压缩\n"
+ "启用/禁用本地涩图\n"
+ "启用/禁用混合本地涩图"
+ )
+ await setu_config.finish(repo)
+ elif msg[0] == "启用r18":
+ R18_ENABLED = 1
+ await setu_config.finish('已启用r18')
+ elif msg[0] == "禁用r18":
+ R18_ENABLED = 0
+ await setu_config.finish('已禁用r18')
+ elif msg[0] == "启用压缩":
+ SIZE_REDUCE = True
+ await setu_config.finish('已启用图片压缩')
+ elif msg[0] == "禁用压缩":
+ SIZE_REDUCE = False
+ await setu_config.finish('已禁用图片压缩')
+ elif msg[0] == "启用本地涩图":
+ USE_LOCAL_DATA = True
+ await setu_config.finish('已启用本地涩图')
+ elif msg[0] == "禁用本地涩图":
+ USE_LOCAL_DATA = False
+ await setu_config.finish('已禁用本地涩图')
+ elif msg[0] == "启用混合本地涩图":
+ MIX_LOCAL_DATA = True
+ await setu_config.finish('启用混合本地涩图')
+ elif msg[0] == "禁用混合本地涩图":
+ MIX_LOCAL_DATA = False
+ await setu_config.finish('禁用混合本地涩图')
+ else:
+ await setu_config.finish('阿!请检查拼写')
+
+
+not_get_se = sv.on_command("不够涩")
+
+@not_get_se.handle()
+async def _not_se(bot: Bot, event: MessageEvent) -> None:
+ user = event.user_id
+ check = is_too_exciting(user, 1, 120)
+ if check:
+ msg = choice([
+ "那你来发",
+ "那你来发❤"
+ ])
+ await not_get_se.finish(msg)
diff --git a/ATRI/plugins/setu/modules/scheduler.py b/ATRI/plugins/setu/modules/scheduler.py
new file mode 100644
index 0000000..3030881
--- /dev/null
+++ b/ATRI/plugins/setu/modules/scheduler.py
@@ -0,0 +1,19 @@
+import shutil
+from ATRI.log import logger as log
+from ATRI.utils.apscheduler import scheduler
+
+from .data_source import TEMP_DIR
+
+
+ 'interval',
+ days=7,
+ misfire_grace_time=10
+)
+async def clear_temp():
+ log.info('正在清除涩图缓存')
+ try:
+ shutil.rmtree(TEMP_DIR)
+ log.info('清除缓存成功!')
+ except Exception:
+ log.warn('清除图片缓存失败!')
diff --git a/ATRI/plugins/setu/modules/store.py b/ATRI/plugins/setu/modules/store.py
new file mode 100644
index 0000000..6f383f4
--- /dev/null
+++ b/ATRI/plugins/setu/modules/store.py
@@ -0,0 +1,138 @@
+import json
+from random import choice
+
+from nonebot.typing import T_State
+from nonebot.permission import SUPERUSER
+from nonebot.adapters.cqhttp import Bot, MessageEvent
+from nonebot.adapters.cqhttp.message import Message, MessageSegment
+
+from ATRI.service import Service as sv
+from ATRI.utils.request import get_bytes
+from ATRI.exceptions import RequestError
+
+from .data_source import SetuData
+
+
+API_URL: str = "https://api.kyomotoi.moe/api/pixiv/illust?id="
+
+
+__doc__ = """
+为本地添加涩图!
+权限组:维护者
+用法:
+ 添加涩图 (pid)
+补充:
+ pid: Pixiv 作品id
+"""
+
+
+add_setu = sv.on_command(
+ cmd="添加涩图",
+ docs=__doc__,
+ permission=SUPERUSER
+)
+
+@add_setu.args_parser # type: ignore
+async def _load_add_setu(bot: Bot, event: MessageEvent, state: T_State) -> None:
+ msg = str(event.message).strip()
+ cancel = ['算了', '罢了']
+ if msg in cancel:
+ await add_setu.finish('好吧...')
+ if not msg:
+ await add_setu.reject('涩图(pid)速发!')
+ else:
+ state['setu_add'] = msg
+
+@add_setu.handle()
+async def _add_setu(bot: Bot, event: MessageEvent, state: T_State) -> None:
+ msg = str(event.message).strip()
+ if msg:
+ state['setu_add'] = msg
+
+@add_setu.got('setu_add', prompt='涩图(pid)速发!')
+async def _deal_add_setu(bot: Bot, event: MessageEvent, state: T_State) -> None:
+ pid = state['setu_add']
+
+ URL = API_URL + pid
+ try:
+ data = json.loads(await get_bytes(URL))['illust']
+ except RequestError:
+ raise RequestError('Request failed!')
+
+ try:
+ pic = data['meta_single_page']['original_image_url'] \
+ .replace('pximg.net', 'pixiv.cat')
+ except Exception:
+ pic = choice(data['meta_pages'])['image_urls']['original'] \
+ .replace('pximg.net', 'pixiv.cat')
+
+ d = {
+ "pid": pid,
+ "title": data['title'],
+ "tags": str(data['tags']),
+ "user_id": data['user']['id'],
+ "user_name": data['user']['name'],
+ "user_account": data['user']['account'],
+ "url": pic
+ }
+ await SetuData.add_data(d)
+
+ show_img = data['image_urls']['medium'].replace('pximg.net', 'pixiv.cat')
+ msg = (
+ "好欸!是新涩图:\n"
+ f"Pid: {pid}\n"
+ f"Title: {data['title']}\n"
+ f"{MessageSegment.image(show_img)}"
+ )
+ await add_setu.finish(Message(msg))
+
+
+__doc__ = """
+删除涩图!
+权限组:维护者
+用法:
+ 删除涩图 (pid)
+补充:
+ pid: Pixiv 作品id
+"""
+
+
+del_setu = sv.on_command(
+ cmd="删除涩图",
+ docs=__doc__,
+ permission=SUPERUSER
+)
+
+@del_setu.args_parser # type: ignore
+async def _load_del_setu(bot: Bot, event: MessageEvent, state: T_State) -> None:
+ msg = str(event.message).strip()
+ cancel = ['算了', '罢了']
+ if msg in cancel:
+ await add_setu.finish('好吧...')
+ if not msg:
+ await add_setu.reject('涩图(pid)速发!')
+ else:
+ state['setu_del'] = msg
+
+@del_setu.handle()
+async def _del_setu(bot: Bot, event: MessageEvent, state: T_State) -> None:
+ msg = str(event.message).strip()
+ if msg:
+ state['setu_del'] = msg
+
+@del_setu.got('setu_del', prompt='涩图(pid)速发!')
+async def _deal_del_setu(bot: Bot, event: MessageEvent, state: T_State) -> None:
+ pid = int(state['setu_del'])
+ await SetuData.del_data(pid)
+ await del_setu.finish(f'涩图({pid})已删除...')
+
+
+count_setu = sv.on_command(
+ cmd="涩图总量",
+ permission=SUPERUSER
+)
+
+@count_setu.handle()
+async def _count_setu(bot: Bot, event: MessageEvent) -> None:
+ msg = f"咱本地搭载了 {await SetuData.count()} 张涩图!"
+ await count_setu.finish(msg)