mirror of
https://github.com/langbot-app/LangBot.git
synced 2025-11-25 19:37:36 +08:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdb2db348e | ||
|
|
5873d4696f | ||
|
|
613787f49c | ||
|
|
f620874251 | ||
|
|
1f08082a58 | ||
|
|
8f5da1677b | ||
|
|
5439a3a31f | ||
|
|
d92ee23764 | ||
|
|
71ecfc2566 | ||
|
|
c0787e0bb6 | ||
|
|
357da2d236 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -29,6 +29,7 @@ qcapi
|
||||
claude.json
|
||||
bard.json
|
||||
/*yaml
|
||||
!components.yaml
|
||||
!/docker-compose.yaml
|
||||
data/labels/instance_id.json
|
||||
.DS_Store
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
|
||||
|
||||
[](https://discord.gg/wdNEHETs87)
|
||||
[](https://qm.qq.com/q/PF9OuQCCcM)
|
||||
[](https://qm.qq.com/q/JLi38whHum)
|
||||
[](https://github.com/RockChinQ/LangBot/releases/latest)
|
||||

|
||||
<img src="https://img.shields.io/badge/python-3.10 | 3.11 | 3.12-blue.svg" alt="python">
|
||||
[](https://gitcode.com/RockChinQ/LangBot)
|
||||
|
||||
[简体中文](README.md) / [English](README_EN.md) / [日本語](README_JP.md)
|
||||
|
||||
@@ -36,7 +37,7 @@
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
- 💬 大模型对话、Agent:支持多种大模型,适配群聊和私聊;具有多轮对话、工具调用、多模态能力,并深度适配 [Dify](https://dify.ai)。目前支持 QQ、QQ频道、企业微信、飞书、Discord、个人微信,后续还将支持 WhatsApp、Telegram 等平台。
|
||||
- 💬 大模型对话、Agent:支持多种大模型,适配群聊和私聊;具有多轮对话、工具调用、多模态能力,并深度适配 [Dify](https://dify.ai)。目前支持 QQ、QQ频道、企业微信、个人微信、飞书、Discord、Telegram 等平台。
|
||||
- 🛠️ 高稳定性、功能完备:原生支持访问控制、限速、敏感词过滤等机制;配置简单,支持多种部署方式。
|
||||
- 🧩 插件扩展、活跃社区:支持事件驱动、组件扩展等插件机制;丰富生态,目前已有数十个[插件](https://docs.langbot.app/plugin/plugin-intro.html)
|
||||
- 😻 [New] Web 管理面板:支持通过浏览器管理 LangBot 实例,具体支持功能,查看[文档](https://docs.langbot.app/webui/intro.html)
|
||||
@@ -112,6 +113,7 @@
|
||||
| [GiteeAI](https://ai.gitee.com/) | ✅ | 大模型接口聚合平台 |
|
||||
| [SiliconFlow](https://siliconflow.cn/) | ✅ | 大模型聚合平台 |
|
||||
| [阿里云百炼](https://bailian.console.aliyun.com/) | ✅ | 大模型聚合平台, LLMOps 平台 |
|
||||
| [火山方舟](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | 大模型聚合平台, LLMOps 平台 |
|
||||
|
||||
## 😘 社区贡献
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 💬 Chat with LLM / Agent: Supports multiple LLMs, adapt to group chats and private chats; Supports multi-round conversations, tool calls, and multi-modal capabilities. Deeply integrates with [Dify](https://dify.ai). Currently supports QQ, QQ Channel, WeCom, Lark, Discord, personal WeChat, and will support WhatsApp, Telegram, etc. in the future.
|
||||
- 💬 Chat with LLM / Agent: Supports multiple LLMs, adapt to group chats and private chats; Supports multi-round conversations, tool calls, and multi-modal capabilities. Deeply integrates with [Dify](https://dify.ai). Currently supports QQ, QQ Channel, WeCom, personal WeChat, Lark, DingTalk, Discord, Telegram, etc.
|
||||
- 🛠️ High Stability, Feature-rich: Native access control, rate limiting, sensitive word filtering, etc. mechanisms; Easy to use, supports multiple deployment methods.
|
||||
- 🧩 Plugin Extension, Active Community: Support event-driven, component extension, etc. plugin mechanisms; Rich ecology, currently has dozens of [plugins](https://docs.langbot.app/plugin/plugin-intro.html)
|
||||
- 😻 [New] Web UI: Support management LangBot instance through the browser, for details, see [documentation](https://docs.langbot.app/webui/intro.html)
|
||||
@@ -110,6 +110,7 @@ Directly use the released version to run, see the [Manual Deployment](https://do
|
||||
| [GiteeAI](https://ai.gitee.com/) | ✅ | LLM interface gateway(MaaS) |
|
||||
| [SiliconFlow](https://siliconflow.cn/) | ✅ | LLM gateway(MaaS) |
|
||||
| [Aliyun Bailian](https://bailian.console.aliyun.com/) | ✅ | LLM gateway(MaaS), LLMOps platform |
|
||||
| [Volc Engine Ark](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | LLM gateway(MaaS), LLMOps platform |
|
||||
|
||||
## 🤝 Community Contribution
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
|
||||
## ✨ 機能
|
||||
|
||||
- 💬 LLM / エージェントとのチャット: 複数のLLMをサポートし、グループチャットとプライベートチャットに対応。マルチラウンドの会話、ツールの呼び出し、マルチモーダル機能をサポート。 [Dify](https://dify.ai) と深く統合。現在、QQ、QQチャンネル、WeCom、Lark、Discord、個人WeChatをサポートし、将来的にはWhatsApp、Telegramなどもサポート予定。
|
||||
- 💬 LLM / エージェントとのチャット: 複数のLLMをサポートし、グループチャットとプライベートチャットに対応。マルチラウンドの会話、ツールの呼び出し、マルチモーダル機能をサポート。 [Dify](https://dify.ai) と深く統合。現在、QQ、QQ チャンネル、WeChat、個人 WeChat、Lark、DingTalk、Discord、Telegram など、複数のプラットフォームをサポートしています。
|
||||
- 🛠️ 高い安定性、豊富な機能: ネイティブのアクセス制御、レート制限、敏感な単語のフィルタリングなどのメカニズムをサポート。使いやすく、複数のデプロイ方法をサポート。
|
||||
- 🧩 プラグイン拡張、活発なコミュニティ: イベント駆動、コンポーネント拡張などのプラグインメカニズムをサポート。豊富なエコシステム、現在数十の[プラグイン](https://docs.langbot.app/plugin/plugin-intro.html)が存在。
|
||||
- 😻 [新機能] Web UI: ブラウザを通じてLangBotインスタンスを管理することをサポート。詳細は[ドキュメント](https://docs.langbot.app/webui/intro.html)を参照。
|
||||
@@ -109,6 +109,7 @@ LangBotはBTPanelにリストされています。BTPanelをインストール
|
||||
| [GiteeAI](https://ai.gitee.com/) | ✅ | LLMインターフェースゲートウェイ(MaaS) |
|
||||
| [SiliconFlow](https://siliconflow.cn/) | ✅ | LLMゲートウェイ(MaaS) |
|
||||
| [Aliyun Bailian](https://bailian.console.aliyun.com/) | ✅ | LLMゲートウェイ(MaaS), LLMOpsプラットフォーム |
|
||||
| [Volc Engine Ark](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | LLMゲートウェイ(MaaS), LLMOpsプラットフォーム |
|
||||
|
||||
## 🤝 コミュニティ貢献
|
||||
|
||||
|
||||
15
components.yaml
Normal file
15
components.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Blueprint
|
||||
metadata:
|
||||
name: builtin-components
|
||||
label:
|
||||
en_US: Builtin Components
|
||||
zh_CN: 内置组件
|
||||
spec:
|
||||
components:
|
||||
ComponentTemplate:
|
||||
fromFiles:
|
||||
- pkg/platform/adapter.yaml
|
||||
MessagePlatformAdapter:
|
||||
fromDirs:
|
||||
- path: pkg/platform/sources/
|
||||
@@ -25,6 +25,7 @@ from ..utils import version as version_mgr, proxy as proxy_mgr, announce as anno
|
||||
from ..persistence import mgr as persistencemgr
|
||||
from ..api.http.controller import main as http_controller
|
||||
from ..api.http.service import user as user_service
|
||||
from ..discover import engine as discover_engine
|
||||
from ..utils import logcache, ip
|
||||
from . import taskmgr
|
||||
from . import entities as core_entities
|
||||
@@ -38,6 +39,8 @@ class Application:
|
||||
# asyncio_tasks: list[asyncio.Task] = []
|
||||
task_mgr: taskmgr.AsyncTaskManager = None
|
||||
|
||||
discover: discover_engine.ComponentDiscoveryEngine = None
|
||||
|
||||
platform_mgr: im_mgr.PlatformManager = None
|
||||
|
||||
cmd_mgr: cmdmgr.CommandManager = None
|
||||
|
||||
@@ -57,7 +57,7 @@ class Query(pydantic.BaseModel):
|
||||
message_chain: platform_message.MessageChain
|
||||
"""消息链,platform收到的原始消息链"""
|
||||
|
||||
adapter: msadapter.MessageSourceAdapter
|
||||
adapter: msadapter.MessagePlatformAdapter
|
||||
"""消息平台适配器对象,单个app中可能启用了多个消息平台适配器,此对象表明发起此query的适配器"""
|
||||
|
||||
session: typing.Optional[Session] = None
|
||||
|
||||
27
pkg/core/migrations/m032_volcark_config.py
Normal file
27
pkg/core/migrations/m032_volcark_config.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from .. import migration
|
||||
|
||||
|
||||
@migration.migration_class("volcark-requester-config", 32)
|
||||
class VolcArkRequesterConfigMigration(migration.Migration):
|
||||
"""迁移"""
|
||||
|
||||
async def need_migrate(self) -> bool:
|
||||
"""判断当前环境是否需要运行此迁移"""
|
||||
|
||||
return 'volcark-chat-completions' not in self.ap.provider_cfg.data['requester']
|
||||
|
||||
async def run(self):
|
||||
"""执行迁移"""
|
||||
self.ap.provider_cfg.data['keys']['volcark'] = [
|
||||
"xxxxxxxx"
|
||||
]
|
||||
|
||||
self.ap.provider_cfg.data['requester']['volcark-chat-completions'] = {
|
||||
"base-url": "https://ark.cn-beijing.volces.com/api/v3",
|
||||
"args": {},
|
||||
"timeout": 120
|
||||
}
|
||||
|
||||
await self.ap.provider_cfg.dump_config()
|
||||
@@ -18,6 +18,7 @@ from ...platform import manager as im_mgr
|
||||
from ...persistence import mgr as persistencemgr
|
||||
from ...api.http.controller import main as http_controller
|
||||
from ...api.http.service import user as user_service
|
||||
from ...discover import engine as discover_engine
|
||||
from ...utils import logcache
|
||||
from .. import taskmgr
|
||||
|
||||
@@ -32,6 +33,12 @@ class BuildAppStage(stage.BootingStage):
|
||||
"""
|
||||
ap.task_mgr = taskmgr.AsyncTaskManager(ap)
|
||||
|
||||
discover = discover_engine.ComponentDiscoveryEngine(ap)
|
||||
discover.discover_blueprint(
|
||||
"components.yaml"
|
||||
)
|
||||
ap.discover = discover
|
||||
|
||||
proxy_mgr = proxy.ProxyManager(ap)
|
||||
await proxy_mgr.initialize()
|
||||
ap.proxy_mgr = proxy_mgr
|
||||
|
||||
@@ -10,7 +10,7 @@ from ..migrations import m010_ollama_requester_config, m011_command_prefix_confi
|
||||
from ..migrations import m015_gitee_ai_config, m016_dify_service_api, m017_dify_api_timeout_params, m018_xai_config, m019_zhipuai_config
|
||||
from ..migrations import m020_wecom_config, m021_lark_config, m022_lmstudio_config, m023_siliconflow_config, m024_discord_config, m025_gewechat_config
|
||||
from ..migrations import m026_qqofficial_config, m027_wx_official_account_config, m028_aliyun_requester_config
|
||||
from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config
|
||||
from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config, m032_volcark_config
|
||||
|
||||
|
||||
@stage.stage_class("MigrationStage")
|
||||
|
||||
0
pkg/discover/__init__.py
Normal file
0
pkg/discover/__init__.py
Normal file
197
pkg/discover/engine.py
Normal file
197
pkg/discover/engine.py
Normal file
@@ -0,0 +1,197 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import typing
|
||||
import importlib
|
||||
import os
|
||||
import inspect
|
||||
|
||||
import yaml
|
||||
import pydantic
|
||||
|
||||
from ..core import app
|
||||
|
||||
|
||||
class I18nString(pydantic.BaseModel):
|
||||
"""国际化字符串"""
|
||||
|
||||
en_US: str
|
||||
"""英文"""
|
||||
|
||||
zh_CN: typing.Optional[str] = None
|
||||
"""中文"""
|
||||
|
||||
ja_JP: typing.Optional[str] = None
|
||||
"""日文"""
|
||||
|
||||
|
||||
class Metadata(pydantic.BaseModel):
|
||||
"""元数据"""
|
||||
|
||||
name: str
|
||||
"""名称"""
|
||||
|
||||
label: I18nString
|
||||
"""标签"""
|
||||
|
||||
description: typing.Optional[I18nString] = None
|
||||
"""描述"""
|
||||
|
||||
icon: typing.Optional[str] = None
|
||||
"""图标"""
|
||||
|
||||
|
||||
class PythonExecution(pydantic.BaseModel):
|
||||
"""Python执行"""
|
||||
|
||||
path: str
|
||||
"""路径"""
|
||||
|
||||
attr: str
|
||||
"""属性"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
if self.path.startswith('./'):
|
||||
self.path = self.path[2:]
|
||||
|
||||
|
||||
class Execution(pydantic.BaseModel):
|
||||
"""执行"""
|
||||
|
||||
python: PythonExecution
|
||||
"""Python执行"""
|
||||
|
||||
|
||||
class Component(pydantic.BaseModel):
|
||||
"""组件清单"""
|
||||
|
||||
owner: str
|
||||
"""组件所属"""
|
||||
|
||||
manifest: typing.Dict[str, typing.Any]
|
||||
"""组件清单内容"""
|
||||
|
||||
rel_path: str
|
||||
"""组件清单相对main.py的路径"""
|
||||
|
||||
_metadata: Metadata
|
||||
"""组件元数据"""
|
||||
|
||||
_spec: typing.Dict[str, typing.Any]
|
||||
"""组件规格"""
|
||||
|
||||
_execution: Execution
|
||||
"""组件执行"""
|
||||
|
||||
def __init__(self, owner: str, manifest: typing.Dict[str, typing.Any], rel_path: str):
|
||||
super().__init__(
|
||||
owner=owner,
|
||||
manifest=manifest,
|
||||
rel_path=rel_path
|
||||
)
|
||||
self._metadata = Metadata(**manifest['metadata'])
|
||||
self._spec = manifest['spec']
|
||||
self._execution = Execution(**manifest['execution']) if 'execution' in manifest else None
|
||||
|
||||
@property
|
||||
def kind(self) -> str:
|
||||
"""组件类型"""
|
||||
return self.manifest['kind']
|
||||
|
||||
@property
|
||||
def metadata(self) -> Metadata:
|
||||
"""组件元数据"""
|
||||
return self._metadata
|
||||
|
||||
@property
|
||||
def spec(self) -> typing.Dict[str, typing.Any]:
|
||||
"""组件规格"""
|
||||
return self._spec
|
||||
|
||||
@property
|
||||
def execution(self) -> Execution:
|
||||
"""组件执行"""
|
||||
return self._execution
|
||||
|
||||
def get_python_component_class(self) -> typing.Type[typing.Any]:
|
||||
"""获取Python组件类"""
|
||||
parent_path = os.path.dirname(self.rel_path)
|
||||
module_path = os.path.join(parent_path, self.execution.python.path)
|
||||
if module_path.endswith('.py'):
|
||||
module_path = module_path[:-3]
|
||||
module_path = module_path.replace('/', '.').replace('\\', '.')
|
||||
module = importlib.import_module(module_path)
|
||||
return getattr(module, self.execution.python.attr)
|
||||
|
||||
|
||||
class ComponentDiscoveryEngine:
|
||||
"""组件发现引擎"""
|
||||
|
||||
ap: app.Application
|
||||
"""应用实例"""
|
||||
|
||||
components: typing.Dict[str, typing.List[Component]] = {}
|
||||
"""组件列表"""
|
||||
|
||||
def __init__(self, ap: app.Application):
|
||||
self.ap = ap
|
||||
|
||||
def load_component_manifest(self, path: str, owner: str = 'builtin', no_save: bool = False) -> Component:
|
||||
"""加载组件清单"""
|
||||
with open(path, 'r') as f:
|
||||
manifest = yaml.safe_load(f)
|
||||
comp = Component(
|
||||
owner=owner,
|
||||
manifest=manifest,
|
||||
rel_path=path
|
||||
)
|
||||
if not no_save:
|
||||
if comp.kind not in self.components:
|
||||
self.components[comp.kind] = []
|
||||
self.components[comp.kind].append(comp)
|
||||
return comp
|
||||
|
||||
def load_component_manifests_in_dir(self, path: str, owner: str = 'builtin', no_save: bool = False) -> typing.List[Component]:
|
||||
"""加载目录中的组件清单"""
|
||||
components: typing.List[Component] = []
|
||||
for file in os.listdir(path):
|
||||
if file.endswith('.yaml') or file.endswith('.yml'):
|
||||
components.append(self.load_component_manifest(os.path.join(path, file), owner, no_save))
|
||||
return components
|
||||
|
||||
def load_blueprint_comp_group(self, group: dict, owner: str = 'builtin', no_save: bool = False) -> typing.List[Component]:
|
||||
"""加载蓝图组件组"""
|
||||
components: typing.List[Component] = []
|
||||
if 'fromFiles' in group:
|
||||
for file in group['fromFiles']:
|
||||
components.append(self.load_component_manifest(file, owner, no_save))
|
||||
if 'fromDirs' in group:
|
||||
for dir in group['fromDirs']:
|
||||
path = dir['path']
|
||||
# depth = dir['depth']
|
||||
components.extend(self.load_component_manifests_in_dir(path, owner, no_save))
|
||||
return components
|
||||
|
||||
def discover_blueprint(self, blueprint_manifest_path: str, owner: str = 'builtin'):
|
||||
"""发现蓝图"""
|
||||
blueprint_manifest = self.load_component_manifest(blueprint_manifest_path, owner, no_save=True)
|
||||
assert blueprint_manifest.kind == 'Blueprint', '`Kind` must be `Blueprint`'
|
||||
components: typing.Dict[str, typing.List[Component]] = {}
|
||||
|
||||
# load ComponentTemplate first
|
||||
if 'ComponentTemplate' in blueprint_manifest.spec['components']:
|
||||
components['ComponentTemplate'] = self.load_blueprint_comp_group(blueprint_manifest.spec['components']['ComponentTemplate'], owner)
|
||||
|
||||
for name, component in blueprint_manifest.spec['components'].items():
|
||||
if name == 'ComponentTemplate':
|
||||
continue
|
||||
components[name] = self.load_blueprint_comp_group(component, owner)
|
||||
return blueprint_manifest, components
|
||||
|
||||
|
||||
def get_components_by_kind(self, kind: str) -> typing.List[Component]:
|
||||
"""获取指定类型的组件"""
|
||||
if kind not in self.components:
|
||||
raise ValueError(f'No components found for kind: {kind}')
|
||||
return self.components[kind]
|
||||
@@ -33,7 +33,7 @@ class QueryPool:
|
||||
sender_id: typing.Union[int, str],
|
||||
message_event: platform_events.MessageEvent,
|
||||
message_chain: platform_message.MessageChain,
|
||||
adapter: msadapter.MessageSourceAdapter
|
||||
adapter: msadapter.MessagePlatformAdapter
|
||||
) -> entities.Query:
|
||||
async with self.condition:
|
||||
query = entities.Query(
|
||||
|
||||
@@ -10,27 +10,7 @@ from .types import message as platform_message
|
||||
from .types import events as platform_events
|
||||
|
||||
|
||||
preregistered_adapters: list[typing.Type[MessageSourceAdapter]] = []
|
||||
|
||||
def adapter_class(
|
||||
name: str
|
||||
):
|
||||
"""消息平台适配器类装饰器
|
||||
|
||||
Args:
|
||||
name (str): 适配器名称
|
||||
|
||||
Returns:
|
||||
typing.Callable[[typing.Type[MessageSourceAdapter]], typing.Type[MessageSourceAdapter]]: 装饰器
|
||||
"""
|
||||
def decorator(cls: typing.Type[MessageSourceAdapter]) -> typing.Type[MessageSourceAdapter]:
|
||||
cls.name = name
|
||||
preregistered_adapters.append(cls)
|
||||
return cls
|
||||
return decorator
|
||||
|
||||
|
||||
class MessageSourceAdapter(metaclass=abc.ABCMeta):
|
||||
class MessagePlatformAdapter(metaclass=abc.ABCMeta):
|
||||
"""消息平台适配器基类"""
|
||||
|
||||
name: str
|
||||
@@ -89,7 +69,7 @@ class MessageSourceAdapter(metaclass=abc.ABCMeta):
|
||||
def register_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_message.Event],
|
||||
callback: typing.Callable[[platform_message.Event, MessageSourceAdapter], None]
|
||||
callback: typing.Callable[[platform_message.Event, MessagePlatformAdapter], None]
|
||||
):
|
||||
"""注册事件监听器
|
||||
|
||||
@@ -102,7 +82,7 @@ class MessageSourceAdapter(metaclass=abc.ABCMeta):
|
||||
def unregister_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_message.Event],
|
||||
callback: typing.Callable[[platform_message.Event, MessageSourceAdapter], None]
|
||||
callback: typing.Callable[[platform_message.Event, MessagePlatformAdapter], None]
|
||||
):
|
||||
"""注销事件监听器
|
||||
|
||||
|
||||
14
pkg/platform/adapter.yaml
Normal file
14
pkg/platform/adapter.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: ComponentTemplate
|
||||
metadata:
|
||||
name: MessagePlatformAdapter
|
||||
label:
|
||||
en_US: Message Platform Adapter
|
||||
zh_CN: 消息平台适配器模板类
|
||||
spec:
|
||||
type:
|
||||
- python
|
||||
execution:
|
||||
python:
|
||||
path: ./adapter.py
|
||||
attr: MessagePlatformAdapter
|
||||
@@ -18,6 +18,8 @@ from .types import message as platform_message
|
||||
from .types import events as platform_events
|
||||
from .types import entities as platform_entities
|
||||
|
||||
from ..discover import engine
|
||||
|
||||
# 处理 3.4 移除了 YiriMirai 之后,插件的兼容性问题
|
||||
from . import types as mirai
|
||||
sys.modules['mirai'] = mirai
|
||||
@@ -27,7 +29,9 @@ sys.modules['mirai'] = mirai
|
||||
class PlatformManager:
|
||||
|
||||
# adapter: msadapter.MessageSourceAdapter = None
|
||||
adapters: list[msadapter.MessageSourceAdapter] = []
|
||||
adapters: list[msadapter.MessagePlatformAdapter] = []
|
||||
|
||||
message_platform_adapter_components: list[engine.Component] = []
|
||||
|
||||
# modern
|
||||
ap: app.Application = None
|
||||
@@ -39,9 +43,13 @@ class PlatformManager:
|
||||
|
||||
async def initialize(self):
|
||||
|
||||
from .sources import nakuru, aiocqhttp, qqbotpy, qqofficial, wecom, lark, discord, gewechat, officialaccount, telegram, dingtalk
|
||||
components = self.ap.discover.get_components_by_kind('MessagePlatformAdapter')
|
||||
|
||||
async def on_friend_message(event: platform_events.FriendMessage, adapter: msadapter.MessageSourceAdapter):
|
||||
self.message_platform_adapter_components = components
|
||||
|
||||
# from .sources import nakuru, aiocqhttp, qqbotpy, qqofficial, wecom, lark, discord, gewechat, officialaccount, telegram, dingtalk
|
||||
|
||||
async def on_friend_message(event: platform_events.FriendMessage, adapter: msadapter.MessagePlatformAdapter):
|
||||
|
||||
await self.ap.query_pool.add_query(
|
||||
launcher_type=core_entities.LauncherTypes.PERSON,
|
||||
@@ -52,7 +60,7 @@ class PlatformManager:
|
||||
adapter=adapter
|
||||
)
|
||||
|
||||
async def on_group_message(event: platform_events.GroupMessage, adapter: msadapter.MessageSourceAdapter):
|
||||
async def on_group_message(event: platform_events.GroupMessage, adapter: msadapter.MessagePlatformAdapter):
|
||||
|
||||
await self.ap.query_pool.add_query(
|
||||
launcher_type=core_entities.LauncherTypes.GROUP,
|
||||
@@ -76,10 +84,10 @@ class PlatformManager:
|
||||
|
||||
found = False
|
||||
|
||||
for adapter in msadapter.preregistered_adapters:
|
||||
if adapter.name == adapter_name:
|
||||
for adapter in self.message_platform_adapter_components:
|
||||
if adapter.metadata.name == adapter_name:
|
||||
found = True
|
||||
adapter_cls = adapter
|
||||
adapter_cls = adapter.get_python_component_class()
|
||||
|
||||
adapter_inst = adapter_cls(
|
||||
cfg_copy,
|
||||
@@ -102,7 +110,7 @@ class PlatformManager:
|
||||
if len(self.adapters) == 0:
|
||||
self.ap.logger.warning('未运行平台适配器,请根据文档配置并启用平台适配器。')
|
||||
|
||||
async def write_back_config(self, adapter_inst: msadapter.MessageSourceAdapter, config: dict):
|
||||
async def write_back_config(self, adapter_inst: msadapter.MessagePlatformAdapter, config: dict):
|
||||
index = -2
|
||||
|
||||
for i, adapter in enumerate(self.adapters):
|
||||
@@ -131,7 +139,7 @@ class PlatformManager:
|
||||
self.ap.platform_cfg.data['platform-adapters'][real_index] = new_cfg
|
||||
await self.ap.platform_cfg.dump_config()
|
||||
|
||||
async def send(self, event: platform_events.MessageEvent, msg: platform_message.MessageChain, adapter: msadapter.MessageSourceAdapter):
|
||||
async def send(self, event: platform_events.MessageEvent, msg: platform_message.MessageChain, adapter: msadapter.MessagePlatformAdapter):
|
||||
|
||||
if self.ap.platform_cfg.data['at-sender'] and isinstance(event, platform_events.GroupMessage):
|
||||
|
||||
@@ -152,7 +160,7 @@ class PlatformManager:
|
||||
try:
|
||||
tasks = []
|
||||
for adapter in self.adapters:
|
||||
async def exception_wrapper(adapter: msadapter.MessageSourceAdapter):
|
||||
async def exception_wrapper(adapter: msadapter.MessagePlatformAdapter):
|
||||
try:
|
||||
await adapter.run_async()
|
||||
except Exception as e:
|
||||
@@ -167,7 +175,7 @@ class PlatformManager:
|
||||
self.ap.task_mgr.create_task(
|
||||
task,
|
||||
kind="platform-adapter",
|
||||
name=f"platform-adapter-{adapter.name}",
|
||||
name=f"platform-adapter-{adapter.__class__.__name__}",
|
||||
scopes=[core_entities.LifecycleControlScope.APPLICATION, core_entities.LifecycleControlScope.PLATFORM],
|
||||
)
|
||||
|
||||
|
||||
@@ -210,8 +210,7 @@ class AiocqhttpEventConverter(adapter.EventConverter):
|
||||
)
|
||||
|
||||
|
||||
@adapter.adapter_class("aiocqhttp")
|
||||
class AiocqhttpAdapter(adapter.MessageSourceAdapter):
|
||||
class AiocqhttpAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
bot: aiocqhttp.CQHttp
|
||||
|
||||
@@ -273,7 +272,7 @@ class AiocqhttpAdapter(adapter.MessageSourceAdapter):
|
||||
def register_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
):
|
||||
async def on_message(event: aiocqhttp.Event):
|
||||
self.bot_account_id = event.self_id
|
||||
@@ -290,7 +289,7 @@ class AiocqhttpAdapter(adapter.MessageSourceAdapter):
|
||||
def unregister_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
):
|
||||
return super().unregister_listener(event_type, callback)
|
||||
|
||||
|
||||
37
pkg/platform/sources/aiocqhttp.yaml
Normal file
37
pkg/platform/sources/aiocqhttp.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: aiocqhttp
|
||||
label:
|
||||
en_US: OneBot v11 Adapter
|
||||
zh_CN: OneBot v11 适配器
|
||||
description:
|
||||
en_US: OneBot v11 Adapter
|
||||
zh_CN: OneBot v11 适配器
|
||||
spec:
|
||||
config:
|
||||
- name: host
|
||||
label:
|
||||
en_US: Host
|
||||
zh_CN: 主机
|
||||
type: string
|
||||
required: true
|
||||
default: 0.0.0.0
|
||||
- name: port
|
||||
label:
|
||||
en_US: Port
|
||||
zh_CN: 端口
|
||||
type: int
|
||||
required: true
|
||||
default: 2280
|
||||
- name: access-token
|
||||
label:
|
||||
en_US: Access Token
|
||||
zh_CN: 访问令牌
|
||||
type: string
|
||||
required: false
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./aiocqhttp.py
|
||||
attr: AiocqhttpAdapter
|
||||
@@ -3,7 +3,7 @@ import traceback
|
||||
import typing
|
||||
from libs.dingtalk_api.dingtalkevent import DingTalkEvent
|
||||
from pkg.platform.types import message as platform_message
|
||||
from pkg.platform.adapter import MessageSourceAdapter
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
from pkg.platform.types import events as platform_events, message as platform_message
|
||||
from pkg.core import app
|
||||
from .. import adapter
|
||||
@@ -96,8 +96,8 @@ class DingTalkEventConverter(adapter.EventConverter):
|
||||
source_platform_object=event
|
||||
)
|
||||
|
||||
@adapter.adapter_class("dingtalk")
|
||||
class DingTalkAdapter(adapter.MessageSourceAdapter):
|
||||
|
||||
class DingTalkAdapter(adapter.MessagePlatformAdapter):
|
||||
bot: DingTalkClient
|
||||
ap: app.Application
|
||||
bot_account_id: str
|
||||
@@ -149,7 +149,7 @@ class DingTalkAdapter(adapter.MessageSourceAdapter):
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[
|
||||
[platform_events.Event, adapter.MessageSourceAdapter], None
|
||||
[platform_events.Event, adapter.MessagePlatformAdapter], None
|
||||
],
|
||||
):
|
||||
async def on_message(event: DingTalkEvent):
|
||||
@@ -176,7 +176,7 @@ class DingTalkAdapter(adapter.MessageSourceAdapter):
|
||||
async def unregister_listener(
|
||||
self,
|
||||
event_type: type,
|
||||
callback: typing.Callable[[platform_events.Event, MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, MessagePlatformAdapter], None],
|
||||
):
|
||||
return super().unregister_listener(event_type, callback)
|
||||
|
||||
|
||||
44
pkg/platform/sources/dingtalk.yaml
Normal file
44
pkg/platform/sources/dingtalk.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: dingtalk
|
||||
label:
|
||||
en_US: DingTalk
|
||||
zh_CN: 钉钉
|
||||
description:
|
||||
en_US: DingTalk Adapter
|
||||
zh_CN: 钉钉适配器
|
||||
spec:
|
||||
config:
|
||||
- name: client_id
|
||||
label:
|
||||
en_US: Client ID
|
||||
zh_CN: 客户端ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: client_secret
|
||||
label:
|
||||
en_US: Client Secret
|
||||
zh_CN: 客户端密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: robot_code
|
||||
label:
|
||||
en_US: Robot Code
|
||||
zh_CN: 机器人代码
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: robot_name
|
||||
label:
|
||||
en_US: Robot Name
|
||||
zh_CN: 机器人名称
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./dingtalk.py
|
||||
attr: DingTalkAdapter
|
||||
@@ -168,8 +168,8 @@ class DiscordEventConverter(adapter.EventConverter):
|
||||
source_platform_object=event,
|
||||
)
|
||||
|
||||
@adapter.adapter_class("discord")
|
||||
class DiscordMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
|
||||
class DiscordAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
bot: discord.Client
|
||||
|
||||
@@ -184,7 +184,7 @@ class DiscordMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
|
||||
listeners: typing.Dict[
|
||||
typing.Type[platform_events.Event],
|
||||
typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
] = {}
|
||||
|
||||
def __init__(self, config: dict, ap: app.Application):
|
||||
@@ -249,14 +249,14 @@ class DiscordMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
def register_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
):
|
||||
self.listeners[event_type] = callback
|
||||
|
||||
def unregister_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
):
|
||||
self.listeners.pop(event_type)
|
||||
|
||||
|
||||
30
pkg/platform/sources/discord.yaml
Normal file
30
pkg/platform/sources/discord.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: discord
|
||||
label:
|
||||
en_US: Discord
|
||||
zh_CN: Discord
|
||||
description:
|
||||
en_US: Discord Adapter
|
||||
zh_CN: Discord 适配器
|
||||
spec:
|
||||
config:
|
||||
- name: client_id
|
||||
label:
|
||||
en_US: Client ID
|
||||
zh_CN: 客户端ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: token
|
||||
label:
|
||||
en_US: Token
|
||||
zh_CN: 令牌
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./discord.py
|
||||
attr: DiscordAdapter
|
||||
@@ -133,8 +133,7 @@ class GewechatEventConverter(adapter.EventConverter):
|
||||
)
|
||||
|
||||
|
||||
@adapter.adapter_class("gewechat")
|
||||
class GewechatMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
class GeWeChatAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
bot: gewechat_client.GewechatClient
|
||||
quart_app: quart.Quart
|
||||
@@ -150,7 +149,7 @@ class GewechatMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
|
||||
listeners: typing.Dict[
|
||||
typing.Type[platform_events.Event],
|
||||
typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
] = {}
|
||||
|
||||
def __init__(self, config: dict, ap: app.Application):
|
||||
@@ -222,14 +221,14 @@ class GewechatMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
def register_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None]
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None]
|
||||
):
|
||||
self.listeners[event_type] = callback
|
||||
|
||||
def unregister_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None]
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None]
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
51
pkg/platform/sources/gewechat.yaml
Normal file
51
pkg/platform/sources/gewechat.yaml
Normal file
@@ -0,0 +1,51 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: gewechat
|
||||
label:
|
||||
en_US: GeWeChat
|
||||
zh_CN: GeWeChat(个人微信)
|
||||
description:
|
||||
en_US: GeWeChat Adapter
|
||||
zh_CN: GeWeChat 适配器
|
||||
spec:
|
||||
config:
|
||||
- name: gewechat_url
|
||||
label:
|
||||
en_US: GeWeChat URL
|
||||
zh_CN: GeWeChat URL
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: port
|
||||
label:
|
||||
en_US: Port
|
||||
zh_CN: 端口
|
||||
type: int
|
||||
required: true
|
||||
default: 2286
|
||||
- name: callback_url
|
||||
label:
|
||||
en_US: Callback URL
|
||||
zh_CN: 回调URL
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: app_id
|
||||
label:
|
||||
en_US: App ID
|
||||
zh_CN: 应用ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: token
|
||||
label:
|
||||
en_US: Token
|
||||
zh_CN: 令牌
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./gewechat.py
|
||||
attr: GeWeChatAdapter
|
||||
@@ -298,8 +298,7 @@ class LarkEventConverter(adapter.EventConverter):
|
||||
)
|
||||
|
||||
|
||||
@adapter.adapter_class("lark")
|
||||
class LarkMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
class LarkAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
bot: lark_oapi.ws.Client
|
||||
api_client: lark_oapi.Client
|
||||
@@ -312,7 +311,7 @@ class LarkMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
|
||||
listeners: typing.Dict[
|
||||
typing.Type[platform_events.Event],
|
||||
typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
] = {}
|
||||
|
||||
config: dict
|
||||
@@ -450,7 +449,7 @@ class LarkMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[
|
||||
[platform_events.Event, adapter.MessageSourceAdapter], None
|
||||
[platform_events.Event, adapter.MessagePlatformAdapter], None
|
||||
],
|
||||
):
|
||||
self.listeners[event_type] = callback
|
||||
@@ -459,7 +458,7 @@ class LarkMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[
|
||||
[platform_events.Event, adapter.MessageSourceAdapter], None
|
||||
[platform_events.Event, adapter.MessagePlatformAdapter], None
|
||||
],
|
||||
):
|
||||
self.listeners.pop(event_type)
|
||||
|
||||
58
pkg/platform/sources/lark.yaml
Normal file
58
pkg/platform/sources/lark.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: lark
|
||||
label:
|
||||
en_US: Lark
|
||||
zh_CN: 飞书
|
||||
description:
|
||||
en_US: Lark Adapter
|
||||
zh_CN: 飞书适配器
|
||||
spec:
|
||||
config:
|
||||
- name: app_id
|
||||
label:
|
||||
en_US: App ID
|
||||
zh_CN: 应用ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: app_secret
|
||||
label:
|
||||
en_US: App Secret
|
||||
zh_CN: 应用密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: bot_name
|
||||
label:
|
||||
en_US: Bot Name
|
||||
zh_CN: 机器人名称
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: enable-webhook
|
||||
label:
|
||||
en_US: Enable Webhook Mode
|
||||
zh_CN: 启用Webhook模式
|
||||
type: boolean
|
||||
required: true
|
||||
default: false
|
||||
- name: port
|
||||
label:
|
||||
en_US: Webhook Port
|
||||
zh_CN: Webhook端口
|
||||
type: int
|
||||
required: true
|
||||
default: 2285
|
||||
- name: encrypt-key
|
||||
label:
|
||||
en_US: Encrypt Key
|
||||
zh_CN: 加密密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./lark.py
|
||||
attr: LarkAdapter
|
||||
@@ -158,8 +158,7 @@ class NakuruProjectEventConverter(adapter_model.EventConverter):
|
||||
raise Exception("未支持转换的事件类型: " + str(event))
|
||||
|
||||
|
||||
@adapter_model.adapter_class("nakuru")
|
||||
class NakuruProjectAdapter(adapter_model.MessageSourceAdapter):
|
||||
class NakuruAdapter(adapter_model.MessagePlatformAdapter):
|
||||
"""nakuru-project适配器"""
|
||||
bot: nakuru.CQHTTP
|
||||
bot_account_id: int
|
||||
@@ -256,7 +255,7 @@ class NakuruProjectAdapter(adapter_model.MessageSourceAdapter):
|
||||
def register_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter_model.MessageSourceAdapter], None]
|
||||
callback: typing.Callable[[platform_events.Event, adapter_model.MessagePlatformAdapter], None]
|
||||
):
|
||||
try:
|
||||
|
||||
@@ -284,7 +283,7 @@ class NakuruProjectAdapter(adapter_model.MessageSourceAdapter):
|
||||
def unregister_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter_model.MessageSourceAdapter], None]
|
||||
callback: typing.Callable[[platform_events.Event, adapter_model.MessagePlatformAdapter], None]
|
||||
):
|
||||
nakuru_event_name = self.event_converter.yiri2target(event_type).__name__
|
||||
|
||||
|
||||
44
pkg/platform/sources/nakuru.yaml
Normal file
44
pkg/platform/sources/nakuru.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: nakuru
|
||||
label:
|
||||
en_US: Nakuru
|
||||
zh_CN: Nakuru
|
||||
description:
|
||||
en_US: Nakuru Adapter
|
||||
zh_CN: Nakuru 适配器(go-cqhttp)
|
||||
spec:
|
||||
config:
|
||||
- name: host
|
||||
label:
|
||||
en_US: Host
|
||||
zh_CN: 主机
|
||||
type: string
|
||||
required: true
|
||||
default: "127.0.0.1"
|
||||
- name: http_port
|
||||
label:
|
||||
en_US: HTTP Port
|
||||
zh_CN: HTTP端口
|
||||
type: int
|
||||
required: true
|
||||
default: 5700
|
||||
- name: ws_port
|
||||
label:
|
||||
en_US: WebSocket Port
|
||||
zh_CN: WebSocket端口
|
||||
type: int
|
||||
required: true
|
||||
default: 8080
|
||||
- name: token
|
||||
label:
|
||||
en_US: Token
|
||||
zh_CN: 令牌
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./nakuru.py
|
||||
attr: NakuruAdapter
|
||||
@@ -5,13 +5,13 @@ import traceback
|
||||
import time
|
||||
import datetime
|
||||
from pkg.core import app
|
||||
from pkg.platform.adapter import MessageSourceAdapter
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
from pkg.platform.types import events as platform_events, message as platform_message
|
||||
|
||||
import aiocqhttp
|
||||
import aiohttp
|
||||
from libs.official_account_api.oaevent import OAEvent
|
||||
from pkg.platform.adapter import MessageSourceAdapter
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
from pkg.platform.types import events as platform_events, message as platform_message
|
||||
from libs.official_account_api.api import OAClient
|
||||
from pkg.core import app
|
||||
@@ -68,8 +68,7 @@ class OAEventConverter(adapter.EventConverter):
|
||||
else:
|
||||
return None
|
||||
|
||||
@adapter.adapter_class("officialaccount")
|
||||
class OfficialAccountAdapter(adapter.MessageSourceAdapter):
|
||||
class OfficialAccountAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
bot : OAClient
|
||||
ap : app.Application
|
||||
@@ -116,7 +115,7 @@ class OfficialAccountAdapter(adapter.MessageSourceAdapter):
|
||||
pass
|
||||
|
||||
|
||||
def register_listener(self, event_type: type, callback: typing.Callable[[platform_events.Event, MessageSourceAdapter], None]):
|
||||
def register_listener(self, event_type: type, callback: typing.Callable[[platform_events.Event, MessagePlatformAdapter], None]):
|
||||
async def on_message(event: OAEvent):
|
||||
self.bot_account_id = event.receiver_id
|
||||
try:
|
||||
@@ -148,7 +147,7 @@ class OfficialAccountAdapter(adapter.MessageSourceAdapter):
|
||||
async def unregister_listener(
|
||||
self,
|
||||
event_type: type,
|
||||
callback: typing.Callable[[platform_events.Event, MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, MessagePlatformAdapter], None],
|
||||
):
|
||||
return super().unregister_listener(event_type, callback)
|
||||
|
||||
|
||||
58
pkg/platform/sources/officialaccount.yaml
Normal file
58
pkg/platform/sources/officialaccount.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: officialaccount
|
||||
label:
|
||||
en_US: Official Account
|
||||
zh_CN: 微信公众号
|
||||
description:
|
||||
en_US: Official Account Adapter
|
||||
zh_CN: 微信公众号适配器
|
||||
spec:
|
||||
config:
|
||||
- name: token
|
||||
label:
|
||||
en_US: Token
|
||||
zh_CN: 令牌
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: EncodingAESKey
|
||||
label:
|
||||
en_US: EncodingAESKey
|
||||
zh_CN: 消息加解密密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: AppID
|
||||
label:
|
||||
en_US: App ID
|
||||
zh_CN: 应用ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: AppSecret
|
||||
label:
|
||||
en_US: App Secret
|
||||
zh_CN: 应用密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: host
|
||||
label:
|
||||
en_US: Host
|
||||
zh_CN: 监听主机
|
||||
type: string
|
||||
required: true
|
||||
default: 0.0.0.0
|
||||
- name: port
|
||||
label:
|
||||
en_US: Port
|
||||
zh_CN: 监听端口
|
||||
type: int
|
||||
required: true
|
||||
default: 2287
|
||||
execution:
|
||||
python:
|
||||
path: ./officialaccount.py
|
||||
attr: OfficialAccountAdapter
|
||||
@@ -360,8 +360,7 @@ class OfficialEventConverter(adapter_model.EventConverter):
|
||||
)
|
||||
|
||||
|
||||
@adapter_model.adapter_class("qq-botpy")
|
||||
class OfficialAdapter(adapter_model.MessageSourceAdapter):
|
||||
class OfficialAdapter(adapter_model.MessagePlatformAdapter):
|
||||
"""QQ 官方消息适配器"""
|
||||
|
||||
bot: botpy.Client = None
|
||||
@@ -535,7 +534,7 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[
|
||||
[platform_events.Event, adapter_model.MessageSourceAdapter], None
|
||||
[platform_events.Event, adapter_model.MessagePlatformAdapter], None
|
||||
],
|
||||
):
|
||||
|
||||
@@ -561,7 +560,7 @@ class OfficialAdapter(adapter_model.MessageSourceAdapter):
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[
|
||||
[platform_events.Event, adapter_model.MessageSourceAdapter], None
|
||||
[platform_events.Event, adapter_model.MessagePlatformAdapter], None
|
||||
],
|
||||
):
|
||||
delattr(self.bot, event_handler_mapping[event_type])
|
||||
|
||||
37
pkg/platform/sources/qqbotpy.yaml
Normal file
37
pkg/platform/sources/qqbotpy.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: qq-botpy
|
||||
label:
|
||||
en_US: QQBotPy
|
||||
zh_CN: QQBotPy
|
||||
description:
|
||||
en_US: QQ Official API (WebSocket)
|
||||
zh_CN: QQ 官方 API (WebSocket)
|
||||
spec:
|
||||
config:
|
||||
- name: appid
|
||||
label:
|
||||
en_US: App ID
|
||||
zh_CN: 应用ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: secret
|
||||
label:
|
||||
en_US: Secret
|
||||
zh_CN: 密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: intents
|
||||
label:
|
||||
en_US: Intents
|
||||
zh_CN: 权限
|
||||
type: array[string]
|
||||
required: true
|
||||
default: []
|
||||
execution:
|
||||
python:
|
||||
path: ./qqbotpy.py
|
||||
attr: OfficialAdapter
|
||||
@@ -6,7 +6,7 @@ import time
|
||||
import datetime
|
||||
import aiocqhttp
|
||||
import aiohttp
|
||||
from pkg.platform.adapter import MessageSourceAdapter
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
from pkg.platform.types import events as platform_events, message as platform_message
|
||||
from pkg.core import app
|
||||
from .. import adapter
|
||||
@@ -137,10 +137,7 @@ class QQOfficialEventConverter(adapter.EventConverter):
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
@adapter.adapter_class("qqofficial")
|
||||
class QQOfficialAdapter(adapter.MessageSourceAdapter):
|
||||
class QQOfficialAdapter(adapter.MessagePlatformAdapter):
|
||||
bot:QQOfficialClient
|
||||
ap:app.Application
|
||||
config:dict
|
||||
@@ -213,7 +210,7 @@ class QQOfficialAdapter(adapter.MessageSourceAdapter):
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[
|
||||
[platform_events.Event, adapter.MessageSourceAdapter], None
|
||||
[platform_events.Event, adapter.MessagePlatformAdapter], None
|
||||
],
|
||||
):
|
||||
async def on_message(event:QQOfficialEvent):
|
||||
@@ -250,7 +247,7 @@ class QQOfficialAdapter(adapter.MessageSourceAdapter):
|
||||
def unregister_listener(
|
||||
self,
|
||||
event_type: type,
|
||||
callback: typing.Callable[[platform_events.Event, MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, MessagePlatformAdapter], None],
|
||||
):
|
||||
return super().unregister_listener(event_type, callback)
|
||||
|
||||
|
||||
44
pkg/platform/sources/qqofficial.yaml
Normal file
44
pkg/platform/sources/qqofficial.yaml
Normal file
@@ -0,0 +1,44 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: qqofficial
|
||||
label:
|
||||
en_US: QQ Official API
|
||||
zh_CN: QQ 官方 API
|
||||
description:
|
||||
en_US: QQ Official API (Webhook)
|
||||
zh_CN: QQ 官方 API (Webhook)
|
||||
spec:
|
||||
config:
|
||||
- name: appid
|
||||
label:
|
||||
en_US: App ID
|
||||
zh_CN: 应用ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: secret
|
||||
label:
|
||||
en_US: Secret
|
||||
zh_CN: 密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: port
|
||||
label:
|
||||
en_US: Port
|
||||
zh_CN: 监听端口
|
||||
type: int
|
||||
required: true
|
||||
default: 2284
|
||||
- name: token
|
||||
label:
|
||||
en_US: Token
|
||||
zh_CN: 令牌
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./qqofficial.py
|
||||
attr: QQOfficialAdapter
|
||||
@@ -147,8 +147,8 @@ class TelegramEventConverter(adapter.EventConverter):
|
||||
source_platform_object=event
|
||||
)
|
||||
|
||||
@adapter.adapter_class("telegram")
|
||||
class TelegramMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
|
||||
class TelegramAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
bot: telegram.Bot
|
||||
application: telegram.ext.Application
|
||||
@@ -163,7 +163,7 @@ class TelegramMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
|
||||
listeners: typing.Dict[
|
||||
typing.Type[platform_events.Event],
|
||||
typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
] = {}
|
||||
|
||||
def __init__(self, config: dict, ap: app.Application):
|
||||
@@ -218,14 +218,14 @@ class TelegramMessageSourceAdapter(adapter.MessageSourceAdapter):
|
||||
def register_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
):
|
||||
self.listeners[event_type] = callback
|
||||
|
||||
def unregister_listener(
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, adapter.MessagePlatformAdapter], None],
|
||||
):
|
||||
self.listeners.pop(event_type)
|
||||
|
||||
|
||||
23
pkg/platform/sources/telegram.yaml
Normal file
23
pkg/platform/sources/telegram.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: telegram
|
||||
label:
|
||||
en_US: Telegram
|
||||
zh_CN: 电报
|
||||
description:
|
||||
en_US: Telegram Adapter
|
||||
zh_CN: 电报适配器
|
||||
spec:
|
||||
config:
|
||||
- name: token
|
||||
label:
|
||||
en_US: Token
|
||||
zh_CN: 令牌
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./telegram.py
|
||||
attr: TelegramAdapter
|
||||
@@ -8,7 +8,7 @@ import datetime
|
||||
import aiocqhttp
|
||||
import aiohttp
|
||||
from libs.wecom_api.api import WecomClient
|
||||
from pkg.platform.adapter import MessageSourceAdapter
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
from pkg.platform.types import events as platform_events, message as platform_message
|
||||
from libs.wecom_api.wecomevent import WecomEvent
|
||||
from pkg.core import app
|
||||
@@ -144,8 +144,7 @@ class WecomEventConverter:
|
||||
)
|
||||
|
||||
|
||||
@adapter.adapter_class("wecom")
|
||||
class WecomeAdapter(adapter.MessageSourceAdapter):
|
||||
class WecomAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
bot: WecomClient
|
||||
ap: app.Application
|
||||
@@ -207,7 +206,7 @@ class WecomeAdapter(adapter.MessageSourceAdapter):
|
||||
self,
|
||||
event_type: typing.Type[platform_events.Event],
|
||||
callback: typing.Callable[
|
||||
[platform_events.Event, adapter.MessageSourceAdapter], None
|
||||
[platform_events.Event, adapter.MessagePlatformAdapter], None
|
||||
],
|
||||
):
|
||||
async def on_message(event: WecomEvent):
|
||||
@@ -242,6 +241,6 @@ class WecomeAdapter(adapter.MessageSourceAdapter):
|
||||
async def unregister_listener(
|
||||
self,
|
||||
event_type: type,
|
||||
callback: typing.Callable[[platform_events.Event, MessageSourceAdapter], None],
|
||||
callback: typing.Callable[[platform_events.Event, MessagePlatformAdapter], None],
|
||||
):
|
||||
return super().unregister_listener(event_type, callback)
|
||||
|
||||
65
pkg/platform/sources/wecom.yaml
Normal file
65
pkg/platform/sources/wecom.yaml
Normal file
@@ -0,0 +1,65 @@
|
||||
apiVersion: v1
|
||||
kind: MessagePlatformAdapter
|
||||
metadata:
|
||||
name: wecom
|
||||
label:
|
||||
en_US: WeCom
|
||||
zh_CN: 企业微信
|
||||
description:
|
||||
en_US: WeCom Adapter
|
||||
zh_CN: 企业微信适配器
|
||||
spec:
|
||||
config:
|
||||
- name: host
|
||||
label:
|
||||
en_US: Host
|
||||
zh_CN: 监听主机
|
||||
type: string
|
||||
required: true
|
||||
default: "0.0.0.0"
|
||||
- name: port
|
||||
label:
|
||||
en_US: Port
|
||||
zh_CN: 监听端口
|
||||
type: int
|
||||
required: true
|
||||
default: 2290
|
||||
- name: corpid
|
||||
label:
|
||||
en_US: Corpid
|
||||
zh_CN: 企业ID
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: secret
|
||||
label:
|
||||
en_US: Secret
|
||||
zh_CN: 密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: token
|
||||
label:
|
||||
en_US: Token
|
||||
zh_CN: 令牌
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: EncodingAESKey
|
||||
label:
|
||||
en_US: EncodingAESKey
|
||||
zh_CN: 消息加解密密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
- name: contacts_secret
|
||||
label:
|
||||
en_US: Contacts Secret
|
||||
zh_CN: 通讯录密钥
|
||||
type: string
|
||||
required: true
|
||||
default: ""
|
||||
execution:
|
||||
python:
|
||||
path: ./wecom.py
|
||||
attr: WecomAdapter
|
||||
@@ -116,7 +116,7 @@ class APIHost:
|
||||
|
||||
# ========== 插件可调用的 API(主程序API) ==========
|
||||
|
||||
def get_platform_adapters(self) -> list[platform_adapter.MessageSourceAdapter]:
|
||||
def get_platform_adapters(self) -> list[platform_adapter.MessagePlatformAdapter]:
|
||||
"""获取已启用的消息平台适配器列表
|
||||
|
||||
Returns:
|
||||
@@ -126,7 +126,7 @@ class APIHost:
|
||||
|
||||
async def send_active_message(
|
||||
self,
|
||||
adapter: platform_adapter.MessageSourceAdapter,
|
||||
adapter: platform_adapter.MessagePlatformAdapter,
|
||||
target_type: str,
|
||||
target_id: str,
|
||||
message: platform_message.MessageChain,
|
||||
|
||||
@@ -6,7 +6,7 @@ from . import entities, requester
|
||||
from ...core import app
|
||||
|
||||
from . import token
|
||||
from .requesters import bailianchatcmpl, chatcmpl, anthropicmsgs, moonshotchatcmpl, deepseekchatcmpl, ollamachat, giteeaichatcmpl, xaichatcmpl, zhipuaichatcmpl, lmstudiochatcmpl, siliconflowchatcmpl
|
||||
from .requesters import bailianchatcmpl, chatcmpl, anthropicmsgs, moonshotchatcmpl, deepseekchatcmpl, ollamachat, giteeaichatcmpl, volcarkchatcmpl, xaichatcmpl, zhipuaichatcmpl, lmstudiochatcmpl, siliconflowchatcmpl, volcarkchatcmpl
|
||||
|
||||
FETCH_MODEL_LIST_URL = "https://api.qchatgpt.rockchin.top/api/v2/fetch/model_list"
|
||||
|
||||
|
||||
14
pkg/provider/modelmgr/requester.yaml
Normal file
14
pkg/provider/modelmgr/requester.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: ComponentTemplate
|
||||
metadata:
|
||||
name: LLMAPIRequester
|
||||
label:
|
||||
en_US: LLM API Requester
|
||||
zh_CN: LLM API 请求器
|
||||
spec:
|
||||
type:
|
||||
- python
|
||||
execution:
|
||||
python:
|
||||
path: ./requester.py
|
||||
attr: LLMAPIRequester
|
||||
21
pkg/provider/modelmgr/requesters/volcarkchatcmpl.py
Normal file
21
pkg/provider/modelmgr/requesters/volcarkchatcmpl.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import openai
|
||||
|
||||
from . import chatcmpl
|
||||
from .. import requester
|
||||
from ....core import app
|
||||
|
||||
|
||||
@requester.requester_class("volcark-chat-completions")
|
||||
class VolcArkChatCompletions(chatcmpl.OpenAIChatCompletions):
|
||||
"""火山方舟大模型平台 ChatCompletion API 请求器"""
|
||||
|
||||
client: openai.AsyncClient
|
||||
|
||||
requester_cfg: dict
|
||||
|
||||
def __init__(self, ap: app.Application):
|
||||
self.ap = ap
|
||||
|
||||
self.requester_cfg = self.ap.provider_cfg.data['requester']['volcark-chat-completions']
|
||||
@@ -1,4 +1,4 @@
|
||||
semantic_version = "v3.4.8"
|
||||
semantic_version = "v3.4.9"
|
||||
|
||||
debug_mode = False
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
],
|
||||
"bailian": [
|
||||
"sk-xxxxxxx"
|
||||
],
|
||||
"volcark": [
|
||||
"xxxxxxxx"
|
||||
]
|
||||
},
|
||||
"requester": {
|
||||
@@ -87,6 +90,11 @@
|
||||
"args": {},
|
||||
"base-url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
||||
"timeout": 120
|
||||
},
|
||||
"volcark-chat-completions": {
|
||||
"args": {},
|
||||
"base-url": "https://ark.cn-beijing.volces.com/api/v3",
|
||||
"timeout": 120
|
||||
}
|
||||
},
|
||||
"model": "gpt-4o",
|
||||
|
||||
@@ -90,6 +90,14 @@
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"volcark": {
|
||||
"type": "array",
|
||||
"title": "火山引擎大模型平台 API 密钥",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -316,6 +324,26 @@
|
||||
"default": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
"volcark-chat-completions": {
|
||||
"type": "object",
|
||||
"title": "火山方舟大模型平台 API 请求配置",
|
||||
"description": "仅可编辑 URL 和 超时时间,额外请求参数不支持可视化编辑,请到编辑器编辑",
|
||||
"properties": {
|
||||
"base-url": {
|
||||
"type": "string",
|
||||
"title": "API URL"
|
||||
},
|
||||
"args": {
|
||||
"type": "object",
|
||||
"default": {}
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number",
|
||||
"title": "API 请求超时时间",
|
||||
"default": 120
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user