Compare commits

..

12 Commits

Author SHA1 Message Date
77cc2fef58 提醒我功能不使用思考模式;宾几人不再查询坏枪的两个服务器 2026-06-15 03:33:27 +08:00
379e677bea No drone anymore!!! I will deploy by myself!!! 2026-06-09 14:54:06 +08:00
1d89d80676 调整 AI 模型不要思考,以及添加 Wolfx 日志详细程度
Some checks reported errors
continuous-integration/drone/push Build was killed
2026-06-09 14:49:37 +08:00
9265c250b3 补充一些权限系统有关的注释
All checks were successful
continuous-integration/drone/push Build is passing
2026-05-20 19:34:12 +08:00
4dd9320678 取消罗文的反应机制
All checks were successful
continuous-integration/drone/push Build is passing
2026-05-20 19:11:46 +08:00
db96202d5d 添加小睦想
All checks were successful
continuous-integration/drone/push Build is passing
2026-05-19 00:11:32 +08:00
881b08c41f 此方晚安文档更新
All checks were successful
continuous-integration/drone/push Build is passing
2026-05-13 15:33:40 +08:00
c11d29e136 Merge branch 'master' of ssh://gitea.service.jazzwhom.top:2221/mttu-developers/konabot
Some checks failed
continuous-integration/drone/push Build is failing
2026-05-13 15:25:25 +08:00
1fa74b61d6 更新各种依赖 2026-05-13 15:25:11 +08:00
f0601acbe9 oyasumi
All checks were successful
continuous-integration/drone/push Build is passing
2026-05-13 15:06:49 +08:00
39c7c043ca Merge branch 'master' of ssh://gitea.service.jazzwhom.top:2221/mttu-developers/konabot
All checks were successful
continuous-integration/drone/push Build is passing
2026-04-29 18:52:01 +08:00
39accb16e0 桂花说 2026-04-29 18:51:37 +08:00
16 changed files with 1024 additions and 899 deletions

View File

@ -1,105 +0,0 @@
---
kind: pipeline
name: 构建 Docker Nightly 镜像
type: docker
trigger:
event:
- push
branch:
- master
steps:
- name: submodules
image: alpine/git
commands:
- git submodule update --init --recursive
- name: 构建 Docker 镜像
image: plugins/docker:latest
privileged: true
settings:
username: kagami-ci
password:
from_secret: KAGAMI-CI-PASSWORD
repo: gitea.service.jazzwhom.top/mttu-developers/konabot
registry: gitea.service.jazzwhom.top
tags:
- nightly
- nightly-${DRONE_COMMIT_SHA}
dockerfile: Dockerfile
volumes:
- name: docker-socket
path: /var/run/docker.sock
- name: 在容器中进行若干测试
image: docker:dind
privileged: true
volumes:
- name: docker-socket
path: /var/run/docker.sock
commands:
- docker run --rm gitea.service.jazzwhom.top/mttu-developers/konabot:nightly-${DRONE_COMMIT_SHA} python scripts/test_plugin_load.py
- docker run --rm gitea.service.jazzwhom.top/mttu-developers/konabot:nightly-${DRONE_COMMIT_SHA} python scripts/test_playwright.py
- docker run --rm gitea.service.jazzwhom.top/mttu-developers/konabot:nightly-${DRONE_COMMIT_SHA} python -m pytest --cov=./konabot/ --cov-report term-missing:skip-covered
- name: 发送构建结果到 ntfy
image: parrazam/drone-ntfy
when:
status: [success, failure]
settings:
url: https://ntfy.service.jazzwhom.top
topic: drone_ci
tags:
- drone-ci
token:
from_secret: NTFY_TOKEN
volumes:
- name: docker-socket
host:
path: /var/run/docker.sock
---
kind: pipeline
name: 构建 Docker Release 镜像
type: docker
trigger:
event:
- tag
steps:
- name: submodules
image: alpine/git
commands:
- git submodule update --init --recursive
- name: 构建并推送 Release Docker 镜像
image: plugins/docker:latest
privileged: true
settings:
username: kagami-ci
password:
from_secret: KAGAMI-CI-PASSWORD
repo: gitea.service.jazzwhom.top/mttu-developers/konabot
registry: gitea.service.jazzwhom.top
tags:
- ${DRONE_TAG}
- latest
dockerfile: Dockerfile
volumes:
- name: docker-socket
path: /var/run/docker.sock
- name: 发送构建结果到 ntfy
image: parrazam/drone-ntfy
when:
status: [success, failure]
settings:
url: https://ntfy.service.jazzwhom.top
topic: drone_ci
tags:
- drone-ci
token:
from_secret: NTFY_TOKEN
volumes:
- name: docker-socket
host:
path: /var/run/docker.sock

BIN
assets/img/meme/xiaomu.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View File

@ -4,7 +4,7 @@ Wolfx 防灾免费 API
import asyncio import asyncio
import json import json
from typing import Literal, TypeVar, cast from typing import TypeVar, cast
import aiohttp import aiohttp
from aiosignal import Signal from aiosignal import Signal
from loguru import logger from loguru import logger
@ -239,7 +239,8 @@ class WolfxAPIService:
logger.info(f"接收到来自 Wolfx API 的信息:{data}") logger.info(f"接收到来自 Wolfx API 的信息:{data}")
await signal.send(obj) await signal.send(obj)
except pydantic.ValidationError as e: except pydantic.ValidationError as e:
logger.warning(f"解析 Wolfx API 时出错 URL={ws.url}") data_text = data.decode('utf-8', 'replace')
logger.warning(f"解析 Wolfx API 时出错 URL={ws.url} raw={data_text}")
logger.error(e) logger.error(e)
async def start(self): # pragma: no cover async def start(self): # pragma: no cover

View File

@ -29,10 +29,21 @@ async def _to_entity_chain(el: _EntityLike):
class PermManager: class PermManager:
"""
权限管理模块
"""
def __init__(self, db: DatabaseManager) -> None: def __init__(self, db: DatabaseManager) -> None:
self.db = db self.db = db
async def get_permission_info(self, entities: _EntityLike, key: str): async def get_permission_info(
self, entities: _EntityLike, key: str
) -> tuple[PermEntity, str, bool] | None:
"""
获得一个权限实体或权限实体串对一个 key 的权限信息。若未入库(默认值)则
代表没有该权限相关的记录
"""
entities = await _to_entity_chain(entities) entities = await _to_entity_chain(entities)
key = key.removesuffix("*").removesuffix(".") key = key.removesuffix("*").removesuffix(".")
key_split = key.split(".") key_split = key.split(".")
@ -52,17 +63,29 @@ class PermManager:
return None return None
async def check_has_permission(self, entities: _EntityLike, key: str) -> bool: async def check_has_permission(self, entities: _EntityLike, key: str) -> bool:
"""
检查一个权限实体或者权限实体串是否有权限
"""
res = await self.get_permission_info(entities, key) res = await self.get_permission_info(entities, key)
if res is None: if res is None:
return False return False
return res[2] return res[2]
async def update_permission(self, entity: PermEntity, key: str, perm: bool | None): async def update_permission(self, entity: PermEntity, key: str, perm: bool | None):
"""
更新一个具体的权限实体的一则权限
"""
async with self.db.get_conn() as conn: async with self.db.get_conn() as conn:
repo = PermRepo(conn) repo = PermRepo(conn)
await repo.update_perm_info(entity, key, perm) await repo.update_perm_info(entity, key, perm)
async def list_permission(self, entities: _EntityLike, query: PagerQuery): async def list_permission(self, entities: _EntityLike, query: PagerQuery):
"""
列出一个权限实体或权限实体串拥有的所有权限记录
"""
entities = await _to_entity_chain(entities) entities = await _to_entity_chain(entities)
async with self.db.get_conn() as conn: async with self.db.get_conn() as conn:
repo = PermRepo(conn) repo = PermRepo(conn)
@ -113,6 +136,22 @@ def register_default_allow_permission(key: str):
def require_permission(perm: str) -> Rule: # pragma: no cover def require_permission(perm: str) -> Rule: # pragma: no cover
"""
`require_permission` 是一个 Nonebot 规则,可以用来要求一个 Nonebot 的指令需
要拥有一定的权限。
```python
from konabot.common.permsys import require_permission
from nonebot import on_command
cmd = on_command("kz", rule=require_permission("kagami.kz"))
@cmd.handle()
async def _():
await cmd.finish("你抓到了普通pt")
```
"""
async def check_permission(event: Event, pm: DepPermManager) -> bool: async def check_permission(event: Event, pm: DepPermManager) -> bool:
return await pm.check_has_permission(event, perm) return await pm.check_has_permission(event, perm)

View File

@ -22,6 +22,11 @@ class PermEntity:
def get_entity_chain_of_entity(entity: PermEntity) -> list[PermEntity]: def get_entity_chain_of_entity(entity: PermEntity) -> list[PermEntity]:
"""
获得一个权限实体的权限串。实际上返回三个权限,从小到大分别是用户、平台全体和
系统全局的权限实体。
"""
return [ return [
PermEntity("sys", "global", "global"), PermEntity("sys", "global", "global"),
PermEntity(entity.platform, "global", "global"), PermEntity(entity.platform, "global", "global"),
@ -30,6 +35,10 @@ def get_entity_chain_of_entity(entity: PermEntity) -> list[PermEntity]:
async def get_entity_chain(event: Event) -> list[PermEntity]: # pragma: no cover async def get_entity_chain(event: Event) -> list[PermEntity]: # pragma: no cover
"""
获得一个 Nonebot Event 的权限实体串。
"""
entities = [PermEntity("sys", "global", "global")] entities = [PermEntity("sys", "global", "global")]
if isinstance(event, OB11Event): if isinstance(event, OB11Event):

View File

@ -1,3 +0,0 @@
# 关于罗文和洛温
AdoreLowen 希望和洛温阿特金森区分,所以最好就不要叫他洛温了!此方 BOT 会在一些群提醒叫错了的人。

View File

@ -0,0 +1,8 @@
# 指令介绍
**此方晚安** - 让此方 BOT 禁言你一段时间
## 指令格式
- `@此方BOT 此方晚安`: 禁言几个小时
- `@此方BOT 此方午安`: 禁言几十分钟

View File

@ -0,0 +1,44 @@
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("guihuasay"),
target="#main",
other_function=page_function,
)
return UniMessage.image(raw=img_data)
cmd = on_message(
rule=match_keyword.match_keyword(
re.compile(r"^(桂花[说想])\s.+", re.I),
),
)
@cmd.handle()
async def _(event: Event, msg: UniMsg):
text = msg.extract_plain_text().lstrip()
_, content = text.split(maxsplit=1)
msg = await render_image(content)
await msg.send(event)

View File

@ -23,7 +23,7 @@ class THQwen(TextHandler):
ostream="你或当前环境没有使用 qwen 的权限。如有疑问请联系管理员", ostream="你或当前环境没有使用 qwen 的权限。如有疑问请联系管理员",
) )
llm = get_llm() llm = get_llm(llm_model="qwen3.7-plus")
messages = [] messages = []
if istream is not None: if istream is not None:
@ -48,7 +48,9 @@ class THQwen(TextHandler):
"content": "除非用户要求,请尽可能短点回答。另外,当前环境不支持 Markdown 语法,如果可以,请使用纯文本回答", "content": "除非用户要求,请尽可能短点回答。另外,当前环境不支持 Markdown 语法,如果可以,请使用纯文本回答",
} }
] + messages ] + messages
result = await llm.chat(cast(Any, messages)) result = await llm.chat(
cast(Any, messages), extra_body={"enable_thinking": False}
)
content = result.content content = result.content
if content is None: if content is None:
return TextHandleResult( return TextHandleResult(

View File

@ -1,6 +1,8 @@
from io import BytesIO from io import BytesIO
from typing import Iterable, cast from typing import Iterable, cast
import PIL.Image
from loguru import logger from loguru import logger
from nonebot import on_message from nonebot import on_message
from nonebot_plugin_alconna import ( from nonebot_plugin_alconna import (
@ -18,7 +20,7 @@ from nonebot_plugin_alconna import (
from playwright.async_api import ConsoleMessage, Page from playwright.async_api import ConsoleMessage, Page
from konabot.common.nb.match_keyword import match_keyword from konabot.common.nb.match_keyword import match_keyword
from konabot.common.nb.extract_image import DepPILImage from konabot.common.nb.extract_image import DepImageBytesOrNone, DepPILImage
from konabot.common.web_render import konaweb from konabot.common.web_render import konaweb
from konabot.common.web_render.core import WebRenderer from konabot.common.web_render.core import WebRenderer
from konabot.common.web_render.host_images import host_tempdir from konabot.common.web_render.host_images import host_tempdir
@ -35,6 +37,7 @@ from konabot.plugins.memepack.drawing.saying import (
draw_pt, draw_pt,
draw_suan, draw_suan,
draw_vr, draw_vr,
draw_xm
) )
from konabot.plugins.memepack.drawing.watermark import draw_doubao_watermark from konabot.plugins.memepack.drawing.watermark import draw_doubao_watermark
@ -361,3 +364,33 @@ async def _(saying: list[str]):
await vrsay.send(await UniMessage().image(raw=img_bytes).export()) await vrsay.send(await UniMessage().image(raw=img_bytes).export())
xmsay = on_alconna(
Alconna(
"小睦说",
Args[
"saying",
MultiVar(str, "*"),
Field(missing_tips=lambda: "你没有写小睦说了什么"),
],
Args["image?", Image | None],
),
use_cmd_start=True,
use_cmd_sep=False,
skip_for_unmatch=False,
aliases={"小睦想"},
)
@xmsay.handle()
async def _(saying: list[str], image: DepImageBytesOrNone):
if image is not None:
img = PIL.Image.open(BytesIO(image))
else:
img = None
img = await draw_xm("\n".join(saying), img)
img_bytes = BytesIO()
img.save(img_bytes, format="PNG")
await xmsay.send(await UniMessage().image(raw=img_bytes).export())

View File

@ -7,23 +7,41 @@ import PIL.Image
from konabot.common.path import ASSETS_PATH from konabot.common.path import ASSETS_PATH
from konabot.common.utils.to_async import make_async from konabot.common.utils.to_async import make_async
from .base.fonts import HARMONYOS_SANS_SC_BLACK, HARMONYOS_SANS_SC_REGULAR, LXGWWENKAI_REGULAR from .base.fonts import (
HARMONYOS_SANS_SC_BLACK,
HARMONYOS_SANS_SC_REGULAR,
LXGWWENKAI_REGULAR,
)
geimao_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "geimao.jpg").convert("RGBA") geimao_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "geimao.jpg").convert(
"RGBA"
)
pt_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "ptsay.png").convert("RGBA") pt_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "ptsay.png").convert("RGBA")
mnk_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "mnksay.jpg").convert("RGBA") mnk_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "mnksay.jpg").convert("RGBA")
dasuan_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "dss.png").convert("RGBA") dasuan_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "dss.png").convert("RGBA")
suan_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "suanleba.png").convert("RGBA") suan_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "suanleba.png").convert(
cute_ten_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "tententen.png").convert("RGBA") "RGBA"
)
cute_ten_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "tententen.png").convert(
"RGBA"
)
kio_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "kiosay.jpg").convert("RGBA") kio_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "kiosay.jpg").convert("RGBA")
vr_image = PIL.Image.open(ASSETS_PATH / 'img' / 'meme' / 'vr.jpg').convert("RGBA") vr_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "vr.jpg").convert("RGBA")
xm_image = PIL.Image.open(ASSETS_PATH / "img" / "meme" / "xiaomu.png").convert("RGBA")
def _draw_geimao(saying: str): def _draw_geimao(saying: str):
img = geimao_image.copy() img = geimao_image.copy()
with imagetext_py.Writer(img) as iw: with imagetext_py.Writer(img) as iw:
iw.draw_text_wrapped( iw.draw_text_wrapped(
saying, 960, 50, 0.5, 0, 1920, 240, HARMONYOS_SANS_SC_BLACK, saying,
960,
50,
0.5,
0,
1920,
240,
HARMONYOS_SANS_SC_BLACK,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")), imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
0.8, 0.8,
imagetext_py.TextAlign.Center, imagetext_py.TextAlign.Center,
@ -42,7 +60,14 @@ def _draw_pt(saying: str):
img = pt_image.copy() img = pt_image.copy()
with imagetext_py.Writer(img) as iw: with imagetext_py.Writer(img) as iw:
iw.draw_text_wrapped( iw.draw_text_wrapped(
saying, 259, 278, 0.5, 0.5, 360, 48, HARMONYOS_SANS_SC_REGULAR, saying,
259,
278,
0.5,
0.5,
360,
48,
HARMONYOS_SANS_SC_REGULAR,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")), imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
1.0, 1.0,
imagetext_py.TextAlign.Center, imagetext_py.TextAlign.Center,
@ -59,7 +84,14 @@ def _draw_mnk(saying: str):
img = mnk_image.copy() img = mnk_image.copy()
with imagetext_py.Writer(img) as iw: with imagetext_py.Writer(img) as iw:
iw.draw_text_wrapped( iw.draw_text_wrapped(
saying, 540, 25, 0.5, 0, 1080, 120, HARMONYOS_SANS_SC_BLACK, saying,
540,
25,
0.5,
0,
1080,
120,
HARMONYOS_SANS_SC_BLACK,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")), imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
0.8, 0.8,
imagetext_py.TextAlign.Center, imagetext_py.TextAlign.Center,
@ -81,7 +113,14 @@ def _draw_suan(saying: str, dasuan: bool = False):
img = suan_image.copy() img = suan_image.copy()
with imagetext_py.Writer(img) as iw: with imagetext_py.Writer(img) as iw:
iw.draw_text_wrapped( iw.draw_text_wrapped(
saying, 1020, 290, 0.5, 0.5, 400, 48, LXGWWENKAI_REGULAR, saying,
1020,
290,
0.5,
0.5,
400,
48,
LXGWWENKAI_REGULAR,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")), imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
1.0, 1.0,
imagetext_py.TextAlign.Center, imagetext_py.TextAlign.Center,
@ -98,7 +137,14 @@ def _draw_cute_ten(saying: str):
img = cute_ten_image.copy() img = cute_ten_image.copy()
with imagetext_py.Writer(img) as iw: with imagetext_py.Writer(img) as iw:
iw.draw_text_wrapped( iw.draw_text_wrapped(
saying, 390, 479, 0.5, 0.5, 760, 96, LXGWWENKAI_REGULAR, saying,
390,
479,
0.5,
0.5,
760,
96,
LXGWWENKAI_REGULAR,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")), imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
1.0, 1.0,
imagetext_py.TextAlign.Center, imagetext_py.TextAlign.Center,
@ -116,7 +162,14 @@ def draw_kiosay(saying: str):
img = kio_image.copy() img = kio_image.copy()
with imagetext_py.Writer(img) as iw: with imagetext_py.Writer(img) as iw:
iw.draw_text_wrapped( iw.draw_text_wrapped(
saying, 450, 540, 0.5, 0.5, 900, 96, LXGWWENKAI_REGULAR, saying,
450,
540,
0.5,
0.5,
900,
96,
LXGWWENKAI_REGULAR,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")), imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
1.0, 1.0,
imagetext_py.TextAlign.Center, imagetext_py.TextAlign.Center,
@ -131,12 +184,19 @@ def draw_vr(saying: str):
w, h = img.size w, h = img.size
hw = 300 hw = 300
img2 = PIL.Image.new("RGBA", (w, h + hw), 'white') img2 = PIL.Image.new("RGBA", (w, h + hw), "white")
img2.paste(img, (0, hw)) img2.paste(img, (0, hw))
with imagetext_py.Writer(img2) as iw: with imagetext_py.Writer(img2) as iw:
iw.draw_text_wrapped( iw.draw_text_wrapped(
saying, w // 2, hw // 2 + 15, 0.5, 0.5, w, 64, LXGWWENKAI_REGULAR, saying,
w // 2,
hw // 2 + 15,
0.5,
0.5,
w,
64,
LXGWWENKAI_REGULAR,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")), imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
1.0, 1.0,
imagetext_py.TextAlign.Center, imagetext_py.TextAlign.Center,
@ -145,3 +205,36 @@ def draw_vr(saying: str):
return img2 return img2
@make_async
def draw_xm(saying: str, image: PIL.Image.Image | None = None):
img_base = PIL.Image.new("RGBA", xm_image.size, (255, 255, 255, 255))
with imagetext_py.Writer(img_base) as iw:
iw.draw_text_wrapped(
saying,
442,
200,
0.5,
0.5,
884,
64,
LXGWWENKAI_REGULAR,
imagetext_py.Paint.Color(imagetext_py.Color.from_hex("000000FF")),
1.0,
imagetext_py.TextAlign.Center,
draw_emojis=True,
)
if image is not None:
image_r = image.copy().convert("RGBA")
width, height = image_r.size
base_width = img_base.size[0]
height = int(height / width * base_width)
image_r = image_r.resize((base_width, height))
# try to align center
y = 215 - image_r.height // 2
img_base.paste(image_r, (0, y), mask=image_r)
img_base.paste(xm_image, (0, 0), mask=xm_image)
return img_base

View File

@ -49,8 +49,8 @@ def dump_server_status(name: str, status: JavaStatusResponse | BaseException) ->
async def _(evt: Event, pm: DepPermManager): async def _(evt: Event, pm: DepPermManager):
servers = ( servers = (
(mcstatus.JavaServer("play.simpfun.cn", 11495), "小帕 Bingo"), (mcstatus.JavaServer("play.simpfun.cn", 11495), "小帕 Bingo"),
(mcstatus.JavaServer("bingo.mujica.tech"), "坏枪 Bingo"), # (mcstatus.JavaServer("bingo.mujica.tech"), "坏枪 Bingo"),
(mcstatus.JavaServer("mc.mujica.tech", 11456), "齿轮盛宴"), # (mcstatus.JavaServer("mc.mujica.tech", 11456), "齿轮盛宴"),
) )
responses = await asyncio.gather( responses = await asyncio.gather(

View File

@ -1,44 +0,0 @@
import nonebot
from nonebot.adapters.onebot.v11.bot import Bot
from nonebot.adapters.onebot.v11.event import GroupMessageEvent
from nonebot_plugin_alconna import UniMsg, UniMessage
from pydantic import BaseModel
class NoLuowenConfig(BaseModel):
plugin_noluowen_qqid: int = -1
plugin_noluowen_enable_group: list[int] = []
config = nonebot.get_plugin_config(NoLuowenConfig)
async def is_luowen_mentioned(evt: GroupMessageEvent, msg: UniMsg) -> bool:
if config.plugin_noluowen_qqid <= 0:
return False
if evt.user_id == config.plugin_noluowen_qqid:
return False
if evt.group_id not in config.plugin_noluowen_enable_group:
return False
txt = msg.extract_plain_text()
if "洛温" not in txt:
return False
if "罗文" in txt:
return False
if "阿特金森" in txt:
return False
return True
evt_luowen_mentioned = nonebot.on_message(rule=is_luowen_mentioned)
@evt_luowen_mentioned.handle()
async def _(evt: GroupMessageEvent, bot: Bot):
msg = (
UniMessage()
.reply(str(evt.message_id))
.at(str(config.plugin_noluowen_qqid))
.text(" 好像有人念错了你的 ID")
)
await evt_luowen_mentioned.send(await msg.export(bot=bot))

View File

@ -0,0 +1,64 @@
import random
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent
from nonebot.rule import to_me
from nonebot_plugin_alconna import UniMessage
from konabot.common.permsys import require_permission
async def make_sleep(event: GroupMessageEvent, bot: Bot, duration: int):
"""
让人睡着
"""
await bot.set_group_ban(
group_id=event.group_id,
user_id=event.user_id,
duration=duration,
)
seconds = duration % 60
minutes = (duration // 60) % 60
hours = duration // 3600
t1 = f"{hours} 小时 {minutes} 分钟 {seconds}"
message = f" 好好睡吧!奖励你 {t1}的睡眠💤"
message = UniMessage.at(str(event.user_id)).text(message)
await message.send(target=event, bot=bot)
cmd_sleep_night = on_command(
"此方晚安",
rule=require_permission("oyasumi") & to_me(),
aliases={"晚安"},
)
@cmd_sleep_night.handle()
async def oyasumi(event: GroupMessageEvent, bot: Bot):
"""
限定只能用 GroupMessageEvent因为它只能在 QQ 群中使用
"""
# 考虑到有人是熬夜很久,所以这里就给一个 3 到 5 小时睡眠的随机数。这个时间内
# 要睡不着我觉得是个小概率事件了!
duration = random.randint(3 * 3600, 5 * 3600)
await make_sleep(event, bot, duration)
await cmd_sleep_night.finish()
cmd_sleep_noon = on_command(
"此方午安",
rule=require_permission("oyasumi") & to_me(),
aliases={"午安"},
)
@cmd_sleep_noon.handle()
async def sleep_noon(event: GroupMessageEvent, bot: Bot):
duration = random.randint(60 * 15, 60 * 30)
await make_sleep(event, bot, duration)
await cmd_sleep_night.finish()

View File

@ -40,8 +40,7 @@ SYSTEM_PROMPT = """你是一个专门解析提醒请求的助手。请分析用
示例: 示例:
用户:"明天下午2点提醒我开会" 用户:"明天下午2点提醒我开会"
输出:{"datetime": "2024-01-16T14:00:00", "datetime_delta": null, 输出:{"datetime": "2024-01-16T14:00:00", "datetime_delta": null, "datetime_delta_minus": false, "content": "开会", "is_notice": true}
"datetime_delta_minus": false, "content": "开会", "is_notice": true}
用户:"5分钟后提醒我关火" 用户:"5分钟后提醒我关火"
输出:{"datetime": null, "datetime_delta": "PT5M", "datetime_delta_minus": false, "content": "关火", "is_notice": true} 输出:{"datetime": null, "datetime_delta": "PT5M", "datetime_delta_minus": false, "content": "关火", "is_notice": true}
@ -95,7 +94,7 @@ async def ask_ai(expression: str, now: datetime.datetime | None = None) -> tuple
message = await llm.chat([ message = await llm.chat([
{ "role": "system", "content": prompt }, { "role": "system", "content": prompt },
{ "role": "user", "content": expression }, { "role": "user", "content": expression },
]) ], extra_body={"enable_thinking": False})
result = message.content result = message.content
if result is None: if result is None:
return (None, "") return (None, "")

1431
poetry.lock generated

File diff suppressed because it is too large Load Diff