feat: discover engine & manifests for platform adapters

This commit is contained in:
Junyan Qin
2025-02-22 14:49:05 +08:00
parent 71ecfc2566
commit d92ee23764
34 changed files with 802 additions and 75 deletions
+3 -4
View File
@@ -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
View 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
+5 -5
View File
@@ -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
View 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
+5 -5
View File
@@ -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
View 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
+4 -5
View File
@@ -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
View 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
+4 -5
View File
@@ -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
View 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
+3 -4
View File
@@ -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
View 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 -6
View File
@@ -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
View 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
+3 -4
View File
@@ -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
View 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
+4 -7
View File
@@ -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
View 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
+5 -5
View File
@@ -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
View 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
+4 -5
View File
@@ -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
View 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