🎈新增接龙插件,修改部分图形结构

This commit is contained in:
EillesWan 2024-07-18 18:18:55 +08:00
parent cc5c0e1dee
commit dd03dd6d3e
18 changed files with 30753 additions and 11 deletions

View File

@ -32,7 +32,7 @@
- 感谢[nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能 - 感谢[nonebot-plugin-alconna](https://github.com/ArcletProject/nonebot-plugin-alconna)提供的命令解析功能
- 十分感谢[神羽SnowyKami](https://github.com/snowykami)提供的技术指导和服务器资源 - 十分感谢[神羽SnowyKami](https://github.com/snowykami)提供的技术指导和服务器资源
- 特别感谢[云裳工作室](https://doc.ysmcc.cn/doc/1/)提供的服务器挂载 - 特别感谢[云裳工作室](https://doc.ysmcc.cn/doc/1/)提供的服务器挂载
- 由衷感谢我在学习生活中遇到的所有朋友们,你们身为我生命中的一处景色,不断地推进我此生的进程。 - 由衷感谢我在学习生活中遇到的所有朋友们,你们身为我生命中的一处景色,不断地推进我此生的进程。
### 许可证 ### 许可证

View File

@ -28,5 +28,7 @@ jieba~=0.42.1
pip~=23.2.1 pip~=23.2.1
fastapi~=0.110.0 fastapi~=0.110.0
python-dotenv~=1.0.1 python-dotenv~=1.0.1
nonebot_plugin_session
pypinyin pypinyin
zhDateTime>=1.0.3 zhDateTime>=1.0.3
Musicreater>=2.2.0

View File

@ -0,0 +1,305 @@
'''
接龙
'''
import asyncio
from asyncio import TimerHandle
from typing import List, Dict, Any
import re
import random
import pypinyin
from pydantic import BaseModel
from nonebot.matcher import Matcher
from nonebot import on_regex, require
from nonebot.params import RegexDict
from nonebot.plugin import PluginMetadata, inherit_supported_adapters
from typing_extensions import Annotated
require("nonebot_plugin_alconna")
require("nonebot_plugin_session")
from nonebot_plugin_alconna import (
Alconna,
AlconnaQuery,
Option,
Query,
Text,
UniMessage,
on_alconna,
store_true,
)
from nonebot.rule import to_me
from nonebot_plugin_session import SessionId, SessionIdType
from .utils import random_idiom, legal_idiom, legal_patted_idiom, get_idiom
__plugin_meta__ = PluginMetadata(
name="接龙",
description="汉字词语或成语接龙",
usage=(
"@我 + “接龙”开始游戏;\n"
# "你有十次的机会猜一个四字词语;\n"
# "每次猜测后,汉字与拼音的颜色将会标识其与正确答案的区别;\n"
# "青色 表示其出现在答案中且在正确的位置;\n"
# "橙色 表示其出现在答案中但不在正确的位置;\n"
# "每个格子的 汉字、声母、韵母、声调 都会独立进行颜色的指示。\n"
# "当四个格子都为青色时,你便赢得了游戏!\n"
# "可发送“结束”结束游戏;可发送“提示”查看提示。\n"
# "使用 --strict 选项开启非默认的成语检查,即猜测的短语必须是成语,\n"
# "如:@我 猜成语 --strict"
),
type="application",
# homepage="https://github.com/noneplugin/nonebot-plugin-handle",
# config=Config,
supported_adapters=inherit_supported_adapters(
"nonebot_plugin_alconna", "nonebot_plugin_session"
),
extra={
"example": "@小羿 接龙",
},
)
# games: Dict[str, Dragle] = {}
games = {}
auto_echo = {}
timers: Dict[str, TimerHandle] = {}
UserId = Annotated[str, SessionId(SessionIdType.GROUP)]
def game_is_running(user_id: UserId) -> bool:
return user_id in games
def game_not_running(user_id: UserId) -> bool:
return user_id not in games
handle = on_alconna(
Alconna("dockdragon", Option("-s|--strict", default=False, action=store_true)),
aliases=("接龙",),
rule=to_me() & game_not_running,
use_cmd_start=True,
block=True,
priority=13,
)
handle_hint = on_alconna(
"提示",
rule=game_is_running,
use_cmd_start=True,
block=True,
priority=13,
)
handle_stop = on_alconna(
"结束",
aliases=("结束游戏", "结束接龙"),
rule=game_is_running,
use_cmd_start=True,
block=True,
priority=13,
)
# handle_update = on_alconna(
# "更新词库",
# aliases=("刷新词库", "猜成语刷新词库"),
# rule=to_me(),
# use_cmd_start=True,
# block=True,
# priority=13,
# )
def is_auto_echo(user_id: UserId) -> bool:
return auto_echo.get(user_id, True)
handle_idiom = on_regex(
r"^(?P<idiom>[\u4e00-\u9fa5]{4})$",
rule=is_auto_echo,
block=True,
priority=14,
)
停止自动回复 = on_alconna(
"自动接龙",
aliases=("自动成语接龙",),
rule=None,
use_cmd_start=True,
block=True,
priority=14,
)
def stop_game(user_id: str):
if timer := timers.pop(user_id, None):
timer.cancel()
games.pop(user_id, None)
async def stop_game_timeout(matcher: Matcher, user_id: str):
game = games.get(user_id, None)
stop_game(user_id)
if game:
msg = "接龙超时,游戏结束。"
if len(game.guessed_idiom) >= 1:
msg += f"\n{game.result}"
await matcher.finish(msg)
def set_timeout(matcher: Matcher, user_id: str, timeout: float = 300):
if timer := timers.get(user_id, None):
timer.cancel()
loop = asyncio.get_running_loop()
timer = loop.call_later(
timeout, lambda: asyncio.ensure_future(stop_game_timeout(matcher, user_id))
)
timers[user_id] = timer
# @handle.handle()
# async def _(
# matcher: Matcher,
# user_id: UserId,
# strict: Query[bool] = AlconnaQuery("strict.value", False),
# ):
# # is_strict = handle_config.handle_strict_mode or strict.result
# idiom, explanation = random_idiom()
# game = Handle(idiom, explanation, strict=is_strict)
# games[user_id] = game
# set_timeout(matcher, user_id)
# msg = Text(
# f"你有{game.times}次机会猜一个四字成语,"
# + ("发送有效成语以参与游戏。" if is_strict else "发送任意四字词语以参与游戏。")
# ) + Image(raw=await run_sync(game.draw)())
# await msg.send()
@停止自动回复.handle()
async def _(matcher: Matcher, user_id: UserId):
if auto_echo.get(user_id, True):
auto_echo[user_id] = False
await matcher.finish("已关闭自动接龙回复")
else:
auto_echo[user_id] = True
await matcher.finish("已开启自动接龙回复")
@handle_idiom.handle()
async def _(matcher: Matcher, user_id: UserId, matched: Dict[str, Any] = RegexDict()):
# game = games[user_id]
# set_timeout(matcher, user_id)
idiom = str(matched["idiom"])
# result = game.guess(idiom)
if legal_idiom(idiom):
# stop_game(user_id)
print(matcher.get_target())
await matcher.finish(get_idiom(idiom,True,True))
# elif result == GuessResult.DUPLICATE:
# await matcher.finish("你已经猜过这个成语了呢")
# elif result == GuessResult.ILLEGAL:
# await matcher.finish(f"你确定“{idiom}”是个成语吗?")
# else:
# await UniMessage.image(raw=await run_sync(game.draw)()).send()
# zh = re.compile(r"[\u4e00-\u9fff]+")
# @cat.on_text(states=["", "idle"])
# async def handled():
# '''自动接龙'''
# text = cat.arg
# r = zh.search(text)
# if not r:
# return
# word = r.group()
# for dragon in dragon_list:
# # 跳过不启用的接龙
# if not dragon.use:
# continue
# # 当前词语符合接龙词库
# if dragon.check(word):
# # 上次接龙
# last = cat.cache.get("dragon", {}).get(dragon.name, "")
# # 成功接龙
# if last and word:
# p1 = lazy_pinyin(last)[-1]
# p2 = lazy_pinyin(word)[0]
# if p1 == p2:
# await cat.send(f"[{cat.user.name}] 接龙成功!")
# # 无论是否成功接龙都发送下一个词
# word = dragon.next(word)
# cat.cache.setdefault("dragon", {})
# cat.cache["dragon"][dragon.name] = word
# if not word:
# word = choice(["%$#*-_", "你赢了", "接不上来..."])
# await cat.send(word)
# break
# cat.set_wakeup_cmds(cmds="接龙管理")
# cat.set_rest_cmds(cmds=["exit", "退出"])
# @cat.on_cmd(cmds="list", states="idle")
# async def list_all():
# '''列出所有词库'''
# items = ["所有词库:"]
# for dragon in dragon_list:
# if dragon.use:
# items.append(f"[{dragon.name}] 正在使用")
# else:
# items.append(f"[{dragon.name}]")
# await cat.send("\n".join(items))
# @cat.on_cmd(cmds="data", states="idle")
# async def show_data():
# '''展示你的答题数据'''
# gid = cat.group.id
# uid = cat.user.id
# stmt = select(DragonUserData).filter_by(group_id=gid, user_id=uid)
# cursor = cat.db_session.exec(stmt)
# user_datas = cursor.all()
# if user_datas:
# info = "\n".join(
# f"[{u.dragon_name}] 接龙次数 {u.cnt}"
# for u in user_datas
# )
# else:
# info = "你还没有用过我...T_T"
# await cat.send(info)

View File

@ -0,0 +1,97 @@
import random
from pypinyin import Style, pinyin
from typing import Dict, List, Tuple
# fmt: off
# 声母
INITIALS = [
"zh", "z", "y", "x", "w", "t", "sh", "s", "r", "q", "p",
"n", "m", "l", "k", "j", "h", "g", "f", "d", "ch", "c", "b"
]
# 韵母
FINALS = [
"ün", "üe", "üan", "ü", "uo", "un", "ui", "ue", "uang",
"uan", "uai","ua", "ou", "iu", "iong", "ong", "io", "ing",
"in", "ie", "iao", "iang", "ian", "ia", "er", "eng", "en",
"ei", "ao", "ang", "an", "ai", "u", "o", "i", "e", "a"
]
# fmt: on
def get_pinyin_of_n(word: str, which: int) -> List[Tuple[str, str, str]]:
pys = pinyin(word, style=Style.TONE3, v_to_u=True,heteronym=True)[which]
# py = p[0]
results = []
for py in pys:
if py[-1].isdigit():
tone = py[-1]
py = py[:-1]
else:
tone = ""
initial = ""
for i in INITIALS:
if py.startswith(i):
initial = i
break
final = ""
for f in FINALS:
if py.endswith(f):
final = f
break
results.append((initial, final, tone)) # 声母,韵母,声调
return results
LEGAL_PHRASES = [
idiom.strip() for idiom in open("./resources/idioms_p.txt","r", encoding="utf-8").readlines()
]
sorted_phrases = dict([i for j in [[(py[0]+py[1],{"":[],"1":[],"2":[],"3":[],"4":[]}) for py in get_pinyin_of_n(idiom[0],0)] for idiom in LEGAL_PHRASES] for i in j])
for idiom in LEGAL_PHRASES:
for py in get_pinyin_of_n(idiom[0],0):
sorted_phrases[py[0]+py[1]][py[2]].append(idiom)
def legal_idiom(word: str) -> bool:
return word in LEGAL_PHRASES
def legal_patted_idiom(former:str, laster: str, diff_word: bool,homophonic: bool) -> bool:
"""
判断成语是否符合接龙条件
Parameters
==========
former: str
前一个成语
laster: str
后一个成语
diff_word: bool
异字模式接龙之字无须一致
homophonic: bool
谐音模式接龙之字可不同音调
"""
return legal_idiom(laster) and legal_idiom(former) and ((((len({i[:2] for i in get_pinyin_of_n(laster[0],0)}.intersection({i[:2] for i in get_pinyin_of_n(former[-1],0)})))>0) if homophonic else (get_pinyin_of_n(laster,0)[0] == get_pinyin_of_n(former,-1)[0])) if diff_word else (former[-1] == laster[0] if homophonic else ((former[-1] == laster[0])and(get_pinyin_of_n(laster,0)[0] == get_pinyin_of_n(former,-1)[0]))))
def get_idiom(idiom: str,diff_word: bool,homophonic: bool) -> str:
return random.choice(([k for o in [[i for j in sorted_phrases[py[0]+py[1]].values() for i in j] for py in get_pinyin_of_n(idiom[-1],0)] for k in o] if homophonic else sorted_phrases[(py:=get_pinyin_of_n(idiom,-1)[0])[0]+py[1]][py[2]])if diff_word else ([k for o in [[i for j in sorted_phrases[py[0]+py[1]].values() for i in j if i[0] == idiom[-1]] for py in get_pinyin_of_n(idiom[-1],0)] for k in o] if homophonic else (lambda py:[i for i in sorted_phrases[py[0]+py[1]][py[2]] if i[0] == idiom[-1]])(get_pinyin_of_n(idiom,-1)[0])))
while True:
dw, homo = (bool(int(i)) for i in input("异字 异音:").split(" "))
print(legal_patted_idiom((phra:=input("成语A:")),(phrb:=input("成语B:")),dw,homo),legal_idiom(phra),legal_idiom(phrb),"\n",get_idiom(phra,dw,homo),get_pinyin_of_n(phra,-1),get_pinyin_of_n(phrb,0))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
import random
from pathlib import Path
from typing import Dict, List, Tuple
from PIL import ImageFont
from PIL.ImageFont import FreeTypeFont
# from watchdog.observers import Observer
# from watchdog.events import FileSystemEventHandler, FileModifiedEvent
from pypinyin import Style, pinyin
resource_dir = Path(__file__).parent / "resources"
fonts_dir = resource_dir / "fonts"
data_dir = resource_dir / "data"
idiom_path = resource_dir / "idioms_p.txt"
# fmt: off
# 声母
INITIALS = [
"zh", "z", "y", "x", "w", "t", "sh", "s", "r", "q", "p",
"n", "m", "l", "k", "j", "h", "g", "f", "d", "ch", "c", "b"
]
# 韵母
FINALS = [
"ün", "üe", "üan", "ü", "uo", "un", "ui", "ue", "uang",
"uan", "uai","ua", "ou", "iu", "iong", "ong", "io", "ing",
"in", "ie", "iao", "iang", "ian", "ia", "er", "eng", "en",
"ei", "ao", "ang", "an", "ai", "u", "o", "i", "e", "a"
]
# fmt: on
def get_pinyin_of_n(word: str, which: int) -> List[Tuple[str, str, str]]:
pys = pinyin(word, style=Style.TONE3, v_to_u=True,heteronym=True)[which]
# py = p[0]
results = []
for py in pys:
if py[-1].isdigit():
tone = py[-1]
py = py[:-1]
else:
tone = ""
initial = ""
for i in INITIALS:
if py.startswith(i):
initial = i
break
final = ""
for f in FINALS:
if py.endswith(f):
final = f
break
results.append((initial, final, tone)) # 声母,韵母,声调
return results
LEGAL_PHRASES = [
idiom.strip() for idiom in idiom_path.open("r", encoding="utf-8").readlines()
]
sorted_phrases = dict([i for j in [[(py[0]+py[1],{"":[],"1":[],"2":[],"3":[],"4":[]}) for py in get_pinyin_of_n(idiom[0],0)] for idiom in LEGAL_PHRASES] for i in j])
for idiom in LEGAL_PHRASES:
for py in get_pinyin_of_n(idiom[0],0):
sorted_phrases[py[0]+py[1]][py[2]].append(idiom)
# class LegalPhrasesModifiedHandler(FileSystemEventHandler):
# """
# Handler for resource file changes
# """
# def on_modified(self, event):
# print(f"{event.src_path} modified, reloading resource...")
# if "idioms.txt" in event.src_path:
# global LEGAL_PHRASES
# LEGAL_PHRASES = [
# idiom.strip()
# for idiom in idiom_path.open("r", encoding="utf-8").readlines()
# ]
# sorted_phrases = dict([i for j in [[(py[0]+py[1],{"":[],"1":[],"2":[],"3":[],"4":[]}) for py in get_pinyin_of_n(idiom[0],0)] for idiom in LEGAL_PHRASES] for i in j])
# for idiom in LEGAL_PHRASES:
# for py in get_pinyin_of_n(idiom[0],0):
# sorted_phrases[py[0]+py[1]][py[2]].append(idiom)
# Observer().schedule(
# LegalPhrasesModifiedHandler(),
# data_dir,
# recursive=False,
# event_filter=FileModifiedEvent,
# )
def legal_idiom(word: str) -> bool:
return word in LEGAL_PHRASES
def random_idiom() -> str:
return random.choice(LEGAL_PHRASES)
def legal_patted_idiom(former:str, laster: str, diff_word: bool,homophonic: bool) -> bool:
"""
判断成语是否符合接龙条件
Parameters
==========
former: str
前一个成语
laster: str
后一个成语
diff_word: bool
异字模式接龙之字无须一致
homophonic: bool
谐音模式接龙之字可不同音调
"""
return legal_idiom(laster) and legal_idiom(former) and ((((len({i[:2] for i in get_pinyin_of_n(laster[0],0)}.intersection({i[:2] for i in get_pinyin_of_n(former[-1],0)})))>0) if homophonic else (get_pinyin_of_n(laster,0)[0] == get_pinyin_of_n(former,-1)[0])) if diff_word else (former[-1] == laster[0] if homophonic else ((former[-1] == laster[0])and(get_pinyin_of_n(laster,0)[0] == get_pinyin_of_n(former,-1)[0]))))
def get_idiom(idiom: str,diff_word: bool,homophonic: bool) -> str:
return random.choice(([k for o in [[i for j in sorted_phrases[py[0]+py[1]].values() for i in j] for py in get_pinyin_of_n(idiom[-1],0)] for k in o] if homophonic else sorted_phrases[(py:=get_pinyin_of_n(idiom,-1)[0])[0]+py[1]][py[2]])if diff_word else ([k for o in [[i for j in sorted_phrases[py[0]+py[1]].values() for i in j if i[0] == idiom[-1]] for py in get_pinyin_of_n(idiom[-1],0)] for k in o] if homophonic else (lambda py:[i for i in sorted_phrases[py[0]+py[1]][py[2]] if i[0] == idiom[-1]])(get_pinyin_of_n(idiom,-1)[0])))
def load_font(name: str, fontsize: int) -> FreeTypeFont:
return ImageFont.truetype(str(fonts_dir / name), fontsize, encoding="utf-8")

View File

@ -1,7 +1,7 @@
from nonebot.plugin import PluginMetadata from nonebot.plugin import PluginMetadata
from .status import * from .status import *
__author__ = "snowykami" __author__ = "snowykami & 金羿Eilles"
__plugin_meta__ = PluginMetadata( __plugin_meta__ = PluginMetadata(
name="状态查看器", name="状态查看器",
description="", description="",

View File

@ -15,8 +15,8 @@ from nonebot_plugin_alconna import (
UniMessage, UniMessage,
Option, Option,
store_true, store_true,
AlconnaQuery, # AlconnaQuery,
Query, # Query,
Arparma, Arparma,
) )

View File

@ -0,0 +1,27 @@
from nonebot.plugin import PluginMetadata
from .msctexec import *
__author__ = "金羿Eilles"
__plugin_meta__ = PluginMetadata(
name="伶伦转换器",
description="",
usage=(
"MARKDOWN## 伶伦转换器\n"
"《我的世界》音乐转换,结构生成与转换\n"
"### 用法\n"
"- `/msctcvt` 转换MIDI音乐\n"
# "- `/stctcvt` 各类结构互转\n"
# "- `/stctbld` 文本指令转结构\n"
# "- `/stctbld` 文本指令转结构\n"
),
type="application",
homepage="https://gitee.com/TriM-Organization/Musicreater",
extra={
"liteyuki": True,
"toggleable" : False,
"default_enable" : True,
}
)

View File

@ -0,0 +1,42 @@
import nonebot
from nonebot import require
require("nonebot_plugin_alconna")
from nonebot_plugin_alconna import (
on_alconna,
Alconna,
Subcommand,
UniMessage,
Option,
store_true,
Arparma,
)
musicreater_convert = on_alconna(
aliases={"musicreater_convert","音乐转换","midi转换"},
command=Alconna(
"msctcvt",
Option(
"-r|--refresh",
default=False,
alias={"refr", "r", "刷新"},
action=store_true,
),
Subcommand(
"memory",
alias={"mem", "m", "内存"},
),
Subcommand(
"process",
alias={"proc", "p", "进程"},
),
# Subcommand(
# "refresh",
# alias={"refr", "r", "刷新"},
# ),
),
)

View File

@ -0,0 +1,2 @@
stat.message=计言
stat.rank=言榜

View File

@ -19,7 +19,7 @@ body {
} }
.info-box { .info-box {
border-radius: 60px; /* border-radius: 60px; */
padding: 40px; padding: 40px;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);

View File

@ -20,7 +20,7 @@
.bot-icon-img { .bot-icon-img {
height: 220px; height: 220px;
width: 220px; width: 220px;
border-radius: 50%; /* border-radius: 50%; */
} }
.bot-name { .bot-name {
@ -88,14 +88,14 @@
width: 100%; width: 100%;
height: 60px; height: 60px;
background-color: #ffffff44; background-color: #ffffff44;
border-radius: 30px; /* border-radius: 30px; */
align-items: center; align-items: center;
} }
.disk-usage { .disk-usage {
background-color: #a2d8f4; background-color: #a2d8f4;
height: 100%; height: 100%;
border-radius: 30px; /* border-radius: 30px; */
position: absolute; position: absolute;
z-index: 1; z-index: 1;
} }

View File

@ -50,7 +50,7 @@ body {
} }
.bot-icon { .bot-icon {
border-radius: 50%; /* border-radius: 50%; */
height: 200px; height: 200px;
background-color: white; background-color: white;
} }
@ -102,6 +102,6 @@ body {
} }
.addition-info { .addition-info {
font-size: 36px; font-size: 32px;
color: #fff; color: #fff;
} }