Compare commits
4 Commits
94db34037b
...
f7cea196ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
f7cea196ec
|
|||
|
d4826e9e8b
|
|||
|
33934ef7b5
|
|||
|
f9f8ae4e67
|
38
konabot/docs/user/celeste.txt
Normal file
38
konabot/docs/user/celeste.txt
Normal file
@ -0,0 +1,38 @@
|
||||
# Celeste
|
||||
|
||||
爬山小游戏,移植自 Ccleste,是 Celeste Classic(即 PICO-8)版。
|
||||
|
||||
使用 `wasdxc` 和数字进行操作。
|
||||
|
||||
## 操作说明
|
||||
|
||||
`wsad` 是上下左右摇杆方向,或者是方向键。`c` 是跳跃键,`x` 是冲刺键。
|
||||
|
||||
使用空格分隔每一个操作,每个操作持续一帧。如果后面跟着数字,则持续那么多帧。
|
||||
|
||||
### 例子 1
|
||||
|
||||
```
|
||||
xc 180
|
||||
```
|
||||
|
||||
按下 xc 一帧,然后空置 180 帧。
|
||||
|
||||
### 例子 2
|
||||
|
||||
```
|
||||
d10 cd d10 xdw d20
|
||||
```
|
||||
|
||||
向右走 10 帧,向右跳一帧,再继续按下右 10 帧,按下向右上冲刺一帧,再按下右 20 帧。
|
||||
|
||||
## 指令使用说明
|
||||
|
||||
直接说 `celeste` 会开启一个新的游戏。但是,你需要在后面跟有操作,才能够渲染 gif 图出来。
|
||||
|
||||
一个常见的开始操作是直接发送 `celeste xc 130`,即按下 xc 两个按键触发 PICO 版的开始游戏,然后等待 130 秒动画播放完毕。
|
||||
|
||||
对于一个已经存在而且时间不是非常久远的 gif 图,只要是由 bot 自己发送出来的,就可以在它的基础上继续游戏。回复这条消息,可以继续游戏。
|
||||
|
||||
一种很常见的技巧是回复一个已经存在的 gif 图 `celeste 1`,此时会空操作一帧并且渲染画面。你可以用这种方法查看一个 gif 图的游戏目前的状态。
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
from typing import Any
|
||||
from loguru import logger
|
||||
from nonebot import on_command
|
||||
from nonebot import on_message
|
||||
from pydantic import BaseModel
|
||||
|
||||
from nonebot.adapters import Event, Bot
|
||||
@ -41,11 +42,12 @@ class CelesteStatus(BaseModel):
|
||||
celeste_status = DataManager(CelesteStatus, DATA_PATH / "celeste-status.json")
|
||||
|
||||
|
||||
cmd = on_command(cmd="celeste", aliases={"蔚蓝", "爬山", "鳌太线"})
|
||||
# ↓ 这里的 Type Hinting 是为了能 fit 进去 set[str | tuple[str, ...]]
|
||||
aliases: set[Any] = {"celeste", "蔚蓝", "爬山", "鳌太线"}
|
||||
ALLOW_CHARS = "wasdxc0123456789 \t\n\r"
|
||||
|
||||
|
||||
@cmd.handle()
|
||||
async def _(msg: UniMsg, evt: Event, bot: Bot):
|
||||
async def get_prev(evt: Event, bot: Bot) -> str | None:
|
||||
prev = None
|
||||
if isinstance(evt, OB11MessageEvent):
|
||||
if evt.reply is not None:
|
||||
@ -55,17 +57,37 @@ async def _(msg: UniMsg, evt: Event, bot: Bot):
|
||||
if seg.type == 'reply':
|
||||
msgid = seg.get('id')
|
||||
prev = f"QQ:{bot.self_id}:" + str(msgid)
|
||||
if prev is not None:
|
||||
async with celeste_status.get_data() as data:
|
||||
prev = data.records.get(prev)
|
||||
return prev
|
||||
|
||||
actions = msg.extract_plain_text().strip().removeprefix("celeste")
|
||||
for alias in {"蔚蓝", "爬山", "鳌太线"}:
|
||||
|
||||
async def match_celeste(evt: Event, bot: Bot, msg: UniMsg) -> bool:
|
||||
prev = await get_prev(evt, bot)
|
||||
text = msg.extract_plain_text().strip()
|
||||
if any(text.startswith(a) for a in aliases):
|
||||
return True
|
||||
if prev is not None:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# cmd = on_command(cmd="celeste", aliases=aliases)
|
||||
cmd = on_message(rule=match_celeste)
|
||||
|
||||
|
||||
@cmd.handle()
|
||||
async def _(msg: UniMsg, evt: Event, bot: Bot):
|
||||
prev = await get_prev(evt, bot)
|
||||
actions = msg.extract_plain_text().strip()
|
||||
for alias in aliases:
|
||||
actions = actions.removeprefix(alias)
|
||||
actions = actions.strip()
|
||||
if len(actions) == 0:
|
||||
return
|
||||
|
||||
if prev is not None:
|
||||
async with celeste_status.get_data() as data:
|
||||
prev = data.records.get(prev)
|
||||
if any((c not in ALLOW_CHARS) for c in actions):
|
||||
return
|
||||
|
||||
await ensure_artifact(arti_ccleste_wrap_linux)
|
||||
await ensure_artifact(arti_ccleste_wrap_windows)
|
||||
|
||||
@ -7,7 +7,7 @@ class THQwen(TextHandler):
|
||||
name = "qwen"
|
||||
|
||||
async def handle(self, env: TextHandlerEnvironment, istream: str | None, args: list[str]) -> TextHandleResult:
|
||||
llm = get_llm("qwen3-max")
|
||||
llm = get_llm()
|
||||
messages = []
|
||||
|
||||
if istream is not None:
|
||||
|
||||
43
konabot/plugins/krgsay.py
Normal file
43
konabot/plugins/krgsay.py
Normal file
@ -0,0 +1,43 @@
|
||||
import re
|
||||
from typing import Any
|
||||
from nonebot import on_message
|
||||
from nonebot.adapters import Event
|
||||
from nonebot_plugin_alconna import UniMessage, UniMsg
|
||||
from playwright.async_api import Page
|
||||
|
||||
from konabot.common.nb import match_keyword
|
||||
from konabot.common.web_render import WebRenderer, konaweb
|
||||
|
||||
|
||||
async def render_image(message: str) -> UniMessage[Any]:
|
||||
"""
|
||||
渲染文本为图片
|
||||
"""
|
||||
|
||||
async def page_function(page: Page):
|
||||
await page.wait_for_function("typeof setContent === 'function'")
|
||||
await page.evaluate(
|
||||
"(message) => { return setContent(message); }",
|
||||
message,
|
||||
)
|
||||
|
||||
img_data = await WebRenderer.render(
|
||||
url=konaweb("krgsay"),
|
||||
target="#main",
|
||||
other_function=page_function,
|
||||
)
|
||||
return UniMessage.image(raw=img_data)
|
||||
|
||||
|
||||
cmd = on_message(
|
||||
rule=match_keyword.match_keyword(
|
||||
re.compile(r"^[kK][rR][gG][sS][aA][yY] .+"),
|
||||
),
|
||||
)
|
||||
|
||||
@cmd.handle()
|
||||
async def _(event: Event, msg: UniMsg):
|
||||
text = msg.extract_plain_text().strip()[6:].strip()
|
||||
msg = await render_image(text)
|
||||
await msg.send(event)
|
||||
|
||||
@ -91,7 +91,7 @@ async def ask_ai(expression: str, now: datetime.datetime | None = None) -> tuple
|
||||
logger.info(f"提醒功能:消息被阿里绿网拦截 message={expression}")
|
||||
return None, ""
|
||||
|
||||
llm = get_llm("qwen3-max")
|
||||
llm = get_llm()
|
||||
message = await llm.chat([
|
||||
{ "role": "system", "content": prompt },
|
||||
{ "role": "user", "content": expression },
|
||||
|
||||
Reference in New Issue
Block a user