From 7207982a303bd4f350de5047949396c55d55db7e Mon Sep 17 00:00:00 2001 From: HibiKier <45528451+HibiKier@users.noreply.github.com> Date: Thu, 20 May 2021 19:23:32 +0800 Subject: [PATCH] Add files via upload --- plugins/draw_card/__init__.py | 174 ++++++++ plugins/draw_card/announcement.py | 119 ++++++ plugins/draw_card/config.py | 104 +++++ plugins/draw_card/genshin_handle.py | 149 +++++++ plugins/draw_card/init_card_pool.py | 33 ++ plugins/draw_card/pretty_handle.py | 105 +++++ plugins/draw_card/prts_handle.py | 162 ++++++++ plugins/draw_card/update_game_info.py | 162 ++++++++ plugins/draw_card/util.py | 161 ++++++++ plugins/epic/__init__.py | 60 +++ plugins/epic/data_source.py | 46 +++ plugins/group_handle/__init__.py | 125 ++++++ plugins/help/__init__.py | 35 ++ plugins/help/config.py | 52 +++ plugins/help/data_source.py | 175 ++++++++ plugins/last_chat/__init__.py | 53 +++ plugins/last_chat/data_source.py | 74 ++++ plugins/luxun/__init__.py | 64 +++ plugins/move_img/__init__.py | 84 ++++ plugins/nonebot_plugin_cocdicer/__init__.py | 72 ++++ plugins/nonebot_plugin_cocdicer/create.py | 147 +++++++ .../nonebot_plugin_cocdicer/data_source.py | 249 ++++++++++++ plugins/nonebot_plugin_cocdicer/madness.py | 36 ++ plugins/nonebot_plugin_cocdicer/messages.py | 258 ++++++++++++ plugins/nonebot_plugin_cocdicer/san_check.py | 38 ++ plugins/nonebot_plugin_manager/__init__.py | 110 +++++ plugins/nonebot_plugin_manager/data.py | 121 ++++++ plugins/nonebot_plugin_manager/handle.py | 138 +++++++ plugins/nonebot_plugin_manager/parser.py | 60 +++ .../nonebot_plugin_picsearcher/__init__.py | 149 +++++++ plugins/nonebot_plugin_picsearcher/ascii2d.py | 63 +++ plugins/nonebot_plugin_picsearcher/ex.py | 107 +++++ .../nonebot_plugin_picsearcher/formdata.py | 27 ++ plugins/nonebot_plugin_picsearcher/iqdb.py | 79 ++++ .../nonebot_plugin_picsearcher/saucenao.py | 116 ++++++ plugins/nonebot_plugin_picsearcher/trace.py | 89 ++++ plugins/nonebot_plugin_picsearcher/yandex.py | 53 +++ plugins/one_friend/__init__.py | 68 ++++ plugins/open_cases/__init__.py | 181 +++++++++ plugins/open_cases/config.py | 180 +++++++++ plugins/open_cases/open_cases_c.py | 381 ++++++++++++++++++ plugins/open_cases/utils.py | 294 ++++++++++++++ plugins/pixiv/__init__.py | 194 +++++++++ plugins/pixiv/data_source.py | 84 ++++ plugins/poke/__init__.py | 52 +++ plugins/reimu/__init__.py | 96 +++++ plugins/reimu/data_source.py | 116 ++++++ plugins/remind/__init__.py | 194 +++++++++ plugins/search_anime/__init__.py | 56 +++ plugins/search_anime/data_source.py | 47 +++ plugins/search_buff_skin_price/__init__.py | 75 ++++ plugins/search_buff_skin_price/data_source.py | 66 +++ plugins/send_dinggong_voice/__init__.py | 33 ++ plugins/send_img/__init__.py | 68 ++++ plugins/send_setu/__init__.py | 276 +++++++++++++ plugins/send_setu/check_setu_hash.py | 62 +++ plugins/send_setu/data_source.py | 168 ++++++++ plugins/shop/buy.py | 78 ++++ plugins/shop/my_gold.py | 26 ++ plugins/shop/my_props.py | 46 +++ plugins/shop/reset_today_gold.py | 41 ++ plugins/shop/shop_help.py | 41 ++ plugins/shop/use/__init__.py | 61 +++ plugins/shop/use/data_source.py | 40 ++ 64 files changed, 6873 insertions(+) create mode 100644 plugins/draw_card/__init__.py create mode 100644 plugins/draw_card/announcement.py create mode 100644 plugins/draw_card/config.py create mode 100644 plugins/draw_card/genshin_handle.py create mode 100644 plugins/draw_card/init_card_pool.py create mode 100644 plugins/draw_card/pretty_handle.py create mode 100644 plugins/draw_card/prts_handle.py create mode 100644 plugins/draw_card/update_game_info.py create mode 100644 plugins/draw_card/util.py create mode 100644 plugins/epic/__init__.py create mode 100644 plugins/epic/data_source.py create mode 100644 plugins/group_handle/__init__.py create mode 100644 plugins/help/__init__.py create mode 100644 plugins/help/config.py create mode 100644 plugins/help/data_source.py create mode 100644 plugins/last_chat/__init__.py create mode 100644 plugins/last_chat/data_source.py create mode 100644 plugins/luxun/__init__.py create mode 100644 plugins/move_img/__init__.py create mode 100644 plugins/nonebot_plugin_cocdicer/__init__.py create mode 100644 plugins/nonebot_plugin_cocdicer/create.py create mode 100644 plugins/nonebot_plugin_cocdicer/data_source.py create mode 100644 plugins/nonebot_plugin_cocdicer/madness.py create mode 100644 plugins/nonebot_plugin_cocdicer/messages.py create mode 100644 plugins/nonebot_plugin_cocdicer/san_check.py create mode 100644 plugins/nonebot_plugin_manager/__init__.py create mode 100644 plugins/nonebot_plugin_manager/data.py create mode 100644 plugins/nonebot_plugin_manager/handle.py create mode 100644 plugins/nonebot_plugin_manager/parser.py create mode 100644 plugins/nonebot_plugin_picsearcher/__init__.py create mode 100644 plugins/nonebot_plugin_picsearcher/ascii2d.py create mode 100644 plugins/nonebot_plugin_picsearcher/ex.py create mode 100644 plugins/nonebot_plugin_picsearcher/formdata.py create mode 100644 plugins/nonebot_plugin_picsearcher/iqdb.py create mode 100644 plugins/nonebot_plugin_picsearcher/saucenao.py create mode 100644 plugins/nonebot_plugin_picsearcher/trace.py create mode 100644 plugins/nonebot_plugin_picsearcher/yandex.py create mode 100644 plugins/one_friend/__init__.py create mode 100644 plugins/open_cases/__init__.py create mode 100644 plugins/open_cases/config.py create mode 100644 plugins/open_cases/open_cases_c.py create mode 100644 plugins/open_cases/utils.py create mode 100644 plugins/pixiv/__init__.py create mode 100644 plugins/pixiv/data_source.py create mode 100644 plugins/poke/__init__.py create mode 100644 plugins/reimu/__init__.py create mode 100644 plugins/reimu/data_source.py create mode 100644 plugins/remind/__init__.py create mode 100644 plugins/search_anime/__init__.py create mode 100644 plugins/search_anime/data_source.py create mode 100644 plugins/search_buff_skin_price/__init__.py create mode 100644 plugins/search_buff_skin_price/data_source.py create mode 100644 plugins/send_dinggong_voice/__init__.py create mode 100644 plugins/send_img/__init__.py create mode 100644 plugins/send_setu/__init__.py create mode 100644 plugins/send_setu/check_setu_hash.py create mode 100644 plugins/send_setu/data_source.py create mode 100644 plugins/shop/buy.py create mode 100644 plugins/shop/my_gold.py create mode 100644 plugins/shop/my_props.py create mode 100644 plugins/shop/reset_today_gold.py create mode 100644 plugins/shop/shop_help.py create mode 100644 plugins/shop/use/__init__.py create mode 100644 plugins/shop/use/data_source.py diff --git a/plugins/draw_card/__init__.py b/plugins/draw_card/__init__.py new file mode 100644 index 00000000..4d8d95c7 --- /dev/null +++ b/plugins/draw_card/__init__.py @@ -0,0 +1,174 @@ +from nonebot import on_regex, on_keyword +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.permission import SUPERUSER +from nonebot.typing import T_State +from .genshin_handle import genshin_draw, update_genshin_info, reset_count +from .prts_handle import update_prts_info, prts_draw, reload_pool +from .pretty_handle import update_pretty_info, pretty_draw +from .update_game_info import update_info +from util.utils import is_number, scheduler +from services.log import logger +import re + + +prts = on_regex(r'.*?方舟[1-9|一][0-9]{0,2}[抽|井]', priority=5, block=True) +prts_update = on_keyword({'更新方舟信息', '更新明日方舟信息'}, permission=SUPERUSER, priority=1, block=True) +prts_reload = on_keyword({'重载方舟卡池'}, priority=1, block=True) + +genshin = on_regex('.*?原神[1-9|一][0-9]{0,2}[抽|井]', priority=5, block=True) +genshin_reset = on_keyword({'重置原神抽卡'}, priority=1, block=True) +genshin_update = on_keyword({'更新原神信息'}, permission=SUPERUSER, priority=1, block=True) + +pretty = on_regex('.*?马娘卡?[1-9|一][0-9]{0,2}[抽|井]', priority=5, block=True) +pretty_update = on_keyword({'更新马娘信息', '更新赛马娘信息'}, permission=SUPERUSER, priority=1, block=True) + + +@prts.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = str(event.get_message()).strip() + if msg in ['方舟一井', '方舟1井']: + num = 300 + else: + rmsg = re.search(r'.*?方舟(.*)抽', msg) + if rmsg and is_number(rmsg.group(1)): + try: + num = int(rmsg.group(1)) + except ValueError: + await prts.finish('必!须!是!数!字!', at_sender=True) + if num > 300: + await prts.finish('一井都满不足不了你嘛!快爬开!', at_sender=True) + if num < 1: + await prts.finish('虚空抽卡???', at_sender=True) + else: + return + # print(num) + await prts.send(await prts_draw(num), at_sender=True) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f" 方舟{num}抽") + + +@prts_reload.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await reload_pool() + await prts_reload.finish('重载完成!') + + +@genshin.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = str(event.get_message()).strip() + if msg in ['原神一井', '原神1井']: + num = 180 + else: + rmsg = re.search(r'.*?原神(.*)抽', msg) + if rmsg and is_number(rmsg.group(1)): + try: + num = int(rmsg.group(1)) + except ValueError: + await genshin.finish('必!须!是!数!字!', at_sender=True) + if num > 300: + await genshin.finish('一井都满不足不了你嘛!快爬开!', at_sender=True) + if num < 1: + await genshin.finish('虚空抽卡???', at_sender=True) + else: + return + await genshin.send(await genshin_draw(event.user_id, num), at_sender=True) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f" 原神{num}抽") + + +@genshin_reset.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + reset_count(event.user_id) + await genshin_reset.send('重置了原神抽卡次数', at_sender=True) + + +@pretty.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = str(event.get_message()).strip() + if msg in ['赛马娘一井', '赛马娘1井', '马娘一井', '马娘1井', '赛马娘卡一井', '赛马娘卡1井', '马娘卡一井', '马娘卡1井']: + num = 200 + if msg.find("卡") == -1: + pool_name = 'horse' + else: + pool_name = 'card' + else: + rmsg = re.search(r'.*?马娘(.*)抽', msg) + if rmsg: + num = rmsg.group(1) + if num[0] == '卡': + num = num[1:] + pool_name = 'card' + else: + pool_name = 'horse' + if is_number(num): + try: + num = int(num) + except ValueError: + await genshin.finish('必!须!是!数!字!', at_sender=True) + if num > 200: + await genshin.finish('一井都满不足不了你嘛!快爬开!', at_sender=True) + if num < 1: + await genshin.finish('虚空抽卡???', at_sender=True) + else: + return + await pretty.send(await pretty_draw(num, pool_name), at_sender=True) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f" 赛马娘{num}抽") + + +@prts_update.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await update_prts_info() + await reload_pool() + await prts_update.finish('更新完成!') + + +@genshin_update.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await update_genshin_info() + await genshin_update.finish('更新完成!') + + +@pretty_update.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await update_pretty_info() + await genshin_update.finish('更新完成!') + + +# 更新资源 +@scheduler.scheduled_job( + 'cron', + hour=4, + minute=1, +) +async def _(): + try: + await update_prts_info() + logger.info('自动更新明日方舟信息') + except Exception as e: + logger.error(f'自动更新明日方舟信息出错 e:{e}') + try: + await update_genshin_info() + logger.info('自动更新原神信息') + except Exception as e: + logger.error(f'自动更新原神信息出错 e:{e}') + try: + await update_pretty_info() + logger.info('自动更新赛马娘信息') + except Exception as e: + logger.error(f'自动更新赛马娘信息出错 e:{e}') + + +# 每天四点重载up卡池 +@scheduler.scheduled_job( + 'cron', + hour=4, + minute=1, +) +async def _(): + await reload_pool() + + diff --git a/plugins/draw_card/announcement.py b/plugins/draw_card/announcement.py new file mode 100644 index 00000000..734a58a9 --- /dev/null +++ b/plugins/draw_card/announcement.py @@ -0,0 +1,119 @@ +import aiohttp +from bs4 import BeautifulSoup +import re +from datetime import datetime +from pathlib import Path +from configs.path_config import DRAW_PATH +from util.user_agent import get_user_agent + +try: + import ujson as json +except ModuleNotFoundError: + import json + +up_char_file = Path(DRAW_PATH) / "draw_card_up" / "prts_up_char.json" + +prts_url = "https://wiki.biligame.com/arknights/%E6%96%B0%E9%97%BB%E5%85%AC%E5%91%8A" + + +def _get_up_char(r: str, text: str): + pr = re.search(r, text) + chars = pr.group(1) + probability = pr.group(2) + chars = chars.replace('[限定]', '').replace('[', '').replace(']', '') + probability = probability.replace('【', '') + return chars, probability + + +class PrtsAnnouncement: + + @staticmethod + async def get_announcement_text(): + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(prts_url, timeout=7) as res: + soup = BeautifulSoup(await res.text(), 'lxml') + trs = soup.find('table').find('tbody').find_all('tr') + for tr in trs: + a = tr.find_all('td')[-1].find('a') + if a.text.find('寻访') != -1: + url = a.get('href') + break + async with session.get(f'https://wiki.biligame.com/{url}', timeout=7) as res: + return await res.text(), a.text[:-4] + + @staticmethod + async def update_up_char(): + up_char_file.parent.mkdir(parents=True, exist_ok=True) + data = {'up_char': {'6': {}, '5': {}, '4': {}}, 'title': '', 'time': ''} + text, title = await PrtsAnnouncement.get_announcement_text() + soup = BeautifulSoup(text, 'lxml') + data['title'] = title + context = soup.find('div', {'id': 'mw-content-text'}).find('div') + data['pool_img'] = str(context.find('div', {'class': 'center'}).find('div').find('a'). + find('img').get('srcset')).split(' ')[-2] + # print(context.find_all('p')) + for p in context.find_all('p')[1:]: + if p.text.find('活动时间') != -1: + pr = re.search(r'.*?活动时间:(.*)', p.text) + data['time'] = pr.group(1) + elif p.text.find('★★★★★★') != -1: + chars, probability = _get_up_char(r'.*?★★★★★★:(.*?)(.*?出率的?(.*?)%.*?).*?', p.text) + slt = '/' + if chars.find('\\') != -1: + slt = '\\' + for char in chars.split(slt): + data['up_char']['6'][char.strip()] = probability.strip() + elif p.text.find('★★★★★') != -1: + chars, probability = _get_up_char(r'.*?★★★★★:(.*?)(.*?出率的?(.*?)%.*?).*?', p.text) + slt = '/' + if chars.find('\\') != -1: + slt = '\\' + for char in chars.split(slt): + data['up_char']['5'][char.strip()] = probability.strip() + elif p.text.find('★★★★') != -1: + chars, probability = _get_up_char(r'.*?★★★★:(.*?)(.*?出率的?(.*?)%.*?).*?', p.text) + slt = '/' + if chars.find('\\') != -1: + slt = '\\' + for char in chars.split(slt): + data['up_char']['4'][char.strip()] = probability.strip() + break + pr = re.search(r'.*?★:(.*?)(在(.*?)★.*?以(.*?)倍权值.*?).*?', p.text) + if pr: + char = pr.group(1) + star = pr.group(2) + weight = pr.group(3) + char = char.replace('[限定]', '').replace('[', '').replace(']', '') + data['up_char'][star][char.strip()] = f'权{weight}' + # data['time'] = '03月09日16:00 - 05月23日03:59' + if not is_expired(data): + data['title'] = '' + else: + with open(up_char_file, 'w', encoding='utf8') as f: + json.dump(data, f, indent=4, ensure_ascii=False) + if not up_char_file.exists(): + with open(up_char_file, 'w', encoding='utf8') as f: + json.dump(data, f, indent=4, ensure_ascii=False) + else: + with open(up_char_file, 'r', encoding='utf8') as f: + old_data = json.load(f) + if is_expired(old_data): + return old_data + else: + with open(up_char_file, 'w', encoding='utf8') as f: + json.dump(data, f, indent=4, ensure_ascii=False) + return data + + +# 是否过时 +def is_expired(data: dict): + times = data['time'].split('-') + for i in range(len(times)): + times[i] = str(datetime.now().year) + '-' + times[i].split('日')[0].strip().replace('月', '-') + start_date = datetime.strptime(times[0], '%Y-%m-%d').date() + end_date = datetime.strptime(times[1], '%Y-%m-%d').date() + now = datetime.now().date() + return start_date < now < end_date + +# ad = Announcement('https://wiki.biligame.com/arknights/%E6%96%B0%E9%97%BB%E5%85%AC%E5%91%8A') +# asyncio.get_event_loop().run_until_complete(check_up_char('prts')) diff --git a/plugins/draw_card/config.py b/plugins/draw_card/config.py new file mode 100644 index 00000000..63fe0a85 --- /dev/null +++ b/plugins/draw_card/config.py @@ -0,0 +1,104 @@ +import nonebot +from pathlib import Path +try: + import ujson as json +except ModuleNotFoundError: + import json + +# 方舟概率 +PRTS_SIX_P = 0.02 +PRTS_FIVE_P = 0.08 +PRTS_FOUR_P = 0.48 +PRTS_THREE_P = 0.42 + +# 原神概率 +GENSHIN_FIVE_P = 0.006 +GENSHIN_FOUR_P = 0.051 +GENSHIN_THREE_P = 0.43 +GENSHIN_G_FOUR_P = 0.13 +GENSHIN_G_FIVE_P = 0.016 +I72_ADD = 0.0585 + +# 赛马娘概率 +PRETTY_THREE = 0.03 +PRETTY_TWO = 0.18 +PRETTY_ONE = 0.79 + +path_dict = { + 'genshin': '原神', + 'prts': '明日方舟', + 'pretty': '赛马娘', +} + + +_draw_config = Path() / "data" / "draw_card" / "draw_card_config" / "draw_card_config.json" + + +driver: nonebot.Driver = nonebot.get_driver() + + +@driver.on_startup +def check_config(): + global PRTS_SIX_P, PRTS_FOUR_P, PRTS_FIVE_P, PRTS_THREE_P, GENSHIN_G_FIVE_P, \ + GENSHIN_G_FOUR_P, GENSHIN_FOUR_P, GENSHIN_FIVE_P, I72_ADD, path_dict, PRETTY_THREE, \ + PRETTY_ONE, PRETTY_TWO, GENSHIN_THREE_P + if _draw_config.exists(): + data = json.load(open(_draw_config, 'r', encoding='utf8')) + PRTS_SIX_P = float(data['prts']['six']) + PRTS_FIVE_P = float(data['prts']['five']) + PRTS_FOUR_P = float(data['prts']['four']) + PRTS_THREE_P = float(data['prts']['three']) + + GENSHIN_FIVE_P = float(data['genshin']['five_char']) + GENSHIN_FOUR_P = float(data['genshin']['four_char']) + GENSHIN_THREE_P = float(data['genshin']['three_char']) + GENSHIN_G_FIVE_P = float(data['genshin']['five_weapon']) + GENSHIN_G_FOUR_P = float(data['genshin']['four_weapon']) + I72_ADD = float(data['genshin']['72_add']) + + PRETTY_THREE = float(data['pretty']['three']) + PRETTY_TWO = float(data['pretty']['two']) + PRETTY_ONE = float(data['pretty']['one']) + + path_dict = data['path_dict'] + else: + _draw_config.parent.mkdir(parents=True, exist_ok=True) + config_dict = { + 'path_dict': { + 'genshin': '原神', + 'prts': '明日方舟', + 'pretty': '赛马娘', + }, + + 'prts': { + 'six': 0.02, + 'five': 0.08, + 'four': 0.48, + 'three': 0.42, + }, + + 'genshin': { + 'five_char': 0.006, + 'four_char': 0.051, + 'three_char': 0.43, + 'five_weapon': 0.13, + 'four_weapon': 0.016, + '72_add': 0.0585, + }, + + 'pretty': { + 'three': 0.03, + 'two': 0.18, + 'one': 0.79, + } + } + json.dump(config_dict, open(_draw_config, 'w', encoding='utf8'), indent=4, ensure_ascii=False) + + + + + + + + + diff --git a/plugins/draw_card/genshin_handle.py b/plugins/draw_card/genshin_handle.py new file mode 100644 index 00000000..07954ad9 --- /dev/null +++ b/plugins/draw_card/genshin_handle.py @@ -0,0 +1,149 @@ + +import os +import nonebot +import random +from .update_game_info import update_info +from .util import generate_img, init_star_rst, BaseData, set_list +from .config import GENSHIN_FIVE_P, GENSHIN_FOUR_P, GENSHIN_G_FIVE_P, GENSHIN_THREE_P, I72_ADD +from dataclasses import dataclass +from .init_card_pool import init_game_pool +from configs.path_config import DRAW_PATH +from util.init_result import image +try: + import ujson as json +except ModuleNotFoundError: + import json + +driver: nonebot.Driver = nonebot.get_driver() + +genshin_five = {} +genshin_count = {} +genshin_pl_count = {} + +ALL_CHAR = [] +ALL_ARM = [] + + +@dataclass +class GenshinChar(BaseData): + pass + + +async def genshin_draw(user_id: int, count: int): + # 0 1 2 + cnlist = ['★★★★★', '★★★★', '★★★'] + genshin_list, five_list, five_olist, five_dict, star_list = _format_card_information(count, user_id) + rst = init_star_rst(star_list, cnlist, five_list, five_olist) + print(five_list) + temp = '' + if count > 90: + genshin_list = set_list(genshin_list) + return image(b64=await generate_img(genshin_list, 'genshin', star_list)) + '\n' + rst[:-1] + \ + temp[:-1] + f'\n距离保底发还剩 {90 - genshin_count[user_id] if genshin_count.get(user_id) else "^"} 抽' \ + + "\n【五星:0.6%,四星:5.1%\n第72抽开始五星概率每抽加0.585%】" + + +async def update_genshin_info(): + global ALL_CHAR, ALL_ARM + url = 'https://wiki.biligame.com/ys/角色筛选' + data, code = await update_info(url, 'genshin') + if code == 200: + ALL_CHAR = init_game_pool('genshin', data, GenshinChar) + url = 'https://wiki.biligame.com/ys/武器图鉴' + data, code = await update_info(url, 'genshin_arm', ['头像', '名称', '类型', '稀有度.alt', '初始基础属性1', + '初始基础属性2', '攻击力(MAX)', '副属性(MAX)', '技能']) + if code == 200: + ALL_ARM = init_game_pool('genshin', data, GenshinChar) + + +# asyncio.get_event_loop().run_until_complete(update_genshin_info()) + + +@driver.on_startup +async def init_data(): + global ALL_CHAR, ALL_ARM + if not os.path.exists(DRAW_PATH + '/draw_card_config/genshin.json') or \ + not os.path.exists(DRAW_PATH + '/draw_card_config/genshin_arm.json'): + await update_genshin_info() + else: + with open(DRAW_PATH + '/draw_card_config/genshin.json', 'r', encoding='utf8') as f: + genshin_dict = json.load(f) + with open(DRAW_PATH + '/draw_card_config/genshin_arm.json', 'r', encoding='utf8') as f: + genshin_arm_dict = json.load(f) + ALL_CHAR = init_game_pool('genshin', genshin_dict, GenshinChar) + ALL_ARM = init_game_pool('genshin', genshin_arm_dict, GenshinChar) + + +# 抽取卡池 +def _get_genshin_card(mode: int = 1, add: float = 0.0): + global ALL_ARM, ALL_CHAR + if mode == 1: + star = random.sample([5, 4, 3], + counts=[int(GENSHIN_FIVE_P * 1000) + int(add * 1000), int(GENSHIN_FOUR_P * 1000), + int(GENSHIN_THREE_P * 1000)], + k=1)[0] + elif mode == 2: + star = random.sample([5, 4], + counts=[int(GENSHIN_G_FIVE_P * 1000) + int(add * 1000), int(GENSHIN_FOUR_P * 1000)], + k=1)[0] + else: + star = 5 + chars = [x for x in (ALL_ARM if random.random() < 0.5 or star == 3 else ALL_CHAR) if x.star == star] + return random.choice(chars), abs(star - 5) + + +def _format_card_information(_count: int, user_id): + genshin_list = [] + star_list = [0, 0, 0] + five_index_list = [] + five_list = [] + five_dict = {} + add = 0.0 + if genshin_count.get(user_id) and _count <= 90: + f_count = genshin_count[user_id] + else: + f_count = 0 + if genshin_pl_count.get(user_id) and _count <= 90: + count = genshin_pl_count[user_id] + else: + count = 0 + for i in range(_count): + count += 1 + f_count += 1 + # 十连保底 + if count == 10 and f_count != 90: + if f_count >= 72: + add += I72_ADD + char, code = _get_genshin_card(2, add) + count = 0 + # 大保底 + elif f_count == 90: + char, code = _get_genshin_card(3) + else: + if f_count >= 72: + add += I72_ADD + char, code = _get_genshin_card(add=add) + if code == 1: + count = 0 + star_list[code] += 1 + if code == 0: + if _count <= 90: + genshin_five[user_id] = f_count + add = 0.0 + f_count = 0 + five_list.append(char.name) + five_index_list.append(i) + try: + five_dict[char.name] += 1 + except KeyError: + five_dict[char.name] = 1 + genshin_list.append(char) + if _count <= 90: + genshin_count[user_id] = f_count + genshin_pl_count[user_id] = count + return genshin_list, five_list, five_index_list, five_dict, star_list + + +def reset_count(user_id: int): + genshin_count[user_id] = 0 + genshin_pl_count[user_id] = 0 diff --git a/plugins/draw_card/init_card_pool.py b/plugins/draw_card/init_card_pool.py new file mode 100644 index 00000000..ff2df043 --- /dev/null +++ b/plugins/draw_card/init_card_pool.py @@ -0,0 +1,33 @@ +from typing import Any + + +def init_game_pool(game: str, data: dict, Operator: Any): + tmp_lst = [] + if game == 'prts': + for key in data.keys(): + limited = False + recruit_only = False + event_only = False + if '限定寻访' in data[key]['获取途径']: + limited = True + if len(data[key]['获取途径']) == 1 and data[key]['获取途径'][0] == '公开招募': + recruit_only = True + if '活动获取' in data[key]['获取途径']: + event_only = True + if key.find('阿米娅') != -1: + continue + tmp_lst.append(Operator(name=key, star=int(data[key]['星级']), + limited=limited, recruit_only=recruit_only, event_only=event_only)) + if game == 'genshin': + for key in data.keys(): + if key.find('旅行者') != -1: + continue + tmp_lst.append(Operator(name=key, star=int(data[key]['稀有度'][:1]), limited=False)) + if game == 'pretty': + for key in data.keys(): + tmp_lst.append(Operator(name=key, star=data[key]['初始星级'], limited=False)) + if game == 'pretty_card': + for key in data.keys(): + tmp_lst.append(Operator(name=data[key]['中文名'], star=len(data[key]['稀有度']), limited=False)) + return tmp_lst + diff --git a/plugins/draw_card/pretty_handle.py b/plugins/draw_card/pretty_handle.py new file mode 100644 index 00000000..afdec8d7 --- /dev/null +++ b/plugins/draw_card/pretty_handle.py @@ -0,0 +1,105 @@ + +import os +import nonebot +from util.init_result import image +from configs.path_config import DRAW_PATH +from .update_game_info import update_info +from .util import download_img, init_star_rst, generate_img, max_card, BaseData, set_list +import random +from .config import PRETTY_THREE, PRETTY_TWO, PRETTY_ONE +from dataclasses import dataclass +from .init_card_pool import init_game_pool +try: + import ujson as json +except ModuleNotFoundError: + import json + +driver: nonebot.Driver = nonebot.get_driver() + +ALL_CHAR = [] +ALL_CARD = [] + + +@dataclass +class PrettyChar(BaseData): + pass + + +async def pretty_draw(count: int, pool_name): + if pool_name == 'card': + cnlist = ['SSR', 'SR', 'R'] + else: + cnlist = ['★★★', '★★', '★'] + obj_list, obj_dict, three_list, star_list, three_olist = _format_card_information(count, pool_name) + rst = init_star_rst(star_list, cnlist, three_list, three_olist) + if count > 90: + obj_list = set_list(obj_list) + return image(b64=await generate_img(obj_list, 'pretty', star_list)) \ + + '\n' + rst[:-1] + '\n' + max_card(obj_dict) + + +async def update_pretty_info(): + global ALL_CHAR, ALL_CARD + url = 'https://wiki.biligame.com/umamusume/赛马娘图鉴' + data, code = await update_info(url, 'pretty') + if code == 200: + ALL_CHAR = init_game_pool('pretty', data, PrettyChar) + url = 'https://wiki.biligame.com/umamusume/支援卡图鉴' + data, code = await update_info(url, 'pretty_card') + if code == 200: + ALL_CARD = init_game_pool('pretty_card', data, PrettyChar) + + +@driver.on_startup +async def init_data(): + global ALL_CHAR, ALL_CARD + if not os.path.exists(DRAW_PATH + '/draw_card_config/pretty.json') or\ + not os.path.exists(DRAW_PATH + '/draw_card_config/pretty_card.json'): + await update_pretty_info() + for icon_url in [ + 'https://patchwiki.biligame.com/images/umamusume/thumb/0/06/q23szwkbtd7pfkqrk3wcjlxxt9z595o.png' + '/40px-SSR.png', + 'https://patchwiki.biligame.com/images/umamusume/thumb/3/3b/d1jmpwrsk4irkes1gdvoos4ic6rmuht.png' + '/40px-SR.png', + 'https://patchwiki.biligame.com/images/umamusume/thumb/f/f7/afqs7h4snmvovsrlifq5ib8vlpu2wvk.png' + '/40px-R.png']: + await download_img(icon_url, 'pretty', icon_url.split('-')[-1][:-4]) + else: + with open(DRAW_PATH + '/draw_card_config/pretty.json', 'r', encoding='utf8') as f: + pretty_char_dict = json.load(f) + with open(DRAW_PATH + '/draw_card_config/pretty_card.json', 'r', encoding='utf8') as f: + pretty_card_dict = json.load(f) + ALL_CHAR = init_game_pool('pretty', pretty_char_dict, PrettyChar) + ALL_CARD = init_game_pool('pretty_card', pretty_card_dict, PrettyChar) + + +# 抽取卡池 +def _get_pretty_card(itype): + global ALL_CHAR, ALL_CARD + star = random.sample([3, 2, 1], + counts=[int(PRETTY_THREE * 100), int(PRETTY_TWO * 100), + int(PRETTY_ONE * 100)], + k=1)[0] + chars = [x for x in (ALL_CARD if itype == 'card' else ALL_CHAR) if x.star == star] + return random.choice(chars), abs(star - 3) + + +# 整理数据 +def _format_card_information(count: int, pool_name: str): + three_list = [] + three_olist = [] + obj_list = [] + obj_dict = {} + star_list = [0, 0, 0] + for i in range(count): + obj, code = _get_pretty_card(pool_name) + star_list[code] += 1 + if code == 0: + three_list.append(obj.name) + three_olist.append(i) + try: + obj_dict[obj.name] += 1 + except KeyError: + obj_dict[obj.name] = 1 + obj_list.append(obj) + return obj_list, obj_dict, three_list, star_list, three_olist diff --git a/plugins/draw_card/prts_handle.py b/plugins/draw_card/prts_handle.py new file mode 100644 index 00000000..f1e841eb --- /dev/null +++ b/plugins/draw_card/prts_handle.py @@ -0,0 +1,162 @@ + +import os +import nonebot +import random +from .config import PRTS_FIVE_P, PRTS_FOUR_P, PRTS_SIX_P, PRTS_THREE_P +from .update_game_info import update_info +from .util import generate_img, init_star_rst, max_card, BaseData, UpEvent, set_list +from .init_card_pool import init_game_pool +from pathlib import Path +from .announcement import PrtsAnnouncement +from dataclasses import dataclass +from util.init_result import image +from configs.path_config import DRAW_PATH +from services.log import logger +try: + import ujson as json +except ModuleNotFoundError: + import json + +driver: nonebot.Driver = nonebot.get_driver() + +up_char_file = Path() / "data" / "draw_card" / "draw_card_up" / "prts_up_char.json" + +prts_dict = {} +UP_OPERATOR = [] +ALL_OPERATOR = [] +_CURRENT_POOL_TITLE = '' + + +@dataclass +class Operator(BaseData): + recruit_only: bool # 公招限定 + event_only: bool # 活动获得干员 + # special_only: bool # 升变/异格干员 + + +async def prts_draw(count: int = 300): + cnlist = ['★★★★★★', '★★★★★', '★★★★', '★★★'] + operator_list, operator_dict, six_list, star_list, six_olist = _format_card_information(count) + up_list = [] + if _CURRENT_POOL_TITLE: + for x in UP_OPERATOR: + for operator in x.operators: + up_list.append(operator) + rst = init_star_rst(star_list, cnlist, six_list, six_olist, up_list) + if count > 90: + operator_list = set_list(operator_list) + pool_info = "当前up池: " if _CURRENT_POOL_TITLE else "" + return pool_info + _CURRENT_POOL_TITLE + image(b64=await generate_img(operator_list, 'prts', star_list)) \ + + '\n' + rst[:-1] + '\n' + max_card(operator_dict) + + +async def update_prts_info(): + global prts_dict, ALL_OPERATOR + url = 'https://wiki.biligame.com/arknights/干员数据表' + data, code = await update_info(url, 'prts', ['头像', '名称', '阵营', '星级', '性别', '是否感染', '初始生命', '初始防御', + '初始法抗', '再部署', '部署费用', '阻挡数', '攻击速度', '标签']) + if code == 200: + prts_dict = data + ALL_OPERATOR = init_game_pool('prts', prts_dict, Operator) + + +@driver.on_startup +async def init_data(): + global prts_dict, ALL_OPERATOR + if not os.path.exists(DRAW_PATH + '/draw_card_config/prts.json'): + await update_prts_info() + else: + with open(DRAW_PATH + '/draw_card_config/prts.json', 'r', encoding='utf8') as f: + prts_dict = json.load(f) + ALL_OPERATOR = init_game_pool('prts', prts_dict, Operator) + await _init_up_char() + # print([x.operators for x in UP_OPERATOR if x.star == 5 and x.zoom > 1]) + + +# 抽取干员 +def _get_operator_card(): + star = random.sample([6, 5, 4, 3], + counts=[int(PRTS_SIX_P * 100), int(PRTS_FIVE_P * 100), + int(PRTS_FOUR_P * 100), int(PRTS_THREE_P * 100)], + k=1)[0] + if _CURRENT_POOL_TITLE: + zooms = [x.zoom for x in UP_OPERATOR if x.star == star] + zoom = 0 + weight = 0 + # 分配概率和权重 + for z in zooms: + if z < 1: + zoom = z + else: + weight = z + # UP + if random.random() < zoom: + up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom < 1][0] + up_operator_name = random.choice(up_operators) + # print(up_operator_name) + acquire_operator = [x for x in ALL_OPERATOR if x.name == up_operator_name][0] + else: + all_star_operators = [x for x in ALL_OPERATOR if x.star == star + and not any([x.limited, x.event_only, x.recruit_only])] + weight_up_operators = [x.operators for x in UP_OPERATOR if x.star == star and x.zoom > 1] + # 权重 + if weight_up_operators and random.random() < 1.0 / float(len(all_star_operators)) * weight: + up_operator_name = random.choice(weight_up_operators[0]) + acquire_operator = [x for x in ALL_OPERATOR if x.name == up_operator_name][0] + else: + acquire_operator = random.choice(all_star_operators) + else: + acquire_operator = random.choice([x for x in ALL_OPERATOR if x.star == star + and not any([x.limited, x.event_only, x.recruit_only])]) + # print(f'{acquire_operator}: {star}') + return acquire_operator, abs(star - 6) + + +# 整理抽卡数据 +def _format_card_information(count: int): + operator_list = [] # 抽取的干员列表 + operator_dict = {} # 抽取各干员次数 + star_list = [0, 0, 0, 0] # 各个星级次数 + six_list = [] # 六星干员列表 + six_index_list = [] # 六星干员获取位置 + for i in range(count): + operator, code = _get_operator_card() + star_list[code] += 1 + if code == 0: + six_list.append(operator.name) + six_index_list.append(i) + try: + operator_dict[operator.name] += 1 + except KeyError: + operator_dict[operator.name] = 1 + operator_list.append(operator) + return operator_list, operator_dict, six_list, star_list, six_index_list + + +# 获取up干员和概率 +async def _init_up_char(): + global up_char_dict, _CURRENT_POOL_TITLE + up_char_dict = await PrtsAnnouncement.update_up_char() + # print(up_char_dict) + _CURRENT_POOL_TITLE = up_char_dict['title'] + up_char_dict = up_char_dict['up_char'] + logger.info(f'成功获取明日方舟当前up信息...当前up池: {_CURRENT_POOL_TITLE}') + average_dict = {'6': {}, '5': {}, '4': {}} + for star in up_char_dict.keys(): + for key in up_char_dict[star].keys(): + if average_dict[star].get(up_char_dict[star][key]): + average_dict[star][up_char_dict[star][key]].append(key) + else: + average_dict[star][up_char_dict[star][key]] = [key] + up_char_dict = {'6': {}, '5': {}, '4': {}} + for star in average_dict.keys(): + for str_zoom in average_dict[star].keys(): + if str_zoom[0] == '权': + zoom = float(str_zoom[1:]) + else: + zoom = float(str_zoom) / 100 + UP_OPERATOR.append(UpEvent(star=int(star), operators=average_dict[star][str_zoom], zoom=zoom)) + + +async def reload_pool(): + await _init_up_char() diff --git a/plugins/draw_card/update_game_info.py b/plugins/draw_card/update_game_info.py new file mode 100644 index 00000000..c95fb4cd --- /dev/null +++ b/plugins/draw_card/update_game_info.py @@ -0,0 +1,162 @@ +#coding:utf-8 +import aiohttp +from configs.path_config import DRAW_PATH +from asyncio.exceptions import TimeoutError +from services.log import logger +from bs4 import BeautifulSoup +from .util import download_img +from urllib.parse import unquote +import bs4 +from util.user_agent import get_user_agent +import re +try: + import ujson as json +except ModuleNotFoundError: + import json + + +async def update_info(url: str, game_name: str, info_list: list = None) -> 'dict, int': + try: + with open(DRAW_PATH + f'/draw_card_config/{game_name}.json', 'r', encoding='utf8') as f: + data = json.load(f) + except (ValueError, FileNotFoundError): + data = {} + try: + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, timeout=7) as response: + soup = BeautifulSoup(await response.text(), 'lxml') + max_count = 0 + _tbody = None + for tbody in soup.find_all('tbody'): + if len(tbody.find_all('tr')) > max_count: + _tbody = tbody + max_count = len(tbody.find_all('tr')) + trs = _tbody.find_all('tr') + att_dict = {'头像': 0, '名称': 1} + index = 2 + for th in trs[0].find_all('th')[2:]: + text = th.text + if text[-1] == '\n': + text = text[:-1] + att_dict[text] = index + index += 1 + for tr in trs[1:]: + member_dict = {} + k_name = '' + tds = tr.find_all('td') + if not info_list: + info_list = att_dict.keys() + for key in info_list: + attr = '' + if key.find('.') != -1: + key = key.split('.') + attr = key[-1] + key = key[0] + td = tds[att_dict[key]] + last_tag = unquote(_find_last_tag(td, attr), 'utf-8') + if game_name.find('pretty') == -1 and last_tag.find('http') == -1: + last_tag = last_tag.split('.')[0] + if key == '名称': + k_name = last_tag + member_dict[key] = last_tag + if game_name == 'pretty' and key == '初始星级': + member_dict['初始星级'] = len(td.find_all('img')) + avatar_img = await _modify_avatar_url(session, game_name, member_dict["名称"]) + if avatar_img: + member_dict['头像'] = avatar_img + name = member_dict['名称'] + if game_name == 'pretty_card': + name = member_dict['中文名'] + await download_img(member_dict['头像'], game_name, name) + if k_name: + data[k_name] = member_dict + logger.info(f'{k_name} is update...') + data = await _last_check(data, game_name, session) + except TimeoutError: + return {}, 999 + with open(DRAW_PATH + f'/draw_card_config/{game_name}.json', 'w', encoding='utf8') as wf: + wf.write(json.dumps(data, ensure_ascii=False, indent=4)) + return data, 200 + + +def _find_last_tag(element: bs4.element.Tag, attr: str) -> str: + last_tag = [] + for des in element.descendants: + last_tag.append(des) + if len(last_tag) == 1 and last_tag[0] == '\n': + last_tag = '' + elif last_tag[-1] == '\n': + last_tag = last_tag[-2] + else: + last_tag = last_tag[-1] + if attr and str(last_tag): + last_tag = last_tag[attr] + elif str(last_tag).find('([\s\S]*)'): + obtain = obtain.split('
') + elif obtain.find('
'): + obtain = obtain.split('
') + data[key]['获取途径'] = obtain + # if game_name == 'genshin': + # for key in data.keys(): + # async with session.get(f'https://wiki.biligame.com/ys/{key}', timeout=7) as res: + # soup = BeautifulSoup(await res.text(), 'lxml') + # trs = soup.find('div', {'class': 'poke-bg'}).find('table').find('tbody').find_all('tr') + # for tr in trs: + # if tr.find('th').text.find('常驻/限定') != -1: + # data[key]['常驻/限定'] = tr.find('td').text + # break + if game_name == 'pretty': + for keys in data.keys(): + for key in data[keys].keys(): + # print(f'key --> {data[keys][key]}') + r = re.search(r'.*?40px-(.*)图标.png', str(data[keys][key])) + if r: + data[keys][key] = r.group(1) + return data + + + + +# ul = soup.find('div', {'class': 'savelist_bot'}).find('ul') + + diff --git a/plugins/draw_card/util.py b/plugins/draw_card/util.py new file mode 100644 index 00000000..77a83467 --- /dev/null +++ b/plugins/draw_card/util.py @@ -0,0 +1,161 @@ +import os +import aiohttp +import aiofiles +from asyncio.exceptions import TimeoutError +from aiohttp.client_exceptions import InvalidURL +from typing import List, Union, Set +import asyncio +from pathlib import Path +from .config import path_dict +import nonebot +from util.utils import cn2py +from util.img_utils import CreateImg +from util.user_agent import get_user_agent +from configs.path_config import IMAGE_PATH +from dataclasses import dataclass +from services.log import logger +try: + import ujson as json +except ModuleNotFoundError: + import json + + +driver: nonebot.Driver = nonebot.get_driver() + + +@dataclass +class BaseData: + name: str + star: int + limited: bool # 限定 + + +@dataclass +class UpEvent: + star: int # 对应up星级 + operators: List[BaseData] # 干员列表 + zoom: float # up提升倍率 + + +async def download_img(url: str, path: str, name: str) -> bool: + path = path.split('_')[0] + codename = cn2py(name) + # if not _p.exists(): + # _p.mkdir(parents=True, exist_ok=True) + if not os.path.exists(IMAGE_PATH + f'/draw_card/{path}/{codename}.png'): + try: + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, timeout=7) as response: + async with aiofiles.open(IMAGE_PATH + f'/draw_card/{path}/{codename}.png', 'wb') as f: + await f.write(await response.read()) + logger.info(f'下载 {path_dict[path]} 图片成功,名称:{name},url:{url}') + return True + except TimeoutError: + logger.info(f'下载 {path_dict[path]} 图片超时,名称:{name},url:{url}') + return False + except InvalidURL: + logger.info(f'下载 {path_dict[path]} 链接错误,名称:{name},url:{url}') + return False + else: + # logger.info(f'{path_dict[path]} 图片 {name} 已存在') + return False + + +@driver.on_startup +def _check_dir(): + for dir_name in path_dict.keys(): + _p = Path(IMAGE_PATH + f'/draw_card/' + dir_name) + if not _p.exists(): + _p.mkdir(parents=True, exist_ok=True) + + +async def generate_img(card_set: Union[Set[BaseData], List[BaseData]], game_name: str, star_list: list) -> str: + # try: + img_list = [] + color_list = [] + for x in card_set: + if game_name == 'prts': + if x.star == 6: + color_list.append('#FFD700') + elif x.star == 5: + color_list.append('#DAA520') + elif x.star == 4: + color_list.append('#9370D8') + else: + color_list.append('white') + pyname = cn2py(x.name) + img_list.append(IMAGE_PATH + f'/draw_card/{game_name}/{pyname}.png') + img_len = len(img_list) + w = 100 * 10 + if img_len <= 10: + w = 100 * img_len + h = 100 + elif img_len % 10 == 0: + h = 100 * int(img_len / 10) + else: + h = 100 * int(img_len / 10) + 100 + card_img = await asyncio.get_event_loop().run_in_executor(None, _pst, h, img_list, game_name, color_list) + num = 0 + for n in star_list: + num += n + A = CreateImg(w, h) + A.paste(card_img) + return A.pic2bs4() + + +def _pst(h: int, img_list: list, game_name: str, color_list: list): + card_img = CreateImg(100 * 10, h, 100, 100) + idx = 0 + for img in img_list: + try: + if game_name == 'prts': + bk = CreateImg(100, 100, color=color_list[idx]) + b = CreateImg(94, 94, background=img) + bk.paste(b, (3, 3)) + b = bk + idx += 1 + else: + b = CreateImg(100, 100, background=img) + except FileNotFoundError: + print(f'{img} not exists') + b = CreateImg(100, 100, color='black') + card_img.paste(b) + return card_img + + +def init_star_rst(star_list: list, cnlist: list, max_star_list: list, max_star_olist: list, up_list: list = None) -> str: + if not up_list: + up_list = [] + rst = '' + for i in range(len(star_list)): + if star_list[i]: + rst += f'[{cnlist[i]}×{star_list[i]}] ' + rst += '\n' + for i in range(len(max_star_list)): + if max_star_list[i] in up_list: + rst += f'第 {max_star_olist[i]+1} 抽获取UP {max_star_list[i]}\n' + else: + rst += f'第 {max_star_olist[i]+1} 抽获取 {max_star_list[i]}\n' + return rst + + +def max_card(_dict: dict): + _max_value = max(_dict.values()) + _max_user = list(_dict.keys())[list(_dict.values()).index(_max_value)] + return f'抽取到最多的是{_max_user},共抽取了{_max_value}次' + # ThreeHighest = nlargest(3, operator_dict, key=operator_dict.get) + # rst = '最喜欢你的前三位是干员是:\n' + # for name in ThreeHighest: + # rst += f'{name} 共投了 {operator_dict[name]} 份简历\n' + # return rst[:-1] + + +def set_list(lst: List[BaseData]) -> list: + tmp = [] + name_lst = [] + for x in lst: + if x.name not in name_lst: + tmp.append(x) + name_lst.append(x.name) + return tmp + diff --git a/plugins/epic/__init__.py b/plugins/epic/__init__.py new file mode 100644 index 00000000..16d454af --- /dev/null +++ b/plugins/epic/__init__.py @@ -0,0 +1,60 @@ +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.typing import T_State +from util.utils import scheduler, get_bot +from .data_source import get_epic_game +from models.group_remind import GroupRemind +from nonebot.adapters.cqhttp.exception import ActionFailed + +__plugin_usage__ = 'epic免费游戏提醒' + + +epic = on_command("epic", priority=5, block=True) + + +@epic.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + # try: + if str(event.get_message()) in ['帮助']: + await epic.finish(__plugin_usage__) + try: + result = await get_epic_game() + except: + result = '网络出错了!' + await epic.send(result) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f" 获取epic免费游戏") + # except Exception as e: + # logger.error(f'epic 出错 e:{e}') + # await epic.finish('网络好像炸了,再试一次?', at_sender=True) + + +# epic免费游戏 +@scheduler.scheduled_job( + 'cron', + hour=12, + minute=1, +) +async def _(): + # try: + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g['group_id'] for g in gl] + for g in gl: + if await GroupRemind.get_status(g, 'epic'): + result = await get_epic_game() + if result == '今天没有游戏可以白嫖了!': + return + try: + await bot.send_group_msg(group_id=g, + message=result) + except ActionFailed: + logger.error(f'{g}群 epic免费游戏推送错误') + # except Exception as e: + # logger.error(f'epic免费游戏推送错误 e:{e}') + + + + diff --git a/plugins/epic/data_source.py b/plugins/epic/data_source.py new file mode 100644 index 00000000..7efdcac3 --- /dev/null +++ b/plugins/epic/data_source.py @@ -0,0 +1,46 @@ +import aiohttp +import aiofiles +from util.utils import get_local_proxy +import feedparser +import platform +from util.init_result import image +from configs.path_config import IMAGE_PATH +from util.user_agent import get_user_agent +if platform.system() == 'Windows': + import asyncio + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + +url = 'https://rsshub.app/epicgames/freegames' + + +async def get_epic_game() -> str: + result = '' + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, proxy=get_local_proxy(), timeout=7) as response: + data = feedparser.parse(await response.text())['entries'] + if len(data) == 0: + return result + index = 0 + for item in data: + title = item['title'] + img_url = item['summary'][item['summary'].find('src="')+5: item['summary'].rfind('"')] + async with session.get(img_url, proxy=get_local_proxy(), timeout=7) as res: + async with aiofiles.open(IMAGE_PATH + f'temp/epic_{index}.jpg', 'wb') as f: + await f.write(await res.read()) + link = item['link'] + result += image(f'epic_{index}.jpg', 'temp') + f'\n【游戏】| {title}\n【链接】 | {link}\n' + index += 1 + if result != '': + result = 'epic限免游戏(速速白嫖):\n' + result + else: + result = '今天没有游戏可以白嫖了!' + print(result) + return result + + + + +# print(asyncio.get_event_loop().run_until_complete(get_epic_game())) + + diff --git a/plugins/group_handle/__init__.py b/plugins/group_handle/__init__.py new file mode 100644 index 00000000..4672c7fc --- /dev/null +++ b/plugins/group_handle/__init__.py @@ -0,0 +1,125 @@ +from nonebot import on_notice, on_request +from configs.path_config import IMAGE_PATH, DATA_PATH +from util.init_result import image +import os +import random +from models.group_member_info import GroupInfoUser +from datetime import datetime +from services.log import logger +from models.group_remind import GroupRemind +from nonebot.adapters.cqhttp import Bot, GroupIncreaseNoticeEvent, GroupDecreaseNoticeEvent, GroupRequestEvent +from nonebot.adapters.cqhttp.exception import ActionFailed +from pathlib import Path +from nonebot import require +try: + import ujson as json +except ModuleNotFoundError: + import json + + +export = require("admin_bot_manage") + +# 群员增加处理 +group_increase_handle = on_notice(priority=5) +# 群员减少处理 +group_decrease_handle = on_notice(priority=5) +# (群管理)加群同意请求 +add_group = on_request(priority=5) + + +@group_increase_handle.handle() +async def _(bot: Bot, event: GroupIncreaseNoticeEvent, state: dict): + if event.user_id == int(bot.self_id): + await export.update_member_info(event.group_id) + else: + join_time = datetime.now() + user_info = await bot.get_group_member_info(group_id=event.group_id, user_id=event.user_id) + if await GroupInfoUser.insert( + user_info['user_id'], + user_info['group_id'], + user_info['nickname'], + join_time, + ): + logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新成功") + else: + logger.info(f"用户{user_info['user_id']} 所属{user_info['group_id']} 更新失败") + if await GroupRemind.get_status(event.group_id, 'hy'): + msg = '' + img = '' + at_flag = False + custom_welcome_msg_json = Path() / "data" / "custom_welcome_msg" / "custom_welcome_msg.json" + if custom_welcome_msg_json.exists(): + data = json.load(open(custom_welcome_msg_json, 'r')) + if data.get(str(event.group_id)): + msg = data[str(event.group_id)] + if msg.find('[at]') != -1: + msg = msg.replace('[at]', '') + at_flag = True + if os.path.exists(DATA_PATH + f'custom_welcome_msg/{event.group_id}.jpg'): + img = image(abspath=DATA_PATH + f'custom_welcome_msg/{event.group_id}.jpg') + if msg or img: + await group_increase_handle.send("\n" + msg + img, at_sender=at_flag) + else: + await group_increase_handle.send( + '新人快跑啊!!本群现状↓(快使用自定义!)' + image(random.choice(os.listdir(IMAGE_PATH + "qxz/")), "qxz")) + + +@group_decrease_handle.handle() +async def _(bot: Bot, event: GroupDecreaseNoticeEvent, state: dict): + # 真寻被踢出群 + if event.sub_type == 'kick_me': + group_id = event.group_id + operator_id = event.operator_id + try: + operator_name = (await GroupInfoUser.select_member_info(event.operator_id, event.group_id)).user_name + except AttributeError: + operator_name = 'None' + coffee = int(list(bot.config.superusers)[0]) + await bot.send_private_msg( + user_id=coffee, + message=f'报告..\n' + f'我被 {operator_name}({operator_id})\n' + f'踢出了 {group_id}') + return + try: + user_name = (await GroupInfoUser.select_member_info(event.user_id, event.group_id)).user_name + except AttributeError: + user_name = str(event.user_id) + rst = '' + if event.sub_type == 'leave': + rst = f'{user_name}离开了我们...' + if event.sub_type == 'kick': + try: + operator_name = (await GroupInfoUser.select_member_info(event.operator_id, event.group_id)).user_name + except AttributeError: + operator_name = event.operator_id + rst = f'{user_name} 被 {operator_name} 送走了.' + try: + await group_decrease_handle.send(f"{rst}") + except ActionFailed: + return + if await GroupInfoUser.delete_member_info(event.user_id, event.group_id): + logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除成功") + else: + logger.info(f"用户{user_name}, qq={event.user_id} 所属{event.group_id} 删除失败") + + +@add_group.handle() +async def _(bot: Bot, event: GroupRequestEvent, state: dict): + pass + # user_info = await bot._get_vip_info(user_id=event.user_id) + # if user_info['level'] > 16: + # bot.set + + + + + + + + + + + + + diff --git a/plugins/help/__init__.py b/plugins/help/__init__.py new file mode 100644 index 00000000..7459ce59 --- /dev/null +++ b/plugins/help/__init__.py @@ -0,0 +1,35 @@ +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, PrivateMessageEvent +from nonebot.typing import T_State +from nonebot.rule import to_me +from configs.path_config import DATA_PATH +from util.init_result import image +import os +from .data_source import create_help_img, create_group_help_img +from nonebot import require + +export = require("nonebot_plugin_manager") + +__plugin_name__ = '帮助' + + +if not os.path.exists(DATA_PATH + 'group_help/'): + os.mkdir(DATA_PATH + 'group_help/') +create_help_img() +for file in os.listdir(DATA_PATH + 'group_help/'): + os.remove(DATA_PATH + 'group_help/' + file) + +_help = on_command("功能", rule=to_me(), aliases={"帮助", 'help'}, priority=1, block=True) + + +@_help.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if not os.path.exists(DATA_PATH + f'group_help/{event.group_id}.png'): + create_group_help_img(event.group_id) + await _help.finish(image(abspath=DATA_PATH + f'group_help/{event.group_id}.png')) + + +@_help.handle() +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + await _help.finish(image('help.png')) + diff --git a/plugins/help/config.py b/plugins/help/config.py new file mode 100644 index 00000000..27d8e36f --- /dev/null +++ b/plugins/help/config.py @@ -0,0 +1,52 @@ + +# 实用 +utility_help = { + 'update_pic': '一些对图片的操作 --> 指令:操作图片/图片/修改图片(包含 10 种图片操作)', + 'search_buff_skin_price': 'BUFF皮肤底价查询 --> 指令:查询皮肤(代理ip不得劲)', + 'weather': '天气查询 --> 指令:xx天气', + 'yiqing': '实时疫情数据 --> 指令:疫情查询/疫情/查询疫情', + 'bt': 'bt(磁力搜索){仅支持私聊,懂的都懂} --> 指令:bt', + 'reimu': '老司机必备!{仅支持私聊,懂的都懂}-> 指令:上车', + 'what_anime': '靠图识番 --> 指令:识番', + 'nonebot_plugin_picsearcher': '以图搜图 --> 指令:识图/这是什么/上一张图是什么', + 'search_anime': '找不到想看的动漫吗? --> 指令:搜番', + 'songpicker2': '来一首歌听听? --> 指令:点歌', + 'epic': 'epic速速白嫖 --> 指令:epic', + 'pixiv_r': 'P站排行榜直接冲 --> 指令:p站排行(可含参数)', + 'pixiv_s': 'P站的图随便搜搜 --> 指令:搜图(可含参数)', + 'translate': '出国旅游助手(狗头) --> 指令:英翻/翻英/日翻/翻日/韩翻/翻韩' +} +# 娱乐 +entertainment_help = { + 'sign_in': '签到(影响色图几率和开箱次数) --> 指令:签到/我的签到/好感度排行', + 'send_img': '发送图片 --> 指令:美图/萝莉/壁纸', + 'send_setu': '不要小看涩图啊混蛋! --> 指令:色图/n张色图/n张xx色图/查色图/...(请查看 色图 帮助)', + 'white2black_img': '黑白草图 --> 指令:黑白图/黑白草图', + 'coser': '三次元也不戳 --> 指令:coser', + 'jitang': '不喝点什么不舒服 --> 指令:鸡汤/语录', + 'send_dinggong_voice': '骂我(傲娇?) --> 指令:骂老子', + 'poke': '戳一戳发送语音美图萝莉图不美哉?', + 'open_cases': '模拟开箱(戒赌) --> 指令:开箱(N连开箱[N<=30])/我的开箱/群开箱统计/我的金色', + 'luxun': '鲁迅说过 --> 指令:鲁迅说', + 'fake_msg': '构造一个假消息 --> 指令:假消息', + 'shop': '商店系统(初始送100金币) --> 指令:商店/我的金币/购买道具/使用道具', + 'draw_card_p': '换个地方当非酋TvT... --> 指令:方舟一井/方舟N抽(0 指令:原神一井/原神N抽(0 指令:赛马娘一井/赛马娘N抽/赛马娘卡一井/赛马娘卡N抽(0 直接查看 骰子娘帮助!', + 'one_friend': '我有一个朋友想问问... --> 指令:我有一个朋友想问问xxx(内容)', + 'nickname': '区区昵称! --> 指令:以后叫我xx(昵称)/我是谁/取消昵称', + 'almanac': '这是一张正经的黄历 --> 指令:原神黄历', + 'material_remind': '看看原神今天要刷什么 --> 指令:今日素材/天赋材料', + 'qiu_qiu_translation': '这家伙到底在说什么? --> 指令:丘丘翻译/丘丘一下/丘丘语翻译', + 'query_resource_points': '地图资源速速查看 --> 指令:原神资源查询xx/原神资源列表/哪里有xx/xx在哪(xx=资源名称)', +} +# 其他 +other_help = [ + '群内csgo服务器(指定群) --> 指令:服务器/ip(其他群请私聊)', + '查看当前的群欢迎消息 --> 指令:群欢迎消息', + '这是一份正经的自我介绍 --> 指令:自我介绍', + '不得看看自己权力多大? --> 指令:我的权限', + '有人记得你是什么时候加入我们的 --> 指令:我的信息', + '让我看看更新了什么 --> 指令:更新信息' +] diff --git a/plugins/help/data_source.py b/plugins/help/data_source.py new file mode 100644 index 00000000..95515f6b --- /dev/null +++ b/plugins/help/data_source.py @@ -0,0 +1,175 @@ +from util.img_utils import CreateImg +from configs.path_config import IMAGE_PATH, DATA_PATH +import ujson as json +import os +from .config import * +from nonebot import require + +export = require("nonebot_plugin_manager") + +width = 1200 +e_height = 0 +u_height = 700 +o_height = 1250 +# f_height = + + +def create_help_img(): + if os.path.exists(IMAGE_PATH + 'help.png'): + os.remove(IMAGE_PATH + 'help.png') + h = (100 + len(utility_help) * 24 + len(entertainment_help) * 24 + len(other_help) * 24) * 2 + A = CreateImg(width, h - 200, font_size=24) + e = CreateImg(width, len(entertainment_help) * 42, font_size=24) + rst = '' + i = 0 + for cmd in entertainment_help: + rst += f'{i + 1}.{entertainment_help[cmd]}\n' + i += 1 + e.text((10, 10), '娱乐功能:') + e.text((40, 40), rst) + u = CreateImg(width, len(utility_help) * 40 + 50, font_size=24, color='black') + rst = '' + i = 0 + for cmd in utility_help: + rst += f'{i + 1}.{utility_help[cmd]}\n' + i += 1 + u.text((10, 10), '实用功能:', fill=(255, 255, 255)) + u.text((40, 40), rst, fill=(255, 255, 255)) + o = CreateImg(width, len(other_help) * 40, font_size=24) + rst = '' + i = 0 + for i in range(len(other_help)): + rst += f'{i + 1}.{other_help[i]}\n' + i += 1 + o.text((10, 10), '其他功能:') + o.text((40, 40), rst) + A.paste(e, (0, 0)) + A.paste(u, (0, u_height)) + A.paste(o, (0, o_height)) + A.text((10, h * 0.72), '大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对我说 “指令名 帮助” 获取对应详细帮助\n' + '可以通过 “滴滴滴- 后接内容” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)\n[群管理员请看 管理员帮助(群主与管理员自带 5 级权限)]') + A.text((10, h * 0.79), f"【注】「色图概率:好感度 + 70%\n" + f"\t\t每 3 点好感度 + 1次开箱,初始 20 次\n" + f"\t\t开启/关闭功能只需输入‘开启/关闭 指令名’(每个功能的第一个指令)」\n" + f"\t\t示例:开启签到") + A.save(IMAGE_PATH + 'help.png') + + +def create_group_help_img(group_id: int): + group_id = str(group_id) + try: + with open(DATA_PATH + 'manager/plugin_list.json', 'r', encoding='utf8') as f: + plugin_list = json.load(f) + except (ValueError, FileNotFoundError): + pass + h = (100 + len(utility_help) * 24 + len(entertainment_help) * 24 + len(other_help) * 24) * 2 + A = CreateImg(1200, h - 200, font_size=24) + u = CreateImg(1200, len(utility_help) * 40, font_size=24, color='black') + o = CreateImg(1200, len(other_help) * 40, font_size=24) + e = CreateImg(width, len(entertainment_help) * 42, font_size=24) + rst = '' + i = 1 + # print(plugin_list) + for cmd in entertainment_help.keys(): + # dfg = '_' + # if cmd == 'draw_card_p': + # cmd = 'draw_card' + # dfg = 'p' + # elif cmd == 'draw_card_g': + # cmd = 'draw_card' + # dfg = 'g' + # flag = '√' + # if group_id in plugin_list[cmd]: + # if not plugin_list[cmd][group_id]: + # flag = '×' + # if cmd in ['nickname']: + # flag = '-' + flag, dfg = parse_cmd(cmd, group_id, plugin_list) + if dfg: + cmd = rcmd(dfg) + # if dfg == 'p': + # cmd = 'draw_card_p' + # elif dfg == 'g': + # cmd = 'draw_card_g' + rst += f'【{flag}】{i}.{entertainment_help[cmd]}\n' + i += 1 + e.text((10, 10), '娱乐功能:') + e.text((40, 40), rst) + + rst = '' + i = 1 + for cmd in utility_help.keys(): + # flag = '√' + # if group_id in plugin_list[cmd]: + # if not plugin_list[cmd][group_id]: + # flag = '×' + # if cmd in ['bt', 'reimu']: + # flag = '-' + flag, dfg = parse_cmd(cmd, group_id, plugin_list) + rst += f'【{flag}】{i}.{utility_help[cmd]}\n' + i += 1 + u.text((10, 10), '实用功能:', fill=(255, 255, 255)) + u.text((40, 40), rst, fill=(255, 255, 255)) + + rst = '' + for i in range(len(other_help)): + rst += f'{i + 1}.{other_help[i]}\n' + o.text((10, 10), '其他功能:') + o.text((40, 40), rst) + + A.paste(e, (0, 0)) + A.paste(u, (0, u_height)) + A.paste(o, (0, o_height)) + # A.text((width, 10), f'总开关【{"√" if data["总开关"] else "×"}】') + A.text((10, h * 0.72), '大部分交互功能可以通过输入‘取消’,‘算了’来取消当前交互\n对我说 “指令名 帮助” 获取对应详细帮助\n' + '可以通过 “滴滴滴- 后接内容” 联系管理员(有趣的想法尽管来吧!<还有Bug和建议>)' + '\n[群管理员请看 管理员帮助(群主与管理员自带 5 级权限)]') + A.text((10, h * 0.79), f"【注】「色图概率:好感度 + 70%\n" + f"\t\t每 3 点好感度 + 1次开箱,初始 20 次\n" + f"\t\t开启/关闭功能只需输入‘开启/关闭 指令名’(每个功能的第一个指令)」\n" + f"\t\t示例:开启签到\n" + f"\t\t可以通过管理员开关自动发送消息(早晚安等)\n" + f"\t\t^请查看管理员帮助^") + A.save(DATA_PATH + f'group_help/{group_id}.png') + + +def parse_cmd(cmd, group_id, plugin_list): + flag = '√' + dfg = None + if cmd == 'draw_card_p': + cmd = 'draw_card' + dfg = 'p' + elif cmd == 'draw_card_g': + cmd = 'draw_card' + dfg = 'g' + elif cmd == 'draw_card_h': + cmd = 'draw_card' + dfg = 'h' + elif cmd == 'pixiv_r': + cmd = 'pixiv' + dfg = 'r' + elif cmd == 'pixiv_s': + cmd = 'pixiv' + dfg = 's' + if group_id in plugin_list[cmd]: + if not plugin_list[cmd][group_id]: + flag = '×' + if cmd in ['bt', 'reimu', 'nickname']: + flag = '- ' + return flag, dfg + + +def rcmd(dfg): + if dfg == 'p': + return 'draw_card_p' + if dfg == 'g': + return 'draw_card_g' + if dfg == 'g': + return 'draw_card_h' + if dfg == 'r': + return 'pixiv_r' + if dfg == 's': + return 'pixiv_s' + + + diff --git a/plugins/last_chat/__init__.py b/plugins/last_chat/__init__.py new file mode 100644 index 00000000..169a7496 --- /dev/null +++ b/plugins/last_chat/__init__.py @@ -0,0 +1,53 @@ +from nonebot import on_message +from nonebot.adapters.cqhttp.permission import GROUP +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +import time +from .data_source import cancel_all_notice, save_data, get_data, set_data_value +from services.log import logger + + +__plugin_name__ = '查看群最后聊天时间 [Hidden]' + + +last_chat = on_message(priority=1, block=False, permission=GROUP) + + +@last_chat.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + time_data = await get_data() + set_data_value(event.group_id, time.time()) + if event.group_id in time_data['_group']: + time_data['_group'].remove(event.group_id) + set_data_value('_group', time_data['_group']) + for key in time_data.keys(): + if key not in ['check_time', '_group']: + if key not in time_data['_group']: + if time.time() - time_data[key] > 60 * 60 * 36: + await cancel_all_notice(key) + time_data["_group"].append(key) + set_data_value('_group', time_data['_group']) + logger.info(f'GROUP {event.group_id} 因群内发言时间大于36小时被取消全部通知') + if time.time() - time_data['check_time'] > 60 * 60 * 1: + set_data_value('check_time', time.time()) + save_data() + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/last_chat/data_source.py b/plugins/last_chat/data_source.py new file mode 100644 index 00000000..87e93fbd --- /dev/null +++ b/plugins/last_chat/data_source.py @@ -0,0 +1,74 @@ +from configs.path_config import DATA_PATH +from util.utils import get_bot +from models.group_remind import GroupRemind +from datetime import datetime +import time +from services.log import logger +try: + import ujson as json +except ModuleNotFoundError: + import json + + +time_data = {} + + +async def init(): + global time_data + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g['group_id'] for g in gl] + data = read_data('group_last_chat_time.json') + for g in gl: + if not data.get(g): + time_data[g] = time.time() + if not time_data.get('check_time'): + time_data['check_time'] = time.time() + if not time_data.get('_group'): + time_data['_group'] = [] + save_data() + return time_data + + +def read_data(file_name: str): + try: + with open(DATA_PATH + file_name, 'r', encoding='utf8') as f: + return json.load(f) + except (ValueError, FileNotFoundError): + return {} + + +def save_data(): + with open(DATA_PATH + 'group_last_chat_time.json', 'w') as f: + json.dump(time_data, f, indent=4) + logger.info(f'自动存储 group_last_chat_time.json 时间:{str(datetime.now()).split(".")[0]}') + + +command_list = ['zwa', 'hy', 'kxcz', 'blpar', 'epic', 'pa'] + + +# 取消全部通知 +async def cancel_all_notice(group_id): + group_id = int(group_id) + for command in command_list: + if await GroupRemind.get_status(group_id, command): + await GroupRemind.set_status(group_id, command, False) + logger.info(f'关闭了 {group_id} 群的全部通知') + + +async def get_data(): + global time_data + if not time_data: + time_data = await init() + return time_data + + +def set_data_value(key, value): + global time_data + time_data[key] = value + + + + + + diff --git a/plugins/luxun/__init__.py b/plugins/luxun/__init__.py new file mode 100644 index 00000000..985f1d55 --- /dev/null +++ b/plugins/luxun/__init__.py @@ -0,0 +1,64 @@ +from PIL import ImageFont, ImageDraw, Image +import textwrap +from configs.path_config import IMAGE_PATH, TTF_PATH +from nonebot import on_command +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent +from util.init_result import image +from services.log import logger +from util.utils import UserExistLimiter, get_message_text +from util.img_utils import pic2b64 + +_ulmt = UserExistLimiter() + + +luxun = on_command("鲁迅说过", aliases={"鲁迅说"}) + + +@luxun.handle() +async def handle(bot: Bot, event: MessageEvent, state: T_State): + if _ulmt.check(event.user_id): + await luxun.finish('你的鲁迅正在说,等会', at_sender=True) + args = get_message_text(event.json()) + if args: + state["content"] = args if args else '烦了,不说了' + + +@luxun.got("content", prompt="你让鲁迅说点啥?") +async def handle_event(bot: Bot, event: MessageEvent, state: T_State): + filename = str(event.user_id) + "_.jpg" + content = state["content"].strip() + if content.startswith(',') or content.startswith(','): + content = content[1:] + _ulmt.set_True(event.user_id) + if len(content) > 20: + _ulmt.set_False(event.user_id) + await luxun.finish("太长了, 鲁迅说不完!", at_sender=True) + else: + if len(content) >= 12: + content = content[:12] + '\n' + content[12:] + img = image(b64=process_pic(content, filename)) + logger.info(f"USER {event.user_id} GROUP " + f"{event.group_id if event.message_type != 'private' else 'private'} 鲁迅说过 {content}") + await luxun.send(img) + _ulmt.set_False(event.user_id) + + +def process_pic(content, filename) -> str: + text = content + para = textwrap.wrap(text, width=15) + MAX_W, MAX_H = 480, 280 + bk_img = Image.open(IMAGE_PATH + "other/luxun.jpg") + font_path = TTF_PATH + "/msyh.ttf" + font = ImageFont.truetype(font_path, 37) + font2 = ImageFont.truetype(font_path, 30) + draw = ImageDraw.Draw(bk_img) + current_h, pad = 300, 10 + for line in para: + w, h = draw.textsize(line, font=font) + draw.text(((MAX_W - w) / 2, current_h), line, font=font) + current_h += h + pad + draw.text((320, 400), "——鲁迅", font=font2, fill=(255, 255, 255)) + return pic2b64(bk_img) + + diff --git a/plugins/move_img/__init__.py b/plugins/move_img/__init__.py new file mode 100644 index 00000000..6f3f593d --- /dev/null +++ b/plugins/move_img/__init__.py @@ -0,0 +1,84 @@ +import os +from services.log import logger +from nonebot import on_command +from nonebot.rule import to_me +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent +from configs.config import IMAGE_DIR_LIST +from util.utils import is_number, cn2py +from configs.path_config import IMAGE_PATH + + +__plugin_name__ = '移动图片' +__plugin_usage__ = '移动图片帮助:\n\t' \ + '1.查看列表 --> 指令: 移动图片 列表/目录\n\t' \ + '2.移动图片 源 目的 id\n\t\t示例: 移动图片 色图 美图 1234' + + +move_img = on_command('移动图片', priority=5, rule=to_me(), block=True) + + +@move_img.args_parser +async def parse(bot: Bot, event: MessageEvent, state: T_State): + if str(event.get_message()) in ['取消', '算了']: + await move_img.finish("已取消操作..", at_sender=True) + if state["_current_key"] in ['source_path', 'destination_path']: + if str(event.get_message()) not in IMAGE_DIR_LIST: + await move_img.reject("此目录不正确,请重新输入目录!") + state[state["_current_key"]] = str(event.get_message()) + if state["_current_key"] == 'id': + if not is_number(str(event.get_message())): + await move_img.reject("id不正确!请重新输入数字...") + state[state["_current_key"]] = str(event.get_message()) + + +@move_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + raw_arg = str(event.get_message()).strip() + if raw_arg: + args = raw_arg.split(" ") + if args[0] in ['帮助']: + await move_img.finish(__plugin_usage__) + if len(args) >= 3 and args[0] in IMAGE_DIR_LIST and args[1] in IMAGE_DIR_LIST and is_number(args[2]): + state['source_path'] = args[0] + state['destination_path'] = args[1] + state['id'] = args[2] + else: + await move_img.finish("参数错误,请重试", at_sender=True) + + +@move_img.got("source_path", prompt="要从哪个图库移出?") +@move_img.got("destination_path", prompt="要移动到哪个图库?") +@move_img.got("id", prompt="要移动的图片id是?") +async def _(bot: Bot, event: MessageEvent, state: T_State): + img_id = state['id'] + source_path = IMAGE_PATH + cn2py(state['source_path']) + destination_path = IMAGE_PATH + cn2py(state['destination_path']) + max_id = len(os.listdir(source_path)) - 1 + des_max_id = len(os.listdir(destination_path)) + if int(img_id) > max_id or int(img_id) < 0: + await move_img.finish(f"Id超过上下限,上限:{max_id}", at_sender=True) + try: + os.rename(source_path + img_id + ".jpg", destination_path + str(des_max_id) + ".jpg") + logger.info(f"移动 {source_path}{img_id}.jpg ---> {destination_path}{des_max_id} 移动成功") + except Exception as e: + logger.warning(f"移动 {source_path}{img_id}.jpg ---> {destination_path}{des_max_id} 移动失败 e:{e}") + await move_img.finish(f"移动图片id:{img_id} 失败了...", at_sender=True) + if max_id > 0: + try: + os.rename(source_path + str(max_id) + ".jpg", source_path + img_id + ".jpg") + logger.info(f"{source_path}{max_id}.jpg 替换 {source_path}{img_id}.jpg 成功") + except Exception as e: + logger.warning(f"{source_path}{max_id}.jpg 替换 {source_path}{img_id}.jpg 失败 e:{e}") + await move_img.finish(f"替换图片id:{max_id} -> {img_id} 失败了...", at_sender=True) + logger.info(f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'} ->" + f" {source_path} --> {destination_path} (id:{img_id}) 移动图片成功") + await move_img.finish(f"移动图片 id:{img_id} --> id:{des_max_id}成功", at_sender=True) + + + + + + + + diff --git a/plugins/nonebot_plugin_cocdicer/__init__.py b/plugins/nonebot_plugin_cocdicer/__init__.py new file mode 100644 index 00000000..5dae4b6f --- /dev/null +++ b/plugins/nonebot_plugin_cocdicer/__init__.py @@ -0,0 +1,72 @@ +from .data_source import rd, help_message, st, en +from .madness import ti, li +from .create import Investigator +from .san_check import sc + +from nonebot.plugin import on_startswith +from nonebot.adapters.cqhttp import Bot, Event + +rdhelp = on_startswith("骰子娘帮助", priority=2) +stcommand = on_startswith(".st", priority=2) +encommand = on_startswith(".en", priority=2) +ticommand = on_startswith(".ti", priority=2) +licommand = on_startswith(".li", priority=2) +coc = on_startswith(".coc", priority=2) +sccommand = on_startswith(".sc", priority=2) +rdcommand = on_startswith(".r", priority=3) + + +@rdhelp.handle() +async def rdhelphandler(bot: Bot): + await rdhelp.finish(help_message()) + + +@stcommand.handle() +async def stcommandhandler(bot: Bot): + await rdhelp.finish(st()) + + +@encommand.handle() +async def enhandler(bot: Bot, event: Event): + args = str(event.get_message())[3:].strip() + await encommand.finish(en(args)) + + +@rdcommand.handle() +async def rdcommandhandler(bot: Bot, event: Event): + args = str(event.get_message())[2:].strip() + uid = event.get_session_id() + if args and not("." in args): + rrd = rd(args) + if type(rrd) == str: + await rdcommand.finish(rrd) + elif type(rrd) == list: + await bot.send_private_msg(user_id=uid, message=rrd[0]) + + +@coc.handle() +async def cochandler(bot: Bot, event: Event): + args = str(event.get_message())[4:].strip() + try: + args = int(args) + except: + args = 20 + inv = Investigator() + inv.age_change(args) + await coc.finish(inv.output()) + + +@ticommand.handle() +async def ticommandhandler(bot: Bot): + await ticommand.finish(ti()) + + +@licommand.handle() +async def licommandhandler(bot: Bot): + await licommand.finish(li()) + + +@sccommand.handle() +async def schandler(bot: Bot, event: Event): + args = str(event.get_message())[3:].strip() + await sccommand.finish(sc(args.lower())) diff --git a/plugins/nonebot_plugin_cocdicer/create.py b/plugins/nonebot_plugin_cocdicer/create.py new file mode 100644 index 00000000..30b66d78 --- /dev/null +++ b/plugins/nonebot_plugin_cocdicer/create.py @@ -0,0 +1,147 @@ +import random + + +build_dict = {64: -2, 84: -1, 124: 0, 164: 1, + 204: 2, 284: 3, 364: 4, 444: 5, 524: 6} +db_dict = {-2: "-2", -1: "-1", 0: "0", 1: "1d4", + 2: "1d6", 3: "2d6", 4: "3d6", 5: "4d6", 6: "5d6"} + + +def randattr(time: int = 3, ex: int = 0): + r = 0 + for _ in range(time): + r += random.randint(1, 6) + return (r+ex)*5 + + +class Investigator(object): + def __init__(self) -> None: + self.age = 20 + self.str = randattr() + self.con = randattr() + self.siz = randattr(2, 6) + self.dex = randattr() + self.app = randattr() + self.int = randattr(2, 6) + self.pow = randattr() + self.edu = randattr(2, 6) + self.luc = randattr() + + def body_build(self) -> int: + build = self.str + self.con + for i, j in build_dict.items(): + if build <= i: + return j + return + + def db(self) -> str: + return db_dict[self.body_build()] + + def lp_max(self) -> int: + return (self.con+self.siz)//10 + + def mov(self) -> int: + r = 8 + if self.age >= 80: + r -= 5 + elif self.age >= 70: + r -= 4 + elif self.age >= 60: + r -= 3 + elif self.age >= 50: + r -= 2 + elif self.age >= 40: + r -= 1 + if self.str < self.siz and self.dex < self.siz: + return r-1 + elif self.str > self.siz and self.dex > self.siz: + return r+1 + else: + return r + + def edu_up(self) -> str: + edu_check = random.randint(1, 100) + if edu_check > self.edu: + edu_en = random.randint(1, 10) + self.edu += edu_en + else: + return "教育成长检定D100=%d,小于%d,无增长。" % (edu_check, self.edu) + if self.edu > 99: + self.edu = 99 + return "教育成长检定D100=%d,成长1D10=%d,成长到了最高值99!" % (edu_check, edu_en) + else: + return "教育成长检定D100=%d,成长1D10=%d,成长到了%d" % (edu_check, edu_en, self.edu) + + def edu_ups(self, times) -> str: + r = "" + for _ in range(times): + r += self.edu_up() + return r + + def sum_down(self, sum) -> str: + if self.str + self.con + self.dex-45 < sum: + self.str = 15 + self.con = 15 + self.dex = 15 + else: + str_lost = random.randint(0, min(sum, self.str-15)) + while sum - str_lost > self.con + self.dex-30: + str_lost = random.randint(0, min(sum, self.str-15)) + self.str -= str_lost + sum -= str_lost + con_lost = random.randint(0, min(sum, self.con-15)) + while sum - con_lost > self.dex-15: + con_lost = random.randint(0, min(sum, self.con-15)) + self.con -= con_lost + sum -= con_lost + self.dex -= sum + return + + def age_change(self, age: int = 20) -> str: + if age < 15: + return "年龄过小,无法担当调查员" + elif age >= 90: + return "该调查员已经作古。" + self.age = age + if 15 <= age < 20: + self.str -= 5 + self.siz -= 5 + self.edu -= 5 + luc = randattr() + self.luc = luc if luc > self.luc else self.luc + return "力量、体型、教育值-5,幸运增强判定一次" + elif age < 40: + self.edu_up() + return "教育增强判定一次" + elif age < 50: + self.app -= 5 + self.sum_down(5) + self.edu_ups(2) + return "外貌-5,力量、体型、敏捷合计降低5,教育增强判定两次" + elif age < 60: + self.app -= 10 + self.sum_down(10) + self.edu_ups(3) + return "外貌-10,力量、体型、敏捷合计降低10,教育增强判定三次" + elif age < 70: + self.app -= 15 + self.sum_down(20) + self.edu_ups(4) + return "外貌-15,力量、体型、敏捷合计降低20,教育增强判定四次" + elif age < 80: + self.app -= 20 + self.sum_down(40) + self.edu_ups(4) + return "外貌-20,力量、体型、敏捷合计降低40,教育增强判定四次" + elif age < 90: + self.app -= 25 + self.sum_down(80) + self.edu_ups(4) + return "外貌-25,力量、体型、敏捷合计降低80,教育增强判定四次" + + def __repr__(self) -> str: + return "调查员 年龄:%d\n力量:%d 体质:%d 体型:%d\n敏捷:%d 外貌:%d 智力:%d\n意志:%d 教育:%d 幸运:%d\nDB:%s 生命值:%d 移动速度:%d" % ( + self.age, self.str, self.con, self.siz, self.dex, self.app, self.int, self.pow, self.edu, self.luc, self.db(), self.lp_max(), self.mov()) + + def output(self) -> str: + return self.__repr__() diff --git a/plugins/nonebot_plugin_cocdicer/data_source.py b/plugins/nonebot_plugin_cocdicer/data_source.py new file mode 100644 index 00000000..3e2bb074 --- /dev/null +++ b/plugins/nonebot_plugin_cocdicer/data_source.py @@ -0,0 +1,249 @@ +# 参考[OlivaDiceDocs](https://oliva.dicer.wiki/userdoc)实现的nonebot2骰娘插件 +import re +import random +from .messages import * + + +class Mylist(list): + def next(self, index: int): + if index < self.__len__()-1: + return self[index+1] + else: + return "" + + +def help_message(): + return main_help_message + + +def dhr(t, o): + if t == 0 and o == 0: + return 100 + else: + return t*10+o + + +def st(): + result = random.randint(1, 20) + if result < 4: + rstr = "右腿" + elif result < 7: + rstr = "左腿" + elif result < 11: + rstr = "腹部" + elif result < 16: + rstr = "胸部" + elif result < 18: + rstr = "右臂" + elif result < 20: + rstr = "左臂" + elif result < 21: + rstr = "头部" + return "D20=%d:命中了%s" % (result, rstr) + + +def en(arg: str) -> str: + try: + arg = int(arg) + except: + return en_help_message + check = random.randint(1, 100) + if check > arg or check > 95: + plus = random.randint(1, 10) + r = "判定值%d,判定成功,技能成长%d+%d=%d" % (check, arg, plus, arg+plus) + return r + "\n温馨提示:如果技能提高到90%或更高,增加2D6理智点数。" + else: + return "判定值%d,判定失败,技能无成长。" % check + + +class Dices(object): + def __init__(self): + self.dices = 1 + self.faces = 100 + self.a = False + self.anum = 0 + self.h = False + self.times = 1 + self.bp = 0 + self._bp_result = "" + self._tens_place = None + self._ones_place = None + self._head = "" + self._mid = "" + self._end = "" + self.result = 0 + self.a_check_mode = self.a_check + self._a_check_result = "" + self.ex_dice = None + self.ex_dice_type = 1 + self._ex_result = "" + + def real_dice(self): + if self.faces == 100: + self._tens_place = random.randint(0, 9) + self._ones_place = random.randint(0, 9) + self.result += dhr(self._tens_place, self._ones_place) + return dhr(self._tens_place, self._ones_place) + else: + rint = random.randint(1, self.faces) + self.result += rint + return rint + + def bp_dice(self): + if not self.bp or self.faces != 100 or self.dices != 1: + self._bp_result = "" + return self._bp_result + self._bp_result = " -> 十位:%d,个位:%d" % ( + self._tens_place, self._ones_place) + bp = self.bp + while bp > 0: + bd = random.randint(0, 9) + self._bp_result += ",奖励:%d" % bd + if dhr(bd, self._ones_place) < dhr(self._tens_place, self._ones_place): + self._tens_place = bd + self.result = dhr(self._tens_place, self._ones_place) + bp -= 1 + while bp < 0: + bd = random.randint(0, 9) + self._bp_result += ",惩罚:%d" % bd + if dhr(bd, self._ones_place) > dhr(self._tens_place, self._ones_place): + self._tens_place = bd + self.set_result() + bp += 1 + return self._bp_result + + def a_check(self): + if not (self.a and self.anum and self.faces == 100 and self.dices == 1): + self._a_check_result = "" + return self._a_check_result + if self.result == 100: + self._a_check_result = " 大失败!" + elif self.anum < 50 and self.result > 95: + self._a_check_result = "\n检定值%d %d>95 大失败!" % ( + self.anum, self.result) + elif self.result == 1: + self._a_check_result = " 大成功!" + elif self.result <= self.anum // 5: + self._a_check_result = "\n检定值%d %d≤%d 极难成功" % ( + self.anum, self.result, self.anum // 5) + elif self.result <= self.anum // 2: + self._a_check_result = "\n检定值%d %d≤%d 困难成功" % ( + self.anum, self.result, self.anum // 2) + elif self.result <= self.anum: + self._a_check_result = "\n检定值%d %d≤%d 成功" % ( + self.anum, self.result, self.anum) + else: + self._a_check_result = "\n检定值%d %d>%d 失败" % ( + self.anum, self.result, self.anum) + return self._a_check_result + + def xdy(self): + self.result = 0 + if self.dices == 1: + self.real_dice() + self.bp_dice() + self.a_check() + return "" + else: + dice_results = [] + for _ in range(self.dices): + dice_result = self.real_dice() + dice_results.append(str(dice_result)) + return "(%s)" % "+".join(dice_results) + + def _ex_handle(self): + if not self.ex_dice: + self._ex_result = "" + elif type(self.ex_dice) == int: + ex_result = self.ex_dice_type*self.ex_dice + self._ex_result = "%s%s%d" % (str( + self.result) if self.dices == 1 else "", "+" if self.ex_dice_type == 1 else "-", self.ex_dice) + self.result += ex_result + elif type(self.ex_dice) == Dices: + self.ex_dice.roll + ex_result = self.ex_dice_type*self.ex_dice.result + self._ex_result = "%s%s%d" % (str( + self.result) if self.dices == 1 else "", "+" if self.ex_dice_type == 1 else "-", self.ex_dice) + self.result += ex_result + return self._ex_result + + def roll(self): + r = "%d次投掷:" % self.times + if self.times != 1: + r += "\n" + for _ in range(self.times): + xdyr = self.xdy() + self._ex_handle() + self._head = "%sD%d%s=" % ( + "" if self.dices == 1 else str(self.dices), + self.faces, + "" if not self.ex_dice else ( + ("+" if self.ex_dice_type == 1 else "-") + str(self.ex_dice) if type(self.ex_dice) == int else (str(self.ex_dice.dices)+"D"+self.ex_dice.faces)) + ) + self._mid = "%s%s=" % (xdyr, self._ex_result) + self._end = "%d%s%s" % ( + self.result, self._bp_result, self._a_check_result) + r += "%s%s%s" % (self._head, self._mid if self.dices != + 1 or self.ex_dice else "", self._end) + self.times -= 1 + if self.times: + r += "\n" + return r + + +def prework(args: list, start=0): + for i in range(start, len(args), 1): + if not re.search("\\d+", args[i]) and len(args[i]) > 1: + p = args.pop(i) + for j in list(p): + args.insert(i, j) + i += 1 + if prework(args, i): + break + return True + + +def rd(arg: str): + try: + h = False + dices = Dices() + args = re.split("(\\d+)", arg.lower()) + prework(args) + args = Mylist(args) + for i in range(len(args)): + if args[i] == "a": + dices.a = True + elif args[i] == "#" and re.search("\\d+", args.next(i)): + dices.times = int(args[i+1]) + elif args[i] == "b": + dices.bp += 1 + elif args[i] == "p": + dices.bp -= 1 + elif args[i] == "d" and re.search("\\d+", args.next(i)): + dices.faces = int(args[i+1]) + elif args[i] == " " and re.search("\\d+", args.next(i)) and dices.a: + dices.anum = int(args[i+1]) + elif args[i] == "h": + h = True + elif re.search("\\d+", args[i]): + if args.next(i) == "d": + dices.dices = int(args[i]) + elif args[i-1] == " " and dices.a: + dices.anum = int(args[i]) + elif args[i] in ["-", "+"]: + dices.ex_dice_type = (-1 if args[i] == "-" else 1) + if args.next(i+1) == "d": + dices.ex_dice = Dices() + dices.ex_dice.dices = int(args.next(i)) + dices.ex_dice.faces = int(args.next(i+2)) + elif args.next(i): + dices.ex_dice = int(args.next(i)) + if h: + return [dices.roll()] + return dices.roll() + except: + return r_help_message + + +if __name__ == "__main__": + rd("2d100") diff --git a/plugins/nonebot_plugin_cocdicer/madness.py b/plugins/nonebot_plugin_cocdicer/madness.py new file mode 100644 index 00000000..6fd85bb2 --- /dev/null +++ b/plugins/nonebot_plugin_cocdicer/madness.py @@ -0,0 +1,36 @@ +import random + +from .messages import temporary_madness, madness_end, phobias, manias + + +def ti(): + i = random.randint(1, 10) + r = "临时疯狂判定1D10=%d\n" % i + r += temporary_madness[i-1] + if i == 9: + j = random.randint(1, 100) + r += "\n恐惧症状为:\n" + r += phobias[j-1] + elif i == 10: + j = random.randint(1, 100) + r += "\n狂躁症状为:\n" + r += manias[j-1] + r += "\n该症状将会持续1D10=%d" % random.randint(1, 10) + return r + + +def li(): + i = random.randint(1, 10) + r = "总结疯狂判定1D10=%d\n" % i + r += madness_end[i-1] + if i in [2, 3, 6, 9, 10]: + r += "\n调查员将在1D10=%d小时后醒来" % random.randint(1, 10) + if i == 9: + j = random.randint(1, 100) + r += "\n恐惧症状为:\n" + r += phobias[j-1] + elif i == 10: + j = random.randint(1, 100) + r += "\n狂躁症状为:\n" + r += manias[j-1] + return r diff --git a/plugins/nonebot_plugin_cocdicer/messages.py b/plugins/nonebot_plugin_cocdicer/messages.py new file mode 100644 index 00000000..20883424 --- /dev/null +++ b/plugins/nonebot_plugin_cocdicer/messages.py @@ -0,0 +1,258 @@ +main_help_message: str = "本骰娘由nonebot2强力驱动\n" \ + ".r 投掷指令 todo\n" \ + " d 制定骰子面数\n" \ + " a 检定\n" \ + " h 暗骰\n" \ + " # 多轮检定\n" \ + " bp 奖励骰&惩罚骰\n" \ + " +/- 附加计算 todo\n" \ + ".sc 疯狂检定\n" \ + ".st 射击命中判定\n" \ + ".ti 临时疯狂症状\n" \ + ".li 总结疯狂症状\n" \ + ".coc coc角色作成\n" \ + ".en 技能成长" +r_help_message: str = ".r[dah#bp] a_number [+/-]ex_number\n" \ + "d:骰子设定指令,标准格式为xdy,x为骰子数量y为骰子面数;\n" \ + "a:检定指令,根据后续a_number设定数值检定;\n" \ + "h:暗骰指令,骰子结构将会私聊发送给该指令者;\n" \ + "#:多轮投掷指令,#后接数字即可设定多轮投掷;\n" \ + "bp:奖励骰与惩罚骰;\n" \ + "+/-:附加计算指令,目前仅支持数字\n" \ + "示例:\n" \ + ".r#2bba 70:两次投掷 1D100 ,附加两个奖励骰,判定值为70\n" \ + ".rah:D100暗骰,由于没有 a_number 参数,判定将被忽略\n" \ + ".ra2d8+10 70:2D8+10,由于非D100,判定将被忽略" +sc_help_message: str = ".sc success/failure san_number\n" \ + "success:判定成功降低 san 值,支持 x 或 xdy 语法( x 与 y 为数字);\n" \ + "failure:判定失败降低 san 值,支持语法如上;\n" \ + "san_number:当前 san 值。" +en_help_message: str = ".en skill_level\nskill_level:需要成长的技能当前等级。" +temporary_madness = [ + "1) 失忆: 调查员会发现自己身处于一个安全的地点却没有任何来到这里的记忆。例如,调查员前一刻还在家中吃着早饭,下一刻就已经直面着不知名的怪物。这将会持续1D10轮。", + "2) 假性残疾:调查员陷入了心理性的失明,失聪以及躯体缺失感中,持续1D10轮。", + "3) 暴力倾向: 调查员陷入了六亲不认的暴力行为中,对周围的敌人与友方进行着无差别的攻击,持续1D10轮。", + "4) 偏执: 调查员陷入了严重的偏执妄想之中,持续1D10轮。有人在暗中窥视着他们,同伴中有人背叛了他们,没有人可以信任,万事皆虚。", + "5) 人际依赖: 守密人适当参考调查员的背景中重要之人的条目,调查员因为一些原因而降他人误认为了他重要的人并且努力的会与那个人保持那种关系,持续1D10轮。", + "6) 昏厥: 调查员当场昏倒,并需要1D10轮才能苏醒。", + "7) 逃避行为: 调查员会用任何的手段试图逃离现在所处的位置,即使这意味着开走唯一一辆交通工具并将其它人抛诸脑后,调查员会试图逃离1D10轮。", + "8) 竭嘶底里:调查员表现出大笑,哭泣,嘶吼,害怕等的极端情绪表现,持续1D10轮。", + "9) 恐惧: 调查员通过一次D100或者由守密人选择,来从恐惧症状表中选择一个恐惧源,就算这一恐惧的事物是并不存在的,调查员的症状会持续1D10轮。", + "10) 躁狂: 调查员通过一次D100或者由守密人选择,来从躁狂症状表中选择一个躁狂的诱因,这个症状将会持续1D10轮。" +] +madness_end = [ + "1) 失忆(Amnesia):回过神来,调查员们发现自己身处一个陌生的地方,并忘记了自己是谁。记忆会随时间恢复。", + "2) 被窃(Robbed):调查员在1D10小时后恢复清醒,发觉自己被盗,身体毫发无损。如果调查员携带着宝贵之物(见调查员背景),做幸运检定来决定其是否被盗。所有有价值的东西无需检定自动消失。", + "3) 遍体鳞伤(Battered):调查员在1D10小时后恢复清醒,发现自己身上满是拳痕和瘀伤。生命值减少到疯狂前的一半,但这不会造成重伤。调查员没有被窃。这种伤害如何持续到现在由守秘人决定。", + "4) 暴力倾向(Violence):调查员陷入强烈的暴力与破坏欲之中。调查员回过神来可能会理解自己做了什么也可能毫无印象。调查员对谁或何物施以暴力,他们是杀人还是仅仅造成了伤害,由守秘人决定。", + "5) 极端信念(Ideology/Beliefs):查看调查员背景中的思想信念,调查员会采取极端和疯狂的表现手段展示他们的思想信念之一。比如一个信教者会在地铁上高声布道。", + "6) 重要之人(Significant People):考虑调查员背景中的重要之人,及其重要的原因。在1D10小时或更久的时间中,调查员将不顾一切地接近那个人,并为他们之间的关系做出行动。", + "7) 被收容(Institutionalized):调查员在精神病院病房或警察局牢房中回过神来,他们可能会慢慢回想起导致自己被关在这里的事情。", + "8) 逃避行为(Flee in panic):调查员恢复清醒时发现自己在很远的地方,也许迷失在荒郊野岭,或是在驶向远方的列车或长途汽车上。", + "9) 恐惧(Phobia):调查员患上一个新的恐惧症状。在表Ⅸ:恐惧症状表上骰1个D100来决定症状,或由守秘人选择一个。调查员在1D10小时后回过神来,并开始为避开恐惧源而采取任何措施。", + "10) 狂躁(Mania):调查员患上一个新的狂躁症状。在表Ⅹ:狂躁症状表上骰1个D100来决定症状,或由守秘人选择一个。调查员会在1D10小时后恢复理智。在这次疯狂发作中,调查员将完全沉浸于其新的狂躁症状。这症状是否会表现给旁人则取决于守秘人和此调查员。" +] +phobias = [ + "1) 洗澡恐惧症(Ablutophobia):对于洗涤或洗澡的恐惧。", + "2) 恐高症(Acrophobia):对于身处高处的恐惧。", + "3) 飞行恐惧症(Aerophobia):对飞行的恐惧。", + "4) 广场恐惧症(Agoraphobia):对于开放的(拥挤)公共场所的恐惧。", + "5) 恐鸡症(Alektorophobia):对鸡的恐惧。", + "6) 大蒜恐惧症(Alliumphobia):对大蒜的恐惧。", + "7) 乘车恐惧症(Amaxophobia):对于乘坐地面载具的恐惧。", + "8) 恐风症(Ancraophobia):对风的恐惧。", + "9) 男性恐惧症(Androphobia):对于成年男性的恐惧。", + "10) 恐英症(Anglophobia):对英格兰或英格兰文化的恐惧。", + "11) 恐花症(Anthophobia):对花的恐惧。", + "12) 截肢者恐惧症(Apotemnophobia):对截肢者的恐惧。", + "13) 蜘蛛恐惧症(Arachnophobia):对蜘蛛的恐惧。", + "14) 闪电恐惧症(Astraphobia):对闪电的恐惧。", + "15) 废墟恐惧症(Atephobia):对遗迹或残址的恐惧。", + "16) 长笛恐惧症(Aulophobia):对长笛的恐惧。", + "17) 细菌恐惧症(Bacteriophobia):对细菌的恐惧。", + "18) 导弹/子弹恐惧症(Ballistophobia):对导弹或子弹的恐惧。", + "19) 跌落恐惧症(Basophobia):对于跌倒或摔落的恐惧。", + "20) 书籍恐惧症(Bibliophobia):对书籍的恐惧。", + "21) 植物恐惧症(Botanophobia):对植物的恐惧。", + "22) 美女恐惧症(Caligynephobia):对美貌女性的恐惧。", + "23) 寒冷恐惧症(Cheimaphobia):对寒冷的恐惧。", + "24) 恐钟表症(Chronomentrophobia):对于钟表的恐惧。", + "25) 幽闭恐惧症(Claustrophobia):对于处在封闭的空间中的恐惧。", + "26) 小丑恐惧症(Coulrophobia):对小丑的恐惧。", + "27) 恐犬症(Cynophobia):对狗的恐惧。", + "28) 恶魔恐惧症(Demonophobia):对邪灵或恶魔的恐惧。", + "29) 人群恐惧症(Demophobia):对人群的恐惧。", + "30) 牙科恐惧症①(Dentophobia):对牙医的恐惧。", + "31) 丢弃恐惧症(Disposophobia):对于丢弃物件的恐惧(贮藏癖)。", + "32) 皮毛恐惧症(Doraphobia):对动物皮毛的恐惧。", + "33) 过马路恐惧症(Dromophobia):对于过马路的恐惧。", + "34) 教堂恐惧症(Ecclesiophobia):对教堂的恐惧。", + "35) 镜子恐惧症(Eisoptrophobia):对镜子的恐惧。", + "36) 针尖恐惧症(Enetophobia):对针或大头针的恐惧。", + "37) 昆虫恐惧症(Entomophobia):对昆虫的恐惧。", + "38) 恐猫症(Felinophobia):对猫的恐惧。", + "39) 过桥恐惧症(Gephyrophobia):对于过桥的恐惧。", + "40) 恐老症(Gerontophobia):对于老年人或变老的恐惧。", + "41) 恐女症(Gynophobia):对女性的恐惧。", + "42) 恐血症(Haemaphobia):对血的恐惧。", + "43) 宗教罪行恐惧症(Hamartophobia):对宗教罪行的恐惧。", + "44) 触摸恐惧症(Haphophobia):对于被触摸的恐惧。", + "45) 爬虫恐惧症(Herpetophobia):对爬行动物的恐惧。", + "46) 迷雾恐惧症(Homichlophobia):对雾的恐惧。", + "47) 火器恐惧症(Hoplophobia):对火器的恐惧。", + "48) 恐水症(Hydrophobia):对水的恐惧。", + "49) 催眠恐惧症(Hypnophobia):对于睡眠或被催眠的恐惧。", + "50) 白袍恐惧症(Iatrophobia):对医生的恐惧。", + "51) 鱼类恐惧症(Ichthyophobia):对鱼的恐惧。", + "52) 蟑螂恐惧症(Katsaridaphobia):对蟑螂的恐惧。", + "53) 雷鸣恐惧症(Keraunophobia):对雷声的恐惧。", + "54) 蔬菜恐惧症(Lachanophobia):对蔬菜的恐惧。", + "55) 噪音恐惧症(Ligyrophobia):对刺耳噪音的恐惧。", + "56) 恐湖症(Limnophobia):对湖泊的恐惧。", + "57) 机械恐惧症(Mechanophobia):对机器或机械的恐惧。", + "58) 巨物恐惧症(Megalophobia):对于庞大物件的恐惧。", + "59) 捆绑恐惧症(Merinthophobia):对于被捆绑或紧缚的恐惧。", + "60) 流星恐惧症(Meteorophobia):对流星或陨石的恐惧。", + "61) 孤独恐惧症(Monophobia):对于一人独处的恐惧。", + "62) 不洁恐惧症(Mysophobia):对污垢或污染的恐惧。", + "63) 黏液恐惧症(Myxophobia):对黏液(史莱姆)的恐惧。", + "64) 尸体恐惧症(Necrophobia):对尸体的恐惧。", + "65) 数字8恐惧症(Octophobia):对数字8的恐惧。", + "66) 恐牙症(Odontophobia):对牙齿的恐惧。", + "67) 恐梦症(Oneirophobia):对梦境的恐惧。", + "68) 称呼恐惧症(Onomatophobia):对于特定词语的恐惧。", + "69) 恐蛇症(Ophidiophobia):对蛇的恐惧。", + "70) 恐鸟症(Ornithophobia):对鸟的恐惧。", + "71) 寄生虫恐惧症(Parasitophobia):对寄生虫的恐惧。", + "72) 人偶恐惧症(Pediophobia):对人偶的恐惧。", + "73) 吞咽恐惧症(Phagophobia):对于吞咽或被吞咽的恐惧。", + "74) 药物恐惧症(Pharmacophobia):对药物的恐惧。", + "75) 幽灵恐惧症(Phasmophobia):对鬼魂的恐惧。", + "76) 日光恐惧症(Phenogophobia):对日光的恐惧。", + "77) 胡须恐惧症(Pogonophobia):对胡须的恐惧。", + "78) 河流恐惧症(Potamophobia):对河流的恐惧。", + "79) 酒精恐惧症(Potophobia):对酒或酒精的恐惧。", + "80) 恐火症(Pyrophobia):对火的恐惧。", + "81) 魔法恐惧症(Rhabdophobia):对魔法的恐惧。", + "82) 黑暗恐惧症(Scotophobia):对黑暗或夜晚的恐惧。", + "83) 恐月症(Selenophobia):对月亮的恐惧。", + "84) 火车恐惧症(Siderodromophobia):对于乘坐火车出行的恐惧。", + "85) 恐星症(Siderophobia):对星星的恐惧。", + "86) 狭室恐惧症(Stenophobia):对狭小物件或地点的恐惧。", + "87) 对称恐惧症(Symmetrophobia):对对称的恐惧。", + "88) 活埋恐惧症(Taphephobia):对于被活埋或墓地的恐惧。", + "89) 公牛恐惧症(Taurophobia):对公牛的恐惧。", + "90) 电话恐惧症(Telephonophobia):对电话的恐惧。", + "91) 怪物恐惧症①(Teratophobia):对怪物的恐惧。", + "92) 深海恐惧症(Thalassophobia):对海洋的恐惧。", + "93) 手术恐惧症(Tomophobia):对外科手术的恐惧。", + "94) 十三恐惧症(Triskadekaphobia):对数字13的恐惧症。", + "95) 衣物恐惧症(Vestiphobia):对衣物的恐惧。", + "96) 女巫恐惧症(Wiccaphobia):对女巫与巫术的恐惧。", + "97) 黄色恐惧症(Xanthophobia):对黄色或“黄”字的恐惧。", + "98) 外语恐惧症(Xenoglossophobia):对外语的恐惧。", + "99) 异域恐惧症(Xenophobia):对陌生人或外国人的恐惧。", + "100) 动物恐惧症(Zoophobia):对动物的恐惧。" +] +manias = [ + "1) 沐浴癖(Ablutomania):执着于清洗自己。", + "2) 犹豫癖(Aboulomania):病态地犹豫不定。", + "3) 喜暗狂(Achluomania):对黑暗的过度热爱。", + "4) 喜高狂(Acromaniaheights):狂热迷恋高处。", + "5) 亲切癖(Agathomania):病态地对他人友好。", + "6) 喜旷症(Agromania):强烈地倾向于待在开阔空间中。", + "7) 喜尖狂(Aichmomania):痴迷于尖锐或锋利的物体。", + "8) 恋猫狂(Ailuromania):近乎病态地对猫友善。", + "9) 疼痛癖(Algomania):痴迷于疼痛。", + "10) 喜蒜狂(Alliomania):痴迷于大蒜。", + "11) 乘车癖(Amaxomania):痴迷于乘坐车辆。", + "12) 欣快癖(Amenomania):不正常地感到喜悦。", + "13) 喜花狂(Anthomania):痴迷于花朵。", + "14) 计算癖(Arithmomania):狂热地痴迷于数字。", + "15) 消费癖(Asoticamania):鲁莽冲动地消费。", + "16) 隐居癖(Automania):过度地热爱独自隐居(原文如此,存疑,Automania实际上是恋车癖)。", + "17) 芭蕾癖(Balletmania):痴迷于芭蕾舞。", + "18) 窃书癖(Biliokleptomania):无法克制偷窃书籍的冲动。", + "19) 恋书狂(Bibliomania):痴迷于书籍和/或阅读。", + "20) 磨牙癖(Bruxomania):无法克制磨牙的冲动。", + "21) 灵臆症(Cacodemomania):病态地坚信自己已被一个邪恶的灵体占据。", + "22) 美貌狂(Callomania):痴迷于自身的美貌。", + "23) 地图狂(Cartacoethes):在何时何处都无法控制查阅地图的冲动。", + "24) 跳跃狂(Catapedamania):痴迷于从高处跳下。", + "25) 喜冷症(Cheimatomania):对寒冷或寒冷的物体的反常喜爱。", + "26) 舞蹈狂(Choreomania):无法控制地起舞或发颤。", + "27) 恋床癖(Clinomania):过度地热爱待在床上。", + "28) 恋墓狂(Coimetormania):痴迷于墓地。", + "29) 色彩狂(Coloromania):痴迷于某种颜色。", + "30) 小丑狂(Coulromania):痴迷于小丑。", + "31) 恐惧狂(Countermania):执着于经历恐怖的场面。", + "32) 杀戮癖(Dacnomania):痴迷于杀戮。", + "33) 魔臆症(Demonomania):病态地坚信自己已被恶魔附身。", + "34) 抓挠癖(Dermatillomania):执着于抓挠自己的皮肤。", + "35) 正义狂(Dikemania):痴迷于目睹正义被伸张。", + "36) 嗜酒狂(Dipsomania):反常地渴求酒精。", + "37) 毛皮狂(Doramania):痴迷于拥有毛皮。(存疑)", + "38) 赠物癖(Doromania):痴迷于赠送礼物。", + "39) 漂泊症(Drapetomania):执着于逃离。", + "40) 漫游癖(Ecdemiomania):执着于四处漫游。", + "41) 自恋狂(Egomania):近乎病态地以自我为中心或自我崇拜。", + "42) 职业狂(Empleomania):对于工作的无尽病态渴求。", + "43) 臆罪症(Enosimania):病态地坚信自己带有罪孽。", + "44) 学识狂(Epistemomania):痴迷于获取学识。", + "45) 静止癖(Eremiomania):执着于保持安静。", + "46) 乙醚上瘾(Etheromania):渴求乙醚。", + "47) 求婚狂(Gamomania):痴迷于进行奇特的求婚。", + "48) 狂笑癖(Geliomania):无法自制地,强迫性的大笑。", + "49) 巫术狂(Goetomania):痴迷于女巫与巫术。", + "50) 写作癖(Graphomania):痴迷于将每一件事写下来。", + "51) 裸体狂(Gymnomania):执着于裸露身体。", + "52) 妄想狂(Habromania):近乎病态地充满愉快的妄想(而不顾现实状况如何)。", + "53) 蠕虫狂(Helminthomania):过度地喜爱蠕虫。", + "54) 枪械狂(Hoplomania):痴迷于火器。", + "55) 饮水狂(Hydromania):反常地渴求水分。", + "56) 喜鱼癖(Ichthyomania):痴迷于鱼类。", + "57) 图标狂(Iconomania):痴迷于图标与肖像。", + "58) 偶像狂(Idolomania):痴迷于甚至愿献身于某个偶像。", + "59) 信息狂(Infomania):痴迷于积累各种信息与资讯。", + "60) 射击狂(Klazomania):反常地执着于射击。", + "61) 偷窃癖(Kleptomania):反常地执着于偷窃。", + "62) 噪音癖(Ligyromania):无法自制地执着于制造响亮或刺耳的噪音。", + "63) 喜线癖(Linonomania):痴迷于线绳。", + "64) 彩票狂(Lotterymania):极端地执着于购买彩票。", + "65) 抑郁症(Lypemania):近乎病态的重度抑郁倾向。", + "66) 巨石狂(Megalithomania):当站在石环中或立起的巨石旁时,就会近乎病态地写出各种奇怪的创意。", + "67) 旋律狂(Melomania):痴迷于音乐或一段特定的旋律。", + "68) 作诗癖(Metromania):无法抑制地想要不停作诗。", + "69) 憎恨癖(Misomania):憎恨一切事物,痴迷于憎恨某个事物或团体。", + "70) 偏执狂(Monomania):近乎病态地痴迷与专注某个特定的想法或创意。", + "71) 夸大癖(Mythomania):以一种近乎病态的程度说谎或夸大事物。", + "72) 臆想症(Nosomania):妄想自己正在被某种臆想出的疾病折磨。", + "73) 记录癖(Notomania):执着于记录一切事物(例如摄影)。", + "74) 恋名狂(Onomamania):痴迷于名字(人物的、地点的、事物的)。", + "75) 称名癖(Onomatomania):无法抑制地不断重复某个词语的冲动。", + "76) 剔指癖(Onychotillomania):执着于剔指甲。", + "77) 恋食癖(Opsomania):对某种食物的病态热爱。", + "78) 抱怨癖(Paramania):一种在抱怨时产生的近乎病态的愉悦感。", + "79) 面具狂(Personamania):执着于佩戴面具。", + "80) 幽灵狂(Phasmomania):痴迷于幽灵。", + "81) 谋杀癖(Phonomania):病态的谋杀倾向。", + "82) 渴光癖(Photomania):对光的病态渴求。", + "83) 背德癖(Planomania):病态地渴求违背社会道德(原文如此,存疑,Planomania实际上是漂泊症)。", + "84) 求财癖(Plutomania):对财富的强迫性的渴望。", + "85) 欺骗狂(Pseudomania):无法抑制的执着于撒谎。", + "86) 纵火狂(Pyromania):执着于纵火。", + "87) 提问狂(Questiong-Asking Mania):执着于提问。", + "88) 挖鼻癖(Rhinotillexomania):执着于挖鼻子。", + "89) 涂鸦癖(Scribbleomania):沉迷于涂鸦。", + "90) 列车狂(Siderodromomania):认为火车或类似的依靠轨道交通的旅行方式充满魅力。", + "91) 臆智症(Sophomania):臆想自己拥有难以置信的智慧。", + "92) 科技狂(Technomania):痴迷于新的科技。", + "93) 臆咒狂(Thanatomania):坚信自己已被某种死亡魔法所诅咒。", + "94) 臆神狂(Theomania):坚信自己是一位神灵。", + "95) 抓挠癖(Titillomaniac):抓挠自己的强迫倾向。", + "96) 手术狂(Tomomania):对进行手术的不正常爱好。", + "97) 拔毛癖(Trichotillomania):执着于拔下自己的头发。", + "98) 臆盲症(Typhlomania):病理性的失明。", + "99) 嗜外狂(Xenomania):痴迷于异国的事物。", + "100) 喜兽癖(Zoomania):对待动物的态度近乎疯狂地友好。" +] diff --git a/plugins/nonebot_plugin_cocdicer/san_check.py b/plugins/nonebot_plugin_cocdicer/san_check.py new file mode 100644 index 00000000..1b8b2570 --- /dev/null +++ b/plugins/nonebot_plugin_cocdicer/san_check.py @@ -0,0 +1,38 @@ +from .data_source import Dices +from .messages import sc_help_message + +import re + + +def number_or_dice(arg: str): + if "d" in arg: + d = Dices() + if dices := re.search(r"\d+d", arg): + d.dices = int(dices.group()[:-1]) + if faces := re.search(r"d\d+", arg): + d.faces = int(faces.group()[1:]) + d.roll() + return d + else: + return int(arg) + + +def sc(arg: str) -> str: + a_num = re.search(r" \d+", arg) + success = re.search(r"\d*d\d+|\d+", arg) + failure = re.search(r"[\/]+(\d*d\d+|\d+)", arg) + if not (a_num and success and failure): + return sc_help_message + check_dice = Dices() + check_dice.a = True + check_dice.anum = int(a_num.group()[1:]) + success = number_or_dice(success.group()) + failure = number_or_dice(failure.group()[1:]) + r = "San Check" + check_dice.roll()[4:] + result = success if check_dice.result <= check_dice.anum else failure + r += "\n理智降低了" + if type(result) == int: + r += "%d点" % result + else: + r = r + result._head + str(result.result) + return r diff --git a/plugins/nonebot_plugin_manager/__init__.py b/plugins/nonebot_plugin_manager/__init__.py new file mode 100644 index 00000000..30a2c3f5 --- /dev/null +++ b/plugins/nonebot_plugin_manager/__init__.py @@ -0,0 +1,110 @@ +from nonebot.plugin import on_shell_command, get_loaded_plugins, export +from nonebot.matcher import Matcher +from nonebot.typing import T_State +from nonebot.exception import IgnoredException +from nonebot.message import run_preprocessor +from nonebot.adapters.cqhttp import Event, Bot, GroupMessageEvent, PrivateMessageEvent +from configs.config import plugins2name_dict +from models.ban_user import BanUser +from .data import ( + block_plugin, + unblock_plugin, + get_group_plugin_list, + auto_update_plugin_list, +) +from .parser import npm_parser + +# 导出给其他插件使用 +export = export() +export.block_plugin = block_plugin +export.unblock_plugin = unblock_plugin +export.unblock_plugin = unblock_plugin +export.get_group_plugin_list = get_group_plugin_list + +# 注册 shell_like 事件响应器 +plugin_manager = on_shell_command("npm", parser=npm_parser, priority=1) + + +# 在 Matcher 运行前检测其是否启用 +@run_preprocessor +async def _(matcher: Matcher, bot: Bot, event: Event, state: T_State): + plugin = matcher.module + group_id = _get_group_id(event) + loaded_plugin_list = _get_loaded_plugin_list() + plugin_list = auto_update_plugin_list(loaded_plugin_list) + + # 无视本插件的 Matcher + if plugin not in plugins2name_dict or matcher.priority in [1, 9] or await BanUser.isban(event.user_id): + return + try: + if isinstance(event, PrivateMessageEvent) and plugin_list[plugin]["default"]: + return + except: + pass + + # print(matcher.module) + # print(f'plugin_list[plugin]["default"] = {plugin_list[plugin]["default"]}') + # print(f'{matcher.module} -> this is hook') + if not plugin_list[plugin]["default"]: + if event.message_type == 'group': + await bot.send_group_msg(group_id=event.group_id, message='此功能正在维护...') + else: + await bot.send_private_msg(user_id=event.user_id, message='此功能正在维护...') + raise IgnoredException(f"Nonebot Plugin Manager has blocked {plugin} !") + # print(plugin_list[plugin]) + # print(group_id) + # print(plugin_list[plugin][group_id]) + # print(not plugin_list[plugin][group_id]) + if group_id in plugin_list[plugin]: + if not plugin_list[plugin][group_id]: + if plugin != 'ai' and matcher.type == 'message': + await bot.send_group_msg(group_id=event.group_id, message='该群未开启此功能..') + raise IgnoredException(f"Nonebot Plugin Manager has blocked {plugin} !") + + +@plugin_manager.handle() +async def _(bot: Bot, event: Event, state: T_State): + args = state["args"] + group_id = _get_group_id(event) + is_admin = _is_admin(event) + is_superuser = _is_superuser(bot, event) + if group_id == 0: + group_id = 'default' + + if hasattr(args, "handle"): + await plugin_manager.finish(args.handle(args, group_id, is_admin, is_superuser)) + + +# 获取插件列表,并自动排除本插件 +def _get_loaded_plugin_list() -> list: + return list( + filter( + lambda plugin: plugin != "nonebot_plugin_manager", + map(lambda plugin: plugin.name, get_loaded_plugins()), + ) + ) + + +def _get_group_id(event: Event) -> str: + try: + group_id = event.group_id + except AttributeError: + group_id = "default" + return str(group_id) if group_id else "default" + + +def _is_admin(event: Event) -> bool: + return ( + event.sender.role in ["admin", "owner"] + if isinstance(event, GroupMessageEvent) + else False + ) + + +def _is_superuser(bot: Bot, event: Event) -> bool: + return str(event.user_id) in bot.config.superusers + + +plugins_dict = { + +} diff --git a/plugins/nonebot_plugin_manager/data.py b/plugins/nonebot_plugin_manager/data.py new file mode 100644 index 00000000..56c0c486 --- /dev/null +++ b/plugins/nonebot_plugin_manager/data.py @@ -0,0 +1,121 @@ +import json +import httpx +from pathlib import Path +from configs.config import plugins2name_dict + +_DATA_PATH = Path() / "data" / "manager" / "plugin_list.json" + + +def get_store_plugin_info(plugin: str) -> str: + store_plugin_list = _get_store_plugin_list() + if plugin in store_plugin_list: + plugin = store_plugin_list[plugin] + return ( + f"ID: {plugin['id']}\n" + f"Name: {plugin['name']}\n" + f"Description: {plugin['desc']}\n" + f"Version: {httpx.get('https://pypi.org/pypi/'+plugin['link']+'/json').json()['info']['version']}\n" + f"Author: {plugin['author']}\n" + f"Repo: https://github.com/{plugin['repo']}" + ) + else: + return "查无此插件!" + + +def get_group_plugin_list(group_id: str) -> dict: + plugin_list = _load_plugin_list() + group_plugin_list = {} + for plugin in plugin_list: + if group_id in plugin_list[plugin]: + group_plugin_list[plugin] = plugin_list[plugin][group_id] + else: + group_plugin_list[plugin] = plugin_list[plugin]["default"] + return group_plugin_list + + +def get_store_pulgin_list() -> str: + message = "商店插件列表如下:" + for plugin in _get_store_plugin_list(): + if plugin in _load_plugin_list() or plugin == "nonebot_plugin_manager": + message += f"\n[o] {plugin}" + else: + message += f"\n[x] {plugin}" + return message + + +def auto_update_plugin_list(loaded_plugin_list: list, keep_history: bool = False): + plugin_list = _load_plugin_list() + for plugin in loaded_plugin_list: + if plugin not in plugin_list: + plugin_list[plugin] = {"default": True} + if not keep_history: + plugin_list = { + key: plugin_list[key] for key in plugin_list if key in loaded_plugin_list + } + _dump_plugin_list(plugin_list) + return plugin_list + + +def block_plugin(group_id: str, *plugins: str): + return _update_plugin_list(group_id, True, *plugins) + + +def unblock_plugin(group_id: str, *plugins: str): + return _update_plugin_list(group_id, False, *plugins) + + +# 获取商店插件列表 +def _get_store_plugin_list() -> dict: + store_plugin_list = {} + for plugin in httpx.get( + "https://cdn.jsdelivr.net/gh/nonebot/nonebot2@master/docs/.vuepress/public/plugins.json" + ).json(): + store_plugin_list.update({plugin["id"]: plugin}) + return store_plugin_list + + +# 更新插件列表 +def _update_plugin_list(group_id: str, block: bool, *plugins: str) -> str: + plugin_list = _load_plugin_list() + message = "结果如下:" + operate = "屏蔽" if block else "启用" + for plugin in plugins: + for values in plugins2name_dict.values(): + if plugin in values: + plugin = list(plugins2name_dict.keys())[list(plugins2name_dict.values()).index(values)] + # print(plugin) + break + message += "\n" + if plugin in plugin_list: + if ( + not group_id in plugin_list[plugin] + or plugin_list[plugin][group_id] == block + ): + plugin_list[plugin][group_id] = not block + message += f"插件{plugin}{operate}成功!" + print(plugin_list[plugin][group_id]) + else: + message += f"插件{plugin}已经{operate}!" + else: + message += f"插件{plugin}不存在!" + _dump_plugin_list(plugin_list) + return message + + +# 加载插件列表 +def _load_plugin_list() -> dict: + try: + return json.load(_DATA_PATH.open("r", encoding="utf-8")) + except FileNotFoundError: + return {} + + +# 保存插件列表 +def _dump_plugin_list(plugin_list: dict): + _DATA_PATH.parent.mkdir(parents=True, exist_ok=True) + json.dump( + plugin_list, + _DATA_PATH.open("w", encoding="utf-8"), + indent=4, + separators=(",", ": "), + ) diff --git a/plugins/nonebot_plugin_manager/handle.py b/plugins/nonebot_plugin_manager/handle.py new file mode 100644 index 00000000..1900ed2f --- /dev/null +++ b/plugins/nonebot_plugin_manager/handle.py @@ -0,0 +1,138 @@ +from argparse import Namespace + +from .data import * + + +def handle_list( + args: Namespace, + group_id: str, + is_admin: bool, + is_superuser: bool, +) -> str: + message = "" + + if args.store: + if is_superuser: + return get_store_pulgin_list() + else: + return "获取商店插件列表需要超级用户权限!" + + if args.default: + if is_superuser: + group_id = "default" + message += "默认" + else: + return "获取默认插件列表需要超级用户权限!" + + if args.group: + if is_superuser: + group_id = args.group + message += f"群{args.group}" + else: + return "获取指定群插件列表需要超级用户权限!" + + message += "插件列表如下:\n" + message += "\n".join( + f"[{'o' if get_group_plugin_list(group_id)[plugin] else 'x'}] {plugin}" + for plugin in get_group_plugin_list(group_id) + ) + return message + + +def handle_block( + args: Namespace, + group_id: str, + is_admin: bool, + is_superuser: bool, +) -> str: + + if not is_admin and not is_superuser: + return "管理插件需要群管理员权限!" + + message = "" + + if args.default: + if is_superuser: + group_id = "default" + message += "默认" + else: + return "管理默认插件需要超级用户权限!" + + if args.group: + if is_superuser: + group_id = args.group + message += f"群{args.group}" + else: + return "管理指定群插件需要超级用户权限!" + + message += block_plugin(group_id, *args.plugins) + return message + + +def handle_unblock( + args: Namespace, + group_id: str, + is_admin: bool, + is_superuser: bool, +) -> str: + message = "" + + if not is_admin and not is_superuser: + return "管理插件需要群管理员权限!" + + if args.default: + if is_superuser: + group_id = "default" + message += "默认" + else: + return "管理默认插件需要超级用户权限!" + + if args.group: + if is_superuser: + group_id = args.group + message += f"群{args.group}" + else: + return "管理指定群插件需要超级用户权限!" + + message += unblock_plugin(group_id, *args.plugins) + return message + + +def handle_info( + args: Namespace, + group_id: str, + is_admin: bool, + is_superuser: bool, +) -> str: + + if not is_admin and not is_superuser: + return "管理插件需要超级权限!" + + return get_store_plugin_info(args.plugin) + + +def handle_install( + args: Namespace, + group_id: str, + is_admin: bool, + is_superuser: bool, +) -> str: + pass + + +def handle_update( + args: Namespace, + group_id: str, + is_admin: bool, + is_superuser: bool, +) -> str: + pass + + +def handle_uninstall( + args: Namespace, + group_id: str, + is_admin: bool, + is_superuser: bool, +) -> str: + pass diff --git a/plugins/nonebot_plugin_manager/parser.py b/plugins/nonebot_plugin_manager/parser.py new file mode 100644 index 00000000..71d2455d --- /dev/null +++ b/plugins/nonebot_plugin_manager/parser.py @@ -0,0 +1,60 @@ +from nonebot.rule import ArgumentParser + +from .handle import * + +npm_parser = ArgumentParser("npm", add_help=False) +npm_parser.add_argument( + "-h", "--help", action="store_true", help="show this help message and exit" +) + +npm_subparsers = npm_parser.add_subparsers() + +list_parser = npm_subparsers.add_parser("list", help="show plugin list") +list_parser.add_argument( + "-s", "--store", action="store_true", help="show plugin store list" +) +list_parser.add_argument( + "-d", "--default", action="store_true", help="show default plugin list" +) +list_parser.add_argument("-g", "--group", action="store", help="show group plugin list") +list_parser.set_defaults(handle=handle_list) + +block_parser = npm_subparsers.add_parser("block", help="block plugin") +block_parser.add_argument("plugins", nargs="*", help="plugins you want to block") +block_parser.add_argument("-d", "--default", action="store_true", help="set default") +block_parser.add_argument("-a", "--all", action="store_true", help="select all plugin") +block_parser.add_argument("-g", "--group", action="store", help="set in group") +block_parser.set_defaults(handle=handle_block) + +unblock_parser = npm_subparsers.add_parser("unblock", help="unblock plugin") +unblock_parser.add_argument("plugins", nargs="*", help="plugins you want to unblock") +unblock_parser.add_argument("-d", "--default", action="store_true", help="set default") +unblock_parser.add_argument( + "-a", "--all", action="store_true", help="select all plugin" +) +unblock_parser.add_argument("-g", "--group", action="store", help="set in group") +unblock_parser.set_defaults(handle=handle_unblock) + +info_parser = npm_subparsers.add_parser("info", help="show plugin info") +info_parser.add_argument("plugin", help="plugins you want to know") +info_parser.set_defaults(handle=handle_info) + +install_parser = npm_subparsers.add_parser("install", help="install plugin") +install_parser.add_argument("plugins", nargs="*", help="plugins you want to install") +install_parser.add_argument("-i", "--index", action="store", help="point to a mirror") +install_parser.set_defaults(handle=handle_install) + +update_parser = npm_subparsers.add_parser("update", help="update plugin") +update_parser.add_argument("plugins", nargs="*", help="plugins you want to update") +update_parser.add_argument("-a", "--all", action="store_true", help="select all plugin") +update_parser.add_argument("-i", "--index", action="store", help="point to a mirror") +update_parser.set_defaults(handle=handle_update) + +uninstall_parser = npm_subparsers.add_parser("uninstall", help="uninstall plugin") +uninstall_parser.add_argument( + "plugins", nargs="*", help="plugins you want to uninstall" +) +uninstall_parser.add_argument( + "-a", "--all", action="store_true", help="select all plugin" +) +uninstall_parser.set_defaults(handle=handle_uninstall) diff --git a/plugins/nonebot_plugin_picsearcher/__init__.py b/plugins/nonebot_plugin_picsearcher/__init__.py new file mode 100644 index 00000000..4fb2ee55 --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/__init__.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +import traceback +from typing import Dict + +from aiohttp.client_exceptions import ClientError +from nonebot.plugin import on_command, on_message +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent +from nonebot.typing import T_State +from services.log import logger +from util.utils import get_message_text, get_message_imgs +from configs.config import MAX_FIND_IMG_COUNT + +from .ex import get_des as get_des_ex +from .iqdb import get_des as get_des_iqdb +from .saucenao import get_des as get_des_sau +from .ascii2d import get_des as get_des_asc +from .trace import get_des as get_des_trace +from .yandex import get_des as get_des_yandex + + +async def get_des(url: str, mode: str, user_id: int): + """ + :param url: 图片链接 + :param mode: 图源 + :return: + """ + if mode == "iqdb": + async for msg in get_des_iqdb(url): + yield msg + elif mode == "ex": + async for msg in get_des_ex(url): + yield msg + elif mode == "trace": + async for msg in get_des_trace(url): + yield msg + elif mode == "yandex": + async for msg in get_des_yandex(url): + yield msg + elif mode.startswith("asc"): + async for msg in get_des_asc(url, user_id): + yield msg + else: + async for msg in get_des_sau(url, user_id): + yield msg + + +setu = on_command("识图", aliases={"search"}, block=True, priority=5) + + +@setu.handle() +async def handle_first_receive(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()) + imgs = get_message_imgs(event.json()) + if msg in ['帮助']: + await setu.finish('示例:\n\t识图 (图片)\n\t识图asc (图片)') + if imgs: + state["setu"] = imgs[0] + if msg: + state["mod"] = msg + + +# ex/nao/trace/iqdb/ascii2d +# @setu.got("mod", prompt="从哪里查找呢? ex/nao/trace/iqdb/ascii2d") +# async def get_func(bot: Bot, event: MessageEvent, state: dict): +# pass + +@setu.args_parser +async def get_setu(bot: Bot, event: MessageEvent, state: T_State): + imgs = get_message_imgs(event.json()) + msg = get_message_text(event.json()) + if not imgs: + await setu.reject() + if msg: + state['mod'] = msg + state["setu"] = imgs[0] + + +@setu.got("setu", prompt="图呢?") +async def get_setu(bot: Bot, event: MessageEvent, state: T_State): + """ + 发现没有的时候要发问 + :return: + """ + url: str = state["setu"] + mod: str = state["mod"] if state.get("mod") else "nao" # 模式 + try: + await bot.send(event=event, message="正在处理图片") + idx = 1 + async for msg in get_des(url, mod, event.user_id): + await bot.send(event=event, message=msg) + if idx == MAX_FIND_IMG_COUNT: + break + idx += 1 + logger.info(f"(USER {event.user_id}, GROUP " + f"{event.group_id if event.message_type != 'private' else 'private'}) 识图:{url}") + # image_data: List[Tuple] = await get_pic_from_url(url) + # await setu.finish("hso") + except IndexError: + # await bot.send(event, traceback.format_exc()) + await setu.finish("参数错误") + except ClientError: + await setu.finish("连接失败") + + +pic_map: Dict[str, str] = {} # 保存这个群的其阿金一张色图 {"123456":http://xxx"} + + +async def check_pic(bot: Bot, event: MessageEvent, state: T_State) -> bool: + if isinstance(event, MessageEvent): + for msg in event.message: + if msg.type == "image": + url: str = msg.data["url"] + state["url"] = url + return True + return False + + +notice_pic = on_message(check_pic, block=False, priority=1) + + +@notice_pic.handle() +async def handle_pic(bot: Bot, event: GroupMessageEvent, state: T_State): + try: + group_id: str = str(event.group_id) + pic_map.update({group_id: state["url"]}) + except AttributeError: + pass + + +previous = on_command("上一张图是什么", aliases={"上一张", "这是什么"}) + + +@previous.handle() +async def handle_previous(bot: Bot, event: GroupMessageEvent, state: T_State): + await bot.send(event=event, message="processing...") + try: + url: str = pic_map[str(event.group_id)] + idx = 1 + async for msg in get_des(url, "nao", event.user_id): + await bot.send(event=event, message=msg) + if idx == MAX_FIND_IMG_COUNT: + break + idx += 1 + except IndexError: + await previous.finish("参数错误") + except ClientError: + await previous.finish("连接错误") + except KeyError: + await previous.finish("没有图啊QAQ") diff --git a/plugins/nonebot_plugin_picsearcher/ascii2d.py b/plugins/nonebot_plugin_picsearcher/ascii2d.py new file mode 100644 index 00000000..941e9d0e --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/ascii2d.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +from typing import List, Tuple +from urllib.parse import urljoin +import aiofiles +from util.utils import get_local_proxy +from util.user_agent import get_user_agent +from configs.path_config import IMAGE_PATH +from asyncio.exceptions import TimeoutError +from util.init_result import image + +from lxml.html import fromstring +import aiohttp + + +def parse_html(html: str): + selector = fromstring(html) + for tag in selector.xpath('//div[@class="container"]/div[@class="row"]/div/div[@class="row item-box"]')[1:5]: + if pic_url := tag.xpath('./div/img[@loading="lazy"]/@src'): # 缩略图url + pic_url = urljoin("https://ascii2d.net/", pic_url[0]) + if description := tag.xpath('./div/div/h6/a[1]/text()'): # 名字 + description = description[0] + if author := tag.xpath('./div/div/h6/a[2]/text()'): # 作者 + author = author[0] + if origin_url := tag.xpath('./div/div/h6/a[1]/@href'): # 原图地址 + origin_url = origin_url[0] + if author_url := tag.xpath('./div/div/h6/a[2]/@href'): # 作者地址 + author_url = author_url[0] + yield pic_url, description, author, origin_url, author_url + + pass + + +async def get_pic_from_url(url: str): + real_url = "https://ascii2d.net/search/url/" + url + async with aiohttp.ClientSession() as session: + async with session.get(real_url) as resp: + html: str = await resp.text() + return [i for i in parse_html(html)] + + +async def get_des(url: str, user_id): + image_data: List[Tuple] = await get_pic_from_url(url) + if not image_data: + msg: str = "找不到高相似度的" + yield msg + return + for pic in image_data: + msg = await download_img(pic[0], user_id) + "\n" + for i in pic[1:]: + msg = msg + f"{i}\n" + yield msg + + +async def download_img(url, user_id): + try: + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, proxy=get_local_proxy(), timeout=7) as response: + async with aiofiles.open(IMAGE_PATH + f'temp/{user_id}_pic_find.png', 'wb') as f: + await f.write(await response.read()) + return image(f'{user_id}_pic_find.png', 'temp') + except TimeoutError: + return image(url) + diff --git a/plugins/nonebot_plugin_picsearcher/ex.py b/plugins/nonebot_plugin_picsearcher/ex.py new file mode 100644 index 00000000..172ac391 --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/ex.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +from base64 import b64encode +from typing import List, Tuple +import io + +from lxml.html import fromstring +import aiohttp +import nonebot +from aiohttp.client_exceptions import InvalidURL +from nonebot.adapters.cqhttp import MessageSegment + +from .formdata import FormData + +driver = nonebot.get_driver() +cookie: str = driver.config.ex_cookie +proxy: str = driver.config.proxy +target: str = "https://exhentai.org/upload/image_lookup.php" + +headers = { + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'Accept-Encoding': 'gzip, deflate', + 'Accept-Language': 'zh-CN,zh;q=0.9', + 'Cache-Control': 'max-age=0', + 'Connection': 'keep-alive', + 'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryB0NrMSYMfjY5r0l1', + 'Host': 'exhentai.org', + 'Origin': 'https://exhentai.org', + 'Referer': 'https://exhentai.org/?filesearch=1', + 'Sec-Fetch-Dest': 'document', + 'Sec-Fetch-Mode': 'navigate', + 'Sec-Fetch-Site': 'same-origin', + 'Sec-Fetch-User': '?1', + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'} + +if cookie: + headers['Cookie'] = cookie +else: + headers['Host'] = 'e-hentai.org' + headers['Origin'] = 'https://e-hentai.org' + headers['Referer'] = 'https://e-hentai.org/?filesearch=1' + target: str = "https://e-hentai.org/upload/image_lookup.php" + + +def parse_html(html: str): + """ + 解析exhentai返回的数据 + :param html: + :return: + """ + selector = fromstring(html) + hrefs = selector.xpath('//td[@class="gl3c glname"]/a/@href') + names = selector.xpath('//td[@class="gl3c glname"]/a/div[1]/text()') + pics = selector.xpath('//tr/td[@class="gl2c"]/div[@class="glthumb"]/div[1]/img/@src') # 缩略图 + for name, href, pic in zip(names, hrefs, pics): + yield name, href, pic + + +async def get_pic_from_url(url: str): + """ + 从接受到的picurl获取图片信息 + :param url: + :return: + """ + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + content = io.BytesIO(await resp.read()) + # Content_Length = resp.content_length + data = FormData(boundary="----WebKitFormBoundaryB0NrMSYMfjY5r0l1") + data.add_field(name="sfile", value=content, content_type="image/jpeg", + filename="0.jpg") + data.add_field(name="f_sfile", value="search") + data.add_field(name="fs_similar", value="on") + async with session.post(target, data=data, headers=headers, proxy=proxy) as res: + html = await res.text() + return [i for i in parse_html(html)] + + +async def get_content_from_url(url: str): + """ + 从url 获得b64 encode + :param url: + :return: + """ + try: + async with aiohttp.ClientSession() as session: + async with session.get(url, headers=headers) as resp: + return "base64://" + b64encode(await resp.read()).decode() + except aiohttp.client_exceptions.InvalidURL: + return url + + +async def get_des(url: str): + """ + 迭代要发送的信息 + :param url: + :return: + """ + image_data: List[Tuple] = await get_pic_from_url(url) + if not image_data: + msg: str = "找不到高相似度的" + yield msg + return + for name, href, pic_url in image_data: + content = await get_content_from_url(pic_url) + msg = MessageSegment.image(file=content) + f"\n本子名称:{name}\n" + f"链接{href}\n" + yield msg diff --git a/plugins/nonebot_plugin_picsearcher/formdata.py b/plugins/nonebot_plugin_picsearcher/formdata.py new file mode 100644 index 00000000..f70f040e --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/formdata.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from typing import Any, Iterable, List, Optional + +from aiohttp import FormData as _FormData +import aiohttp.multipart as multipart + + +class FormData(_FormData): + def __init__( + self, + fields: Iterable[Any] = (), + quote_fields: bool = True, + charset: Optional[str] = None, + boundary: Optional[str] = None + ) -> None: + self._writer = multipart.MultipartWriter("form-data", boundary=boundary) + self._fields = [] # type: List[Any] + self._is_multipart = False + self._is_processed = False + self._quote_fields = quote_fields + self._charset = charset + + if isinstance(fields, dict): + fields = list(fields.items()) + elif not isinstance(fields, (list, tuple)): + fields = (fields,) + self.add_fields(*fields) diff --git a/plugins/nonebot_plugin_picsearcher/iqdb.py b/plugins/nonebot_plugin_picsearcher/iqdb.py new file mode 100644 index 00000000..60b9f934 --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/iqdb.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +import asyncio +from typing import List, Tuple +import io + +from urllib.parse import urljoin +from lxml.html import fromstring +import aiohttp +from nonebot.adapters.cqhttp import MessageSegment + +from .formdata import FormData + +headers = { + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'zh-CN,zh;q=0.9', 'Cache-Control': 'max-age=0', + 'Connection': 'keep-alive', + 'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryuwjSiBcpPag4k159', + 'Cookie': 'Hm_lvt_765ecde8c11b85f1ac5f168fa6e6821f=1602471368; Hm_lpvt_765ecde8c11b85f1ac5f168fa6e6821f=1602472300', + 'Host': 'iqdb.org', 'Origin': 'http://iqdb.org', 'Referer': 'http://iqdb.org/', 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'} + + +def parse_html(html: str): + selector = fromstring(html) + for tag in selector.xpath('//div[@id="pages"]/div[position()>1]/table'): + # 第一个是bestmatch + if pic_url := tag.xpath('./tr[2]/td/a/img/@src'): + pic_url = urljoin("http://iqdb.org/", pic_url[0]) # 缩略图 + else: + pic_url = "没有最相似的" + similarity = tag.xpath('./tr[last()]/td/text()')[0] # 相似度 + href: List[str] = tag.xpath('./tr/td/a/@href') # 第一个href + href.extend(tag.xpath('./tr/td/span/a/@href')) # 第二个 可能是空 + href = list(map(lambda x: "https:" + x if not x.startswith("https") else x, href)) + yield pic_url, similarity, href + + pass + + +async def get_pic_from_url(url: str): + """ + 返回信息元祖 + :param url: + :return: + """ + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + content = io.BytesIO(await resp.read()) + data = FormData(boundary="----WebKitFormBoundaryuwjSiBcpPag4k159") + data.add_field(name="MAX_FILE_SIZE", value="") + for i in range(1, 7): + data.add_field(name="service[]", value=str(i)) + data.add_field(name="service[]", value="11") + data.add_field(name="service[]", value="13") + data.add_field(name="file", value=content, content_type="application/octet-stream", filename="0.jpg") + data.add_field(name="url", value="") + async with session.post("http://iqdb.org/", data=data, headers=headers) as res: + html = await res.text() + return [i for i in parse_html(html)] + pass + + +async def get_des(url: str): + """ + 返回详细简介 cq码转义 + :param url: + :return: + """ + image_data: List[Tuple] = await get_pic_from_url(url) + if not image_data: + msg: str = "找不到高相似度的" + yield msg + return + for pic in image_data: + msg = MessageSegment.image(file=pic[0]) + f"\n{pic[1]}\n" + for i in pic[2]: + msg = msg + f"{i}\n" + yield msg + diff --git a/plugins/nonebot_plugin_picsearcher/saucenao.py b/plugins/nonebot_plugin_picsearcher/saucenao.py new file mode 100644 index 00000000..14987b83 --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/saucenao.py @@ -0,0 +1,116 @@ +# -*- coding: utf-8 -*- +import io +from typing import List, Tuple, Union +import aiofiles +from util.utils import get_local_proxy +from util.user_agent import get_user_agent +from configs.path_config import IMAGE_PATH +from asyncio.exceptions import TimeoutError +from util.init_result import image + +import aiohttp +from lxml.html import fromstring +from nonebot.adapters.cqhttp import Message + +from .formdata import FormData + +header = { + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', + 'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'zh-CN,zh;q=0.9', + 'Cache-Control': 'max-age=0', + "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryPpuR3EZ1Ap2pXv8W", + 'Connection': 'keep-alive', + 'Host': 'saucenao.com', 'Origin': 'https://saucenao.com', 'Referer': 'https://saucenao.com/index.php', + 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'same-origin', 'Sec-Fetch-User': '?1', + 'Upgrade-Insecure-Requests': '1', + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.163 Safari/537.36'} + + +def parse_html(html: str): + """ + 解析nao返回的html + :param html: + :return: + """ + selector = fromstring(html) + for tag in selector.xpath('//div[@class="result"]/table'): + pic_url = tag.xpath('./tr/td/div/a/img/@src') + if pic_url: + pic_url = pic_url[0] + else: + pic_url = None # 相似度 + xsd: List[str] = tag.xpath( + './tr/td[@class="resulttablecontent"]/div[@class="resultmatchinfo"]/div[@class="resultsimilarityinfo"]/text()') + if xsd: + xsd = xsd[0] + else: + xsd = "没有写" # 相似度 + title: List[str] = tag.xpath( + './tr/td[@class="resulttablecontent"]/div[@class="resultcontent"]/div[@class="resulttitle"]/strong/text()') + if title: + title = title[0] + else: + title = "没有写" # 标题 + # pixiv id + pixiv_id: List[str] = tag.xpath( + './tr/td[@class="resulttablecontent"]/div[@class="resultcontent"]/div[@class="resultcontentcolumn"]/a[1]/@href') + if pixiv_id: + pixiv_id = pixiv_id[0] + else: + pixiv_id = "没有说" + member: List[str] = tag.xpath( + './tr/td[@class="resulttablecontent"]/div[@class="resultcontent"]/div[@class="resultcontentcolumn"]/a[2]/@href') + if member: + member = member[0] + else: + member = "没有说" + yield pic_url, xsd, title, pixiv_id, member + + +async def get_pic_from_url(url: str): + """ + 从url搜图 + :param url: + :return: + """ + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + content = io.BytesIO(await resp.read()) + data = FormData(boundary="----WebKitFormBoundaryPpuR3EZ1Ap2pXv8W") + data.add_field(name="file", value=content, content_type="image/jpeg", + filename="blob") + async with session.post("https://saucenao.com/search.php", data=data, headers=header) as res: + html = await res.text() + image_data = [each for each in parse_html(html)] + return image_data + + +async def get_des(url: str, user_id: int): + image_data: List[Tuple] = await get_pic_from_url(url) + if not image_data: + msg: Union[str, Message] = "找不到高相似度的" + yield msg + return + for pic in image_data: + # print(pic) + msg = await download_img(pic[0], user_id) \ + + f"\n相似度:{pic[1]}" \ + f"\n标题:{pic[2] if (str(pic[2]).strip() != 'Creator:' and len(str(pic[2]).split('-')) < 3) else '未知'}" \ + f"\nPID:{pic[3]}" \ + f"\nmember:{pic[4]}\n" + yield msg + pass + + +async def download_img(url, user_id): + try: + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, proxy=get_local_proxy(), timeout=7) as response: + async with aiofiles.open(IMAGE_PATH + f'temp/{user_id}_pic_find.png', 'wb') as f: + await f.write(await response.read()) + return image(f'{user_id}_pic_find.png', 'temp') + except TimeoutError: + return image(url) + + + diff --git a/plugins/nonebot_plugin_picsearcher/trace.py b/plugins/nonebot_plugin_picsearcher/trace.py new file mode 100644 index 00000000..0c28d2f2 --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/trace.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +import io +from copy import deepcopy + +from base64 import b64encode +from typing import List, Tuple + +import aiohttp +from nonebot.adapters.cqhttp import MessageSegment + +header = {':authority': 'api.trace.moe', + 'accept': '*/*', + 'accept-encoding': 'gzip, deflate, br', + 'accept-language': 'zh-CN,zh;q=0.9', + 'content-type': 'multipart/form-data; boundary=----WebKitFormBoundary9cyjY8YBBN8SGdG4', + 'origin': 'https://trace.moe', + 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-site', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/84.0.4147.105 Safari/537.36'} + + +async def parse_json(session: aiohttp.ClientSession, data: dict): + count = 0 + for i in data["result"]: + title: dict = i["anilist"]["title"] + similarity = i["similarity"] + from_ = i["from"] + to = i["to"] + file = i["filename"] # 名字 + is_adult = i["anilist"]["isAdult"] + episode = i["episode"] # 集 + header_new = deepcopy(header) + del header_new["content-type"] + header_new[":method"] = 'GET' + header_new["accept"] = "image/webp,image/apng,image/*,*/*;q=0.8" + header_new["sec-fetch-dest"] = "image" + header_new["sec-fetch-mode"] = "no-cors" + async with session.get(i["image"], headers=header_new) as resp: + pic = "base64://" + b64encode(await resp.read()).decode() + yield pic, similarity, file, is_adult, from_, to, title, episode + count += 1 + if count > 4: + break + + +# POST https://api.trace.moe/search?cutBorders=1&anilistID= +async def get_pic_from_url(url: str): + """ + 从url搜图 + :param url: + :return: + """ + async with aiohttp.ClientSession() as session: + async with session.get(url) as resp: + content = io.BytesIO(await resp.read()) + # with open("F:\elu.PNG", "rb") as f: + # content = io.BytesIO(f.read()) + data = aiohttp.FormData(boundary="----WebKitFormBoundary9cyjY8YBBN8SGdG4") + data.add_field(name="image", value=content, content_type="image/jpeg", + filename="blob") + # data.add_field(name="filter", value="") + # data.add_field(name="trial", value="0") + async with session.post("https://api.trace.moe/search?cutBorders=1&anilistID=", data=data, + headers=header) as res: + data: dict = await res.json() + image_data = [each async for each in parse_json(session, data)] + return image_data + + +async def get_des(url: str): + image_data: List[Tuple] = await get_pic_from_url(url) + if not image_data: + msg: str = "找不到高相似度的" + yield msg + return + for pic in image_data: + msg = MessageSegment.image( + file=pic[ + 0]) + f"\n相似度:{pic[1]}%\n标题:{pic[6]['native'] + ' ' + pic[6]['chinese']}\n第{pic[7]}集\nR18:{pic[3]}\n开始时间:{pic[4]}s\n结束时间{pic[5]}s" + yield msg + pass + + +if __name__ == "__main__": + import asyncio + + data = asyncio.run(get_pic_from_url( + "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1606681978562&di=6d6c90aef5ff1f9f8915bbc2e18e3c98&imgtype=0&src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fblog%2F202011%2F15%2F20201115190356_c5b95.thumb.1000_0.jpg")) + pass diff --git a/plugins/nonebot_plugin_picsearcher/yandex.py b/plugins/nonebot_plugin_picsearcher/yandex.py new file mode 100644 index 00000000..0632e442 --- /dev/null +++ b/plugins/nonebot_plugin_picsearcher/yandex.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +from typing import List, Tuple + +import nonebot +from nonebot.adapters.cqhttp import MessageSegment +from lxml.html import fromstring +import aiohttp + +""" +http://yandex.com/clck/jsredir?from=yandex.com%3Bimages%2Fsearch%3Bimages%3B%3B&text=&etext=9185.K4iyzsNBG9xrJrSJCUTF4i-XPMAfmBQYR_Igss1ESRc.65568e796f3375fae39da91273ae8a1a82410929&uuid=&state=iric5OQ0sS2054x1_o8yG9mmGMT8WeQxqpuwa4Ft4KVzd9aE_Y4Dfw,,&data=eEwyM2lDYU9Gd1VROE1ZMXhZYkJTYW5fZC1TWjIzaFh5TmR1Z09fQm5DdDB3bFJSSUpVdUxfZmUzcVhfaXhTN1BCU2dINGxmdkY4NFVNcHYyUmw0emFKT2pnOWJoVmlPVzAzX1FIbWh6aXVFV3F0YWFaMGdxeGFtY2dxTzFZZl9VY1huZmlLaGVGOFZleUthZXBlM1pxUGM2elVDLXdvZEo3OGJwdVFqYmVkTDJxWElHSzFZR2NVQUhVcTdzelJwSXlrTjhlS0txdHpYY1RMMHRLOU5HSTYtT0VDb0hpdll6YjVYRXNVcUhCRFJaeDExNTQwZlhMdjh4M2YtTVFUbVJ5ZzBxMTVJcG9DNW51UWhvRzE0WjlFS19uS0VUZWhNRGxOZWlPUkFlRUUs&sign=7ba9ee25d3716868ec8464fb766c9e25&keyno=IMGS_0&b64e=2&l10n=en +""" + +driver = nonebot.get_driver() +proxy: str = driver.config.proxy + + +def parse_html(html: str): + selector = fromstring(html) + for item in selector.xpath('//li[@class="other-sites__item"]'): + pic_url = item.xpath('./a[@class="other-sites__preview-link"]/img/@src')[0].lstrip("//") # 图床 + des = item.xpath( + './div[@class="other-sites__snippet"]/div[@class="other-sites__snippet-title"]/a/text()')[0] # 简介 + url = item.xpath( + './div[@class="other-sites__snippet"]/div[@class="other-sites__snippet-site"]/a/@href')[0] # 链接 + yield pic_url, des, url + + +async def get_pic_from_url(url: str): + real_url = f"https://yandex.com/images/search?rpt=imageview&url={url}" + async with aiohttp.ClientSession() as session: + async with session.get(real_url, proxy=proxy) as resp: + html: str = await resp.text() + return [i for i in parse_html(html)] + + +async def get_des(url: str): + image_data: List[Tuple] = await get_pic_from_url(url) + if not image_data: + msg: str = "找不到高相似度的" + yield msg + return + for pic in image_data: + msg = MessageSegment.image(file=pic[0]) + "\n" + for i in pic[1:]: + msg = msg + f"{i}\n" + yield msg + + +if __name__ == "__main__": + with open("yandex.html", "r", encoding="utf-8") as f: + data = f.read() + for item in parse_html(data): + print(item) diff --git a/plugins/one_friend/__init__.py b/plugins/one_friend/__init__.py new file mode 100644 index 00000000..a62d3635 --- /dev/null +++ b/plugins/one_friend/__init__.py @@ -0,0 +1,68 @@ +import json +import os +import aiohttp +from util.user_agent import get_user_agent +from io import BytesIO +from random import choice +from PIL import Image, ImageDraw, ImageFont +from nonebot import on_regex +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from util.utils import get_message_text, get_local_proxy +from util.img_utils import pic2b64 +from configs.path_config import TTF_PATH +import re +from nonebot.adapters.cqhttp import MessageSegment + +one_friend = on_regex('^我.*?朋友.*?(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我' + '帮他问|让我帮忙问|让我帮忙问问|问).*', priority=5, block=True) + + +async def get_pic(qq): + url = f'http://q1.qlogo.cn/g?b=qq&nk={qq}&s=100' + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, proxy=get_local_proxy(), timeout=5) as response: + return await response.read() + + +@one_friend.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + arr = [] + member_list = await bot.get_group_member_list(self_id=event.self_id, group_id=event.group_id) + for member in member_list: + arr.append(member['user_id']) + msg = get_message_text(event.json()) + msg = re.search(r'^我.*?朋友.*?' + r'(想问问|说|让我问问|想问|让我问|想知道|让我帮他问问|让我帮他问|让我帮忙问|让我帮忙问问|问)(.*)', + msg) + msg = msg.group(2) + if not msg: + msg = '都不知道问什么' + msg = msg.replace('他', '我').replace('她', '我') + image = Image.open(BytesIO(await get_pic(choice(arr)))) + img_origin = Image.new('RGBA', (100, 100), (255, 255, 255)) + scale = 3 + # 使用新的半径构建alpha层 + r = 100 * scale + alpha_layer = Image.new('L', (r, r), 0) + draw = ImageDraw.Draw(alpha_layer) + draw.ellipse((0, 0, r, r), fill=255) + # 使用ANTIALIAS采样器缩小图像 + alpha_layer = alpha_layer.resize((100, 100), Image.ANTIALIAS) + img_origin.paste(image, (0, 0), alpha_layer) + + # 创建Font对象: + font = ImageFont.truetype(os.path.join(os.path.dirname(__file__), TTF_PATH + 'yz.ttf'), 30) + font2 = ImageFont.truetype(os.path.join(os.path.dirname(__file__), TTF_PATH + 'yz.ttf'), 25) + + # 创建Draw对象: + image_text = Image.new('RGB', (450, 150), (255, 255, 255)) + draw = ImageDraw.Draw(image_text) + draw.text((0, 0), '朋友', fill=(0, 0, 0), font=font) + draw.text((0, 40), msg, fill=(125, 125, 125), font=font2) + + image_back = Image.new('RGB', (700, 150), (255, 255, 255)) + image_back.paste(img_origin, (25, 25)) + image_back.paste(image_text, (150, 40)) + + await one_friend.send(MessageSegment.image(pic2b64(image_back))) diff --git a/plugins/open_cases/__init__.py b/plugins/open_cases/__init__.py new file mode 100644 index 00000000..31689c05 --- /dev/null +++ b/plugins/open_cases/__init__.py @@ -0,0 +1,181 @@ +from nonebot import on_command +from util.utils import FreqLimiter, scheduler, get_message_text, is_number +from nonebot.adapters.cqhttp.permission import GROUP +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent, MessageEvent +from nonebot.permission import SUPERUSER +import random +from nonebot.plugin import MatcherGroup +import re +from .open_cases_c import open_case, total_open_statistics, group_statistics, my_knifes_name, open_shilian_case +from .utils import util_get_buff_price, util_get_buff_img, update_count_daily + +__plugin_name__ = '开箱' +__plugin_usage__ = ( + '用法:\n' + '看看你的人品罢了\n' + '目前只支持\n\t' + '1.狂牙大行动武器箱\n\t' + '2.突围大行动武器箱\n\t' + '3.命悬一线武器箱\n\t' + '4.裂空武器箱\n\t' + '5.光谱武器箱\n\t' + '示例:小真寻开箱 突围大行动(不输入指定武器箱则随机)\n' + '示例:我的开箱(开箱统计)\n' + '示例:群开箱统计\n' + '示例:我的金色' +) + +_flmt = FreqLimiter(3) + +cases_name = ['狂牙大行动', '突围大行动', '命悬一线', '裂空'] + +cases_matcher_group = MatcherGroup(priority=5, permission=GROUP, block=True) + + +k_open_case = cases_matcher_group.on_command("开箱") + + +@k_open_case.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if str(event.get_message()).strip() in ['帮助']: + await k_open_case.finish(__plugin_usage__) + if not _flmt.check(event.user_id): + await k_open_case.finish('着什么急啊,慢慢来!', at_sender=True) + _flmt.start_cd(event.user_id) + case_name = get_message_text(event.json()) + if case_name: + result = await open_case(event.user_id, event.group_id, case_name) + else: + result = await open_case(event.user_id, event.group_id, random.choice(cases_name)) + await k_open_case.finish(result, at_sender=True) + + +total_case_data = cases_matcher_group.on_command("我的开箱", aliases={'开箱统计', '开箱查询', '查询开箱'}) + + +@total_case_data.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await total_case_data.finish( + await total_open_statistics(event.user_id, event.group_id), + at_sender=True, + ) + + +group_open_case_statistics = cases_matcher_group.on_command("群开箱统计") + + +@group_open_case_statistics.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await group_open_case_statistics.finish(await group_statistics(event.group_id)) + + +my_kinfes = on_command("我的金色", priority=1, permission=GROUP, block=True) + + +@my_kinfes.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await my_kinfes.finish(await my_knifes_name(event.user_id, event.group_id), at_sender=True) + + +open_shilian = cases_matcher_group.on_regex(".*连开箱") + + +@open_shilian.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + # if not _flmt.check(event.user_id): + # await k_open_case.finish('着什么急啊,慢慢来!', at_sender=True) + _flmt.start_cd(event.user_id) + msg = get_message_text(event.json()) + rs = re.search(r'(.*)连开箱(.*)', msg) + if rs: + num = rs.group(1).strip() + if is_number(num) or num_dict.get(num): + try: + num = num_dict[num] + except KeyError: + num = int(num) + if num > 30: + await open_shilian.finish('开箱次数不要超过30啊笨蛋!', at_sender=True) + else: + await open_shilian.finish('必须要是数字切不要超过30啊笨蛋!中文也可!', at_sender=True) + case_name = rs.group(2).strip() + if case_name.find('武器箱') != -1: + case_name = case_name.replace('武器箱', '').strip() + if not case_name: + case_name = random.choice(cases_name) + elif case_name not in cases_name: + await open_shilian.finish('武器箱未收录!', at_sender=True) + await open_shilian.finish(await open_shilian_case(event.user_id, event.group_id, case_name, num), at_sender=True) + # await open_shilian.send("嘟嘟嘟...开箱中...") + # num = 10 + # if str(state["_prefix"]["raw_command"]).find('十') != -1: + # num = 10 + # elif str(state["_prefix"]["raw_command"]).find('五') != -1: + # num = 5 + # if case_name: + # await open_shilian.finish(await open_shilian_case(event.user_id, event.group_id, case_name, num), at_sender=True) + # else: + # await open_shilian.finish(await open_shilian_case(event.user_id, event.group_id, random.choice(cases_name), num), + # at_sender=True) + + +num_dict = { + '一': 1, + '二': 2, + '三': 3, + '四': 4, + '五': 5, + '六': 6, + '七': 7, + '八': 8, + '九': 9, + '十': 10, + '十一': 11, + '十二': 12, + '十三': 13, + '十四': 14, + '十五': 15, + '十六': 16, + '十七': 17, + '十八': 18, + '十九': 19, + '二十': 20, + '二十一': 21, + '二十二': 22, + '二十三': 23, + '二十四': 24, + '二十五': 25, + '二十六': 26, + '二十七': 27, + '二十八': 28, + '二十九': 29, + '三十': 30 +} + + +update_price = on_command("更新价格", priority=1, permission=SUPERUSER, block=True) + + +@update_price.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await update_price.send(await util_get_buff_price(str(event.get_message()))) + + +update_img = on_command("更新图片", priority=1, permission=SUPERUSER, block=True) + + +@update_img.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + await update_img.send(await util_get_buff_img(str(event.get_message()))) + + +# 重置开箱 +@scheduler.scheduled_job( + 'cron', + hour=0, + minute=1, +) +async def _(): + await update_count_daily() + diff --git a/plugins/open_cases/config.py b/plugins/open_cases/config.py new file mode 100644 index 00000000..4caab25e --- /dev/null +++ b/plugins/open_cases/config.py @@ -0,0 +1,180 @@ +import random +import pypinyin + +BLUE = 0.7981 +BLUE_ST = 0.0699 +PURPLE = 0.1626 +PURPLE_ST = 0.0164 +PINK = 0.0315 +PINK_ST = 0.0048 +RED = 0.0057 +RED_ST = 0.00021 +KNIFE = 0.0021 +KNIFE_ST = 0.000041 + +# 崭新 +FACTORY_NEW_S = 0 +FACTORY_NEW_E = 0.0699999 +# 略磨 +MINIMAL_WEAR_S = 0.07 +MINIMAL_WEAR_E = 0.14999 +# 久经 +FIELD_TESTED_S = 0.15 +FIELD_TESTED_E = 0.37999 +# 破损 +WELL_WORN_S = 0.38 +WELL_WORN_E = 0.44999 +# 战痕 +BATTLE_SCARED_S = 0.45 +BATTLE_SCARED_E = 0.99999 + +# 狂牙大行动 +KUANGYADAXINGDONG_CASE_KNIFE = ['摩托手套 | 第三特种兵连', '狂牙手套 | 翡翠', '驾驶手套 | 美洲豹女王', '运动手套 | 弹弓', '专业手套 | 老虎精英' + , '专业手套 | 渐变大理石', '运动手套 | 夜行衣', '驾驶手套 | 西装革履', '摩托手套 | 终点线', '摩托手套 | 血压', + '运动手套 | 猩红头巾', '驾驶手套 | 雪豹', '裹手 | 长颈鹿', '驾驶手套 | 绯红列赞', '裹手 | 沙漠头巾', + '专业手套 | 一线特工', '狂牙手套 | 黄色斑纹', '摩托手套 | 小心烟幕弹', '裹手 | 蟒蛇', '裹手 | 警告!', + '狂牙手套 | 精神错乱', '运动手套 | 大型猎物', '狂牙手套 | 针尖', '专业手套 | 陆军少尉长官'] +KUANGYADAXINGDONG_CASE_RED = ['M4A1 | 印花集', '格洛克 | 黑色魅影'] +KUANGYADAXINGDONG_CASE_PINK = ['FN57 | 童话城堡', 'M4A4 | 赛博', 'USP | 小绿怪'] +KUANGYADAXINGDONG_CASE_PURPLE = ['AWP | 亡灵之主', '双持贝瑞塔 | 灾难', '新星 | 一见青心', 'SSG 08 | 抖枪', 'UMP-45 | 金铋辉煌'] +KUANGYADAXINGDONG_CASE_BLUE = ['CZ75 | 世仇', 'P90 | 大怪兽RUSH', 'G3SG1 | 血腥迷彩', '加利尔 AR | 破坏者', 'P250 | 污染物', + 'M249 | 等高线', 'MP5-SD | 零点行动'] + +# 突围大行动 +TUWEIDAXINGDONG_CASE_KNIFE = ['蝴蝶刀 | 无涂装', '蝴蝶刀 | 蓝钢', '蝴蝶刀 | 屠夫', '蝴蝶刀 | 森林 DDPAT', '蝴蝶刀 | 北方森林', + '蝴蝶刀 | 狩猎网格', '蝴蝶刀 | 枯焦之色', '蝴蝶刀 | 人工染色', '蝴蝶刀 | 都市伪装', '蝴蝶刀 | 表面淬火', + '蝴蝶刀 | 深红之网', '蝴蝶刀 | 渐变之色', '蝴蝶刀 | 噩梦之夜'] +TUWEIDAXINGDONG_CASE_RED = ['P90 | 二西莫夫', 'M4A1 | 次时代'] +TUWEIDAXINGDONG_CASE_PINK = ['沙漠之鹰 | 阴谋者', 'FN57 | 狩猎利器', '格洛克 | 水灵'] +TUWEIDAXINGDONG_CASE_PURPLE = ['PP-野牛 | 死亡主宰者', 'CZ75 | 猛虎', '新星 | 锦鲤', 'P250 | 超新星'] +TUWEIDAXINGDONG_CASE_BLUE = ['MP7 | 都市危机', '内格夫 | 沙漠精英', 'P2000 | 乳白象牙', 'SSG 08 | 无尽深海', 'UMP-45 | 迷之宫'] + + +# 命悬一线 +MINGXUANYIXIAN_CASE_KNIFE = ['专业手套 | 大腕', '专业手套 | 深红之网', '专业手套 | 渐变之色', '专业手套 | 狩鹿', '九头蛇手套 | 响尾蛇', + '九头蛇手套 | 红树林', '九头蛇手套 | 翡翠色调', '九头蛇手套 | 表面淬火', '摩托手套 | 交运', '摩托手套 | 嘭!', + '摩托手套 | 多边形', '摩托手套 | 玳瑁', '裹手 | 套印', '裹手 | 森林色调', '裹手 | 钴蓝骷髅', '裹手 | 防水布胶带', + '运动手套 | 双栖', '运动手套 | 欧米伽', '运动手套 | 迈阿密风云', '运动手套 | 青铜形态', '驾驶手套 | 墨绿色调', + '驾驶手套 | 王蛇', '驾驶手套 | 蓝紫格子', '驾驶手套 | 超越'] +MINGXUANYIXIAN_CASE_RED = ['M4A4 | 黑色魅影', 'MP7 | 血腥运动'] +MINGXUANYIXIAN_CASE_PINK = ['AUG | 湖怪鸟', 'AWP | 死神', 'USP | 脑洞大开'] +MINGXUANYIXIAN_CASE_PURPLE = ['MAG-7 | SWAG-7', 'UMP-45 | 白狼', '内格夫 | 狮子鱼', '新星 | 狂野六号', '格洛克 | 城里的月光'] +MINGXUANYIXIAN_CASE_BLUE = ['FN57 | 焰色反应', 'MP9 | 黑砂', 'P2000 | 都市危机', 'PP-野牛 | 黑夜暴乱', 'R8 左轮手枪 | 稳', + 'SG 553 | 阿罗哈', 'XM1014 | 锈蚀烈焰'] + + +LIEKONG_CASE_KNIFE = ['求生匕首 | 无涂装', '求生匕首 | 人工染色', '求生匕首 | 北方森林', '求生匕首 | 夜色', '求生匕首 | 屠夫', + '求生匕首 | 枯焦之色', '求生匕首 | 森林 DDPAT', '求生匕首 | 深红之网', '求生匕首 | 渐变之色', '求生匕首 | 狩猎网格', + '求生匕首 | 蓝钢', '求生匕首 | 表面淬火', '求生匕首 | 都市伪装', '流浪者匕首 | 无涂装', '流浪者匕首 | 人工染色', + '流浪者匕首 | 北方森林', '流浪者匕首 | 夜色', '流浪者匕首 | 屠夫', '流浪者匕首 | 枯焦之色', '流浪者匕首 | 森林 DDPAT', + '流浪者匕首 | 深红之网', '流浪者匕首 | 渐变之色', '流浪者匕首 | 狩猎网格', '流浪者匕首 | 蓝钢', '流浪者匕首 | 表面淬火', + '流浪者匕首 | 都市伪装', '系绳匕首 | 无涂装', '系绳匕首 | 人工染色', '系绳匕首 | 北方森林', '系绳匕首 | 夜色', + '系绳匕首 | 屠夫', '系绳匕首 | 枯焦之色', '系绳匕首 | 森林 DDPAT', '系绳匕首 | 深红之网', '系绳匕首 | 渐变之色', + '系绳匕首 | 狩猎网格', '系绳匕首 | 蓝钢', '系绳匕首 | 表面淬火', '系绳匕首 | 都市伪装', '骷髅匕首 | 无涂装', + '骷髅匕首 | 人工染色', '骷髅匕首 | 北方森林', '骷髅匕首 | 夜色', '骷髅匕首 | 屠夫', '骷髅匕首 | 枯焦之色', + '骷髅匕首 | 森林 DDPAT', '骷髅匕首 | 深红之网', '骷髅匕首 | 渐变之色', '骷髅匕首 | 狩猎网格', '骷髅匕首 | 蓝钢', + '骷髅匕首 | 表面淬火', '骷髅匕首 | 都市伪装'] +LIEKONG_CASE_RED = ['AK-47 | 阿努比斯军团', '沙漠之鹰 | 印花集'] +LIEKONG_CASE_PINK = ['M4A4 | 齿仙', 'XM1014 | 埋葬之影', '格洛克 | 摩登时代'] +LIEKONG_CASE_PURPLE = ['加利尔 AR | 凤凰商号', 'Tec-9 | 兄弟连', 'MP5-SD | 猛烈冲锋', 'MAG-7 | 北冥有鱼', 'MAC-10 | 魅惑'] +LIEKONG_CASE_BLUE = ['内格夫 | 飞羽', 'SSG 08 | 主机001', 'SG 553 | 锈蚀之刃', 'PP-野牛 | 神秘碑文', 'P90 | 集装箱', 'P250 | 卡带', + 'P2000 | 盘根错节'] + + +GUANGPU_CASE_KNIFE = ['弯刀 | 外表生锈', '弯刀 | 多普勒', '弯刀 | 大马士革钢', '弯刀 | 渐变大理石', '弯刀 | 致命紫罗兰', '弯刀 | 虎牙', + '暗影双匕 | 外表生锈', '暗影双匕 | 多普勒', '暗影双匕 | 大马士革钢', '暗影双匕 | 渐变大理石', '暗影双匕 | 致命紫罗兰', + '暗影双匕 | 虎牙', '猎杀者匕首 | 外表生锈', '猎杀者匕首 | 多普勒', '猎杀者匕首 | 大马士革钢', '猎杀者匕首 | 渐变大理石', + '猎杀者匕首 | 致命紫罗兰', '猎杀者匕首 | 虎牙', '蝴蝶刀 | 外表生锈', '蝴蝶刀 | 多普勒', '蝴蝶刀 | 大马士革钢', + '蝴蝶刀 | 渐变大理石', '蝴蝶刀 | 致命紫罗兰', '蝴蝶刀 | 虎牙', '鲍伊猎刀 | 外表生锈', '鲍伊猎刀 | 多普勒', + '鲍伊猎刀 | 大马士革钢', '鲍伊猎刀 | 渐变大理石', '鲍伊猎刀 | 致命紫罗兰', '鲍伊猎刀 | 虎牙'] +GUANGPU_CASE_RED = ['USP | 黑色魅影', 'AK-47 | 血腥运动'] +GUANGPU_CASE_PINK = ['M4A1 | 毁灭者 2000', 'CZ75 | 相柳', 'AWP | 浮生如梦'] +GUANGPU_CASE_PURPLE = ['加利尔 AR | 深红海啸', 'XM1014 | 四季', 'UMP-45 | 支架', 'MAC-10 | 绝界之行', 'M249 | 翠绿箭毒蛙'] +GUANGPU_CASE_BLUE = ['沙漠之鹰 | 锈蚀烈焰', '截短霰弹枪 | 梭鲈', 'SCAR-20 | 蓝图', 'PP-野牛 | 丛林滑流', 'P250 | 涟漪', 'MP7 | 非洲部落', + 'FN57 | 毛细血管'] + + +NO_STA_KNIFE = ['求生匕首 | 北方森林', '求生匕首 | 夜色', '求生匕首 | 枯焦之色', '流浪者匕首 | 夜色', '流浪者匕首 | 枯焦之色', '流浪者匕首 | 森林 DDPAT', + '系绳匕首 | 夜色', '系绳匕首 | 狩猎网格', '骷髅匕首 | 夜色', '骷髅匕首 | 森林 DDPAT', '骷髅匕首 | 狩猎网格'] + + +def get_wear(num: float) -> str: + if num <= FACTORY_NEW_E: + return "崭新出厂" + if MINIMAL_WEAR_S <= num <= MINIMAL_WEAR_E: + return "略有磨损" + if FIELD_TESTED_S <= num <= FIELD_TESTED_E: + return "久经沙场" + if WELL_WORN_S <= num <= WELL_WORN_E: + return "破损不堪" + return "战痕累累" + + +def get_color_quality(rand: float, case_name: str): + case = "" + mosun = random.random()/2 + random.random()/2 + for i in pypinyin.pinyin(case_name, style=pypinyin.NORMAL): + case += ''.join(i) + case = case.upper() + CASE_KNIFE = eval(case + "_CASE_KNIFE") + CASE_RED = eval(case + "_CASE_RED") + CASE_PINK = eval(case + "_CASE_PINK") + CASE_PURPLE = eval(case + "_CASE_PURPLE") + CASE_BLUE = eval(case + "_CASE_BLUE") + if rand <= KNIFE: + skin = "罕见级(金色): " + random.choice(CASE_KNIFE) + if random.random() <= KNIFE_ST and (skin[2:4] != "手套" or skin[:2] != "裹手") and skin.split(':')[1] \ + not in NO_STA_KNIFE: + skin_sp = skin.split("|") + skin = skin_sp[0] + "(StatTrak™) | " + skin_sp[1] + elif KNIFE < rand <= RED: + skin = "隐秘级(红色): " + random.choice(CASE_RED) + if random.random() <= RED_ST: + skin_sp = skin.split("|") + skin = skin_sp[0] + "(StatTrak™) | " + skin_sp[1] + elif RED < rand <= PINK: + skin = "保密级(粉色): " + random.choice(CASE_PINK) + if random.random() <= PINK_ST: + skin_sp = skin.split("|") + skin = skin_sp[0] + "(StatTrak™) | " + skin_sp[1] + elif PINK < rand <= PURPLE: + skin = "受限级(紫色): " + random.choice(CASE_PURPLE) + if random.random() <= PURPLE_ST: + skin_sp = skin.split("|") + skin = skin_sp[0] + "(StatTrak™) | " + skin_sp[1] + else: + skin = "军规级(蓝色): " + random.choice(CASE_BLUE) + if random.random() <= BLUE_ST: + skin_sp = skin.split("|") + skin = skin_sp[0] + "(StatTrak™) | " + skin_sp[1] + if skin.find("(") != -1: + cpskin = skin.split(":")[1] + ybskin = cpskin.split("|") + temp_skin = ybskin[0].strip()[:-11] + " | " + ybskin[1].strip() + else: + temp_skin = skin.split(":")[1].strip() + # 崭新 -> 略磨 + if temp_skin in [] or temp_skin.find('渐变之色') != -1 or temp_skin.find('多普勒') != -1 or temp_skin.find('虎牙') != -1\ + or temp_skin.find('渐变大理石') != -1: + mosun = random.uniform(FACTORY_NEW_S, MINIMAL_WEAR_E) / 2 + random.uniform(FACTORY_NEW_S, MINIMAL_WEAR_E) / 2 + # 崭新 -> 久经 + if temp_skin in ['沙漠之鹰 | 阴谋者', '新星 | 锦鲤'] or temp_skin.find('屠夫') != -1: + mosun = random.uniform(FACTORY_NEW_S, FIELD_TESTED_E) / 2 + random.uniform(FACTORY_NEW_S, FIELD_TESTED_E) / 2 + # 崭新 -> 破损 + if temp_skin in ['UMP-45 | 迷之宫', 'P250 | 超新星', '系绳匕首 | 深红之网', 'M249 | 翠绿箭毒蛙', 'AK-47 | 血腥运动']: + mosun = random.uniform(FACTORY_NEW_S, WELL_WORN_E) / 2 + random.uniform(FACTORY_NEW_S, WELL_WORN_E) / 2 + # 破损 -> 战痕 + if temp_skin in [] or temp_skin.find('外表生锈') != -1: + mosun = random.uniform(WELL_WORN_S, BATTLE_SCARED_E) / 2 + random.uniform(WELL_WORN_S, BATTLE_SCARED_E) / 2 + if mosun > MINIMAL_WEAR_E: + for _ in range(2): + if random.random() < 5: + if random.random() < 0.2: + mosun /= 3 + else: + mosun /= 2 + break + skin += " (" + get_wear(mosun) + ")" + return skin, mosun + +# M249(StatTrak™) | 等高线 \ No newline at end of file diff --git a/plugins/open_cases/open_cases_c.py b/plugins/open_cases/open_cases_c.py new file mode 100644 index 00000000..068278e0 --- /dev/null +++ b/plugins/open_cases/open_cases_c.py @@ -0,0 +1,381 @@ +from datetime import datetime, timedelta +from .config import * +from services.log import logger +from services.db_context import db +from models.open_cases_user import OpenCasesUser +from models.sigin_group_user import SignGroupUser +from util.init_result import image +import pypinyin +import random +from .utils import get_price +from models.buff_price import BuffPrice +from PIL import Image +from util.img_utils import alphabg2white_PIL, CreateImg +from configs.path_config import IMAGE_PATH +import asyncio +from util.utils import cn2py +from configs.config import INITIAL_OPEN_CASE_COUNT + + +MAX_COUNT = INITIAL_OPEN_CASE_COUNT + + +async def open_case(user_qq: int, group: int, case_name: str = "狂牙大行动") -> str: + if case_name not in ["狂牙大行动", "突围大行动", "命悬一线", '裂空', '光谱']: + return "武器箱未收录" + knifes_flag = False + # lan zi fen hong jin price + uplist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0] + case = "" + for i in pypinyin.pinyin(case_name, style=pypinyin.NORMAL): + case += ''.join(i) + impression = (await SignGroupUser.ensure(user_qq, group)).impression + rand = random.random() + async with db.transaction(): + user = await OpenCasesUser.ensure(user_qq, group, for_update=True) + # 一天次数上限 + if user.today_open_total == int(MAX_COUNT + int(impression) / 3): + return _handle_is_MAX_COUNT() + skin, mosun = get_color_quality(rand, case_name) + # 调侃 + if skin[:2] == "军规": + if skin.find("StatTrak") == -1: + uplist[0] = 1 + else: + uplist[1] = 1 + ridicule_result = random.choice(['这样看着才舒服', + '是自己人,大伙把刀收好', + '非常舒适~']) + if skin[:2] == "受限": + if skin.find("StatTrak") == -1: + uplist[2] = 1 + else: + uplist[3] = 1 + ridicule_result = random.choice(['还行吧,勉强接受一下下', + '居然不是蓝色,太假了', + '运气-1-1-1-1-1...']) + if skin[:2] == "保密": + if skin.find("StatTrak") == -1: + uplist[4] = 1 + else: + uplist[5] = 1 + ridicule_result = random.choice(['开始不适....', + '你妈妈买菜必涨价!涨三倍!', + '你最近不适合出门,真的']) + if skin[:2] == "隐秘": + if skin.find("StatTrak") == -1: + uplist[6] = 1 + else: + uplist[7] = 1 + ridicule_result = random.choice(['已经非常不适', + '好兄弟你开的什么箱子啊,一般箱子不是只有蓝色的吗', + '开始拿阳寿开箱子了?']) + if skin[:2] == "罕见": + knifes_flag = True + if skin.find("StatTrak") == -1: + uplist[8] = 1 + else: + uplist[9] = 1 + ridicule_result = random.choice(['你的好运我收到了,你可以去喂鲨鱼了', + '最近该吃啥就迟点啥吧,哎,好好的一个人怎么就....哎', + '众所周知,欧皇寿命极短.']) + if skin.find("(") != -1: + cskin = skin.split("(") + skin = cskin[0].strip() + "(" + cskin[1].strip() + skin = skin.split("|")[0].strip() + " | " + skin.split("|")[1].strip() + # 价格 + if skin.find('无涂装') == -1: + dbprice = await BuffPrice.ensure(skin[9:]) + else: + dbprice = await BuffPrice.ensure(skin[9: skin.rfind('(')].strip()) + if dbprice.skin_price != 0: + price_result = dbprice.skin_price + print("数据库查询到价格: ", dbprice.skin_price) + uplist[10] = dbprice.skin_price + else: + price = -1 + price_result = "未查询到" + price_list, status = await get_price(skin[9:]) + if price_list not in ["访问超时! 请重试或稍后再试!", "访问失败!"]: + for price_l in price_list[1:]: + pcp = price_l.split(":") + if pcp[0] == skin[9:]: + price = float(pcp[1].strip()) + break + if price != -1: + print("存储入数据库---->", price) + uplist[10] = price + price_result = str(price) + await dbprice.update( + skin_price=price, + update_date=datetime.now(), + ).apply() + # sp = skin.split("|") + # cskin_word = sp[1][:sp[1].find("(") - 1].strip() + if knifes_flag: + await user.update( + knifes_name=user.knifes_name + f"{skin.split(':')[1].strip()} 磨损:{str(mosun)[:11]}, 价格:{uplist[10]}," + ).apply() + cskin_word = skin.split(':')[1].replace('|', '-').replace('(StatTrak™)', '') + cskin_word = cskin_word[: cskin_word.rfind('(')].strip() + skin_name = cn2py(cskin_word.replace('|', '-').replace('(StatTrak™)', '').strip()) + img = image(f'{skin_name}.png', "cases/" + case) + # if knifes_flag: + # await user.update( + # knifes_name=user.knifes_name + f"{skin} 磨损:{mosun}, 价格:{uplist[10]}" + # ).apply() + if await update_user_total(user, uplist): + logger.info(f"qq:{user_qq} 群:{group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}, 数据更新成功") + else: + logger.warning(f"qq:{user_qq} 群:{group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}, 数据更新失败") + user = await OpenCasesUser.ensure(user_qq, group, for_update=True) + over_count = int(MAX_COUNT + int(impression) / 3) - user.today_open_total + return f"开启{case_name}武器箱.\n剩余开箱次数:{over_count}.\n" \ + + img + "\n" + \ + f"皮肤:{skin}\n" \ + f"磨损:{mosun:.9f}\n" \ + f"价格:{price_result}\n" \ + f"{ridicule_result}" + + +async def open_shilian_case(user_qq: int, group: int, case_name: str, num: int = 10): + user = await OpenCasesUser.ensure(user_qq, group, for_update=True) + impression = (await SignGroupUser.ensure(user_qq, group)).impression + if user.today_open_total == int(MAX_COUNT + int(impression) / 3): + return _handle_is_MAX_COUNT() + if int(MAX_COUNT + int(impression) / 3) - user.today_open_total < num: + return f"今天开箱次数不足{num}次噢,请单抽试试看(也许单抽运气更好?)" \ + f"\n剩余开箱次数:{int(MAX_COUNT + int(impression) / 3) - user.today_open_total}" + if num < 5: + h = 270 + elif num % 5 == 0: + h = 270 * int(num / 5) + else: + h = 270 * int(num / 5) + 270 + case = cn2py(case_name) + # lan zi fen hong jin + # skin_list = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + # lan zi fen hong jin price + uplist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0] + img_list = [] + name_list = ['蓝', '蓝(暗金)', '紫', '紫(暗金)', '粉', '粉(暗金)', '红', '红(暗金)', '金', '金(暗金)'] + async with db.transaction(): + for _ in range(num): + knifes_flag = False + rand = random.random() + skin, mosun = get_color_quality(rand, case_name) + if skin[:2] == "军规": + if skin.find("StatTrak") == -1: + uplist[0] += 1 + else: + uplist[1] += 1 + if skin[:2] == "受限": + if skin.find("StatTrak") == -1: + uplist[2] += 1 + else: + uplist[3] += 1 + if skin[:2] == "保密": + if skin.find("StatTrak") == -1: + uplist[4] += 1 + else: + uplist[5] += 1 + if skin[:2] == "隐秘": + if skin.find("StatTrak") == -1: + uplist[6] += 1 + else: + uplist[7] += 1 + if skin[:2] == "罕见": + knifes_flag = True + if skin.find("StatTrak") == -1: + uplist[8] += 1 + else: + uplist[9] += 1 + if skin.find("(") != -1: + cskin = skin.split("(") + skin = cskin[0].strip() + "(" + cskin[1].strip() + skin = skin.split("|")[0].strip() + " | " + skin.split("|")[1].strip() + # 价格 + if skin.find('无涂装') == -1: + dbprice = await BuffPrice.ensure(skin[9:]) + else: + dbprice = await BuffPrice.ensure(skin[9: skin.rfind('(')].strip()) + if dbprice.skin_price != 0: + price_result = dbprice.skin_price + uplist[10] += price_result + else: + price_result = '未查询到' + if knifes_flag: + await user.update( + knifes_name=user.knifes_name + f"{case}||{skin.split(':')[1].strip()} 磨损:{str(mosun)[:11]}, 价格:{dbprice.skin_price}," + ).apply() + cskin_word = skin.split(':')[1].replace('|', '-').replace('(StatTrak™)', '') + cskin_word = cskin_word[: cskin_word.rfind('(')].strip() + skin_name = "" + for i in pypinyin.pinyin(cskin_word.replace('|', '-').replace('(StatTrak™)', '').strip(), + style=pypinyin.NORMAL): + skin_name += ''.join(i) + # img = image(skin_name, "cases/" + case, "png") + wImg = CreateImg(200, 270, 200, 200) + wImg.paste(alphabg2white_PIL(Image.open(IMAGE_PATH + f'cases/{case}/{skin_name}.png').resize((200, 200), Image.ANTIALIAS)), (0, 0)) + wImg.text((5, 200), skin) + wImg.text((5, 220), f'磨损:{str(mosun)[:9]}') + wImg.text((5, 240), f'价格:{price_result}') + img_list.append(wImg) + logger.info(f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 获得 {skin} 磨损:{mosun}, 价格:{uplist[10]}") + if await update_user_total(user, uplist, num): + logger.info(f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 数据更新成功") + else: + logger.warning(f"USER {user_qq} GROUP {group} 开启{case_name}武器箱 {num} 次, 价格:{uplist[10]}, 数据更新失败") + # markImg = CreateImg(1000, h, 200, 270) + # for img in img_list: + # markImg.paste(img) + markImg = await asyncio.get_event_loop().run_in_executor(None, paste_markImg, h, img_list) + over_count = int(MAX_COUNT + int(impression) / 3) - user.today_open_total + result = '' + for i in range(len(name_list)): + if uplist[i]: + result += f'[{name_list[i]}:{uplist[i]}] ' + return f"开启{case_name}武器箱\n剩余开箱次数:{over_count}\n" \ + + image(b64=markImg.pic2bs4()) + \ + '\n' + result[:-1] + f'\n总获取金额:{uplist[-1]:.2f}\n总花费:{17 * num}' + + +def paste_markImg(h: int, img_list: list): + markImg = CreateImg(1000, h, 200, 270) + for img in img_list: + markImg.paste(img) + return markImg + + +def _handle_is_MAX_COUNT() -> str: + return f"今天已达开箱上限了喔,明天再来吧\n(提升好感度可以增加每日开箱数 #疯狂暗示)" + + +async def update_user_total(user: OpenCasesUser, uplist: list, num: int = 1) -> bool: + try: + await user.update( + total_count=user.total_count + num, + blue_count=user.blue_count + uplist[0], + blue_st_count=user.blue_st_count + uplist[1], + purple_count=user.purple_count + uplist[2], + purple_st_count=user.purple_st_count + uplist[3], + pink_count=user.pink_count + uplist[4], + pink_st_count=user.pink_st_count + uplist[5], + red_count=user.red_count + uplist[6], + red_st_count=user.red_st_count + uplist[7], + knife_count=user.knife_count + uplist[8], + knife_st_count=user.knife_st_count + uplist[9], + spend_money=user.spend_money + 17 * num, + make_money=user.make_money + uplist[10], + today_open_total=user.today_open_total + num, + open_cases_time_last=datetime.now() + ).apply() + return True + except: + return False + + +async def total_open_statistics(user_qq: int, group: int) -> str: + async with db.transaction(): + user = await OpenCasesUser.ensure(user_qq, group, for_update=True) + return f"开箱总数:{user.total_count}\n" \ + f"今日开箱:{user.today_open_total}\n" \ + f"蓝色军规:{user.blue_count}\n" \ + f"蓝色暗金:{user.blue_st_count}\n" \ + f"紫色受限:{user.purple_count}\n" \ + f"紫色暗金:{user.purple_st_count}\n" \ + f"粉色保密:{user.pink_count}\n" \ + f"粉色暗金:{user.pink_st_count}\n" \ + f"红色隐秘:{user.red_count}\n" \ + f"红色暗金:{user.red_st_count}\n" \ + f"金色罕见:{user.knife_count}\n" \ + f"金色暗金:{user.knife_st_count}\n" \ + f"花费金额:{user.spend_money}\n" \ + f"获取金额:{user.make_money:.2f}\n" \ + f"最后开箱日期:{(user.open_cases_time_last + timedelta(hours=8)).date()}" + + +async def group_statistics(group: int): + user_list = await OpenCasesUser.get_user_all(group) + # lan zi fen hong jin pricei + uplist = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0, 0, 0] + for user in user_list: + uplist[0] += user.blue_count + uplist[1] += user.blue_st_count + uplist[2] += user.purple_count + uplist[3] += user.purple_st_count + uplist[4] += user.pink_count + uplist[5] += user.pink_st_count + uplist[6] += user.red_count + uplist[7] += user.red_st_count + uplist[8] += user.knife_count + uplist[9] += user.knife_st_count + uplist[10] += user.make_money + uplist[11] += user.total_count + uplist[12] += user.today_open_total + return f"群开箱总数:{uplist[11]}\n" \ + f"群今日开箱:{uplist[12]}\n" \ + f"蓝色军规:{uplist[0]}\n" \ + f"蓝色暗金:{uplist[1]}\n" \ + f"紫色受限:{uplist[2]}\n" \ + f"紫色暗金:{uplist[3]}\n" \ + f"粉色保密:{uplist[4]}\n" \ + f"粉色暗金:{uplist[5]}\n" \ + f"红色隐秘:{uplist[6]}\n" \ + f"红色暗金:{uplist[7]}\n" \ + f"金色罕见:{uplist[8]}\n" \ + f"金色暗金:{uplist[9]}\n" \ + f"花费金额:{uplist[11] * 17}\n" \ + f"获取金额:{uplist[10]:.2f}" + + +async def my_knifes_name(user_id: int, group: int): + knifes_name = (await OpenCasesUser.ensure(user_id, group)).knifes_name + if knifes_name: + knifes_list = knifes_name[:-1].split(",") + length = len(knifes_list) + if length < 5: + h = 600 + w = length * 540 + elif length % 5 == 0: + h = 600 * int(length / 5) + w = 540 * 5 + else: + h = 600 * int(length / 5) + 600 + w = 540 * 5 + A = await asyncio.get_event_loop().run_in_executor(None, _pst_my_knife, w, h, knifes_list) + return image(b64=A.pic2bs4()) + else: + return "您木有开出金色级别的皮肤喔" + + +def _pst_my_knife(w, h, knifes_list): + A = CreateImg(w, h, 540, 600) + for knife in knifes_list: + case = knife.split('||')[0] + knife = knife.split('||')[1] + name = knife[:knife.find('(')].strip() + itype = knife[knife.find('(')+1: knife.find(')')].strip() + mosun = knife[knife.find('磨损:')+3: knife.rfind('价格:')].strip() + if mosun[-1] == ',' or mosun[-1] == ',': + mosun = mosun[:-1] + price = knife[knife.find('价格:')+3:] + skin_name = "" + for i in pypinyin.pinyin(name.replace('|', '-').replace('(StatTrak™)', '').strip(), + style=pypinyin.NORMAL): + skin_name += ''.join(i) + knife_img = CreateImg(470, 600, 470, 470, font_size=20) + knife_img.paste(alphabg2white_PIL( + Image.open(IMAGE_PATH + f'cases/{case}/{skin_name}.png').resize((470, 470), Image.ANTIALIAS)), (0, 0)) + knife_img.text((5, 500), f'\t{name}({itype})') + knife_img.text((5, 530), f'\t磨损:{mosun}') + knife_img.text((5, 560), f'\t价格:{price}') + A.paste(knife_img) + return A + + + + +# G3SG1(StatTrak™) | 血腥迷彩 (战痕累累) +# G3SG1(StatTrak™) | 血腥迷彩 (战痕累累) +# G3SG1(StatTrak™) | 血腥迷彩 (战痕累累) diff --git a/plugins/open_cases/utils.py b/plugins/open_cases/utils.py new file mode 100644 index 00000000..ff440ea0 --- /dev/null +++ b/plugins/open_cases/utils.py @@ -0,0 +1,294 @@ +from models.buff_price import BuffPrice +from services.db_context import db +from datetime import datetime, timedelta +from util.user_agent import get_user_agent +from configs.path_config import IMAGE_PATH +import aiohttp +import aiofiles +from models.open_cases_user import OpenCasesUser +import os +from services.log import logger +from util.utils import get_bot +from models.group_remind import GroupRemind +from util.utils import get_cookie_text +from asyncio.exceptions import TimeoutError +import pypinyin +from nonebot.adapters.cqhttp.exception import ActionFailed +<<<<<<< HEAD +from configs.config import buff_proxy +======= +>>>>>>> master + +url = "https://buff.163.com/api/market/goods" +# proxies = 'http://49.75.59.242:3128' + + +async def util_get_buff_price(case_name: str = "狂牙大行动") -> str: + cookie = {'session': get_cookie_text('buff')} + failed_list = [] + case = "" + for i in pypinyin.pinyin(case_name, style=pypinyin.NORMAL): + case += ''.join(i) + if case_name == "狂牙大行动": + case_id = 1 + elif case_name == "突围大行动": + case_id = 2 + elif case_name == "命悬一线": + case_id = 3 + elif case_name == '裂空': + case_id = 4 + elif case_name == '光谱': + case_id = 5 + else: + return "未查询到武器箱" + case = case.upper() + CASE_KNIFE = eval(case + "_CASE_KNIFE") + CASE_RED = eval(case + "_CASE_RED") + CASE_PINK = eval(case + "_CASE_PINK") + CASE_PURPLE = eval(case + "_CASE_PURPLE") + CASE_BLUE = eval(case + "_CASE_BLUE") + async with aiohttp.ClientSession(cookies=cookie, headers=get_user_agent()) as session: + for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]: + print("----------------------------------") + for skin in total_list: + print(skin) + if skin in ["蝴蝶刀 | 无涂装", '求生匕首 | 无涂装', '流浪者匕首 | 无涂装', '系绳匕首 | 无涂装', '骷髅匕首 | 无涂装']: + skin = skin.split('|')[0].strip() + async with db.transaction(): + name_list = [] + price_list = [] + parameter = { + "game": "csgo", + "page_num": "1", + "search": skin + } + try: +<<<<<<< HEAD + async with session.get(url, proxy=buff_proxy, params=parameter, timeout=20) as response: +======= + async with session.get(url, params=parameter, timeout=20) as response: +>>>>>>> master + if response.status == 200: + data = (await response.json())["data"] + total_page = data["total_page"] + data = data["items"] + flag = False + if skin.find('|') == -1: # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: + for i in range(1, total_page + 1): + name_list = [] + price_list = [] + parameter = { + "game": "csgo", + "page_num": f"{i}", + "search": skin + } + async with session.get(url, params=parameter, timeout=20) as res: + data = (await response.json())["data"]["items"] + for j in range(len(data)): + if data[j]['name'] in [f'{skin}(★)']: + name = data[j]["name"] + price = data[j]["sell_reference_price"] + name_list.append(name.split('(')[0].strip() + ' | 无涂装') + price_list.append(price) + print(name_list[-1]) + print(price_list[-1]) + flag = True + break + if flag: + break + else: + try: + for _ in range(total_page): + for i in range(len(data)): + name = data[i]["name"] + price = data[i]["sell_reference_price"] + name_list.append(name) + price_list.append(price) + except Exception as e: + failed_list.append(skin) + print(f"{skin}更新失败") + else: + failed_list.append(skin) + print(f"{skin}更新失败") + except Exception: + failed_list.append(skin) + print(f"{skin}更新失败") + continue + for i in range(len(name_list)): + name = name_list[i].strip() + price = float(price_list[i]) + if name.find("(★)") != -1: + name = name[: name.find("(")] + name[name.find(")") + 1:] + if name.find("消音") != -1 and name.find("(S") != -1: + name = name.split("(")[0][:-4] + "(" + name.split("(")[1] + name = name.split("|")[0].strip() + " | " + name.split("|")[1].strip() + elif name.find("消音") != -1: + name = name.split("|")[0][:-5].strip() + " | " + name.split("|")[1].strip() + if name.find(" 18 ") != -1 and name.find("(S") != -1: + name = name.split("(")[0][:-5] + "(" + name.split("(")[1] + name = name.split("|")[0].strip() + " | " + name.split("|")[1].strip() + elif name.find(" 18 ") != -1: + name = name.split("|")[0][:-6].strip() + " | " + name.split("|")[1].strip() + dbskin = await BuffPrice.ensure(name, True) + if (dbskin.update_date + timedelta(8)).date() == datetime.now().date(): + continue + await dbskin.update( + case_id=case_id, + skin_price=price, + update_date=datetime.now(), + ).apply() + print(f"{name_list[i]}---------->成功更新") + result = None + if failed_list: + result = "" + for fail_skin in failed_list: + result += fail_skin + "\n" + return result[:-1] if result else "更新价格成功" + + +async def util_get_buff_img(case_name: str = "狂牙大行动") -> str: + cookie = {'session': get_cookie_text('buff')} + error_list = [] + case = "" + for i in pypinyin.pinyin(case_name, style=pypinyin.NORMAL): + case += ''.join(i) + path = "cases/" + case + "/" + if not os.path.exists(IMAGE_PATH + path): + os.mkdir(IMAGE_PATH + path) + case = case.upper() + CASE_KNIFE = eval(case + "_CASE_KNIFE") + CASE_RED = eval(case + "_CASE_RED") + CASE_PINK = eval(case + "_CASE_PINK") + CASE_PURPLE = eval(case + "_CASE_PURPLE") + CASE_BLUE = eval(case + "_CASE_BLUE") + async with aiohttp.ClientSession(cookies=cookie, headers=get_user_agent()) as session: + for total_list in [CASE_KNIFE, CASE_RED, CASE_PINK, CASE_PURPLE, CASE_BLUE]: + for skin in total_list: + parameter = { + "game": "csgo", + "page_num": "1", + "search": skin + } + if skin in ["蝴蝶刀 | 无涂装", '求生匕首 | 无涂装', '流浪者匕首 | 无涂装', '系绳匕首 | 无涂装', '骷髅匕首 | 无涂装']: + skin = skin.split('|')[0].strip() + print("开始更新----->", skin) + print(skin) + skin_name = '' + # try: +<<<<<<< HEAD + async with session.get(url, proxy=buff_proxy, params=parameter, timeout=20) as response: +======= + async with session.get(url, params=parameter, timeout=20) as response: +>>>>>>> master + if response.status == 200: + data = (await response.json())["data"] + total_page = data["total_page"] + flag = False + if skin.find('|') == -1: # in ['蝴蝶刀', '求生匕首', '流浪者匕首', '系绳匕首', '骷髅匕首']: + for i in range(1, total_page + 1): + async with session.get(url, params=parameter, timeout=20) as res: + data = (await response.json())["data"]["items"] + for j in range(len(data)): + if data[j]['name'] in [f'{skin}(★)']: + img_url = data[j]['goods_info']['icon_url'] + for k in pypinyin.pinyin(skin + '无涂装', style=pypinyin.NORMAL): + skin_name += ''.join(k) + async with aiofiles.open(IMAGE_PATH + path + skin_name + ".png", 'wb') as f: + print("------->开始写入 ", skin) + async with session.get(img_url, timeout=7) as res: + await f.write(await res.read()) + flag = True + break + if flag: + break + else: + img_url = (await response.json())["data"]['items'][0]['goods_info']['icon_url'] + for i in pypinyin.pinyin(skin.replace('|', '-').strip(), style=pypinyin.NORMAL): + skin_name += ''.join(i) + async with aiofiles.open(IMAGE_PATH + path + skin_name + ".png", 'wb') as f: + print("------->开始写入 ", skin) + async with session.get(img_url, timeout=7) as res: + await f.write(await res.read()) + # async with session.get(url, params=parameter, timeout=7) as response: + # if response.status == 200: + # img_url = (await response.json())["data"]['items'][0]['goods_info']['icon_url'] + # skin_name = '' + # for i in pypinyin.pinyin(skin.split("|")[1].strip(), style=pypinyin.NORMAL): + # skin_name += ''.join(i) + # async with aiofiles.open(IMAGE_PATH + path + skin_name + ".png", 'wb') as f: + # print("------->开始写入 ", skin) + # async with session.get(img_url, timeout=7) as res: + # await f.write(await res.read()) + # except Exception: + # print("图片更新失败 ---->", skin) + # error_list.append(skin) + result = None + if error_list: + result = "" + for errskin in error_list: + result += errskin + "\n" + return result[:-1] if result else "更新图片成功" + + +async def get_price(dname): + cookie = {'session': get_cookie_text('buff')} + name_list = [] + price_list = [] + parameter = { + "game": "csgo", + "page_num": "1", + "search": dname + } + try: + async with aiohttp.ClientSession(cookies=cookie, headers=get_user_agent()) as session: + async with session.get(url, params=parameter, timeout=7) as response: + if response.status == 200: + try: + data = (await response.json())["data"] + total_page = data["total_page"] + data = data["items"] + for _ in range(total_page): + for i in range(len(data)): + name = data[i]["name"] + price = data[i]["sell_reference_price"] + name_list.append(name) + price_list.append(price) + except Exception as e: + return "没有查询到...", 998 + else: + return "访问失败!", response.status + except TimeoutError as e: + return "访问超时! 请重试或稍后再试!", 997 + result = f"皮肤: {dname}({len(name_list)})\n" + # result = "皮肤: " + dname + "\n" + for i in range(len(name_list)): + result += name_list[i] + ": " + price_list[i] + "\n" + return result[:-1], 999 + + +async def update_count_daily(): + try: + users = await OpenCasesUser.get_user_all() + if users: + for user in users: + await user.update( + today_open_total=0, + ).apply() + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g['group_id'] for g in gl] + for g in gl: + if await GroupRemind.get_status(g, 'kxcz'): + try: + await bot.send_group_msg(group_id=g, message="今日开箱次数重置成功") + except ActionFailed: + logger.warning(f'{g} 群被禁言,无法发送 开箱重置提醒') + logger.info("今日开箱次数重置成功") + except Exception as e: + logger.error(f'开箱重置错误 e:{e}') + + + +# 蝴蝶刀(★) | 噩梦之夜 (久经沙场) +if __name__ == '__main__': + print(util_get_buff_img("xxxx/")) diff --git a/plugins/pixiv/__init__.py b/plugins/pixiv/__init__.py new file mode 100644 index 00000000..67f93605 --- /dev/null +++ b/plugins/pixiv/__init__.py @@ -0,0 +1,194 @@ +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot import on_command +from util.utils import get_message_text, UserExistLimiter, is_number +from .data_source import get_pixiv_urls, download_pixiv_imgs, search_pixiv_urls +import time +from services.log import logger +from nonebot.adapters.cqhttp.exception import NetworkError + +__plugin_usage__1 = '''P站排行榜帮助: +可选参数: +类型: + 1. 日排行 + 2. 周排行 + 3. 月排行 + 4. 原创排行 + 5. 新人排行 + 6. R18日排行 + 7. R18周排行 + 8. R18受男性欢迎排行 + 9. R18重口排行【慎重!】 +【使用时选择参数序号即可,R18仅可私聊】 +p站排行榜 类型 数量(可选) 日期(可选) +示例: + p站排行榜 (无参数默认为日榜) + p站排行榜 1 + p站排行榜 1 5 + p站排行榜 1 5 2018-4-25 +【注意空格!!】【在线搜索会较慢】 +''' + +__plugin_usage__2 = '''P站搜图帮助: +可选参数: + 1.热度排序 + 2.时间排序 +【使用时选择参数序号即可,R18仅可私聊】 +搜图 关键词 数量(可选) 排序方式(可选) r18(可选) +示例: + 搜图 樱岛麻衣 + 搜图 樱岛麻衣 5 1 + 搜图 樱岛麻衣 5 2 r18 +【默认为 热度排序】 +【注意空格!!】【在线搜索会较慢】【数量可能不符】 +''' + +rank_dict = { + '1': 'day', + '2': 'week', + '3': 'month', + '4': 'week_original', + '5': 'week_rookie', + '6': 'day_r18', + '7': 'week_r18', + '8': 'day_male_r18', + '9': 'week_r18g' +} + +_ulmt = UserExistLimiter() + +pixiv_rank = on_command('p站排行', aliases={'P站排行榜', 'p站排行榜', 'P站排行榜'}, priority=5, block=True) +pixiv_keyword = on_command('搜图', priority=5, block=True) + + +@pixiv_rank.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).strip() + if msg in ['帮助']: + await pixiv_rank.finish(__plugin_usage__1) + msg = msg.split(' ') + msg = [m for m in msg if m] + if not msg: + msg = ['1'] + if msg[0] in ['6', '7', '8', '9']: + if event.message_type == 'group': + await pixiv_rank.finish('羞羞脸!私聊里自己看!', at_sender=True) + # print(msg) + if _ulmt.check(event.user_id): + await pixiv_rank.finish("P站排行榜正在搜索噢,不要重复触发命令呀") + _ulmt.set_True(event.user_id) + if len(msg) == 0 or msg[0] == '': + text_list, urls, code = await get_pixiv_urls(rank_dict.get('1')) + elif len(msg) == 1: + if msg[0] not in ['1', '2', '3', '4', '5', '6', '7', '8', '9']: + await pixiv_rank.finish("要好好输入要看什么类型的排行榜呀!", at_sender=True) + text_list, urls, code = await get_pixiv_urls(rank_dict.get(msg[0])) + elif len(msg) == 2: + text_list, urls, code = await get_pixiv_urls(rank_dict.get(msg[0]), int(msg[1])) + elif len(msg) == 3: + if not check_date(msg[2]): + await pixiv_rank.finish('日期格式错误了', at_sender=True) + text_list, urls, code = await get_pixiv_urls(rank_dict.get(msg[0]), int(msg[1]), msg[2]) + else: + _ulmt.set_False(event.user_id) + await pixiv_rank.finish('格式错了噢,看看帮助?', at_sender=True) + if code != 200: + await pixiv_keyword.finish(text_list[0]) + else: + if not text_list or not urls: + await pixiv_rank.finish('没有找到啊,等等再试试吧~V', at_sender=True) + for i in range(len(text_list)): + try: + await pixiv_rank.send(text_list[i] + await download_pixiv_imgs(urls[i], event.user_id)) + except NetworkError: + await pixiv_keyword.send('这张图网络炸了!', at_sender=True) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f" 查看了P站排行榜 code:{msg[0]}") + _ulmt.set_False(event.user_id) + + +@pixiv_keyword.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + msg = get_message_text(event.json()).strip() + if msg in ['帮助'] or not msg: + await pixiv_keyword.finish(__plugin_usage__2) + if event.message_type == 'group': + if msg.find('r18') != -1: + await pixiv_keyword.finish('(脸红#) 你不会害羞的 八嘎!', at_sender=True) + if msg.find('r18') == -1: + r18 = 1 + else: + r18 = 2 + msg = msg.replace('r18', '').strip() + if _ulmt.check(event.user_id): + await pixiv_rank.finish("P站关键词正在搜索噢,不要重复触发命令呀") + _ulmt.set_True(event.user_id) + msg = msg.split(' ') + msg = [m for m in msg if m] + if len(msg) == 1: + keyword = msg[0].strip() + num = 5 + order = 'popular' + elif len(msg) == 2: + keyword = msg[0].strip() + if not is_number(msg[1].strip()): + _ulmt.set_False(event.user_id) + await pixiv_keyword.finish('图片数量必须是数字!', at_sender=True) + num = int(msg[1].strip()) + order = 'popular' + elif len(msg) == 3: + keyword = msg[0].strip() + if not is_number(msg[1].strip()): + _ulmt.set_False(event.user_id) + await pixiv_keyword.finish('图片数量必须是数字!', at_sender=True) + num = int(msg[1].strip()) + if not is_number(msg[2].strip()): + _ulmt.set_False(event.user_id) + await pixiv_keyword.finish('排序方式必须是数字!', at_sender=True) + if msg[2].strip() == '1': + order = 'popular' + else: + order = 'xxx' + else: + _ulmt.set_False(event.user_id) + await pixiv_keyword.finish('参数不正确,一定要好好看看帮助啊!', at_sender=True) + text_list, urls, code = await search_pixiv_urls(keyword, num, order, r18) + if code != 200: + _ulmt.set_False(event.user_id) + await pixiv_keyword.finish(text_list[0]) + else: + for i in range(len(text_list)): + try: + await pixiv_keyword.send(text_list[i] + await download_pixiv_imgs(urls[i], event.user_id)) + except NetworkError: + await pixiv_keyword.send('这张图网络炸了!', at_sender=True) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'})" + f" 查看了搜索 {keyword} R18:{r18}") + _ulmt.set_False(event.user_id) + + +def check_date(date): + try: + time.strptime(date, "%Y-%m-%d") + return True + except: + return False + + + + + + + + + + + + + + + + + diff --git a/plugins/pixiv/data_source.py b/plugins/pixiv/data_source.py new file mode 100644 index 00000000..a82e2f57 --- /dev/null +++ b/plugins/pixiv/data_source.py @@ -0,0 +1,84 @@ +import aiohttp +import aiofiles +from configs.path_config import IMAGE_PATH +from util.utils import get_local_proxy +from util.user_agent import get_user_agent +from bs4 import BeautifulSoup +import feedparser +from util.init_result import image +from asyncio.exceptions import TimeoutError +import platform +if platform.system() == 'Windows': + import asyncio + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) + + +async def get_pixiv_urls(mode: str, num: int = 5, date: str = '') -> 'list, list, int': + url = 'https://rsshub.app/pixiv/ranking/{}' + iurl = url.format(mode) + if date: + iurl += f'/{date}' + return await parser_data(iurl, num) + + +async def download_pixiv_imgs(urls: list, user_id: int) -> str: + result = '' + index = 0 + for img in urls: + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + for _ in range(3): + async with session.get(img, proxy=get_local_proxy(), timeout=3) as response: + async with aiofiles.open(IMAGE_PATH + f'temp/{user_id}_{index}_pixiv.jpg', 'wb') as f: + try: + await f.write(await response.read()) + result += image(f'{user_id}_{index}_pixiv.jpg', 'temp') + index += 1 + break + except TimeoutError: + # result += '\n这张图下载失败了..\n' + pass + else: + result += '\n这张图下载失败了..\n' + return result + + +async def search_pixiv_urls(keyword: str, num: int, order: str, r18: int) -> 'list, list': + url = 'https://rsshub.app/pixiv/search/{}/{}/{}'.format(keyword, order, r18) + return await parser_data(url, num) + + +async def parser_data(url: str, num: int) -> 'list, list, int': + text_list = [] + urls = [] + async with aiohttp.ClientSession() as session: + for _ in range(3): + try: + async with session.get(url, proxy=get_local_proxy(), timeout=4) as response: + data = feedparser.parse(await response.text())['entries'] + break + except TimeoutError: + pass + else: + return ['网络不太好,也许过一会就好了'], [], 998 + try: + if len(data) == 0: + return ['没有搜索到喔'], [], 997 + if num > len(data): + num = len(data) + data = data[:num] + for data in data: + soup = BeautifulSoup(data['summary'], 'lxml') + title = "标题:" + data['title'] + pl = soup.find_all('p') + author = pl[0].text.split('-')[0].strip() + imgs = [] + text_list.append(f'{title}\n{author}\n') + for p in pl[1:]: + imgs.append(p.find('img').get('src')) + urls.append(imgs) + except ValueError: + return ['是网站坏了啊,也许过一会就好了'], [], 999 + return text_list, urls, 200 + + +# asyncio.get_event_loop().run_until_complete(get_pixiv_urls('day')) diff --git a/plugins/poke/__init__.py b/plugins/poke/__init__.py new file mode 100644 index 00000000..e893f43e --- /dev/null +++ b/plugins/poke/__init__.py @@ -0,0 +1,52 @@ +from nonebot import on_notice +from nonebot.adapters.cqhttp import Bot, PokeNotifyEvent +from nonebot.typing import T_State +from configs.path_config import VOICE_PATH, IMAGE_PATH +import os +from util.init_result import record, image, poke +from services.log import logger +import random +from util.utils import CountLimiter +from models.ban_user import BanUser + +# 戳 一 戳 + +poke__reply = [ + "lsp你再戳?", "连个可爱美少女都要戳的肥宅真恶心啊。", + "你再戳!", "?再戳试试?", "别戳了别戳了再戳就坏了555", "我爪巴爪巴,球球别再戳了", "你戳你🐎呢?!", + "那...那里...那里不能戳...绝对...", "(。´・ω・)ん?", "有事恁叫我,别天天一个劲戳戳戳!", "欸很烦欸!你戳🔨呢", + "?", "再戳一下试试?", "???", "正在关闭对您的所有服务...关闭成功", "啊呜,太舒服刚刚竟然睡着了。什么事?", "正在定位您的真实地址。。。\r\n定位成功。轰炸机已起飞" +] + + +_clmt = CountLimiter(3) + +poke_ = on_notice(priority=5) + + +@poke_.handle() +async def _poke_(bot: Bot, event: PokeNotifyEvent, state: T_State) -> None: + if event.notice_type == 'notify' and event.sub_type == 'poke' and event.self_id == event.target_id: + _clmt.add(event.user_id) + if _clmt.check(event.user_id) or random.random() < 0.3: + rst = '' + if random.random() < 0.15: + await BanUser.ban(event.user_id, 1, 60) + rst = '气死我了!' + await poke_.finish(rst + random.choice(poke__reply), at_sender=True) + rand = random.random() + if rand <= 0.3: + path = random.choice(['loli/', 'meitu/']) + index = random.randint(0, len(os.listdir(IMAGE_PATH + path))) + result = f'id:{index}' + image(f'{index}.jpg', path) + await poke_.send(result) + logger.info(f'USER {event.user_id} 戳了戳我 回复: {result} \n {result}') + elif 0.3 < rand < 0.6: + voice = random.choice(os.listdir(VOICE_PATH + 'dinggong/')) + result = record(voice, "dinggong") + await poke_.send(result) + await poke_.send(voice.split('_')[1]) + logger.info(f'USER {event.user_id} 戳了戳我 回复: {result} \n {voice.split("_")[1]}') + else: + await poke_.send(poke(event.user_id)) + diff --git a/plugins/reimu/__init__.py b/plugins/reimu/__init__.py new file mode 100644 index 00000000..5134c1f5 --- /dev/null +++ b/plugins/reimu/__init__.py @@ -0,0 +1,96 @@ +from nonebot import on_command +from nonebot.adapters.cqhttp.permission import PRIVATE +from .data_source import from_reimu_get_info +from services.log import logger +from nonebot.adapters.cqhttp import Bot, Event +from nonebot.typing import T_State +from util.utils import is_number, get_message_text, UserExistLimiter, scheduler +from models.count_user import UserCount +from configs.config import COUNT_PER_DAY_REIMU + +__plugin_name__ = '上车' +__plugin_usage__ = r""" +* 请各位使用后不要转发 * +* 大部分解压密码是⑨ * +/ 每人每天仅提供 5 次上车机会(只能私聊)更多次数请向管理员申请(用爱发电)限制小色批乱搜 / +/ 并不推荐小色批使用此功能(主要是不够色,目的不够明确) / +上车 [目的地] +上车 5 [目的地] 该目的地第5页停车场 +ps: 请尽量提供具体的目的地名称 +""".strip() + + +_ulmt = UserExistLimiter() + +reimu = on_command('上车', permission=PRIVATE, block=True, priority=1) + + +@reimu.args_parser +async def _(bot: Bot, event: Event, state: T_State): + if get_message_text(event.json()) in ['取消', '算了']: + await reimu.finish("已取消操作..", at_sender=True) + if not get_message_text(event.json()): + await reimu.reject('没时间等了!快说你要去哪里?', at_sender=True) + state['keyword'] = get_message_text(event.json()) + state['page'] = 1 + + +@reimu.handle() +async def _(bot: Bot, event: Event, state: T_State): + if str(event.get_message()) in ['帮助']: + await reimu.finish(__plugin_usage__) + if await UserCount.check_count(event.user_id, 'reimu', COUNT_PER_DAY_REIMU): + await reimu.finish('今天已经没车了,请明天再来...', at_sender=True) + if _ulmt.check(event.user_id): + await reimu.finish('您还没下车呢,请稍等...', at_sender=True) + _ulmt.set_True(event.user_id) + msg = get_message_text(event.json()) + args = msg.split(" ") + if msg in ['!', '!', '?', '?', ',', ',', '.', '。']: + await reimu.finish(__plugin_usage__) + if msg: + if len(args) > 1 and is_number(args[0]): + state['keyword'] = args[1] + state['page'] = args[0] + else: + state['keyword'] = msg + state['page'] = 1 + + +@reimu.got('keyword', '你的目的地是哪?') +async def _(bot: Bot, event: Event, state: T_State): + try: + keyword = state['keyword'] + page = state['page'] + print(keyword, page) + await UserCount.add_count(event.user_id, 'reimu') + await reimu.send('已经帮你关好车门了', at_sender=True) + reimu_report = await from_reimu_get_info(keyword, page) + if reimu_report: + await reimu.send(reimu_report) + else: + logger.error("Not found reimuInfo") + await reimu.send("没找着") + _ulmt.set_False(event.user_id) + except: + _ulmt.set_False(event.user_id) + + +@scheduler.scheduled_job( + 'cron', + # year=None, + # month=None, + # day=None, + # week=None, + # day_of_week="mon,tue,wed,thu,fri", + hour=0, + minute=1, + # second=None, + # start_date=None, + # end_date=None, + # timezone=None, +) +async def _(): + await UserCount.reset_count() + + diff --git a/plugins/reimu/data_source.py b/plugins/reimu/data_source.py new file mode 100644 index 00000000..98efde62 --- /dev/null +++ b/plugins/reimu/data_source.py @@ -0,0 +1,116 @@ +from lxml import etree +import time +import aiohttp +from services.log import logger +from configs.config import MAXINFO_REIMU +from util.user_agent import get_user_agent +from util.utils import get_local_proxy +from asyncio.exceptions import TimeoutError + + +async def from_reimu_get_info(key_word: str, page: int) -> str: + if "miku" in key_word.lower(): + logger.warning("Taboo words are being triggered") + return None + repass = "" + url = 'https://blog.reimu.net/search/' + key_word + '/page/' + str(page) + url_s = 'https://blog.reimu.net/' + try: + if key_word == "最近的存档": + logger.debug("Now starting get the {}".format(url_s)) + repass = await get_repass(url_s) + else: + logger.debug("Now starting get the {}".format(url)) + repass = await get_repass(url) + except TimeoutError as e: + logger.error("Timeout! {}".format(e)) + + return repass + + +async def get_repass(url: str) -> str: + repass = "" + info = "[Note]注意大部分资源解压密码为⑨\n" + fund = None + print(url) + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url, proxy=get_local_proxy(), timeout=15) as response: + html = etree.HTML(await response.text()) + + fund_l = html.xpath('//h1[@class="page-title"]/text()') + if fund_l: + fund = fund_l[0] + if fund == "未找到": + return "老司机也找不到路了……" + else: + pass + + headers = html.xpath('//article/header/h2/a/text()') + urls = html.xpath('//article/header/h2/a/@href') + logger.debug("Now get {} post from search page".format(len(headers))) + + headers_d = [] + urls_d = [] + for i, header in enumerate(headers): + if check_need_list(header): + headers_d.append(headers[i]) + urls_d.append(urls[i]) + else: + logger.debug("This title {} does not meet the requirements".format(header)) + + header_len = len(headers_d) + logger.debug("Get {} post after processing".format(header_len)) + if header_len > MAXINFO_REIMU: + headers_d = headers_d[:MAXINFO_REIMU] + urls_d = urls_d[:MAXINFO_REIMU] + + for h_s, url_s in zip(headers_d, urls_d): + if h_s != "审核结果存档": + time.sleep(1.5) + putline = await get_son_html_info(h_s, url_s) + if putline: + if repass: + repass = "\n\n- - - - - - - - \n".join([repass, putline]) + else: + repass = putline + else: + logger.info("审核归档页面已跳过") + + if repass: + repass = info + repass + return repass + + +async def get_son_html_info(h_s, url_s) -> str: + repass = "" + logger.debug("Now starting get the {}".format(url_s)) + async with aiohttp.ClientSession(headers=get_user_agent()) as session: + async with session.get(url_s, proxy=get_local_proxy(), timeout=15) as response: + html = etree.HTML(await response.text()) + pres = html.xpath('//div[@class="entry-content"]/pre/text()') + a_texts = html.xpath('//div[@class="entry-content"]/pre//a/text()') + a_hrefs = html.xpath('//div[@class="entry-content"]/pre//a/@href') + + if pres and a_texts and a_hrefs: + while "" in pres: + pres.remove("") + + repass = "【资源名称】 {}\n\n{}".format(h_s, pres[0].strip()) + for i, (a_t_s, a_h_s) in enumerate(zip(a_texts, a_hrefs)): + a = "\n {} {} {} ".format(a_t_s, a_h_s, pres[i + 1].strip()) + repass += a + else: + logger.warning("Not get putline from {}".format(url_s)) + + return repass + + +def check_need_list(header: str) -> bool: + not_need = ['音乐', '御所动态'] + for nd in not_need: + if nd in header: + return False + return True + + +# print(asyncio.get_event_loop().run_until_complete(from_reimu_get_info('萝莉'))) \ No newline at end of file diff --git a/plugins/remind/__init__.py b/plugins/remind/__init__.py new file mode 100644 index 00000000..a8bce100 --- /dev/null +++ b/plugins/remind/__init__.py @@ -0,0 +1,194 @@ +from util.init_result import image +from util.utils import scheduler, get_bot +from services.log import logger +from models.group_remind import GroupRemind +from models.group_info import GroupInfo +from models.friend_user import FriendUser +from nonebot.adapters.cqhttp.exception import ActionFailed + +__name__ = "早晚安 [Hidden]" + + +# 早上好 +@scheduler.scheduled_job( + 'cron', + # year=None, + # month=None, + # day=None, + # week=None, + # day_of_week="mon,tue,wed,thu,fri", + hour=6, + minute=1, + # second=None, + # start_date=None, + # end_date=None, + # timezone=None, +) +async def _(): + try: + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g['group_id'] for g in gl] + for g in gl: + if await GroupRemind.get_status(g, 'zwa'): + result = image("zao.jpg", "zhenxun") + try: + await bot.send_group_msg(group_id=g, + message="早上好" + result) + except ActionFailed: + logger.warning(f'{g} 群被禁言中,无法发送早安') + except Exception as e: + logger.error(f'早晚安错误 e:{e}') + + +# 睡觉了 +@scheduler.scheduled_job( + 'cron', + hour=23, + minute=59, +) +async def _(): + try: + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g['group_id'] for g in gl] + for g in gl: + if await GroupRemind.get_status(g, 'zwa'): + result = image("sleep.jpg", "zhenxun") + try: + await bot.send_group_msg(group_id=g, + message="小真寻要睡觉了,你们也要早点睡呀" + result) + except ActionFailed: + logger.warning(f'{g} 群被禁言中,无法发送晚安') + except Exception as e: + logger.error(f'早晚安错误 e:{e}') + + +# 自动更新群组信息 +@scheduler.scheduled_job( + 'cron', + hour=3, + minute=1, +) +async def _(): + try: + bot = get_bot() + gl = await bot.get_group_list(self_id=bot.self_id) + gl = [g['group_id'] for g in gl] + for g in gl: + group_info = await bot.get_group_info(group_id=g) + await GroupInfo.add_group_info(group_info['group_id'], group_info['group_name'], + group_info['max_member_count'], group_info['member_count']) + logger.info(f'自动更新群组 {g} 信息成功') + except Exception as e: + logger.error(f'自动更新群组信息错误 e:{e}') + + +# 自动更新好友信息 +@scheduler.scheduled_job( + 'cron', + hour=3, + minute=1, +) +async def _(): + try: + bot = get_bot() + fl = await bot.get_friend_list(self_id=bot.self_id) + for f in fl: + if await FriendUser.add_friend_info(f['user_id'], f['nickname']): + logger.info(f'自动更新好友 {f["user_id"]} 信息成功') + else: + logger.warning(f'自动更新好友 {f["user_id"]} 信息失败') + except Exception as e: + logger.error(f'自动更新群组信息错误 e:{e}') + +# 一次性任务 +# 固定时间触发,仅触发一次: +# +# from datetime import datetime +# +# @nonebot.scheduler.scheduled_job( +# 'date', +# run_date=datetime(2021, 1, 1, 0, 0), +# # timezone=None, +# ) +# async def _(): +# await bot.send_group_msg(group_id=123456, +# message="2021,新年快乐!") + +# 定期任务 +# 从 start_date 开始到 end_date 结束,根据类似 Cron +# +# 的规则触发任务: +# +# @nonebot.scheduler.scheduled_job( +# 'cron', +# # year=None, +# # month=None, +# # day=None, +# # week=None, +# day_of_week="mon,tue,wed,thu,fri", +# hour=7, +# # minute=None, +# # second=None, +# # start_date=None, +# # end_date=None, +# # timezone=None, +# ) +# async def _(): +# await bot.send_group_msg(group_id=123456, +# message="起床啦!") + +# 间隔任务 +# +# interval 触发器 +# +# 从 start_date 开始,每间隔一段时间触发,到 end_date 结束: +# +# @nonebot.scheduler.scheduled_job( +# 'interval', +# # weeks=0, +# # days=0, +# # hours=0, +# minutes=5, +# # seconds=0, +# # start_date=time.now(), +# # end_date=None, +# ) +# async def _(): +# has_new_item = check_new_item() +# if has_new_item: +# await bot.send_group_msg(group_id=123456, +# message="XX有更新啦!") + + +# 动态的计划任务 +# import datetime +# +# from apscheduler.triggers.date import DateTrigger # 一次性触发器 +# # from apscheduler.triggers.cron import CronTrigger # 定期触发器 +# # from apscheduler.triggers.interval import IntervalTrigger # 间隔触发器 +# from nonebot import on_command, scheduler +# +# @on_command('赖床') +# async def _(session: CommandSession): +# await session.send('我会在5分钟后再喊你') +# +# # 制作一个“5分钟后”触发器 +# delta = datetime.timedelta(minutes=5) +# trigger = DateTrigger( +# run_date=datetime.datetime.now() + delta +# ) +# +# # 添加任务 +# scheduler.add_job( +# func=session.send, # 要添加任务的函数,不要带参数 +# trigger=trigger, # 触发器 +# args=('不要再赖床啦!',), # 函数的参数列表,注意:只有一个值时,不能省略末尾的逗号 +# # kwargs=None, +# misfire_grace_time=60, # 允许的误差时间,建议不要省略 +# # jobstore='default', # 任务储存库,在下一小节中说明 +# ) + + + diff --git a/plugins/search_anime/__init__.py b/plugins/search_anime/__init__.py new file mode 100644 index 00000000..387ea5c3 --- /dev/null +++ b/plugins/search_anime/__init__.py @@ -0,0 +1,56 @@ +from nonebot import on_command +from .data_source import from_anime_get_info +from services.log import logger +from nonebot.adapters.cqhttp import Bot, Event +from nonebot.typing import T_State +from configs.config import MAXINFO_GROUP_ANIME, MAXINFO_PRIVATE_ANIME +from util.utils import get_message_text, get_message_type, UserExistLimiter + + +__plugin_name__ = '搜番' +__plugin_usage__ = r""" +在群内使用此功能只返还5个结果,私聊返还 20 个结果(绝不能打扰老色批们看色图!) +搜索动漫资源 +搜番 [番剧名称或者关键词] +搜番 Aria +""".strip() +_ulmt = UserExistLimiter() + +search_anime = on_command('搜番', aliases={'搜动漫'}, priority=5, block=True) + + +@search_anime.args_parser +async def _(bot: Bot, event: Event, state: T_State): + msg = get_message_text(event.json()) + if not msg: + await search_anime.reject('番名番名番名呢?', at_sender=True) + state['anime'] = msg + + +@search_anime.handle() +async def _(bot: Bot, event: Event, state: T_State): + if get_message_text(event.json()) in ['帮助'] or get_message_text(event.json()) == '': + await search_anime.finish(__plugin_usage__) + if _ulmt.check(event.user_id): + await search_anime.finish('您有动漫正在搜索,请稍等...', at_sender=True) + _ulmt.set_True(event.user_id) + if get_message_text(event.json()): + state['anime'] = get_message_text(event.json()) + + +@search_anime.got('anime', prompt='是不是少了番名?') +async def _(bot: Bot, event: Event, state: T_State): + key_word = state['anime'] + await search_anime.send(f'开始搜番 {key_word}', at_sender=True) + anime_report = await from_anime_get_info(key_word, MAXINFO_GROUP_ANIME if get_message_type(event.json()) in ['group', 'discuss'] else MAXINFO_PRIVATE_ANIME) + if anime_report: + await search_anime.send(anime_report) + logger.info(f"USER {event.user_id} GROUP" + f" {event.group_id if event.message_type != 'private' else 'private'} 搜索番剧 {key_word} 成功") + else: + logger.warning(f"未找到番剧 {key_word}") + await search_anime.send(f"未找到番剧 {key_word}(也有可能是超时,再尝试一下?)") + _ulmt.set_False(event.user_id) + + + diff --git a/plugins/search_anime/data_source.py b/plugins/search_anime/data_source.py new file mode 100644 index 00000000..7acb8e86 --- /dev/null +++ b/plugins/search_anime/data_source.py @@ -0,0 +1,47 @@ +from lxml import etree +import feedparser +from urllib import parse +from services.log import logger +import aiohttp +import time +from util.utils import get_local_proxy + + +async def from_anime_get_info(key_word: str, max: int) -> str: + s_time = time.time() + repass = "" + url = 'https://share.dmhy.org/topics/rss/rss.xml?keyword=' + parse.quote(key_word) + try: + logger.debug("Now starting get the {}".format(url)) + repass = await get_repass(url, max) + except Exception as e: + logger.error("Timeout! {}".format(e)) + + return f"搜索 {key_word} 结果(耗时 {int(time.time() - s_time)} 秒):\n" + repass + + +async def get_repass(url: str, max: int) -> str: + putline = [] + async with aiohttp.ClientSession() as session: + async with session.get(url, proxy=get_local_proxy(), timeout=20) as response: + d = feedparser.parse(await response.text()) + url_list = [e.link for e in d.entries][:max] + for u in url_list: + print(u) + try: + async with session.get(u, proxy=get_local_proxy(), timeout=20) as res: + html = etree.HTML(await res.text()) + magent = html.xpath('.//a[@id="a_magnet"]/text()')[0] + title = html.xpath('.//h3/text()')[0] + item = html.xpath('//div[@class="info resource-info right"]/ul/li') + class_a = item[0].xpath('string(.)')[5:].strip().replace("\xa0", "").replace("\t", "") + size = item[3].xpath('string(.)')[5:].strip() + + putline.append("【{}】| {}\n【{}】| {}".format(class_a, title, size, magent)) + except Exception as e: + logger.warning(f'搜番超时 e:{e}') + + repass = '\n\n'.join(putline) + + return repass +# print(asyncio.get_event_loop().run_until_complete(from_anime_get_info('进击的巨人', 1234556))) \ No newline at end of file diff --git a/plugins/search_buff_skin_price/__init__.py b/plugins/search_buff_skin_price/__init__.py new file mode 100644 index 00000000..4ecc2846 --- /dev/null +++ b/plugins/search_buff_skin_price/__init__.py @@ -0,0 +1,75 @@ +from nonebot import on_command +from .data_source import get_price, update_buff_cookie +from services.log import logger +from nonebot.typing import T_State +from nonebot.adapters import Bot, Event +from nonebot.rule import to_me +from nonebot.permission import SUPERUSER +from util.utils import UserExistLimiter, get_message_text + + +__plugin_name__ = '查询皮肤' +__plugin_usage__ = '查询皮肤帮助:\n\t' \ + '对我说 "查询皮肤 xxx yyyy",我会回复xxx的底价哦\n\t' \ + '示例: 查询皮肤 awp 二西莫夫' + + +_ulmt = UserExistLimiter() + + +search_skin = on_command('查询皮肤', aliases={'皮肤查询'}, priority=5, block=True) + + +@search_skin.args_parser +async def parse(bot: Bot, event: Event, state: T_State): + if get_message_text(event.json()) in ['取消', '算了']: + await search_skin.finish("已取消操作..", at_sender=True) + state[state["_current_key"]] = str(event.get_message()) + + +@search_skin.handle() +async def _(bot: Bot, event: Event, state: T_State): + if str(event.get_message()) in ['帮助']: + await search_skin.finish(__plugin_usage__) + raw_arg = get_message_text(event.json()) + if _ulmt.check(event.user_id): + await search_skin.finish('您有皮肤正在搜索,请稍等...', at_sender=True) + if raw_arg: + args = raw_arg.split(" ") + if len(args) >= 2: + state['name'] = args[0] + state['skin'] = args[1] + + +@search_skin.got('name', prompt="要查询什么武器呢?") +@search_skin.got('skin', prompt="要查询该武器的什么皮肤呢?") +async def arg_handle(bot: Bot, event: Event, state: T_State): + _ulmt.set_True(event.user_id) + if state['name'] in ['ak', 'ak47']: + state['name'] = 'ak-47' + name = state['name'] + " | " + state['skin'] + try: + result, status_code = await get_price(name) + except FileNotFoundError: + await search_skin.finish('请先对真寻说"设置cookie"来设置cookie!') + if status_code in [996, 997, 998]: + _ulmt.set_False(event.user_id) + await search_skin.finish(result) + if result: + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 查询皮肤:" + name) + _ulmt.set_False(event.user_id) + await search_skin.finish(result) + else: + logger.info(f"USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}" + f" 查询皮肤:{name} 没有查询到") + _ulmt.set_False(event.user_id) + await search_skin.finish("没有查询到哦,请检查格式吧") + + +update_buff_session = on_command("更新cookie", rule=to_me(), permission=SUPERUSER, priority=1) + + +@update_buff_session.handle() +async def _(bot: Bot, event: Event, state: T_State): + await update_buff_session.finish(update_buff_cookie(str(event.get_message())), at_sender=True) diff --git a/plugins/search_buff_skin_price/data_source.py b/plugins/search_buff_skin_price/data_source.py new file mode 100644 index 00000000..e51793fb --- /dev/null +++ b/plugins/search_buff_skin_price/data_source.py @@ -0,0 +1,66 @@ +from util.user_agent import get_user_agent +import aiohttp +from util.utils import get_cookie_text +from configs.path_config import TXT_PATH +from asyncio.exceptions import TimeoutError +from configs.config import buff_proxy +from pathlib import Path +from services.log import logger + + +url = "https://buff.163.com/api/market/goods" + + +async def get_price(dname): + cookie = {'session': get_cookie_text('buff')} + name_list = [] + price_list = [] + parameter = { + "game": "csgo", + "page_num": "1", + "search": dname + } + try: + async with aiohttp.ClientSession(cookies=cookie, headers=get_user_agent()) as session: + async with session.get(url, proxy=buff_proxy, params=parameter, timeout=5) as response: + if response.status == 200: + try: + if str(await response.text()).find("Login Required") != -1: + return "BUFF登录被重置,请联系管理员重新登入", 996 + data = (await response.json())["data"] + total_page = data["total_page"] + data = data["items"] + for _ in range(total_page): + for i in range(len(data)): + name = data[i]["name"] + price = data[i]["sell_reference_price"] + name_list.append(name) + price_list.append(price) + except Exception as e: + return "没有查询到...", 998 + else: + return "访问失败!", response.status + except TimeoutError as e: + return "访问超时! 请重试或稍后再试!", 997 + result = f"皮肤: {dname}({len(name_list)})\n" + # result = "皮肤: " + dname + "\n" + for i in range(len(name_list)): + result += name_list[i] + ": " + price_list[i] + "\n" + return result[:-1], 999 + + +def update_buff_cookie(cookie: str): + _cookie = Path(TXT_PATH + "cookie/buff.txt") + try: + _cookie.parent.mkdir(parents=True, exist_ok=True) + with open(_cookie, 'w') as f: + f.write(cookie) + return "更新cookie成功" + except Exception as e: + logger.error(f'更新cookie失败 e:{e}') + return "更新cookie失败" + + +if __name__ == '__main__': + print(get_price("awp 二西莫夫")) + diff --git a/plugins/send_dinggong_voice/__init__.py b/plugins/send_dinggong_voice/__init__.py new file mode 100644 index 00000000..1248019f --- /dev/null +++ b/plugins/send_dinggong_voice/__init__.py @@ -0,0 +1,33 @@ +from nonebot import on_keyword +from util.init_result import record +from configs.path_config import VOICE_PATH +import random +from services.log import logger +from util.utils import FreqLimiter +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent +from nonebot.rule import to_me +import os + +__plugin_name__ = '骂我' +__plugin_usage__ = '对我说 "骂我",我真的会骂你哦' + +_flmt = FreqLimiter(3) + + +dg_voice = on_keyword({'骂'}, rule=to_me(), priority=5, block=True) + + +@dg_voice.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if len(str((event.get_message()))) == 1: + return + if not _flmt.check(event.user_id): + await dg_voice.finish('就...就算求我骂你也得慢慢来...', at_sender=True) + _flmt.start_cd(event.user_id) + voice = random.choice(os.listdir(VOICE_PATH + 'dinggong/')) + result = record(voice, "dinggong") + await dg_voice.send(result) + await dg_voice.send(voice.split('_')[1]) + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送钉宫骂人:" + result) \ No newline at end of file diff --git a/plugins/send_img/__init__.py b/plugins/send_img/__init__.py new file mode 100644 index 00000000..232e5998 --- /dev/null +++ b/plugins/send_img/__init__.py @@ -0,0 +1,68 @@ +from nonebot import on_command, on_keyword +from configs.path_config import IMAGE_PATH +from util.init_result import image +import os +import random +from util.utils import is_number +from services.log import logger +from nonebot.typing import T_State +from nonebot.adapters import Bot, Event +from util.utils import FreqLimiter, cn2py +from models.group_remind import GroupRemind +from configs.config import IMAGE_DIR_LIST + + +__plugin_name__ = '壁纸/萝莉/美图' +__plugin_usage__ = '用法: 发送"壁纸/萝莉/美图", 回复图片,后添加id获得指定图片' + +_flmt = FreqLimiter(1) + +IMAGE_DIR_LIST.remove('色图') +cmd = set(IMAGE_DIR_LIST) + +# print(cmd) + +send_img = on_command("img", aliases=cmd, priority=5, block=True) + + +@send_img.handle() +async def _(bot: Bot, event: Event, state: T_State): + img_id = str(event.get_message()) + if img_id in ['帮助']: + await send_img.finish(__plugin_usage__) + path = cn2py(state["_prefix"]["raw_command"]) + '/' + if not os.path.exists(IMAGE_PATH + path): + logger.warning(f'未找到 {path} 文件夹,调用取消!') + return + length = len(os.listdir(IMAGE_PATH + path)) - 1 + index = img_id if img_id else str(random.randint(0, length)) + if not is_number(index): + await send_img.finish("id错误!") + if int(index) > length or int(index) < 0: + await send_img.finish(f"超过当前上下限!({length - 1})") + result = image(f'{index}.jpg', path) + if result: + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送{path}:" + result) + await send_img.finish(f"id:{index}" + result) + else: + logger.info( + f"(USER {event.user_id}, GROUP {event.group_id if event.message_type != 'private' else 'private'}) 发送 {path} 失败") + await send_img.finish(f"不想给你看Ov|") + + +pa = on_keyword({"爬"}, priority=1, block=True) + + +@pa.handle() +async def _(bot: Bot, event: Event, state: T_State): + if await GroupRemind.get_status(event.group_id, 'pa'): + try: + if str(event.get_message()[:2]) in ['开启', '关闭']: + return + except: + return + if not _flmt.check(event.user_id): + return + _flmt.start_cd(event.user_id) + await pa.finish(image(random.choice(os.listdir(IMAGE_PATH + "pa")), 'pa')) diff --git a/plugins/send_setu/__init__.py b/plugins/send_setu/__init__.py new file mode 100644 index 00000000..3ff3dff7 --- /dev/null +++ b/plugins/send_setu/__init__.py @@ -0,0 +1,276 @@ +import random +from nonebot import on_command, on_regex +from services.log import logger +from models.sigin_group_user import SignGroupUser +from util.utils import FreqLimiter, UserExistLimiter, is_number, get_message_text, get_message_imgs +from nonebot.typing import T_State +from nonebot.adapters.cqhttp import Bot, MessageEvent, GroupMessageEvent, PrivateMessageEvent +from .data_source import get_setu, get_luoxiang, search_online_setu, get_setu_urls, \ + check_r18_and_keyword, find_img_index +from nonebot.adapters.cqhttp.exception import ActionFailed +import re +from models.count_user import UserCount +from aiohttp.client_exceptions import ClientConnectorError +from configs.config import LOCAL_SETU + +__plugin_name__ = '色图' +__plugin_usage__ = '''示例: + 1. 色图 (随机本地色图) + 2. 色图r (随机在线十张r18涩图) + 3. 色图 666 (本地色图id) + 4. 色图 xx (在线搜索xx色图) + 5. 色图r xx (搜索十张xx的r18涩图,注意空格)(仅私聊) + 6. 来n张涩图 (本地涩图连发)(1<=n<=9) + 7. 来n张xx的涩图 (在线搜索xx涩图)(较慢,看网速) +注:【色图r每日提供5次 + 本地涩图没有r18! + 联网搜索会较慢! + 如果图片数量与数字不符, + 原因1:网络不好,网线被拔QAQ + 原因2:搜索到的总数小于数字 + 原因3:图太色或者小错误了】''' + +url = "https://api.lolicon.app/setu/" +_flmt = FreqLimiter(5) +_ulmt = UserExistLimiter() +path = "setu/" +MAX_COUNT = 5 + + +setu = on_command("色图", aliases={"涩图", "不够色", "来一发", "再来点"}, priority=5, block=True) +setu_reg = on_regex('(.*)[份|发|张|个|次|点](.*)[瑟|色|涩]图', priority=5, block=True) +find_setu = on_command("查色图", priority=5, block=True) + + +@setu.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + img_id = get_message_text(event.json()) + if img_id in ['帮助']: + await setu.finish(__plugin_usage__) + impression = (await SignGroupUser.ensure(event.user_id, event.group_id)).impression + luox = get_luoxiang(impression) + if luox: + await setu.finish(luox) + if not _flmt.check(event.user_id): + await setu.finish('您冲得太快了,请稍候再冲', at_sender=True) + _flmt.start_cd(event.user_id) + if _ulmt.check(event.user_id): + await setu.finish(f"您有色图正在处理,请稍等") + _ulmt.set_True(event.user_id) + setu_img, index = get_setu(img_id) + if setu_img and LOCAL_SETU: + try: + await setu.send(setu_img) + except: + _ulmt.set_False(event.user_id) + await setu.finish('这张图色过头了,我自己看看就行了!', at_sender=True) + logger.info( + f"USER {event.user_id} GROUP {event.group_id} 发送色图 {index}.jpg 成功") + else: + msg = img_id + if msg.find(list(bot.config.nickname)[0]) != -1: + _ulmt.set_False(event.user_id) + await setu.finish('咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀') + keyword, r18, num = await check_r18_and_keyword(msg, event.user_id) + if r18 == 1: + _ulmt.set_False(event.user_id) + await setu.finish(random.choice([ + "这种不好意思的东西怎么可能给这么多人看啦", + "羞羞脸!给我滚出克私聊!", + "变态变态变态变态大变态!" + ])) + try: + urls, text_list, code = await get_setu_urls(keyword, num, r18=r18) + except ClientConnectorError: + await setu.finish('网络失败了..别担心!正在靠运气上网!', at_sender=True) + else: + if code == 200: + for i in range(num): + try: + setu_img, index = await search_online_setu(urls[i]) + await setu.send(text_list[i] + setu_img) + logger.info( + f"USER {event.user_id} GROUP {event.group_id}" + f" 发送在线色图 {keyword}.jpg 成功") + except Exception as e: + logger.error(f'色图发送错误 e:{e}') + await setu.send('图片下载惜败!', at_sender=True) + else: + await setu.send(urls) + _ulmt.set_False(event.user_id) + + +@setu.handle() +async def _(bot: Bot, event: PrivateMessageEvent, state: T_State): + img_id = get_message_text(event.json()) + if img_id in ['帮助']: + await setu.finish(__plugin_usage__) + if not _flmt.check(event.user_id): + await setu.finish('您冲得太快了,请稍候再冲', at_sender=True) + _flmt.start_cd(event.user_id) + if _ulmt.check(event.user_id): + await setu.finish(f"您有色图正在处理,请稍等") + _ulmt.set_True(event.user_id) + setu_img, index = get_setu(img_id) + if setu_img: + await setu.send(setu_img) + logger.info( + f"USER {event.user_id} GROUP private 发送色图 {index}.jpg 成功") + else: + msg = img_id + if msg.find(list(bot.config.nickname)[0]) != -1: + _ulmt.set_False(event.user_id) + await setu.finish('咳咳咳,虽然我很可爱,但是我木有自己的色图~~~有的话记得发我一份呀') + keyword, r18, num = await check_r18_and_keyword(msg, event.user_id) + if r18 == 1: + if await UserCount.check_count(event.user_id, 'setu_r18', MAX_COUNT): + _ulmt.set_False(event.user_id) + await setu.finish('要节制啊,请明天再来...\n【每日提供5次】', at_sender=True) + try: + urls, text_list, code = await get_setu_urls(keyword, num, r18=r18) + except ClientConnectorError: + await UserCount.add_count(event.user_id, 'setu_r18', count=-1) + await setu.finish('网络失败了..别担心!这次搜索不算数喔', at_sender=True) + else: + count = 0 + if code == 200: + for i in range(num): + try: + setu_img, index = await search_online_setu(urls[i]) + await setu.send(text_list[i] + setu_img) + logger.info( + f"USER {event.user_id} GROUP private" + f" 发送{'r18' if img_id == 'r' else ''}色图 {index}.jpg 成功") + except Exception as e: + logger.error(f'色图发送错误 e:{e}') + await setu.send('图片下载惜败!', at_sender=True) + count += 1 + if count > 6: + await setu.send('检测到下载惜败的图片过多,这次就不算数了,果咩..', at_sender=True) + await UserCount.add_count(event.user_id, 'setu_r18', count=-1) + else: + if code == 401: + if r18 == 1: + await UserCount.add_count(event.user_id, 'setu_r18', count=-1) + await setu.send(urls + ' 色图r次数返还!') + else: + await setu.send(urls, at_sender=True) + else: + if r18 == 1: + await setu.send('这次不是小真寻的戳!色图r次数返还!', at_sender=True) + await UserCount.add_count(event.user_id, 'setu_r18', count=-1) + else: + await setu.send(urls, at_sender=True) + _ulmt.set_False(event.user_id) + + +num_key = { + '一': 1, + '二': 2, + '两': 2, + '双': 2, + '三': 3, + '四': 4, + '五': 5, + '六': 6, + '七': 7, + '八': 8, + '九': 9 +} + + +@setu_reg.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if event.message_type == "group": + impression = (await SignGroupUser.ensure(event.user_id, event.group_id)).impression + luox = get_luoxiang(impression) + if luox: + await setu.finish(luox, at_sender=True) + if _ulmt.check(event.user_id): + await setu.finish(f"您有色图正在处理,请稍等") + _ulmt.set_True(event.user_id) + if not _flmt.check(event.user_id): + _ulmt.set_False(event.user_id) + await setu.finish('您冲得太快了,请稍候再冲', at_sender=True) + _flmt.start_cd(event.user_id) + msg = get_message_text(event.json()) + num = 1 + msg = re.search(r'(.*)[份|发|张|个|次|点](.*)[瑟|涩|色]图', msg) + if msg: + num = msg.group(1) + keyword = msg.group(2) + if keyword: + if keyword[-1] == '的': + keyword = keyword[:-1] + if num: + num = num[-1] + if num_key.get(num): + num = num_key[num] + elif is_number(num): + try: + num = int(num) + except ValueError: + num = 1 + else: + num = 1 + else: + return + # try: + if not keyword: + for _ in range(num): + try: + img, index = get_setu('') + await setu_reg.send(img) + except Exception as e: + await setu_reg.send('有图太色了发不出来...') + else: + logger.info( + f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'}" + f" 发送 {index} 色图成功") + else: + urls, text_list, code = await get_setu_urls(keyword, num) + if code == 200: + for i in range(len(urls)): + try: + setu_img, index = await search_online_setu(urls[i]) + await setu_reg.send(text_list[i] + '\n' + setu_img) + except ActionFailed as e: + await setu_reg.send('这图太色了,会教坏小孩子的,不给看..') + else: + logger.info( + f"USER {event.user_id} GROUP {event.group_id if event.message_type != 'private' else 'private'}" + f" 发送 {keyword} {num}连 色图成功") + else: + _ulmt.set_False(event.user_id) + await setu_reg.finish(urls, at_sender=True) + _ulmt.set_False(event.user_id) + + +@find_setu.args_parser +async def _(bot: Bot, event: MessageEvent, state: T_State): + if str(event.message) == '取消': + await find_setu.finish('取消了操作', at_sender=True) + imgs = get_message_imgs(event.json()) + if not imgs: + await find_setu.reject("不搞错了,俺要图!") + state['img'] = imgs[0] + + +@find_setu.handle() +async def _(bot: Bot, event: MessageEvent, state: T_State): + if get_message_text(event.json()) in ['帮助']: + await find_setu.finish('通过图片获取本地色图id\n\t示例:查色图(图片)') + imgs = get_message_imgs(event.json()) + if imgs: + state['img'] = imgs[0] + + +@find_setu.got('img', prompt="速速来图!") +async def _(bot: Bot, event: MessageEvent, state: T_State): + img = state['img'] + await find_setu.send(await find_img_index(img, event.user_id), at_sender=True) + + + + + diff --git a/plugins/send_setu/check_setu_hash.py b/plugins/send_setu/check_setu_hash.py new file mode 100644 index 00000000..e4ce21b1 --- /dev/null +++ b/plugins/send_setu/check_setu_hash.py @@ -0,0 +1,62 @@ +import os +import imagehash +from PIL import Image +try: + import ujson as json +except ModuleNotFoundError: + import json + +IMAGE_PATH = r"/home/hibiki/hibikibot/resources/img/" +TXT_PATH = r"/home/hibiki/hibikibot/resources/txt/" + + +def get_img_hash(image_file): + with open(image_file, 'rb') as fp: + hash_value = imagehash.average_hash(Image.open(fp)) + return hash_value + + +def check_file_index(): + for dir_name in ['setu/', 'r18/']: + lens = len(os.listdir(IMAGE_PATH + dir_name)) + for i in range(lens): + if i > lens: + return + if not os.path.exists(f"{IMAGE_PATH}{dir_name}{i}.jpg"): + os.rename(f"{IMAGE_PATH}{dir_name}{lens}.jpg", f"{IMAGE_PATH}{dir_name}{i}.jpg") + print(f'{lens}.jpg --> {i}.jpg') + lens -= 1 + if not i % 100: + print(f'已检测 {i} 份数据') + print(f'{dir_name} 检测完毕') + + +def check_setu_hash(): + check_file_index() + for dir_name in ['setu/', 'r18/']: + img_data = {} + if dir_name == 'setu/': + fn = 'setu_img_hash.json' + else: + fn = 'r18_setu_img_hash.json' + file_list_len = len(os.listdir(IMAGE_PATH + dir_name)) - 1 + print(file_list_len) + for i in range(file_list_len): + file = f"{i}.jpg" + index = file.split(".")[0] + img_hash = str(get_img_hash(IMAGE_PATH + dir_name + file)) + print(f'{index}.jpg --> {img_hash}') + if img_hash in img_data.values(): + k = [k for k, v in img_data.items() if v == img_hash] + print(f'文件 {index}.jpg 与 {k}.jpg 重复,使用 {file_list_len}.jpg 进行替换') + os.remove(IMAGE_PATH + dir_name + file) + os.rename(IMAGE_PATH + f'{dir_name}{file_list_len}.jpg', IMAGE_PATH + dir_name + file) + file_list_len -= 1 + continue + img_data[index] = img_hash + # print(f'{index}.jpg --> {img_data}') + with open(TXT_PATH + fn, 'w') as f: + json.dump(img_data, f, indent=4) + + +check_setu_hash() \ No newline at end of file diff --git a/plugins/send_setu/data_source.py b/plugins/send_setu/data_source.py new file mode 100644 index 00000000..6db1281c --- /dev/null +++ b/plugins/send_setu/data_source.py @@ -0,0 +1,168 @@ +from configs.path_config import IMAGE_PATH, TXT_PATH +import os +import random +from util.init_result import image +from configs.config import LOLICON_KEY +import aiohttp +import aiofiles +from services.log import logger +from util.img_utils import get_img_hash +from util.utils import get_local_proxy, is_number +from asyncio.exceptions import TimeoutError +from models.count_user import UserCount +from configs.config import DOWNLOAD_SETU +try: + import ujson as json +except ModuleNotFoundError: + import json + + +url = "https://api.lolicon.app/setu/" +path = 'setu/' + + +async def get_setu_urls(keyword: str, num: int = 1, r18: int = 0): + # print(keyword) + if r18 == 1: + file_name = 'setu_r18_url.txt' + else: + file_name = 'setu_url.txt' + try: + with open(TXT_PATH + file_name, 'r') as f: + txt_data = f.read() + except FileNotFoundError: + txt_data = '' + params = { + "apikey": LOLICON_KEY, # 添加apikey + 'r18': r18, # 添加r18参数 0为否,1为是,2为混合 + 'keyword': keyword, # 若指定关键字,将会返回从插画标题、作者、标签中模糊搜索的结果 + 'num': num, # 一次返回的结果数量,范围为1到10,不提供 APIKEY 时固定为1 + 'size1200': 1, # 是否使用 master_1200 缩略图,以节省流量或提升加载速度 + } + urls = [] + text_list = [] + for count in range(3): + print(f'get_setu_url: count --> {count}') + async with aiohttp.ClientSession() as session: + try: + async with session.get(url, proxy=get_local_proxy(), timeout=5, params=params) as response: + if response.status == 429: + return '调用达到上限,明日赶早呀~', '', 429 + if response.status == 404: + return "网站裂开了...", '', 998 + if response.status == 200: + data = await response.json() + if data['code'] == 0: + # print(len(data['data'])) + for i in range(len(data['data'])): + img_url = data['data'][i]['url'] + title = data['data'][i]['title'] + author = data['data'][i]['author'] + pid = data['data'][i]['pid'] + urls.append(img_url) + text_list.append(f'title:{title}\nauthor:{author}\nPID:{pid}') + img_url = str(img_url).replace('img-master', 'img-original').replace('_master1200', '') + txt_data += img_url + ',' + if DOWNLOAD_SETU: + with open(TXT_PATH + file_name, 'w') as f: + f.write(txt_data) + return urls, text_list, 200 + else: + return "没找到符合条件的色图...", '', 401 + except TimeoutError: + pass + return '我网线被人拔了..QAQ', '', 999 + + +async def search_online_setu(url: str): + async with aiohttp.ClientSession() as session: + for i in range(3): + print(f'search_online_setu --> {i}') + try: + async with session.get(url, proxy=get_local_proxy(), timeout=7) as res: + if res.status == 200: + index = str(random.randint(1, 100000)) + async with aiofiles.open(IMAGE_PATH + 'temp/' + index + "_temp_setu.jpg", 'wb') as f: + try: + await f.write(await res.read()) + except TimeoutError: + # return '\n这图没下载过来~(网太差?)', -1, False + continue + logger.info(f"下载 lolicon图片 {url} 成功, id:{index}") + return image(f'{index}_temp_setu.jpg', 'temp'), index + else: + logger.warning(f"访问 lolicon图片 {url} 失败 status:{res.status}") + # return '\n这图好难下载啊!QAQ', -1, False + except TimeoutError: + pass + return '\n图片被小怪兽恰掉啦..!QAQ', -1 + + +def get_setu(index: str): + length = len(os.listdir(IMAGE_PATH + path)) + if not index: + index = random.randint(0, length - 1) + if is_number(index): + if int(index) > length or int(index) < 0: + return f"超过当前上下限!({length - 1})", 999 + else: + return f'id:{index}' + image(f'{index}.jpg', 'setu'), index + return None, None + + +def get_luoxiang(impression): + probability = impression + 70 + if probability < random.randint(1, 101): + return "我为什么要给你发这个?" + image(random.choice(os.listdir(IMAGE_PATH + "luoxiang/")), 'luoxiang') + \ + "\n(快向小真寻签到提升好感度吧!)" + return None + + +async def check_r18_and_keyword(msg: str, user_id) -> 'str, int, int': + msg_list = msg.split(' ') + num = 1 + r18 = 0 + keyword = '' + if len(msg_list) == 1: + if msg_list[0].strip().lower() in ['r', 'r18']: + r18 = 1 + num = 10 + else: + keyword = msg_list[0] + elif len(msg_list) == 2: + keyword = msg_list[1].strip() + if msg_list[0].strip().lower() in ['r', 'r18']: + r18 = 1 + num = 10 + else: + keyword = msg[0] + if r18 == 1: + await UserCount.add_user(user_id) + return keyword, r18, num + + +# def delete_img(index): +# if os.path.exists(IMAGE_PATH + path + f"{index}.jpg"): +# img_hash = str(get_img_hash(IMAGE_PATH + f"setu/{index}.jpg")) +# tp = list(setu_hash_dict.keys())[list(setu_hash_dict.values()).index(img_hash)] +# logger.info(f"色图 {index}.jpg 与 {tp}.jpg 相似, 删除....") +# os.remove(IMAGE_PATH + path + f"{index}.jpg") + + +async def find_img_index(img_url, user_id): + try: + setu_hash_dict = json.load(open(TXT_PATH + 'setu_img_hash.json')) + except (FileNotFoundError, ValueError): + setu_hash_dict = {} + async with aiohttp.ClientSession() as session: + async with session.get(img_url, proxy=get_local_proxy(), timeout=5) as res: + async with aiofiles.open(IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg", 'wb') as f: + await f.write(await res.read()) + img_hash = str(get_img_hash(IMAGE_PATH + f"temp/{user_id}_find_setu_index.jpg")) + try: + tp = list(setu_hash_dict.keys())[list(setu_hash_dict.values()).index(img_hash)] + return "id --> " + str(tp) + except ValueError: + return "该图不在色图库中!" + + diff --git a/plugins/shop/buy.py b/plugins/shop/buy.py new file mode 100644 index 00000000..9531b9b9 --- /dev/null +++ b/plugins/shop/buy.py @@ -0,0 +1,78 @@ +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from util.utils import get_message_text, is_number +from models.bag_user import UserBag +from services.db_context import db +from nonebot.adapters.cqhttp.permission import GROUP + + +__plugin_name__ = '商店购买' +__plugin_usage__ = '格式:购买 名称或序号 数量(选填,默认为1)\n\t示例:购买 好感双倍加持卡Ⅰ\n\t示例:购买 1 4' + + +buy = on_command("购买", aliases={'购买道具'}, priority=5, block=True, permission=GROUP) + +goods = [ + '好感双倍加持卡Ⅰ\t\t售价:30金币\n\t\t今日双倍好感度的概率 + 10%(谁才是真命天子?)(同类道具将覆盖)', + '好感双倍加持卡Ⅱ\t\t售价:140金币\n\t\t今日双倍好感度的概率 + 20%(平平庸庸~)(同类道具将覆盖)', + '好感双倍加持卡Ⅲ\t\t售价:250金币\n\t\t今日双倍好感度的概率 + 30%(金币才是真命天子!)(同类道具将覆盖)' +] +glist = [] +plist = [] +for i in range(len(goods)): + glist.append(goods[i].split('\t\t')[0]) + plist.append(int(goods[i].split('\t\t')[1].split(':')[1].split('金币')[0])) + + +@buy.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + if get_message_text(event.json()) in ['', '帮助']: + await buy.finish(__plugin_usage__) + if get_message_text(event.json()) in ['神秘药水']: + await buy.finish("你们看看就好啦,这是不可能卖给你们的~", at_sender=True) + msg = get_message_text(event.json()).strip().split(' ') + index = -1 + num = 1 + if len(msg) > 1: + if is_number(msg[1]): + num = int(msg[1]) + print(msg, num) + if is_number(msg[0]): + msg = int(msg[0]) + if msg > len(goods) or msg < 1: + await buy.finish('请输入正确的商品id!', at_sender=True) + index = msg - 1 + else: + if msg[0] in glist: + for i in range(len(glist)): + if msg == glist[i]: + index = i + break + else: + await buy.finish('请输入正确的商品名称!', at_sender=True) + async with db.transaction(): + if index != -1: + if (await UserBag.get_gold(event.user_id, event.group_id)) < plist[index] * num: + await buy.finish('您的金币好像不太够哦', at_sender=True) + if await UserBag.spend_glod(event.user_id, event.group_id, plist[index] * num): + for _ in range(num): + await UserBag.add_props(event.user_id, event.group_id, glist[index]) + await buy.send(f'花费 {plist[index]*num} 金币购买 {glist[index]} ×{num} 成功!', at_sender=True) + logger.info(f'USER {event.user_id} GROUP {event.group_id} ' + f'花费 {plist[index]*num} 金币购买 {glist[index]} ×{num} 成功!') + else: + await buy.send(f'{glist[index]} 购买失败!', at_sender=True) + logger.info(f'USER {event.user_id} GROUP {event.group_id} ' + f'花费 {plist[index]*num} 金币购买 {glist[index]} ×{num} 失败!') + + + + + + + + + + diff --git a/plugins/shop/my_gold.py b/plugins/shop/my_gold.py new file mode 100644 index 00000000..5306d345 --- /dev/null +++ b/plugins/shop/my_gold.py @@ -0,0 +1,26 @@ +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from models.bag_user import UserBag +from nonebot.adapters.cqhttp.permission import GROUP + + +__plugin_name__ = '我的金币' + + +my_gold = on_command("我的金币", priority=5, block=True, permission=GROUP) + + +@my_gold.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await my_gold.finish(await UserBag.get_my_total_gold(event.user_id, event.group_id)) + + + + + + + + + + diff --git a/plugins/shop/my_props.py b/plugins/shop/my_props.py new file mode 100644 index 00000000..959ac07c --- /dev/null +++ b/plugins/shop/my_props.py @@ -0,0 +1,46 @@ +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from models.bag_user import UserBag +from nonebot.adapters.cqhttp.permission import GROUP + + +__plugin_name__ = '商店基础显示' + + +my_props = on_command("我的道具", priority=5, block=True, permission=GROUP) + + +@my_props.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + props = await UserBag.get_props(event.user_id, event.group_id) + if props: + pname_list = [] + pnum_list = [] + rst = '' + props = props[:-1].split(',') + for p in props: + if p != '': + if p in pname_list: + pnum_list[pname_list.index(p)] += 1 + else: + pname_list.append(p) + pnum_list.append(1) + for i in range(len(pname_list)): + rst += f'{i+1}.{pname_list[i]}\t×{pnum_list[i]}\n' + await my_props.send('\n' + rst[:-1], at_sender=True) + logger.info(f'USER {event.user_id} GROUP {event.group_id} 查看我的道具') + else: + await my_props.finish('您的背包里没有任何的道具噢~', at_sender=True) + + + + + + + + + + + diff --git a/plugins/shop/reset_today_gold.py b/plugins/shop/reset_today_gold.py new file mode 100644 index 00000000..7d76a2f6 --- /dev/null +++ b/plugins/shop/reset_today_gold.py @@ -0,0 +1,41 @@ +from util.utils import scheduler +from models.bag_user import UserBag +from services.log import logger + + +# 重置每日金币 +@scheduler.scheduled_job( + 'cron', + # year=None, + # month=None, + # day=None, + # week=None, + # day_of_week="mon,tue,wed,thu,fri", + hour=0, + minute=1, + # second=None, + # start_date=None, + # end_date=None, + # timezone=None, +) +async def _(): + try: + user_list = await UserBag.get_user_all() + if user_list: + for user in user_list: + await user.update( + get_today_gold=0, + spend_today_gold=0, + ).apply() + except Exception as e: + logger.error(f'重置每日金币错误 e:{e}') + + + + + + + + + + diff --git a/plugins/shop/shop_help.py b/plugins/shop/shop_help.py new file mode 100644 index 00000000..bb402085 --- /dev/null +++ b/plugins/shop/shop_help.py @@ -0,0 +1,41 @@ +from nonebot import on_command +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from util.img_utils import CreateImg +from configs.path_config import IMAGE_PATH +from util.init_result import image + + +__plugin_name__ = '商店' + + +shop_help = on_command("商店", priority=5, block=True) + +goods = [ + '好感双倍加持卡Ⅰ\t\t售价:30金币\n\t\t下次签到双倍好感度的概率 + 10%(谁才是真命天子?)(同类商品将覆盖)', + '好感双倍加持卡Ⅱ\t\t售价:150金币\n\t\t下次签到双倍好感度的概率 + 20%(平平庸庸~)(同类商品将覆盖)', + '好感双倍加持卡Ⅲ\t\t售价:250金币\n\t\t下次签到双倍好感度的概率 + 30%(金币才是真命天子!)(同类商品将覆盖)' +] + +result = '' +for i in range(len(goods)): + result += f'{i + 1}.{goods[i]}\n' +shop = CreateImg(1000, 1000, background=IMAGE_PATH + 'other/shop.png', font_size=20) +shop.text((100, 170), '注【通过 数字 或者 商品名称 购买】') +shop.text((20, 230), result) +shop.text((20, 900), '神秘药水\t\t售价:9999999金币\n\t\t鬼知道会有什么效果~') +shop.save(IMAGE_PATH + 'shop.png') + + +@shop_help.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + await shop_help.send(image('shop.png')) + + + + + + + + + diff --git a/plugins/shop/use/__init__.py b/plugins/shop/use/__init__.py new file mode 100644 index 00000000..2bb30df2 --- /dev/null +++ b/plugins/shop/use/__init__.py @@ -0,0 +1,61 @@ +from nonebot import on_command +from services.log import logger +from nonebot.adapters.cqhttp import Bot, GroupMessageEvent +from nonebot.typing import T_State +from util.utils import is_number, get_message_text +from models.bag_user import UserBag +from nonebot.adapters.cqhttp.permission import GROUP +from services.db_context import db +from .data_source import effect + + +__plugin_name__ = '使用道具' +__plugin_usage__ = '输入 “使用道具 xxx(序号 或 道具名称)“ 即可使用道具\n【注】序号以我的道具序号为准,更推荐使用道具名称使用道具(怕出错)' + + +use_props = on_command("使用道具", priority=5, block=True, permission=GROUP) + + +@use_props.handle() +async def _(bot: Bot, event: GroupMessageEvent, state: T_State): + msg = get_message_text(event.json()) + if msg in ['', '帮助']: + await use_props.finish(__plugin_usage__) + props = await UserBag.get_props(event.user_id, event.group_id) + if props: + async with db.transaction(): + pname_list = [] + props = props[:-1].split(',') + for p in props: + if p != '': + if p not in pname_list: + pname_list.append(p) + if is_number(msg): + if 0 < int(msg) <= len(pname_list): + name = pname_list[int(msg) - 1] + else: + await use_props.finish('仔细看看自己的道具仓库有没有这个道具?', at_sender=True) + else: + if msg not in pname_list: + await use_props.finish('道具名称错误!', at_sender=True) + name = msg + if await UserBag.del_props(event.user_id, event.group_id, name) and\ + await effect(event.user_id, event.group_id, name): + await use_props.send(f'使用道具 {name} 成功!', at_sender=True) + logger.info(f'USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 成功') + else: + await use_props.send(f'使用道具 {name} 失败!', at_sender=True) + logger.info(f'USER {event.user_id} GROUP {event.group_id} 使用道具 {name} 失败') + else: + await use_props.send('您的背包里没有任何的道具噢~', at_sender=True) + + + + + + + + + + + diff --git a/plugins/shop/use/data_source.py b/plugins/shop/use/data_source.py new file mode 100644 index 00000000..d6d70a29 --- /dev/null +++ b/plugins/shop/use/data_source.py @@ -0,0 +1,40 @@ +from models.sigin_group_user import SignGroupUser + + +async def effect(user_id: int, group_id: int, name: str) -> bool: + print(name) + # try: + if name == '好感双倍加持卡Ⅰ': + user = await SignGroupUser.ensure(user_id, group_id) + await user.update( + add_probability=0.1 + ).apply() + if name == '好感双倍加持卡Ⅱ': + user = await SignGroupUser.ensure(user_id, group_id) + await user.update( + add_probability=0.2 + ).apply() + if name == '好感双倍加持卡Ⅲ': + user = await SignGroupUser.ensure(user_id, group_id) + print(user.user_qq) + await user.update( + add_probability=0.3 + ).apply() + return True + # except Exception: + # return False + + + + + + + + + + + + + + +