2022-06-17 05:59:15 +00:00
|
|
|
|
import asyncio
|
2022-06-19 13:42:53 +00:00
|
|
|
|
import os
|
2022-06-17 05:59:15 +00:00
|
|
|
|
import random
|
2022-06-19 12:24:44 +00:00
|
|
|
|
import re
|
2022-06-19 06:36:25 +00:00
|
|
|
|
import traceback
|
2022-07-31 08:27:58 +00:00
|
|
|
|
from datetime import datetime
|
2022-06-17 05:59:15 +00:00
|
|
|
|
|
2022-07-31 08:27:58 +00:00
|
|
|
|
from PIL import Image as PILImage
|
2022-06-19 06:12:41 +00:00
|
|
|
|
from bs4 import BeautifulSoup
|
2022-06-17 05:59:15 +00:00
|
|
|
|
from tenacity import retry, stop_after_attempt
|
|
|
|
|
|
2023-02-05 14:33:33 +00:00
|
|
|
|
from core.builtins import Bot
|
|
|
|
|
from core.builtins import Image, Plain
|
2023-03-04 08:51:56 +00:00
|
|
|
|
from core.component import module
|
2022-06-17 06:35:59 +00:00
|
|
|
|
from core.logger import Logger
|
2023-02-05 14:33:33 +00:00
|
|
|
|
from core.utils.cache import random_cache_path
|
|
|
|
|
from core.utils.http import get_url, download_to_cache
|
2022-06-19 06:58:43 +00:00
|
|
|
|
|
2022-06-19 10:02:51 +00:00
|
|
|
|
csr_link = 'https://www.chemspider.com' # ChemSpider 的链接
|
2022-06-19 13:11:04 +00:00
|
|
|
|
special_id = ["22398", "140526", "4509317", "4509318", "4510681", "4510778", "4512975", "4514248", "4514266", "4514293",
|
2022-07-30 09:56:57 +00:00
|
|
|
|
"4514330", "4514408", "4514534", "4514586", "4514603", "4515054", "4573995", "4574465", "4575369",
|
|
|
|
|
"4575370",
|
2023-01-28 05:53:11 +00:00
|
|
|
|
"4575371", "4885606", "4885717", "4886482", "4886484", "20473555", "21865276",
|
|
|
|
|
"21865280"] # 可能会导致识别问题的物质(如部分单质)ID,这些 ID 的图片将会在本地调用
|
2022-06-19 06:12:41 +00:00
|
|
|
|
|
|
|
|
|
|
2022-06-19 06:36:25 +00:00
|
|
|
|
@retry(stop=stop_after_attempt(3), reraise=True)
|
2022-06-19 10:02:51 +00:00
|
|
|
|
async def search_csr(id=None): # 根据 ChemSpider 的 ID 查询 ChemSpider 的链接,留空(将会使用缺省值 None)则随机查询
|
|
|
|
|
if id is not None: # 如果传入了 ID,则使用 ID 查询
|
2022-06-19 13:42:53 +00:00
|
|
|
|
answer_id = id
|
2022-06-19 08:06:11 +00:00
|
|
|
|
else:
|
2023-02-19 05:45:48 +00:00
|
|
|
|
answer_id = random.randint(1, 116000000) # 否则随机查询一个题目
|
2022-06-19 13:42:53 +00:00
|
|
|
|
answer_id = str(answer_id)
|
2022-06-20 13:52:40 +00:00
|
|
|
|
Logger.info("ChemSpider ID: " + answer_id)
|
2022-06-19 13:42:53 +00:00
|
|
|
|
get = await get_url(csr_link + '/Search.aspx?q=' + answer_id, 200, fmt='text') # 在 ChemSpider 上搜索此化学式或 ID
|
2022-06-19 06:36:25 +00:00
|
|
|
|
# Logger.info(get)
|
2022-06-19 10:02:51 +00:00
|
|
|
|
soup = BeautifulSoup(get, 'html.parser') # 解析 HTML
|
2022-06-19 11:49:56 +00:00
|
|
|
|
name = soup.find('span',
|
|
|
|
|
id='ctl00_ctl00_ContentSection_ContentPlaceHolder1_RecordViewDetails_rptDetailsView_ctl00_prop_MF').text # 获取化学式名称
|
2022-06-19 12:24:44 +00:00
|
|
|
|
values_ = re.split(r'[A-Za-z]+', name) # 去除化学式名称中的字母
|
|
|
|
|
value = 0 # 起始元素记数,忽略单个元素(有无意义不大)
|
|
|
|
|
for v in values_: # 遍历剔除字母后的数字
|
|
|
|
|
if v.isdigit():
|
|
|
|
|
value += int(v) # 加一起
|
2022-06-19 12:29:52 +00:00
|
|
|
|
wh = 500 * value // 100
|
2022-06-19 12:39:52 +00:00
|
|
|
|
if wh < 500:
|
|
|
|
|
wh = 500
|
2022-06-19 15:56:38 +00:00
|
|
|
|
return {'id': answer_id, 'name': name,
|
|
|
|
|
'image': f'https://www.chemspider.com/ImagesHandler.ashx?id={answer_id}' +
|
2022-07-30 09:56:57 +00:00
|
|
|
|
(f"&w={wh}&h={wh}" if answer_id not in special_id else ""), 'length': value}
|
2022-06-19 06:12:41 +00:00
|
|
|
|
|
|
|
|
|
|
2023-03-04 08:51:56 +00:00
|
|
|
|
cc = module('chemical_code', alias={'cc': 'chemical_code',
|
2022-06-28 15:46:14 +00:00
|
|
|
|
'chemicalcode': 'chemical_code',
|
|
|
|
|
'captcha': 'chemical_code captcha'},
|
2023-04-05 02:22:37 +00:00
|
|
|
|
desc='{chemical_code.help.desc}', developers=['OasisAkari'])
|
2022-06-19 10:02:51 +00:00
|
|
|
|
play_state = {} # 创建一个空字典用于存放游戏状态
|
2022-06-17 05:59:15 +00:00
|
|
|
|
|
|
|
|
|
|
2023-04-06 04:48:50 +00:00
|
|
|
|
@cc.command('{{chemical_code.help}}') # 直接使用 cc 命令将触发此装饰器
|
2023-02-05 14:33:33 +00:00
|
|
|
|
async def chemical_code_by_random(msg: Bot.MessageSession):
|
2022-06-19 10:05:56 +00:00
|
|
|
|
await chemical_code(msg) # 将消息会话传入 chemical_code 函数
|
2022-06-19 08:06:11 +00:00
|
|
|
|
|
|
|
|
|
|
2023-03-15 11:07:09 +00:00
|
|
|
|
@cc.command('captcha {{chemical_code.help.captcha}}')
|
2023-02-05 14:33:33 +00:00
|
|
|
|
async def _(msg: Bot.MessageSession):
|
2022-06-28 15:44:54 +00:00
|
|
|
|
await chemical_code(msg, captcha_mode=True)
|
|
|
|
|
|
|
|
|
|
|
2023-04-06 04:48:50 +00:00
|
|
|
|
@cc.command('stop {{chemical_code.stop.help}}')
|
2023-02-05 14:33:33 +00:00
|
|
|
|
async def s(msg: Bot.MessageSession):
|
2022-06-19 10:02:51 +00:00
|
|
|
|
state = play_state.get(msg.target.targetId, False) # 尝试获取 play_state 中是否有此对象的游戏状态
|
|
|
|
|
if state: # 若有
|
|
|
|
|
if state['active']: # 检查是否为活跃状态
|
|
|
|
|
play_state[msg.target.targetId]['active'] = False # 标记为非活跃状态
|
2023-04-06 04:48:50 +00:00
|
|
|
|
await msg.sendMessage(msg.locale.t('chemical_code.stop.message', answer=play_state[msg.target.targetId]["answer"]), quote=False) # 发送存储于 play_state 中的答案
|
2022-06-19 08:15:14 +00:00
|
|
|
|
else:
|
2023-04-06 04:48:50 +00:00
|
|
|
|
await msg.sendMessage(msg.locale.t('chemical_code.stop.message.none'))
|
2022-06-19 08:15:14 +00:00
|
|
|
|
else:
|
2023-04-06 04:48:50 +00:00
|
|
|
|
await msg.sendMessage(msg.locale.t('chemical_code.stop.message.none'))
|
2022-06-19 08:15:14 +00:00
|
|
|
|
|
|
|
|
|
|
2023-03-15 11:07:09 +00:00
|
|
|
|
@cc.command('<csid> {{chemical_code.help.csid}}')
|
2023-02-05 14:42:13 +00:00
|
|
|
|
async def chemical_code_by_id(msg: Bot.MessageSession):
|
2022-06-19 11:30:55 +00:00
|
|
|
|
id = msg.parsed_msg['<csid>'] # 从已解析的消息中获取 ChemSpider ID
|
2023-02-23 14:40:46 +00:00
|
|
|
|
if (id.isdigit() and int(id) > 0): # 如果 ID 为纯数字
|
2022-06-19 10:05:56 +00:00
|
|
|
|
await chemical_code(msg, id) # 将消息会话和 ID 一并传入 chemical_code 函数
|
2022-06-19 08:09:52 +00:00
|
|
|
|
else:
|
2023-03-15 11:07:09 +00:00
|
|
|
|
await msg.finish(msg.locale.t('chemical_code.message.csid.invalid'))
|
2022-06-19 08:06:11 +00:00
|
|
|
|
|
|
|
|
|
|
2023-02-06 10:39:42 +00:00
|
|
|
|
async def chemical_code(msg: Bot.MessageSession, id=None, captcha_mode=False):
|
|
|
|
|
# 要求传入消息会话和 ChemSpider ID,ID 留空将会使用缺省值 None
|
2022-07-30 09:56:57 +00:00
|
|
|
|
if msg.target.targetId in play_state and play_state[msg.target.targetId][
|
|
|
|
|
'active']: # 检查对象(群组或私聊)是否在 play_state 中有记录及是否为活跃状态
|
2023-03-15 11:07:09 +00:00
|
|
|
|
await msg.finish(msg.locale.t('chemical_code.message.running'))
|
2022-06-19 10:02:51 +00:00
|
|
|
|
play_state.update({msg.target.targetId: {'active': True}}) # 若无,则创建一个新的记录并标记为活跃状态
|
2022-06-19 06:36:25 +00:00
|
|
|
|
try:
|
2022-06-19 10:02:51 +00:00
|
|
|
|
csr = await search_csr(id) # 尝试获取 ChemSpider ID 对应的化学式列表
|
|
|
|
|
except Exception as e: # 意外情况
|
|
|
|
|
traceback.print_exc() # 打印错误信息
|
|
|
|
|
play_state[msg.target.targetId]['active'] = False # 将对象标记为非活跃状态
|
2023-03-15 11:07:09 +00:00
|
|
|
|
return await msg.finish(msg.locale.t('chemical_code.message.error'))
|
2022-06-19 06:58:43 +00:00
|
|
|
|
# print(csr)
|
2022-06-19 11:51:42 +00:00
|
|
|
|
play_state[msg.target.targetId]['answer'] = csr['name'] # 将正确答案标记于 play_state 中存储的对象中
|
|
|
|
|
Logger.info(f'Answer: {csr["name"]}') # 在日志中输出正确答案
|
2022-06-19 12:24:44 +00:00
|
|
|
|
Logger.info(f'Image: {csr["image"]}') # 在日志中输出图片链接
|
2022-06-19 13:42:53 +00:00
|
|
|
|
download = False
|
|
|
|
|
if csr["id"] in special_id: # 如果正确答案在 special_id 中
|
|
|
|
|
file_path = os.path.abspath(f'./assets/chemicalcode/special_id/{csr["id"]}.png')
|
|
|
|
|
Logger.info(f'File path: {file_path}') # 在日志中输出文件路径
|
|
|
|
|
exists_file = os.path.exists(file_path) # 尝试获取图片文件是否存在
|
|
|
|
|
if exists_file:
|
|
|
|
|
download = file_path
|
|
|
|
|
if not download:
|
|
|
|
|
download = await download_to_cache(csr['image']) # 从结果中获取链接并下载图片
|
2022-06-19 06:58:43 +00:00
|
|
|
|
|
2022-06-19 10:02:51 +00:00
|
|
|
|
with PILImage.open(download) as im: # 打开下载的图片
|
2022-06-21 14:19:52 +00:00
|
|
|
|
im = im.convert("RGBA") # 转换为 RGBA 格式
|
2022-06-21 14:19:28 +00:00
|
|
|
|
image = PILImage.new("RGBA", im.size, 'white') # 创建新图片
|
|
|
|
|
image.alpha_composite(im, (0, 0)) # 将图片合并到新图片中
|
2022-06-19 10:02:51 +00:00
|
|
|
|
newpath = random_cache_path() + '.png' # 创建新文件名
|
|
|
|
|
image.save(newpath) # 保存新图片
|
2022-06-19 06:58:43 +00:00
|
|
|
|
|
2022-06-19 15:56:38 +00:00
|
|
|
|
set_timeout = csr['length'] // 30
|
|
|
|
|
if set_timeout < 2:
|
|
|
|
|
set_timeout = 2
|
2022-06-19 10:02:51 +00:00
|
|
|
|
|
2023-02-05 14:42:13 +00:00
|
|
|
|
async def ans(msg: Bot.MessageSession, answer): # 定义回答函数的功能
|
2022-06-19 10:02:51 +00:00
|
|
|
|
wait = await msg.waitAnyone() # 等待对象内的任意人回答
|
|
|
|
|
if play_state[msg.target.targetId]['active']: # 检查对象是否为活跃状态
|
2023-03-04 09:40:38 +00:00
|
|
|
|
if wait.asDisplay(text_only=True) != answer: # 如果回答不正确
|
2022-06-19 10:02:51 +00:00
|
|
|
|
Logger.info(f'{wait.asDisplay()} != {answer}') # 输出日志
|
|
|
|
|
return await ans(wait, answer) # 进行下一轮检查
|
2022-06-18 06:03:40 +00:00
|
|
|
|
else:
|
2023-03-15 11:07:09 +00:00
|
|
|
|
await wait.sendMessage(msg.locale.t('chemical_code.message.correct'))
|
2022-06-19 10:02:51 +00:00
|
|
|
|
play_state[msg.target.targetId]['active'] = False # 将对象标记为非活跃状态
|
2022-06-18 06:03:40 +00:00
|
|
|
|
|
2022-06-19 10:02:51 +00:00
|
|
|
|
async def timer(start): # 计时器函数
|
|
|
|
|
if play_state[msg.target.targetId]['active']: # 检查对象是否为活跃状态
|
2022-06-19 15:56:38 +00:00
|
|
|
|
if datetime.now().timestamp() - start > 60 * set_timeout: # 如果超过2分钟
|
2023-03-15 11:07:09 +00:00
|
|
|
|
await msg.sendMessage(msg.locale.t('chemical_code.message.timeup', answer=play_state[msg.target.targetId]["answer"]))
|
2022-06-18 06:03:40 +00:00
|
|
|
|
play_state[msg.target.targetId]['active'] = False
|
2022-06-19 10:02:51 +00:00
|
|
|
|
else: # 如果未超时
|
|
|
|
|
await asyncio.sleep(1) # 等待1秒
|
|
|
|
|
await timer(start) # 重新调用计时器函数
|
2022-06-17 05:59:15 +00:00
|
|
|
|
|
2022-06-28 15:44:54 +00:00
|
|
|
|
if not captcha_mode:
|
|
|
|
|
await msg.sendMessage([Image(newpath),
|
2023-04-06 04:48:50 +00:00
|
|
|
|
Plain(msg.locale.t('chemical_code.message', times=set_timeout))])
|
2022-06-28 15:44:54 +00:00
|
|
|
|
time_start = datetime.now().timestamp() # 记录开始时间
|
|
|
|
|
|
|
|
|
|
await asyncio.gather(ans(msg, csr['name']), timer(time_start)) # 同时启动回答函数和计时器函数
|
|
|
|
|
else:
|
2023-01-28 05:53:11 +00:00
|
|
|
|
result = await msg.waitNextMessage(
|
2023-03-15 11:07:09 +00:00
|
|
|
|
[Image(newpath), Plain(msg.locale.t('chemical_code.message.captcha', times=set_timeout))])
|
2022-06-28 16:02:56 +00:00
|
|
|
|
if play_state[msg.target.targetId]['active']: # 检查对象是否为活跃状态
|
2023-03-04 09:40:38 +00:00
|
|
|
|
if result.asDisplay(text_only=True) == csr['name']:
|
2023-03-15 11:07:09 +00:00
|
|
|
|
await result.sendMessage(msg.locale.t('chemical_code.message.correct'))
|
2022-06-28 16:02:56 +00:00
|
|
|
|
else:
|
2023-03-15 11:07:09 +00:00
|
|
|
|
await result.sendMessage(msg.locale.t('chemical_code.message.incorrect', answer=play_state[msg.target.targetId]["answer"]))
|
2022-06-28 16:02:56 +00:00
|
|
|
|
play_state[msg.target.targetId]['active'] = False
|