Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 18785f034b | |||
| 7ba1a92623 | |||
| f6670eb672 | |||
| eb32c1af9a |
27
Dockerfile
27
Dockerfile
@ -1,22 +1,31 @@
|
||||
# copied from https://www.martinrichards.me/post/python_poetry_docker/
|
||||
FROM python:3.13-slim AS base
|
||||
|
||||
ENV VIRTUAL_ENV=/app/.venv \
|
||||
PATH="/app/.venv/bin:$PATH"
|
||||
PATH="/app/.venv/bin:$PATH" \
|
||||
PLAYWRIGHT_BROWSERS_PATH=0
|
||||
|
||||
# 安装所有都需要的底层依赖
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libfontconfig1 \
|
||||
libgl1 \
|
||||
libegl1 \
|
||||
libglvnd0 \
|
||||
mesa-vulkan-drivers \
|
||||
libfontconfig1 libgl1 libegl1 libglvnd0 mesa-vulkan-drivers at-spi2-common fontconfig \
|
||||
libasound2-data libavahi-client3 libavahi-common-data libavahi-common3 libdatrie1 \
|
||||
libfontenc1 libfribidi0 libgraphite2-3 libharfbuzz0b libice6 libpixman-1-0 \
|
||||
libsm6 libthai-data libthai0 libunwind8 libxaw7 libxcb-render0 libxfont2 libxi6 \
|
||||
libxkbfile1 libxmu6 libxpm4 libxrender1 libxt6t64 x11-common x11-xkb-utils \
|
||||
xfonts-encodings xfonts-utils xkb-data xserver-common libnspr4 libatk1.0-0t64 \
|
||||
libatk-bridge2.0-0t64 libatspi2.0-0t64 libxcomposite1 libxdamage1 libxfixes3 \
|
||||
libxkbcommon0 libasound2t64 libnss3 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
# 安装构建依赖
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential cmake git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV POETRY_NO_INTERACTION=1 \
|
||||
POETRY_VIRTUALENVS_IN_PROJECT=1 \
|
||||
POETRY_VIRTUALENVS_CREATE=1 \
|
||||
@ -24,7 +33,7 @@ ENV POETRY_NO_INTERACTION=1 \
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip install poetry
|
||||
RUN pip install --no-cache-dir poetry
|
||||
|
||||
COPY pyproject.toml poetry.lock ./
|
||||
RUN python -m poetry install --no-root && rm -rf $POETRY_CACHE_DIR
|
||||
@ -37,6 +46,8 @@ COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN python -m playwright install chromium
|
||||
|
||||
COPY bot.py pyproject.toml .env.prod .env.test ./
|
||||
COPY assets ./assets
|
||||
COPY scripts ./scripts
|
||||
|
||||
BIN
assets/img/dog/haha_dog.jpg
Normal file
BIN
assets/img/dog/haha_dog.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
86
konabot/common/web_render/__init__.py
Normal file
86
konabot/common/web_render/__init__.py
Normal file
@ -0,0 +1,86 @@
|
||||
import asyncio
|
||||
import queue
|
||||
from loguru import logger
|
||||
from playwright.async_api import async_playwright, Browser
|
||||
|
||||
class WebRenderer:
|
||||
browser_pool: queue.Queue["WebRendererInstance"] = queue.Queue()
|
||||
|
||||
@classmethod
|
||||
async def render(cls, url: str, target: str, params: dict = {}, other_function: callable = None, timeout: int = 30) -> bytes:
|
||||
'''
|
||||
访问指定URL并返回截图
|
||||
|
||||
:param url: 目标URL
|
||||
:param target: 渲染目标,如 ".box"、"#main" 等CSS选择器
|
||||
:param timeout: 页面加载超时时间,单位秒
|
||||
:param params: URL键值对参数
|
||||
:param other_function: 其他自定义操作函数,接受page参数
|
||||
:return: 截图的字节数据
|
||||
|
||||
'''
|
||||
logger.debug(f"Requesting render for {url} targeting {target} with timeout {timeout}")
|
||||
if cls.browser_pool.empty():
|
||||
instance = await WebRendererInstance.create()
|
||||
cls.browser_pool.put(instance)
|
||||
instance = cls.browser_pool.get()
|
||||
cls.browser_pool.put(instance)
|
||||
logger.debug(f"Using WebRendererInstance {id(instance)} to render {url} targeting {target}")
|
||||
return await instance.render(url, target, params=params, other_function=other_function, timeout=timeout)
|
||||
|
||||
class WebRendererInstance:
|
||||
def __init__(self):
|
||||
self.playwright = None
|
||||
self.browser: Browser = None
|
||||
self.lock: asyncio.Lock = None
|
||||
|
||||
@classmethod
|
||||
async def create(cls) -> "WebRendererInstance":
|
||||
instance = cls()
|
||||
instance.playwright = await async_playwright().start()
|
||||
instance.browser = await instance.playwright.chromium.launch(headless=True)
|
||||
instance.lock = asyncio.Lock()
|
||||
return instance
|
||||
|
||||
async def render(self, url: str, target: str, index: int = 0, params: dict = {}, other_function: callable = None, timeout: int = 30) -> bytes:
|
||||
'''
|
||||
访问指定URL并返回截图
|
||||
|
||||
:param url: 目标URL
|
||||
:param target: 渲染目标,如 ".box"、"#main" 等CSS选择器
|
||||
:param timeout: 页面加载超时时间,单位秒
|
||||
:param index: 如果目标是一个列表,指定要截图的元素索引
|
||||
:param params: URL键值对参数
|
||||
:param other_function: 其他自定义操作函数,接受page参数
|
||||
:return: 截图的字节数据
|
||||
|
||||
'''
|
||||
async with self.lock:
|
||||
context = await self.browser.new_context()
|
||||
page = await context.new_page()
|
||||
logger.debug(f"Navigating to {url} with timeout {timeout}")
|
||||
try:
|
||||
url_with_params = url + ("?" + "&".join(f"{k}={v}" for k, v in params.items()) if params else "")
|
||||
await page.goto(url_with_params, timeout=timeout * 1000, wait_until="load")
|
||||
logger.debug(f"Page loaded successfully")
|
||||
# 等待目标元素出现
|
||||
await page.wait_for_selector(target, timeout=timeout * 1000)
|
||||
logger.debug(f"Target element '{target}' found, taking screenshot")
|
||||
if other_function:
|
||||
await other_function(page)
|
||||
elements = await page.query_selector_all(target)
|
||||
if not elements:
|
||||
raise Exception(f"Target element '{target}' not found on the page.")
|
||||
if index >= len(elements):
|
||||
raise Exception(f"Index {index} out of range for elements matching '{target}'.")
|
||||
element = elements[index]
|
||||
screenshot = await element.screenshot()
|
||||
logger.debug(f"Screenshot taken successfully")
|
||||
return screenshot
|
||||
finally:
|
||||
await page.close()
|
||||
await context.close()
|
||||
|
||||
async def close(self):
|
||||
await self.browser.close()
|
||||
await self.playwright.stop()
|
||||
217
konabot/plugins/hanzi/__init__.py
Normal file
217
konabot/plugins/hanzi/__init__.py
Normal file
@ -0,0 +1,217 @@
|
||||
import random
|
||||
from typing import Optional
|
||||
import opencc
|
||||
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters import Event as BaseEvent
|
||||
from nonebot.adapters.discord.event import MessageEvent as DiscordMessageEvent
|
||||
from nonebot_plugin_alconna import (
|
||||
Alconna,
|
||||
Args,
|
||||
UniMessage,
|
||||
UniMsg,
|
||||
on_alconna,
|
||||
)
|
||||
|
||||
convert_type = ["简","簡","繁","正","港","日"]
|
||||
|
||||
compiled_str = "|".join([f"{a}{mid}{b}" for mid in ["转","轉","転"] for a in convert_type for b in convert_type if a != b])
|
||||
|
||||
def hanzi_to_abbr(hanzi: str) -> str:
|
||||
mapping = {
|
||||
"简": "s",
|
||||
"簡": "s",
|
||||
"繁": "t",
|
||||
"正": "t",
|
||||
"港": "hk",
|
||||
"日": "jp",
|
||||
}
|
||||
return mapping.get(hanzi, "")
|
||||
|
||||
def check_valid_convert_type(convert_type: str) -> bool:
|
||||
avaliable_set = ["s2t","t2s","s2tw","tw2s","s2hk","hk2s","s2twp","tw2sp","t2tw","hk2t","t2hk","t2jp","jp2t","tw2t"]
|
||||
if convert_type in avaliable_set:
|
||||
return True
|
||||
return False
|
||||
|
||||
def convert(source, src_abbr, dst_abbr):
|
||||
convert_type_key = f"{src_abbr}2{dst_abbr}"
|
||||
if not check_valid_convert_type(convert_type_key):
|
||||
# 先转为繁体,再转为目标
|
||||
converter = opencc.OpenCC(f"{src_abbr}2t.json")
|
||||
source = converter.convert(source)
|
||||
src_abbr = "t"
|
||||
converter = opencc.OpenCC(f"{src_abbr}2{dst_abbr}.json")
|
||||
converted = converter.convert(source)
|
||||
return converted
|
||||
|
||||
evt = on_alconna(
|
||||
Alconna(
|
||||
f"re:({compiled_str})",
|
||||
Args["source?", str],
|
||||
),
|
||||
use_cmd_start=True,
|
||||
use_cmd_sep=False,
|
||||
skip_for_unmatch=True,
|
||||
)
|
||||
|
||||
@evt.handle()
|
||||
async def _(msg: UniMsg, event: BaseEvent, source: Optional[str] = None):
|
||||
if isinstance(event, DiscordMessageEvent):
|
||||
content = event.get_message().extract_plain_text()
|
||||
else:
|
||||
content = event.get_message().extract_plain_text()
|
||||
|
||||
prefix = content.split()[0]
|
||||
to_convert = ""
|
||||
# 如果回复了消息,则转换回复的内容
|
||||
if(source is None):
|
||||
if event.reply:
|
||||
to_convert = event.reply.message.extract_plain_text()
|
||||
if not to_convert:
|
||||
return
|
||||
else:
|
||||
return
|
||||
else:
|
||||
to_convert = source
|
||||
parts = []
|
||||
if "转" in prefix:
|
||||
parts = prefix.split("转")
|
||||
elif "轉" in prefix:
|
||||
parts = prefix.split("轉")
|
||||
elif "転" in prefix:
|
||||
parts = prefix.split("転")
|
||||
if len(parts) != 2:
|
||||
notice = "转换格式错误,请使用“简转繁”、“繁转简”等格式。"
|
||||
await evt.send(await UniMessage().text(notice).export())
|
||||
return
|
||||
src, dst = parts
|
||||
src_abbr = hanzi_to_abbr(src)
|
||||
dst_abbr = hanzi_to_abbr(dst)
|
||||
if not src_abbr or not dst_abbr:
|
||||
notice = "不支持的转换类型,请使用“简”、“繁”、“正”、“港”、“日”等。"
|
||||
if src_abbr:
|
||||
notice = convert(notice, "s", src_abbr)
|
||||
await evt.send(await UniMessage().text(notice).export())
|
||||
return
|
||||
|
||||
converted = convert(to_convert, src_abbr, dst_abbr)
|
||||
|
||||
converted_prefix = convert("转换结果", "s", dst_abbr)
|
||||
|
||||
await evt.send(await UniMessage().text(f"{converted_prefix}:{converted}").export())
|
||||
|
||||
shuo = ["说","說"]
|
||||
|
||||
full_name_type = ["简体","簡體","繁體","繁体","正體","正体","港話","港话","日文"]
|
||||
|
||||
combined_list = [f"{a}{b}" for a in shuo for b in full_name_type]
|
||||
|
||||
compiled_str_2 = "|".join(combined_list)
|
||||
|
||||
evt = on_alconna(
|
||||
Alconna(
|
||||
f"re:({compiled_str_2})",
|
||||
Args["source?", str]
|
||||
),
|
||||
use_cmd_start=True,
|
||||
use_cmd_sep=False,
|
||||
skip_for_unmatch=True,
|
||||
)
|
||||
|
||||
@evt.handle()
|
||||
async def _(msg: UniMsg, event: BaseEvent, source: Optional[str] = None):
|
||||
if isinstance(event, DiscordMessageEvent):
|
||||
content = event.get_message().extract_plain_text()
|
||||
else:
|
||||
content = event.get_message().extract_plain_text()
|
||||
|
||||
prefix = content.split()[0]
|
||||
to_convert = ""
|
||||
# 如果回复了消息,则转换回复的内容
|
||||
if(source is None):
|
||||
if event.reply:
|
||||
to_convert = event.reply.message.extract_plain_text()
|
||||
if not to_convert:
|
||||
return
|
||||
else:
|
||||
return
|
||||
else:
|
||||
to_convert = source
|
||||
# 获取目标转换类型
|
||||
dst = ""
|
||||
match prefix:
|
||||
case "说简体" | "說簡體" | "说簡體" | "說简体":
|
||||
dst = "简"
|
||||
case "說繁體" | "说繁体" | "說繁体" | "说繁體":
|
||||
dst = "繁"
|
||||
case "說正體" | "说正体" | "說正体" | "说正體":
|
||||
dst = "正"
|
||||
case "說港話" | "说港话" | "說港话" | "说港話":
|
||||
dst = "港"
|
||||
case "說日文" | "说日文":
|
||||
dst = "日"
|
||||
dst_abbr = hanzi_to_abbr(dst)
|
||||
if not dst_abbr:
|
||||
notice = "不支持的转换类型,请使用“简体”、“繁體”、“正體”、“港話”、“日文”等。"
|
||||
await evt.send(await UniMessage().text(notice).export())
|
||||
return
|
||||
# 循环,将源语言一次次转换为目标语言
|
||||
current_text = to_convert
|
||||
for src_abbr in ["s","hk","jp","tw","t"]:
|
||||
if src_abbr != dst_abbr:
|
||||
current_text = convert(current_text, src_abbr, dst_abbr)
|
||||
|
||||
converted_prefix = convert("转换结果", "s", dst_abbr)
|
||||
|
||||
await evt.send(await UniMessage().text(f"{converted_prefix}:{current_text}").export())
|
||||
|
||||
def random_char(char: str) -> str:
|
||||
dst_abbr = random.choice(["s","t","hk","jp","tw"])
|
||||
for src_abbr in ["s","hk","jp","tw","t"]:
|
||||
if src_abbr != dst_abbr:
|
||||
char = convert(char, src_abbr, dst_abbr)
|
||||
return char
|
||||
|
||||
def random_string(text: str) -> str:
|
||||
final_text = ""
|
||||
for char in text:
|
||||
final_text += random_char(char)
|
||||
return final_text
|
||||
|
||||
random_match = ["混乱字形","混亂字形","乱数字形","亂數字形","ランダム字形"]
|
||||
|
||||
evt = on_alconna(
|
||||
Alconna(
|
||||
f"re:({'|'.join(random_match)})",
|
||||
Args["source?", str]
|
||||
),
|
||||
use_cmd_start=True,
|
||||
use_cmd_sep=False,
|
||||
skip_for_unmatch=True,
|
||||
)
|
||||
@evt.handle()
|
||||
async def _(msg: UniMsg, event: BaseEvent, source: Optional[str] = None):
|
||||
if isinstance(event, DiscordMessageEvent):
|
||||
content = event.get_message().extract_plain_text()
|
||||
else:
|
||||
content = event.get_message().extract_plain_text()
|
||||
|
||||
prefix = content.split()[0]
|
||||
to_convert = ""
|
||||
# 如果回复了消息,则转换回复的内容
|
||||
if(source is None):
|
||||
if event.reply:
|
||||
to_convert = event.reply.message.extract_plain_text()
|
||||
if not to_convert:
|
||||
return
|
||||
else:
|
||||
return
|
||||
else:
|
||||
to_convert = source
|
||||
|
||||
final_text = ""
|
||||
final_text = random_string(to_convert)
|
||||
converted_prefix = convert(random_string("转换结果"), "s", "s")
|
||||
|
||||
await evt.send(await UniMessage().text(f"{converted_prefix}:{final_text}").export())
|
||||
@ -71,8 +71,8 @@ class TryVerifyState(Enum):
|
||||
VERIFIED_AND_REAL = 1
|
||||
NOT_IDIOM = 2
|
||||
WRONG_FIRST_CHAR = 3
|
||||
VERIFIED_BUT_NO_NEXT = 4
|
||||
VERIFIED_GAME_END = 5
|
||||
BUT_NO_NEXT = 4
|
||||
GAME_END = 5
|
||||
|
||||
|
||||
class IdiomGame:
|
||||
@ -185,29 +185,33 @@ class IdiomGame:
|
||||
"""
|
||||
return last_char in IdiomGame.AVALIABLE_IDIOM_FIRST_CHAR
|
||||
|
||||
def _verify_idiom(self, idiom: str, user_id: str) -> TryVerifyState:
|
||||
def _verify_idiom(self, idiom: str, user_id: str) -> list[TryVerifyState]:
|
||||
state = []
|
||||
# 新成语的首字应与上一条成语的尾字相同
|
||||
if idiom[0] != self.last_char:
|
||||
return TryVerifyState.WRONG_FIRST_CHAR
|
||||
state.append(TryVerifyState.WRONG_FIRST_CHAR)
|
||||
return state
|
||||
if idiom not in IdiomGame.ALL_IDIOMS and idiom not in IdiomGame.ALL_WORDS:
|
||||
self.add_score(user_id, -0.1)
|
||||
return TryVerifyState.NOT_IDIOM
|
||||
state.append(TryVerifyState.NOT_IDIOM)
|
||||
return state
|
||||
# 成语合法,更新状态
|
||||
state.append(TryVerifyState.VERIFIED)
|
||||
self.last_idiom = idiom
|
||||
self.last_char = idiom[-1]
|
||||
self.add_score(user_id, 1)
|
||||
if idiom in IdiomGame.ALL_IDIOMS:
|
||||
state.append(TryVerifyState.VERIFIED_AND_REAL)
|
||||
self.add_score(user_id, 4) # 再加 4 分
|
||||
self.remain_rounds -= 1
|
||||
if self.remain_rounds <= 0:
|
||||
self.now_playing = False
|
||||
return TryVerifyState.VERIFIED_GAME_END
|
||||
state.append(TryVerifyState.GAME_END)
|
||||
if not self.is_nextable(self.last_char):
|
||||
# 没有成语可以接了,自动跳过
|
||||
self._skip_idiom_async()
|
||||
return TryVerifyState.VERIFIED_BUT_NO_NEXT
|
||||
if idiom in IdiomGame.ALL_IDIOMS:
|
||||
return TryVerifyState.VERIFIED_AND_REAL # 真实成语
|
||||
return TryVerifyState.VERIFIED
|
||||
state.append(TryVerifyState.BUT_NO_NEXT)
|
||||
return state
|
||||
|
||||
def get_user_score(self, user_id: str) -> float:
|
||||
if user_id not in self.score_board:
|
||||
@ -233,9 +237,9 @@ class IdiomGame:
|
||||
@classmethod
|
||||
def random_idiom_starting_with(cls, first_char: str) -> Optional[str]:
|
||||
cls.init_lexicon()
|
||||
if first_char not in cls.IDIOM_FIRST_CHAR:
|
||||
if first_char not in cls.AVALIABLE_IDIOM_FIRST_CHAR:
|
||||
return None
|
||||
return secrets.choice(cls.IDIOM_FIRST_CHAR[first_char])
|
||||
return secrets.choice(cls.AVALIABLE_IDIOM_FIRST_CHAR[first_char])
|
||||
|
||||
@classmethod
|
||||
def init_lexicon(cls):
|
||||
@ -249,7 +253,10 @@ class IdiomGame:
|
||||
|
||||
# 词语大表
|
||||
with open(ASSETS_PATH / "lexicon" / "ci.json", "r", encoding="utf-8") as f:
|
||||
cls.ALL_WORDS = json.load(f)
|
||||
jsonData = json.load(f)
|
||||
cls.ALL_WORDS = [item["ci"] for item in jsonData]
|
||||
logger.debug(f"Loaded {len(cls.ALL_WORDS)} words from ci.json")
|
||||
logger.debug(f"Sample words: {cls.ALL_WORDS[:5]}")
|
||||
|
||||
COMMON_WORDS = []
|
||||
# 读取 COMMON 词语大表
|
||||
@ -258,6 +265,8 @@ class IdiomGame:
|
||||
word = line.strip()
|
||||
if len(word) == 4:
|
||||
COMMON_WORDS.append(word)
|
||||
logger.debug(f"Loaded {len(COMMON_WORDS)} common words from common.txt")
|
||||
logger.debug(f"Sample common words: {COMMON_WORDS[:5]}")
|
||||
|
||||
# 读取 THUOCL 成语库
|
||||
with open(
|
||||
@ -265,7 +274,9 @@ class IdiomGame:
|
||||
"r",
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
THUOCL_IDIOMS = [line.split(" ")[0].strip() for line in f]
|
||||
THUOCL_IDIOMS = [line.split(" ")[0].split("\t")[0].strip() for line in f]
|
||||
logger.debug(f"Loaded {len(THUOCL_IDIOMS)} idioms from THUOCL_chengyu.txt")
|
||||
logger.debug(f"Sample idioms: {THUOCL_IDIOMS[:5]}")
|
||||
|
||||
# 读取 THUOCL 剩下的所有 txt 文件,只保留四字词
|
||||
THUOCL_WORDS = []
|
||||
@ -279,9 +290,11 @@ class IdiomGame:
|
||||
encoding="utf-8",
|
||||
) as f:
|
||||
for line in f:
|
||||
word = line.lstrip().split(" ")[0].strip()
|
||||
word = line.lstrip().split(" ")[0].split("\t")[0].strip()
|
||||
if len(word) == 4:
|
||||
THUOCL_WORDS.append(word)
|
||||
logger.debug(f"Loaded {len(THUOCL_WORDS)} words from THUOCL txt files")
|
||||
logger.debug(f"Sample words: {THUOCL_WORDS[:5]}")
|
||||
|
||||
# 只有成语的大表
|
||||
cls.ALL_IDIOMS = [idiom["word"] for idiom in ALL_IDIOMS_INFOS] + THUOCL_IDIOMS
|
||||
@ -441,6 +454,10 @@ async def _(target: DepLongTaskTarget):
|
||||
if not instance or not instance.get_playing_state():
|
||||
return
|
||||
avaliable_idiom = IdiomGame.random_idiom_starting_with(instance.get_last_char())
|
||||
# 发送哈哈狗图片
|
||||
with open(ASSETS_PATH / "img" / "dog" / "haha_dog.jpg", "rb") as f:
|
||||
img_data = f.read()
|
||||
await evt.send(await UniMessage().image(raw=img_data).export())
|
||||
await evt.send(await UniMessage().text(f"你们太菜了,全部扣100分!明明还可以接「{avaliable_idiom}」的!").export())
|
||||
idiom = await instance.skip_idiom(-100)
|
||||
await evt.send(
|
||||
@ -472,9 +489,9 @@ async def _(event: BaseEvent, msg: UniMsg, target: DepLongTaskTarget):
|
||||
user_idiom = msg.extract_plain_text().strip()
|
||||
user_id, user_name = get_user_info(event)
|
||||
state = await instance.try_verify_idiom(user_idiom, user_id)
|
||||
if state == TryVerifyState.WRONG_FIRST_CHAR:
|
||||
if TryVerifyState.WRONG_FIRST_CHAR in state:
|
||||
return
|
||||
if state == TryVerifyState.NOT_IDIOM:
|
||||
if TryVerifyState.NOT_IDIOM in state:
|
||||
await evt.send(
|
||||
await UniMessage()
|
||||
.at(user_id)
|
||||
@ -482,25 +499,25 @@ async def _(event: BaseEvent, msg: UniMsg, target: DepLongTaskTarget):
|
||||
.export()
|
||||
)
|
||||
return
|
||||
if state == TryVerifyState.VERIFIED:
|
||||
await evt.send(
|
||||
await UniMessage()
|
||||
.at(user_id)
|
||||
.text(f" 接上了,喜提 1 分!你有 {instance.get_user_score(user_id)} 分!")
|
||||
.export()
|
||||
)
|
||||
elif state == TryVerifyState.VERIFIED_AND_REAL:
|
||||
if TryVerifyState.VERIFIED_AND_REAL in state:
|
||||
await evt.send(
|
||||
await UniMessage()
|
||||
.at(user_id)
|
||||
.text(f" 接上了,这是个真实成语,喜提 5 分!你有 {instance.get_user_score(user_id)} 分!")
|
||||
.export()
|
||||
)
|
||||
if state == TryVerifyState.VERIFIED_GAME_END:
|
||||
elif TryVerifyState.VERIFIED in state:
|
||||
await evt.send(
|
||||
await UniMessage()
|
||||
.at(user_id)
|
||||
.text(f" 接上了,喜提 1 分!你有 {instance.get_user_score(user_id)} 分!")
|
||||
.export()
|
||||
)
|
||||
if TryVerifyState.GAME_END in state:
|
||||
await evt.send(await UniMessage().text("全部回合结束!").export())
|
||||
await end_game(event, group_id)
|
||||
return
|
||||
if state == TryVerifyState.VERIFIED_BUT_NO_NEXT:
|
||||
if TryVerifyState.BUT_NO_NEXT in state:
|
||||
await evt.send(
|
||||
await UniMessage()
|
||||
.text("但是,这是条死路!你们全部都要扣 100 分!")
|
||||
|
||||
80
konabot/plugins/xibao_generate/__init__.py
Normal file
80
konabot/plugins/xibao_generate/__init__.py
Normal file
@ -0,0 +1,80 @@
|
||||
from typing import Optional
|
||||
from nonebot_plugin_alconna import Alconna, Args, UniMessage, UniMsg, on_alconna
|
||||
from konabot.common.web_render import WebRenderer
|
||||
from nonebot.adapters import Event as BaseEvent
|
||||
from playwright.async_api import Page
|
||||
|
||||
async def continue_handle(page: Page, content: str) -> None:
|
||||
# 这里可以添加一些预处理逻辑
|
||||
# 找到 id 为 input 的 textarea 元素
|
||||
textarea = await page.query_selector("#input")
|
||||
if textarea:
|
||||
# 在 textarea 中输入内容
|
||||
await textarea.fill(content)
|
||||
# 找到 id 为 submit-btn 的按钮元素
|
||||
submit_button = await page.query_selector("#submit-btn")
|
||||
if submit_button:
|
||||
# 点击按钮提交
|
||||
await submit_button.click()
|
||||
|
||||
evt = on_alconna(
|
||||
Alconna(
|
||||
f"生成喜报",
|
||||
Args["content?", str]
|
||||
),
|
||||
use_cmd_start=True,
|
||||
use_cmd_sep=False,
|
||||
skip_for_unmatch=True,
|
||||
)
|
||||
@evt.handle()
|
||||
async def _(msg: UniMsg, event: BaseEvent, content: Optional[str] = ""):
|
||||
|
||||
screenshot = await WebRenderer.render(
|
||||
url="https://witnessbot.mxowl.com/services/congratulations/",
|
||||
target="#main-canvas",
|
||||
other_function=lambda page: continue_handle(page, content),
|
||||
timeout=30
|
||||
)
|
||||
await evt.send(
|
||||
await UniMessage().image(raw=screenshot).export()
|
||||
)
|
||||
|
||||
async def beibao_continue_handle(page: Page, content: str) -> None:
|
||||
# 这里可以添加一些预处理逻辑
|
||||
# 找到 id 为 input 的 textarea 元素
|
||||
textarea = await page.query_selector("#input")
|
||||
if textarea:
|
||||
# 在 textarea 中输入内容
|
||||
await textarea.fill(content)
|
||||
# 找到 class 为 btn btn-outline-primary,for属性为 mode-2 的标签元素
|
||||
mode_radio = await page.query_selector("label.btn.btn-outline-primary[for='mode-2']")
|
||||
if mode_radio:
|
||||
# 点击选择悲报模式
|
||||
await mode_radio.click()
|
||||
# 找到 id 为 submit-btn 的按钮元素
|
||||
submit_button = await page.query_selector("#submit-btn")
|
||||
if submit_button:
|
||||
# 点击按钮提交
|
||||
await submit_button.click()
|
||||
|
||||
evt = on_alconna(
|
||||
Alconna(
|
||||
f"生成悲报",
|
||||
Args["content?", str]
|
||||
),
|
||||
use_cmd_start=True,
|
||||
use_cmd_sep=False,
|
||||
skip_for_unmatch=True,
|
||||
)
|
||||
@evt.handle()
|
||||
async def _(msg: UniMsg, event: BaseEvent, content: Optional[str] = ""):
|
||||
|
||||
screenshot = await WebRenderer.render(
|
||||
url="https://witnessbot.mxowl.com/services/congratulations/",
|
||||
target="#main-canvas",
|
||||
other_function=lambda page: beibao_continue_handle(page, content),
|
||||
timeout=30
|
||||
)
|
||||
await evt.send(
|
||||
await UniMessage().image(raw=screenshot).export()
|
||||
)
|
||||
156
poetry.lock
generated
156
poetry.lock
generated
@ -989,6 +989,79 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.2.4"
|
||||
description = "Lightweight in-process concurrent programming"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f"},
|
||||
{file = "greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa"},
|
||||
{file = "greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f"},
|
||||
{file = "greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae"},
|
||||
{file = "greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337"},
|
||||
{file = "greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:671df96c1f23c4a0d4077a325483c1503c96a1b7d9db26592ae770daa41233d4"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b"},
|
||||
{file = "greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb"},
|
||||
{file = "greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["Sphinx", "furo"]
|
||||
test = ["objgraph", "psutil", "setuptools"]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.16.0"
|
||||
@ -2127,6 +2200,37 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "opencc"
|
||||
version = "1.1.9"
|
||||
description = "Conversion between Traditional and Simplified Chinese"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "OpenCC-1.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a33941dd4cb67457e6f44dfe36dddc30a602363a4f6a29b41d79b062b332c094"},
|
||||
{file = "OpenCC-1.1.9-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:92769f9a60301574c73096f9ab8a9060fe0d13a9f8266735d82a2a3a92adbd26"},
|
||||
{file = "OpenCC-1.1.9-cp310-cp310-win_amd64.whl", hash = "sha256:84e35e5ecfad445a64c0dcd6567d9e9f3a6aed9a6ffd89cdbc071f36cb9e089e"},
|
||||
{file = "OpenCC-1.1.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3fb7c84f7c182cb5208e7bc1c104b817a3ca1a8fe111d4d19816be0d6e1ab396"},
|
||||
{file = "OpenCC-1.1.9-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:64994c68796d93cdba42f37e0c073fb8ed6f9d6707232be0ba84f24dc5a36bbb"},
|
||||
{file = "OpenCC-1.1.9-cp311-cp311-win_amd64.whl", hash = "sha256:9f6a1413ca2ff490e65a55822e4cae8c3f104bfab46355288de4893a14470fbb"},
|
||||
{file = "OpenCC-1.1.9-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48bc3e37942b91a9cf51f525631792f79378e5332bdba9e10c05f6e7fe9036ca"},
|
||||
{file = "OpenCC-1.1.9-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:1c5d1489bdaf9dc2865f0ea30eb565093253e73c1868d9c19554c8a044b545d4"},
|
||||
{file = "OpenCC-1.1.9-cp312-cp312-win_amd64.whl", hash = "sha256:64f8d22c8505b65e8ee2d6e73241cbc92785d38b3c93885b423d7c4fcd31c679"},
|
||||
{file = "OpenCC-1.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f4267b66ed6e656b5d8199f94e9673950ac39d49ebaf0e7927330801f06f038f"},
|
||||
{file = "OpenCC-1.1.9-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:c6d5f9756ed08e67de36c53dc4d8f0bdc72889d6f57a8fc4d8b073d99c58d4dc"},
|
||||
{file = "OpenCC-1.1.9-cp38-cp38-win_amd64.whl", hash = "sha256:a6c2650bd3d6a9e3c31fc2057e0f36122c9507af1661627542f618c97d420293"},
|
||||
{file = "OpenCC-1.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4d66473405c2e360ef346fe1625f201f3f3c4adbb16d5c1c7749a150ae42d875"},
|
||||
{file = "OpenCC-1.1.9-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:436c43e0855b4f9c9e4fd1191e8ac638e9d9f2c7e2d5753952e6e31aa231d36c"},
|
||||
{file = "OpenCC-1.1.9-cp39-cp39-win_amd64.whl", hash = "sha256:b4c36d6974afd94b444ad5ad17364f40d228092ce89b86e46653f7ff38075201"},
|
||||
{file = "opencc-1.1.9.tar.gz", hash = "sha256:8ad72283732951303390fae33a1ceda98ac9b03368a8f2912edc934d74077e4a"},
|
||||
]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "opencv-python-headless"
|
||||
version = "4.12.0.88"
|
||||
@ -2304,6 +2408,33 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "playwright"
|
||||
version = "1.55.0"
|
||||
description = "A high-level API to automate web browsers"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "playwright-1.55.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:d7da108a95001e412effca4f7610de79da1637ccdf670b1ae3fdc08b9694c034"},
|
||||
{file = "playwright-1.55.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8290cf27a5d542e2682ac274da423941f879d07b001f6575a5a3a257b1d4ba1c"},
|
||||
{file = "playwright-1.55.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:25b0d6b3fd991c315cca33c802cf617d52980108ab8431e3e1d37b5de755c10e"},
|
||||
{file = "playwright-1.55.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:c6d4d8f6f8c66c483b0835569c7f0caa03230820af8e500c181c93509c92d831"},
|
||||
{file = "playwright-1.55.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29a0777c4ce1273acf90c87e4ae2fe0130182100d99bcd2ae5bf486093044838"},
|
||||
{file = "playwright-1.55.0-py3-none-win32.whl", hash = "sha256:29e6d1558ad9d5b5c19cbec0a72f6a2e35e6353cd9f262e22148685b86759f90"},
|
||||
{file = "playwright-1.55.0-py3-none-win_amd64.whl", hash = "sha256:7eb5956473ca1951abb51537e6a0da55257bb2e25fc37c2b75af094a5c93736c"},
|
||||
{file = "playwright-1.55.0-py3-none-win_arm64.whl", hash = "sha256:012dc89ccdcbd774cdde8aeee14c08e0dd52ddb9135bf10e9db040527386bd76"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
greenlet = ">=3.1.1,<4.0.0"
|
||||
pyee = ">=13,<14"
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "propcache"
|
||||
version = "0.3.2"
|
||||
@ -2732,6 +2863,29 @@ type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "pyee"
|
||||
version = "13.0.0"
|
||||
description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498"},
|
||||
{file = "pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
typing-extensions = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "mypy", "pytest", "pytest-asyncio ; python_version >= \"3.4\"", "pytest-trio ; python_version >= \"3.7\"", "sphinx", "toml", "tox", "trio", "trio ; python_version > \"3.6\"", "trio-typing ; python_version > \"3.6\"", "twine", "twisted", "validate-pyproject[all]"]
|
||||
|
||||
[package.source]
|
||||
type = "legacy"
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
reference = "mirrors"
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.19.2"
|
||||
@ -3824,4 +3978,4 @@ reference = "mirrors"
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.12,<4.0"
|
||||
content-hash = "96080ea588b3ac52b19909379585cd647646faf3dce291f8d2b5801a3111c838"
|
||||
content-hash = "ec73430f70658a303c47e6f536ccb0863a475f7f25d5334c8766e6149075648c"
|
||||
|
||||
@ -25,6 +25,8 @@ dependencies = [
|
||||
"qrcode (>=8.2,<9.0)",
|
||||
"ptimeparse (>=0.2.1,<0.3.0)",
|
||||
"nanoid (>=2.0.0,<3.0.0)",
|
||||
"opencc (>=1.1.9,<2.0.0)",
|
||||
"playwright (>=1.55.0,<2.0.0)",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
|
||||
Reference in New Issue
Block a user