Skip to main content

a useful plugin providing convinient tools for the development of textual game on QQ

Project description

Ayaka 0.3.6

针对Nonebot2框架 Onebot_v11协议的文字游戏开发辅助插件

注意:由于更新pypi的readme.md需要占用版本号,因此其readme.md可能不是最新的,强烈建议读者前往github仓库以获取最新版本的帮助

更新记录

更新记录

0.3.0

借助contextvar内置模块,全部重写了之间的代码,现在它们被合并为一个单文件,并能实现ayaka插件先前提供的所有功能,但不幸的是,其无法兼容0.2.x的ayaka插件,需要代码迁移

0.3.1

规定了应用启动后的默认初始状态为 init

0.3.2

增加了较为完善的注释

0.3.3

在本文档中更新了部分帮助

0.3.5

将ayaka_master集成进本插件中

0.3.6

不兼容

修改了AyakaStorage和AyakaApp的两处方法:

  • plugin_storage
  • group_storage

修改内容:

取消了suffix参数,现在,你需要在最后一个name中指定suffix,否则,AyakaStorage中的path属性将指向一个文件夹

迁移方式:

app.plugin_storage("name") -> app.plugin_storage("name.json")

app.plugin_storage("test", "name", suffix=".txt") -> app.plugin_storage("test", "name.txt")

安装

  1. poetry add nonebot-plugin-ayaka
  2. poetry run playwright install chromium
  3. bot.py无需修改,只要在ayaka衍生插件里正常导入就行:from ayaka import AyakaApp

ayaka衍生插件需要nonebot来加载

如果没有用到无头浏览器截图的功能,可忽略第二步

配置

推荐配置(非强制要求)

COMMAND_START=["#"]
COMMAND_SEP=[" "]

快速了解

通过ayaka插件,二次封装nonebot2提供的api,提供专用api,便于其他文字游戏插件的编写

基于ayaka的衍生插件库 https://github.com/bridgeL/ayaka_plugins

基于ayaka的小游戏合集 https://github.com/bridgeL/nonebot-plugin-ayaka-games

基于ayaka的谁是卧底小游戏 https://github.com/bridgeL/nonebot-plugin-ayaka-who-is-suspect

基本特性

  • 状态机
  • 命令隔离
  • 数据隔离

ayaka 内置插件

ayaka内部已安装一份特殊的综合管理插件,它基于ayaka插件而实现

命令一览:

  • 启用/permit
  • 禁用/forbid
  • 插件/plugin
  • 状态/state
  • 帮助/help

所有ayaka衍生插件只需要编写app.help,就可以在用户输入 #help <插件名> 后获取该插件的帮助

代码速看

'''
    hello world
    
    ayaka可以帮助你实现命令隔离
'''
from ayaka import AyakaApp

app = AyakaApp("hello-world")

# 你可以不写帮助
# app.help


# 桌面状态下
@app.on_command("hw")
async def app_entrance():
    await app.start()
    # app运行后,进入指定状态(state = "world")
    app.set_state("world")


# 只有world状态可以退出,其他状态运行该指令均为返回world状态
@app.on_state_command(["exit", "退出"], "*")
async def app_exit():
    if app.state == "world":
        await app.close()
    else:
        app.set_state("world")
        await app.send("跳转到 world")


# 对世界、月亮和太阳打个招呼
@app.on_state_command("hi", ["world", "moon", "sun"])
async def hello():
    await app.send(f"hello,{app.state}!")


# 对世界、月亮和太阳来个大比兜
@app.on_state_command("hit", "world")
async def hit():
    await app.send("earthquake")


@app.on_state_command("hit", "moon")
async def hit():
    await app.send("moon fall")


@app.on_state_command("hit", "sun")
async def hit():
    await app.send("big bang!")


# 跳转状态
@app.on_state_command("jump", "*")
async def jump_to_somewhere():
    if not app.arg:
        await app.send("没有参数!")
    else:
        next_state = str(app.arg)
        app.set_state(next_state)
        await app.send(f"跳转到 [{next_state}]")

术语解释

无状态应用

例如,简单复读我说的话的应用

那么对于该应用而言,我之前说了啥都是无所谓的,它只需要复读我当前正在说的这句就行了。也就是说,无论我曾经下达过什么指令,都不会影响到该应用的状态,它是永恒不变的,即无状态应用

无状态应用在注册回调时,使用app.on_commandapp.on_text即可

有状态应用

例如,代码速看中的hello-world应用

我发送hi指令时,应用需要根据自身的状态(earth/moon/sun)给出不同的响应,因此它是有状态应用

有状态应用在注册回调时,需要使用app.on_state_commandapp.on_state_text

应用启用/禁用

该应用(不管是无状态应用还是有状态应用)的指令是否可以响应

比如禁用了hello-world应用后,当你发送hw时,bot将不会触发回调

应用运行/关闭

是指有状态应用是否正在运行

比如当前群聊并没有运行应用时,你发送hw,bot将运行hello-world应用

单一应用原则

一个群聊同一时间只能运行一个有状态应用,避免过于混乱的情形,如果想要运行另一个应用,请先关闭当前的应用

无状态应用没有运行和关闭的概念,你发完指令,它立刻就会回应你

API

app属性一览表

名称 类型 功能
intro str 应用介绍(帮助dict中key为init所对应的value)
help str 当前应用在当前状态下的帮助
all_help str 当前应用的所有帮助
state bool 当前应用的状态
valid bool 应用在当前设备 是否被启用
bot Bot 当前机器人
group AyakaGroup 当前群组
event MessageEvent 当前消息事件
message Message 当前消息
arg Message 删除了命令后剩下的消息部分
args List[MessageSegment] 删除命令后,依照分隔符分割,并移除空数据
cmd str 本次响应是针对哪个命令
bot_id int 当前机器人的qq号
group_id int 当前群聊的群号
user_id int 当前消息的发送者的qq号
user_name str 当前消息的发送者的群名片或昵称(优先为群名片)
cache AyakaCache 为当前群组当前应用提供的独立缓存数据空间

app方法一览表

名称 功能 是否异步
start 运行应用
close 关闭应用
send 发送消息
send_many 发送合并转发消息
t_send 定时器触发回调时所使用的专用发送消息方法
set_state 设置应用状态(在应用运行时可以设置) \
on_command 注册桌面模式下的命令回调 \
on_state_command 注册应用运行时在不同状态下的命令回调 \
on_text 注册桌面模式下的消息回调 \
on_state_text 注册应用运行时在不同状态下的消息回调 \
on_everyday 每日定时触发回调(东8区) \
on_interval 在指定的时间点后开始循环触发(东8区) \
add_listener 为该群组添加对 指定私聊 的监听 \
remove_listener 移除该群组对 指定私聊/所有其他私聊 的监听 \

如何编写ayaka衍生插件

给出几份例程代码以帮助读者理解使用

如何使用例程代码?

以下两种方式皆可

手动复制本文档中的代码

你可以在nonebot工作目录的src/plugins中新建一个代码文件,手动复制代码进去,nonebot会在读到bot.py文件中的nonebot.load_from_toml(...)语句后导入该插件

从仓库下载

你也可以前往ayaka衍生插件库,下载其中的example文件夹,放到nonebot工作目录下,然后在bot.py中添加nonebot.load_plugin("example")

插件编写范例 echo

'''
    具有状态机的复读模块
'''
from ayaka import AyakaApp

app = AyakaApp("echo")

# 得益于ayaka内置插件 ayaka_master
# 用户可通过#help命令展示插件帮助,只需编写app.help即可
app.help = '''复读只因
特殊命令一览:
- reverse 开始说反话
- back 停止说反话
- exit 退出
'''

# 另一种写法
app.help = {
    "init": "复读只因\n特殊命令一览:\n- reverse 开始说反话\n- exit 退出",
    "reverse": "说反话模式\n- back 停止说反话"
}


# 桌面状态下
@app.on_command("echo")
async def app_entrance():
    # 输入参数则复读参数(无状态响应
    # > #echo hihi
    # < hihi
    if app.arg:
        await app.send(app.arg)
        return

    # 没有输入参数则运行该应用
    await app.start()


# app运行后,进入初始状态(state = "init")

# 正常复读
@app.on_state_text()
async def repeat():
    await app.send(app.message)


# 任意状态均可直接退出
@app.on_state_command(["exit", "退出"], "*")
async def app_exit():
    await app.close()


# 通过命令,跳转到reverse状态
@app.on_state_command(["rev", "reverse", "话反说", "反", "说反话"])
async def start_rev():
    app.set_state("reverse")
    await app.send("开始说反话")


# 反向复读
@app.on_state_text("reverse")
async def reverse_echo():
    msg = str(app.message)
    msg = "".join(s for s in reversed(msg))
    await app.send(msg)


# 通过命令,跳转回初始状态
@app.on_state_command("back", "reverse")
async def back():
    app.set_state()
    await app.send("话反说止停")

插件编写范例 a plus b

'''
    a + b 
    
    各群聊间、各插件间,数据独立,互不影响;不需要自己再专门建个字典了
'''
from ayaka import AyakaApp

app = AyakaApp("a-plus-b")


@app.on_command("set_a")
async def set_a():
    app.cache.a = int(str(app.arg)) if app.arg else 0
    await app.send(app.cache.a)


@app.on_command("set_b")
async def set_b():
    app.cache.b = int(str(app.arg)) if app.arg else 0
    await app.send(app.cache.b)


@app.on_command("calc")
async def calc():
    a = app.cache.a or 0
    b = app.cache.b or 0
    await app.send(str(a+b))

定时器 Timer

注意,定时器触发回调时,由于缺乏消息激励源,app的大部分属性(bot、group、event、valid、cache、user_name等)将无法正确访问到,并且无法使用app.send方法,需要使用专用的t_send方法

'''
    整点报时
'''
from ayaka import AyakaApp

app = AyakaApp("整点报时")


@app.on_interval(60, s=0)
async def every_minute():
    await app.t_send(bot_id=2317709898, group_id=666214666, message="小乐")


@app.on_interval(3600, m=0, s=0)
async def every_hour():
    await app.t_send(bot_id=2317709898, group_id=666214666, message="大乐")


@app.on_everyday(h=23, m=59, s=59)
async def every_day():
    await app.t_send(bot_id=2317709898, group_id=666214666, message="呃呃呃一天要结束了")

截图 playwright

注意,win平台使用playwright时需要关闭fastapi的reload功能

'''
    can can baidu
'''

from pathlib import Path
from ayaka import get_new_page, AyakaApp, MessageSegment

app = AyakaApp("看看baidu")


@app.on_command("ccb")
async def _():
    async with get_new_page() as p:
        await p.goto("http://www.baidu.com", wait_until="networkidle")
        path = Path("test.png").absolute()
        await p.screenshot(path=path)
    image = MessageSegment.image(path)
    await app.send(image)

自动分割消息

ayaka插件将会自动根据配置项中的分割符来分割消息,例如

#test a   b c

会在ayaka插件处理后变为

@app.on_command("test")
async def _():
    # 此时app身上的如下属性的值应该是:...
    assert app.cmd == "test"
    assert str(app.arg) == "a   b c"
    assert str(app.args[0]) == "a"
    assert str(app.args[1]) == "b"
    assert str(app.args[2]) == "c"

未来计划

提供aiosqlite数据库支持

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nonebot_plugin_ayaka-0.3.6.tar.gz (16.5 kB view hashes)

Uploaded Source

Built Distribution

nonebot_plugin_ayaka-0.3.6-py3-none-any.whl (15.2 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page