源与适配器

添加源、适配器

此前的章节中提到过源与适配器。实际上一个 bot 实例允许你添加任意协议,任意数量的源与适配器。

from melobot import Bot

bot = Bot(...)
# 添加一个输入源
bot.add_input(...)
# 添加一个输出源
bot.add_output(...)
# 添加一个输入输出源
bot.add_io(...)
# 添加一个适配器
bot.add_adapter(...)

假设使用 in[A], out[A], io[A], @[A] 分别表示 A 协议的输入源、输出源、输入输出源和适配器

以下的 + 并无顺序之分,只是应用在同一 bot 实例上的意思

典型的组合方式:

组合方式

效果

in[A] + @[A]

A 协议输入 √,A 协议输出 ×(因为有些协议不一定有输出源)

out[A] + @[A]

A 协议输入 ×,A 协议输出 √(因为有些协议不一定有输入源)

in[A] + out[A] + @[A]

A 协议输入 √,A 协议输出 √

io[A] + @[A]

A 协议输入 √,A 协议输出 √

不太合理的组合:

组合方式

效果

in[A]

A 协议输入 ×,A 协议输出 ×(没有适配器,协议无法正常工作)

out[A]

A 协议输入 ×,A 协议输出 ×(没有适配器,协议无法正常工作)

@[A]

A 协议输入 ×,A 协议输出 ×(没有源,协议无法正常工作)

@[A] + @[A]

A 协议输入 ×,A 协议输出 ×(同一协议的适配器不能添加第二次,且没有源)

提示

对 bot 实例来说,同时存在同一协议的源与适配器才能正常工作。

同协议多路输入、多路输出组合:

组合方式

效果

in[A] + in[A] + ... + out[A] + out[A] + ... + @[A]

产生协议 A 的多路输入和输出(例如 OneBot 协议的多个账号输入和输出)

提示

多路输入和输出,如何选择、分配和管理,后续的章节中会介绍。

多协议组合:

组合方式

效果

in[A] + @[A] + out[B] + @[B]

A 协议输入 √,A 协议输出 ×;B 协议输入 ×,B 协议输出 √

多协议且多路的组合:

组合方式

效果

io[A] + in[A] + @[A] + io[B] + io[B] + @[B]

A 协议 2 路输入,1 路输出;B 协议 2 路输入,2 路输出

添加协议栈

协议栈是一组同协议的源与适配器的包装,用于简化分步添加源、适配器的繁琐过程。

例如 OneBot 协议的协议栈对象,允许你直接添加源对象,内部自动添加适配器:

from melobot.protocols.onebot.v11 import OneBotV11Protocol, ForwardWebSocketIO, \
    ReverseWebSocketIO

# 添加指定数量的源
protocol = OneBotV11Protocol(
    ForwardWebSocketIO(...), 
    ReverseWebSocketIO(...), 
    ...
)
# 随后把协议栈对象提供给 bot 实例
# 协议栈对象会自动完成适配器添加
bot.add_protocol(protocol)

其他协议的协议栈对象,可能会有更抽象的包装行为:例如简化掉源对象创建,让用户专注于配置参数本身。

获取适配器对象

很自然的,可以在适配器创建时获取:

from melobot.protocols.onebot.v11 import Adapter
# 但一般只会在主脚本创建适配器
adapter = Adapter(...)
# 因为随后要提供给 bot 实例
bot.add_adapter(adapter)

在插件中,通过 bot 获取适配器:

from melobot import get_bot

bot = get_bot()
adapter = bot.get_adapter(...)

或者通过依赖注入获取:

from melobot.protocols.onebot.v11 import Adapter

@on_xxx(...)
async def _(adapter: Adapter) -> None: ...

获取源对象

很自然的,可以在源对象创建时获取:

from melobot.protocols.onebot.v11 import ForwardWebSocketIO
# 但一般只会在主脚本创建源
src = ForwardWebSocketIO(...)
# 因为随后要提供给 bot 实例
bot.add_io(src)

在插件中,通过适配器获取源:

src = adapter.get_isrc(...)
src = adapter.get_osrc(...)

此外,还可以在事件处理过程中,获取事件的来源适配器和来源输入源:

@on_xxx(...)
async def _(event: Event) -> None:
    info = event.get_origin_info()
    adapter, src = info.adapter, info.in_src

协议标识

所有源对象和适配器对象,都拥有 protocol 属性,为协议字符串。表示了所适用的协议类型。

from melobot.protocols.onebot.v11 import ForwardWebSocketIO, Adapter

src = ForwardWebSocketIO(...)
adapter = Adapter(...)
assert src.protocol == "OneBot-v11@Meloland"
assert adapter.protocol == "OneBot-v11@Meloland"

一般协议字符串的风格为:{PROTOCOL_NAME}-v{PROTOCOL_VERSION}@{PROTOCOL_SUPPORT_AUTHOR}

生命周期钩子

所有源对象和适配器对象都是可 hook 的。使用 on() 方法可以绑定一个 hook 函数:

# hook 函数同步异步均可
async def f1() -> None: ...
def f2() -> None: ...

使用 SourceLifeSpan 为源对象绑定 hook 函数:

# 直接使用
from melobot.io import SourceLifeSpan
src.on(SourceLifeSpan.STARTED)(f1)

# 或取得装饰器
on_close = src.on(SourceLifeSpan.CLOSE)
on_close(f2)

# 或直接使用装饰器语法
@src.on(SourceLifeSpan.STOPPED)
async def f3() -> None: ...

使用 AdapterLifeSpan 为适配器对象绑定 hook 函数:

# 直接使用
from melobot.adapter import AdapterLifeSpan
adapter.on(AdapterLifeSpan.STARTED)(f1)

# 或取得装饰器
on_started = adapter.on(AdapterLifeSpan.CLOSE)
on_started(f2)

# 或直接使用装饰器语法
@adapter.on(AdapterLifeSpan.STOPPED)
async def f3() -> None: ...

其他非通用属性与特性,请参考各个源、适配器的 API 文档。

总结

本篇主要说明了 melobot 源与适配器的使用方法和特性。

下一篇将重点说明:适配器层其他组件。