feat(dingtalk): support group subscriptions and card interaction events

This commit is contained in:
Hyu
2026-10-10 21:29:49 +08:00
parent 387cc9c30c
commit d2ec53ec91
15 changed files with 197 additions and 21 deletions
@@ -7,6 +7,8 @@ import typing
import dingtalk_stream
import pydantic
from .subscriptions import DingTalkSubscriptionHandler
from langbot.libs.dingtalk_api.api import DingTalkClient, is_stream_card_configured
from langbot.libs.dingtalk_api.dingtalkevent import DingTalkEvent
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
@@ -40,6 +42,7 @@ class DingTalkCardCallbackHandler(dingtalk_stream.CallbackHandler):
{
'conversation_type': 'CardCallback',
'Type': 'card_callback',
'timestamp': float(message.headers.time or 0) / 1000,
'CardCallback': {
'extension': callback.extension,
'corp_id': callback.corp_id,
@@ -107,6 +110,10 @@ class DingTalkAdapter(DingTalkAPIMixin, abstract_platform_adapter.AbstractPlatfo
return [
'message.received',
'feedback.received',
'group.member_joined',
'group.member_left',
'group.info_updated',
'dingtalk.card_action',
'platform.specific',
]
@@ -280,6 +287,7 @@ class DingTalkAdapter(DingTalkAPIMixin, abstract_platform_adapter.AbstractPlatfo
async def on_message(event: DingTalkEvent):
await self._handle_native_event(event)
self.bot.client.register_all_event_handler(DingTalkSubscriptionHandler(self))
self.bot.on_message('FriendMessage')(on_message)
self.bot.on_message('GroupMessage')(on_message)
self.bot.client.register_callback_handler(
@@ -298,7 +306,9 @@ class DingTalkAdapter(DingTalkAPIMixin, abstract_platform_adapter.AbstractPlatfo
'DingTalk event received: '
f'conversation={event.conversation}, message_id={getattr(event.incoming_message, "message_id", None)}'
)
if platform_events.FriendMessage in self.listeners or platform_events.GroupMessage in self.listeners:
if event.conversation in {'FriendMessage', 'GroupMessage'} and (
platform_events.FriendMessage in self.listeners or platform_events.GroupMessage in self.listeners
):
legacy_event = await self.event_converter.target2legacy(event, self.bot_account_id)
if legacy_event:
callback = self.listeners.get(type(legacy_event))
@@ -23,7 +23,18 @@ class DingTalkEventConverter(abstract_platform_adapter.AbstractEventConverter):
feedback = DingTalkEventConverter.card_callback_to_feedback(event)
if feedback is not None:
return feedback
return DingTalkEventConverter.platform_specific(event, 'card.callback')
callback = event.get('CardCallback') or {}
content = callback.get('content') or {}
return platform_events.DingTalkCardActionEvent(
adapter_name=ADAPTER_NAME,
user=platform_entities.User(id=str(callback.get('user_id') or '')),
space_id=str(callback.get('space_id') or ''),
card_instance_id=str(callback.get('card_instance_id') or ''),
action=str(content.get('action') or content.get('actionKey') or ''),
data=callback,
timestamp=float(event.get('timestamp') or 0),
source_platform_object=event,
)
return DingTalkEventConverter.platform_specific(event, f'message.{event.conversation or "unknown"}')
@staticmethod
@@ -151,6 +151,10 @@ spec:
supported_events:
- message.received
- feedback.received
- group.member_joined
- group.member_left
- group.info_updated
- dingtalk.card_action
- platform.specific
supported_apis:
@@ -0,0 +1,77 @@
"""Translate DingTalk Stream EVENT envelopes, keeping unknown payloads intact."""
import asyncio
from collections import OrderedDict
import dingtalk_stream
from langbot_plugin.api.entities.builtin.platform import entities, events
from .types import ADAPTER_NAME
def subscription_events(message):
data = message.data
headers = message.headers
action = headers.event_type or data.get('EventType') or data.get('eventType') or ''
raw = {'headers': vars(headers).copy(), 'data': data}
stamp = headers.event_born_time or data.get('TimeStamp') or data.get('EventTime') or data.get('timestamp') or headers.time or 0
stamp = float(stamp)
common = dict(
adapter_name=ADAPTER_NAME,
timestamp=stamp / 1000 if stamp > 10_000_000_000 else stamp,
source_platform_object=raw,
)
group_id = data.get('OpenConversationId') or data.get('openConversationId') or data.get('chatId') or data.get('ChatId')
group = entities.UserGroup(id=str(group_id or ''), name=str(data.get('Title') or data.get('title') or ''))
owner_id = data.get('Owner') or data.get('owner')
if owner_id:
group.owner_id = str(owner_id)
operator_id = data.get('Operator') or data.get('operator')
operator = entities.User(id=str(operator_id)) if operator_id else None
members = data.get('UserId') or data.get('userId') or []
if isinstance(members, str):
members = [members]
if group_id and members and action in {'chat_add_member', 'chat_remove_member', 'chat_quit'}:
for member_id in dict.fromkeys(members):
member = entities.User(id=str(member_id))
if action == 'chat_add_member':
yield events.MemberJoinedEvent(group=group, member=member, inviter=operator, **common)
else:
yield events.MemberLeftEvent(
group=group, member=member, operator=operator,
is_kicked=action == 'chat_remove_member', **common,
)
elif group_id and action in {'chat_update_title', 'chat_update_owner'}:
yield events.GroupInfoUpdatedEvent(
group=group, operator=operator,
changed_fields=['name' if action == 'chat_update_title' else 'owner_id'], **common,
)
else:
yield events.PlatformSpecificEvent(action=action or 'unknown', data=raw, **common)
class DingTalkSubscriptionHandler(dingtalk_stream.EventHandler):
def __init__(self, adapter):
super().__init__()
self.adapter = adapter
self.completed = OrderedDict()
self.lock = asyncio.Lock()
async def process(self, message):
# Serialize retries; mark each fan-out event only after successful dispatch.
async with self.lock:
try:
identity = message.headers.event_id or message.headers.message_id
for index, event in enumerate(subscription_events(message)):
key = (message.headers.event_corp_id, identity, index)
if identity and key in self.completed:
continue
await self.adapter._dispatch_eba_event(event)
if identity:
self.completed[key] = None
while len(self.completed) > 4096:
self.completed.popitem(last=False)
return dingtalk_stream.AckMessage.STATUS_OK, 'OK'
except Exception:
self.logger.exception('DingTalk subscription dispatch failed')
return 500, 'Event dispatch failed'
@@ -3,7 +3,7 @@ from __future__ import annotations
import json
import pathlib
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock, Mock, patch
import pytest
import yaml
@@ -38,7 +38,7 @@ class DummyLogger(AbstractEventLogger):
class DummyDingTalkClient(DingTalkClient):
def __init__(self, *args, **kwargs):
self._message_handlers = {}
self.client = SimpleNamespace(register_callback_handler=AsyncMock())
self.client = SimpleNamespace(register_callback_handler=Mock(), register_all_event_handler=Mock())
self.markdown_card = kwargs.get('markdown_card', True)
self.access_token = ''
self.send_message = AsyncMock()
@@ -287,8 +287,8 @@ async def test_dingtalk_event_converter_maps_card_feedback():
assert feedback.session_id == 'space-1'
assert feedback.message_id == 'bot-msg-1'
assert isinstance(callback, platform_events.PlatformSpecificEvent)
assert callback.action == 'card.callback'
assert isinstance(callback, platform_events.DingTalkCardActionEvent)
assert callback.action == 'open_details'
@pytest.mark.asyncio
@@ -522,3 +522,40 @@ async def test_dingtalk_adapter_dispatches_native_input_submission():
assert calls[0].data['callback_token'] == 'callback-token'
assert calls[0].data['values'] == {'score': 42.5}
assert result['message_id'] not in adapter.interaction_callback_contexts
def test_dingtalk_subscription_member_and_group_mapping():
from dingtalk_stream.frames import EventMessage
from langbot.pkg.platform.adapters.dingtalk.subscriptions import subscription_events
message = EventMessage()
message.headers.event_type = 'chat_add_member'
message.data = {'OpenConversationId': 'group', 'UserId': ['a', 'b']}
converted = list(subscription_events(message))
assert [event.member.id for event in converted] == ['a', 'b']
assert all(event.type == 'group.member_joined' for event in converted)
message.headers.event_type = 'chat_update_owner'
message.data = {'OpenConversationId': 'group', 'Owner': 'new-owner'}
event = list(subscription_events(message))[0]
assert event.changed_fields == ['owner_id']
assert event.group.owner_id == 'new-owner'
message.headers.event_type = 'chat_add_member'
message.data = {'chatId': 'group', 'unionId': ['union-id']}
assert list(subscription_events(message))[0].type == 'platform.specific'
@pytest.mark.asyncio
async def test_dingtalk_subscription_retry_does_not_repeat_successful_members():
from dingtalk_stream.frames import EventMessage
from langbot.pkg.platform.adapters.dingtalk.subscriptions import DingTalkSubscriptionHandler
message = EventMessage()
message.headers.event_id = 'event-1'
message.headers.event_type = 'chat_add_member'
message.data = {'OpenConversationId': 'group', 'UserId': ['a', 'b']}
adapter = SimpleNamespace(_dispatch_eba_event=AsyncMock(side_effect=[None, RuntimeError('retry'), None]))
handler = DingTalkSubscriptionHandler(adapter)
assert (await handler.process(message))[0] == 500
assert (await handler.process(message))[0] == 200
assert (await handler.process(message))[0] == 200
assert [call.args[0].member.id for call in adapter._dispatch_eba_event.await_args_list] == ['a', 'b', 'b']
@@ -90,6 +90,10 @@ const DEBUG_EVENT_DEFINITIONS: Record<string, DebugEventDefinition> = {
messageField: 'text',
},
'message.deleted': deletion,
'dingtalk.card_action': {
fields: [user],
defaults: { space_id: 'dtv1.card//IM_GROUP.debug-group', card_instance_id: 'debug-card', action: 'confirm', data: {} },
},
'wecombot.enter_chat': {
fields: [user],
defaults: { chat_id: 'debug-user', chat_type: 'private', message_id: 'debug-entry', data: {} },
@@ -331,7 +331,14 @@ function eventLabel(event: string, t: TFunction) {
function eventDescription(event: string, t: TFunction) {
if (event === '*') return t('bots.eventDescriptions.all');
if (event.endsWith('.*')) return t('bots.eventDescriptions.namespace');
if (event.endsWith('.*')) {
const group = event.slice(0, -2);
const key = `bots.eventDescriptions.namespace_${group}`;
const description = t(key);
return description === key
? t('bots.eventDescriptions.namespace', { group })
: description;
}
const key = `bots.eventDescriptions.${event.replace(/\./g, '_')}`;
const description = t(key);
return description === key ? t('bots.eventDescriptions.custom') : description;
+5 -2
View File
@@ -1005,6 +1005,7 @@ const enUS = {
eventNames: {
wecombot_enter_chat: "User entered bot chat",
wecombot_template_card_event: "Template card interaction",
dingtalk_card_action: "DingTalk card interaction",
wecomcs_enter_session: "Customer entered conversation",
wecomcs_msg_send_fail: "Customer service message failed",
wecomcs_servicer_status_change: "Representative availability changed",
@@ -1019,6 +1020,7 @@ const enUS = {
friend_added: 'Friend added',
group_member_joined: 'Member joined group',
group_member_left: 'Member left group',
group_info_updated: "Group information updated",
group_member_banned: 'Member banned',
bot_invited_to_group: 'Bot invited to group',
bot_removed_from_group: 'Bot removed from group',
@@ -1029,6 +1031,7 @@ const enUS = {
eventDescriptions: {
wecombot_enter_chat: "A user opened the bot chat. Use this event for welcome workflows.",
wecombot_template_card_event: "A user clicked or submitted a template card. Managed form callbacks are handled separately.",
dingtalk_card_action: "Card clicks and submissions; feedback and managed forms are handled separately.",
wecomcs_enter_session: "A customer opened a service link; includes entry source and welcome code.",
wecomcs_msg_send_fail: "WeChat reported an outgoing message failure, with message ID and failure type.",
wecomcs_servicer_status_change: "A representative started, stopped or suspended service.",
@@ -1040,8 +1043,7 @@ const enUS = {
'Matches bot invitations, removals, mutes, and other status events.',
namespace_feedback: 'Matches feedback events from a platform or user.',
namespace_friend: 'Matches friend requests and friendship changes.',
namespace_group:
'Matches member joins, leaves, removals, and other group events.',
namespace_group: "Matches group events such as membership changes and group information updates.",
namespace_message:
'Matches received, edited, deleted, and reaction message events.',
namespace_platform:
@@ -1057,6 +1059,7 @@ const enUS = {
friend_added: 'A friend relationship was created.',
group_member_joined: 'A member joins a group where the bot is present.',
group_member_left: 'A member leaves a group where the bot is present.',
group_info_updated: "Group details such as its name or owner have changed.",
group_member_banned: 'A group member is banned or removed.',
bot_invited_to_group: 'The bot is invited to a group.',
bot_removed_from_group: 'The bot is removed from a group.',
+5 -2
View File
@@ -1024,6 +1024,7 @@ const esES = {
eventNames: {
wecombot_enter_chat: "Usuario entró al chat del bot",
wecombot_template_card_event: "Interacción con tarjeta",
dingtalk_card_action: "Interacción con tarjeta de DingTalk",
wecomcs_enter_session: "Cliente entró en la conversación",
wecomcs_msg_send_fail: "Fallo de envío de atención al cliente",
wecomcs_servicer_status_change: "Disponibilidad del agente modificada",
@@ -1038,6 +1039,7 @@ const esES = {
friend_added: 'Amigo añadido',
group_member_joined: 'Miembro unido al grupo',
group_member_left: 'Miembro salió del grupo',
group_info_updated: "Información del grupo actualizada",
group_member_banned: 'Miembro bloqueado',
bot_invited_to_group: 'Bot invitado al grupo',
bot_removed_from_group: 'Bot eliminado del grupo',
@@ -1048,6 +1050,7 @@ const esES = {
eventDescriptions: {
wecombot_enter_chat: "Un usuario abrió el chat del bot. Úsalo para flujos de bienvenida.",
wecombot_template_card_event: "Un usuario pulsó o envió una tarjeta. Las respuestas de formularios gestionados se procesan por separado.",
dingtalk_card_action: "Clics y envíos de tarjetas; las valoraciones y los formularios gestionados se procesan por separado.",
wecomcs_enter_session: "Un cliente abrió el enlace de atención; incluye origen y código de bienvenida.",
wecomcs_msg_send_fail: "WeChat notificó un fallo de envío con el ID y el tipo de error.",
wecomcs_servicer_status_change: "Un agente inició, detuvo o suspendió la atención.",
@@ -1060,8 +1063,7 @@ const esES = {
namespace_feedback:
'Coincide con comentarios recibidos de la plataforma o de usuarios.',
namespace_friend: 'Coincide con solicitudes y cambios de amistad.',
namespace_group:
'Coincide con entradas, salidas, expulsiones y otros eventos de grupo.',
namespace_group: "Coincide con cambios de miembros y actualizaciones de información del grupo.",
namespace_message:
'Coincide con mensajes recibidos, editados, eliminados y reacciones.',
namespace_platform:
@@ -1078,6 +1080,7 @@ const esES = {
friend_added: 'Se ha creado una relación de amistad.',
group_member_joined: 'Un miembro se une a un grupo donde está el bot.',
group_member_left: 'Un miembro abandona un grupo donde está el bot.',
group_info_updated: "Han cambiado datos del grupo, como su nombre o propietario.",
group_member_banned: 'Un miembro del grupo es bloqueado o expulsado.',
bot_invited_to_group: 'El bot es invitado a un grupo.',
bot_removed_from_group: 'El bot es expulsado de un grupo.',
+5 -2
View File
@@ -1010,6 +1010,7 @@ const jaJP = {
eventNames: {
wecombot_enter_chat: "ボットとのチャットに参加",
wecombot_template_card_event: "テンプレートカード操作",
dingtalk_card_action: "DingTalk カード操作",
wecomcs_enter_session: "顧客が会話に参加",
wecomcs_msg_send_fail: "カスタマーサービスの送信失敗",
wecomcs_servicer_status_change: "担当者の受付状態変更",
@@ -1024,6 +1025,7 @@ const jaJP = {
friend_added: '友達追加',
group_member_joined: 'メンバー参加',
group_member_left: 'メンバー退出',
group_info_updated: "グループ情報更新",
group_member_banned: 'メンバーBAN',
bot_invited_to_group: 'ボットがグループに招待された',
bot_removed_from_group: 'ボットがグループから削除された',
@@ -1034,6 +1036,7 @@ const jaJP = {
eventDescriptions: {
wecombot_enter_chat: "ユーザーがボットのチャットを開きました。歓迎や案内のフローに使用できます。",
wecombot_template_card_event: "ユーザーがカードをクリックまたは送信しました。管理対象フォームの応答は別途処理します。",
dingtalk_card_action: "カードのクリックや送信。フィードバックと管理対象フォームは別途処理します。",
wecomcs_enter_session: "顧客がリンクから会話を開きました。流入元と歓迎メッセージ用コードを含みます。",
wecomcs_msg_send_fail: "WeChat が送信失敗を通知しました。メッセージ ID と失敗種別を含みます。",
wecomcs_servicer_status_change: "担当者が受付を開始、停止、または一時停止しました。",
@@ -1046,8 +1049,7 @@ const jaJP = {
namespace_feedback:
'プラットフォームまたはユーザーからのフィードバックイベントに一致します。',
namespace_friend: '友達リクエストや友達関係の変更イベントに一致します。',
namespace_group:
'メンバーの参加、退出、削除などのグループイベントに一致します。',
namespace_group: "メンバーの変更やグループ情報の更新などに一致します。",
namespace_message:
'メッセージの受信、編集、削除、リアクションイベントに一致します。',
namespace_platform:
@@ -1067,6 +1069,7 @@ const jaJP = {
friend_added: '友達関係が作成されました。',
group_member_joined: 'ボットがいるグループにメンバーが参加しました。',
group_member_left: 'ボットがいるグループからメンバーが退出しました。',
group_info_updated: "グループ名や所有者などの情報が変更されました。",
group_member_banned: 'グループメンバーがBANまたは削除されました。',
bot_invited_to_group: 'ボットがグループに招待されました。',
bot_removed_from_group: 'ボットがグループから削除されました。',
+5 -2
View File
@@ -1020,6 +1020,7 @@ const ruRU = {
eventNames: {
wecombot_enter_chat: "Пользователь открыл чат бота",
wecombot_template_card_event: "Взаимодействие с карточкой",
dingtalk_card_action: "Взаимодействие с карточкой DingTalk",
wecomcs_enter_session: "Клиент вошёл в диалог",
wecomcs_msg_send_fail: "Ошибка отправки сообщения поддержки",
wecomcs_servicer_status_change: "Статус оператора изменён",
@@ -1034,6 +1035,7 @@ const ruRU = {
friend_added: 'Друг добавлен',
group_member_joined: 'Участник вступил в группу',
group_member_left: 'Участник покинул группу',
group_info_updated: "Информация о группе обновлена",
group_member_banned: 'Участник заблокирован',
bot_invited_to_group: 'Бот приглашён в группу',
bot_removed_from_group: 'Бот удалён из группы',
@@ -1044,6 +1046,7 @@ const ruRU = {
eventDescriptions: {
wecombot_enter_chat: "Пользователь открыл чат бота. Событие подходит для сценариев приветствия.",
wecombot_template_card_event: "Пользователь нажал или отправил карточку. Ответы управляемых форм обрабатываются отдельно.",
dingtalk_card_action: "Нажатия и отправка карточек; отзывы и управляемые формы обрабатываются отдельно.",
wecomcs_enter_session: "Клиент открыл ссылку поддержки; событие содержит источник и код приветствия.",
wecomcs_msg_send_fail: "WeChat сообщил об ошибке отправки с ID сообщения и типом ошибки.",
wecomcs_servicer_status_change: "Оператор начал, остановил или приостановил приём обращений.",
@@ -1057,8 +1060,7 @@ const ruRU = {
'Соответствует обратной связи от платформы или пользователя.',
namespace_friend:
'Соответствует заявкам в друзья и изменениям дружеских связей.',
namespace_group:
'Соответствует вступлениям, выходам, удалениям участников и другим событиям группы.',
namespace_group: "Соответствует изменениям состава и информации группы.",
namespace_message:
'Соответствует получению, редактированию, удалению сообщений и реакциям.',
namespace_platform:
@@ -1077,6 +1079,7 @@ const ruRU = {
friend_added: 'Установлена дружеская связь.',
group_member_joined: 'Участник вступает в группу, где есть бот.',
group_member_left: 'Участник покидает группу, где есть бот.',
group_info_updated: "Изменились сведения о группе, например название или владелец.",
group_member_banned: 'Участник группы заблокирован или удалён.',
bot_invited_to_group: 'Бот приглашён в группу.',
bot_removed_from_group: 'Бот удалён из группы.',
+5 -2
View File
@@ -988,6 +988,7 @@ const thTH = {
eventNames: {
wecombot_enter_chat: "ผู้ใช้เข้าสู่แชตบอต",
wecombot_template_card_event: "การโต้ตอบกับการ์ด",
dingtalk_card_action: "การโต้ตอบกับการ์ด DingTalk",
wecomcs_enter_session: "ลูกค้าเข้าสู่การสนทนา",
wecomcs_msg_send_fail: "ส่งข้อความบริการลูกค้าไม่สำเร็จ",
wecomcs_servicer_status_change: "สถานะเจ้าหน้าที่เปลี่ยนแปลง",
@@ -1002,6 +1003,7 @@ const thTH = {
friend_added: 'เพิ่มเพื่อน',
group_member_joined: 'สมาชิกเข้าร่วมกลุ่ม',
group_member_left: 'สมาชิกออกจากกลุ่ม',
group_info_updated: "อัปเดตข้อมูลกลุ่ม",
group_member_banned: 'สมาชิกถูกแบน',
bot_invited_to_group: 'บอตได้รับเชิญเข้ากลุ่ม',
bot_removed_from_group: 'บอตถูกนำออกจากกลุ่ม',
@@ -1012,6 +1014,7 @@ const thTH = {
eventDescriptions: {
wecombot_enter_chat: "ผู้ใช้เปิดแชตบอต ใช้สำหรับขั้นตอนต้อนรับและแนะนำ",
wecombot_template_card_event: "ผู้ใช้คลิกหรือส่งการ์ด การตอบกลับแบบฟอร์มที่ระบบจัดการจะประมวลผลแยกกัน",
dingtalk_card_action: "การคลิกและส่งการ์ด ความคิดเห็นและแบบฟอร์มที่ระบบจัดการจะประมวลผลแยกกัน",
wecomcs_enter_session: "ลูกค้าเปิดลิงก์บริการ พร้อมข้อมูลแหล่งที่มาและรหัสต้อนรับ",
wecomcs_msg_send_fail: "WeChat แจ้งการส่งล้มเหลว พร้อม ID ข้อความและประเภทข้อผิดพลาด",
wecomcs_servicer_status_change: "เจ้าหน้าที่เริ่ม หยุด หรือพักการให้บริการ",
@@ -1023,8 +1026,7 @@ const thTH = {
'ตรงกับการเชิญ การนำบอทออก การปิดเสียง และเหตุการณ์สถานะอื่น',
namespace_feedback: 'ตรงกับเหตุการณ์ข้อเสนอแนะจากแพลตฟอร์มหรือผู้ใช้',
namespace_friend: 'ตรงกับคำขอเป็นเพื่อนและการเปลี่ยนแปลงความสัมพันธ์',
namespace_group:
'ตรงกับการเข้ากลุ่ม ออกจากกลุ่ม ถูกนำออก และเหตุการณ์กลุ่มอื่น',
namespace_group: "ตรงกับการเปลี่ยนแปลงสมาชิกและการอัปเดตข้อมูลกลุ่ม",
namespace_message: 'ตรงกับการรับ แก้ไข ลบข้อความ และแสดงปฏิกิริยา',
namespace_platform: 'ตรงกับเหตุการณ์เฉพาะแพลตฟอร์มที่อะแดปเตอร์ให้มา',
custom: 'เหตุการณ์กำหนดเองหรือยังไม่มีคำอธิบาย',
@@ -1037,6 +1039,7 @@ const thTH = {
friend_added: 'สร้างความสัมพันธ์เพื่อนแล้ว',
group_member_joined: 'สมาชิกเข้าร่วมกลุ่มที่มีบอท',
group_member_left: 'สมาชิกออกจากกลุ่มที่มีบอท',
group_info_updated: "ข้อมูลกลุ่ม เช่น ชื่อหรือเจ้าของกลุ่ม มีการเปลี่ยนแปลง",
group_member_banned: 'สมาชิกกลุ่มถูกแบนหรือนำออก',
bot_invited_to_group: 'บอทได้รับเชิญเข้ากลุ่ม',
bot_removed_from_group: 'บอทถูกนำออกจากกลุ่ม',
+5 -2
View File
@@ -1005,6 +1005,7 @@ const viVN = {
eventNames: {
wecombot_enter_chat: "Người dùng vào cuộc trò chuyện với bot",
wecombot_template_card_event: "Tương tác thẻ mẫu",
dingtalk_card_action: "Tương tác thẻ DingTalk",
wecomcs_enter_session: "Khách hàng vào hội thoại",
wecomcs_msg_send_fail: "Gửi tin nhắn hỗ trợ thất bại",
wecomcs_servicer_status_change: "Trạng thái nhân viên thay đổi",
@@ -1019,6 +1020,7 @@ const viVN = {
friend_added: 'Đã thêm bạn',
group_member_joined: 'Thành viên tham gia nhóm',
group_member_left: 'Thành viên rời nhóm',
group_info_updated: "Thông tin nhóm được cập nhật",
group_member_banned: 'Thành viên bị cấm tham gia nhóm',
bot_invited_to_group: 'Bot được mời vào nhóm',
bot_removed_from_group: 'Bot bị xóa khỏi nhóm',
@@ -1029,6 +1031,7 @@ const viVN = {
eventDescriptions: {
wecombot_enter_chat: "Người dùng mở cuộc trò chuyện với bot. Dùng cho quy trình chào mừng.",
wecombot_template_card_event: "Người dùng nhấp hoặc gửi thẻ. Phản hồi biểu mẫu do hệ thống quản lý được xử lý riêng.",
dingtalk_card_action: "Nhấp và gửi thẻ; phản hồi và biểu mẫu được quản lý được xử lý riêng.",
wecomcs_enter_session: "Khách hàng mở liên kết hỗ trợ; gồm nguồn truy cập và mã chào mừng.",
wecomcs_msg_send_fail: "WeChat thông báo gửi thất bại, gồm ID tin nhắn và loại lỗi.",
wecomcs_servicer_status_change: "Nhân viên bắt đầu, dừng hoặc tạm dừng tiếp nhận.",
@@ -1040,8 +1043,7 @@ const viVN = {
'Khớp sự kiện mời, xóa, tắt tiếng bot và các thay đổi trạng thái khác.',
namespace_feedback: 'Khớp sự kiện phản hồi từ nền tảng hoặc người dùng.',
namespace_friend: 'Khớp yêu cầu kết bạn và thay đổi quan hệ bạn bè.',
namespace_group:
'Khớp sự kiện tham gia, rời, bị xóa khỏi nhóm và sự kiện nhóm khác.',
namespace_group: "Khớp các thay đổi thành viên và cập nhật thông tin nhóm.",
namespace_message: 'Khớp sự kiện nhận, sửa, xóa và thả cảm xúc tin nhắn.',
namespace_platform:
'Khớp sự kiện riêng của nền tảng do bộ chuyển đổi cung cấp.',
@@ -1055,6 +1057,7 @@ const viVN = {
friend_added: 'Đã tạo quan hệ bạn bè.',
group_member_joined: 'Thành viên tham gia nhóm có bot.',
group_member_left: 'Thành viên rời nhóm có bot.',
group_info_updated: "Thông tin nhóm như tên hoặc chủ sở hữu đã thay đổi.",
group_member_banned: 'Thành viên nhóm bị cấm hoặc xóa.',
bot_invited_to_group: 'Bot được mời vào nhóm.',
bot_removed_from_group: 'Bot bị xóa khỏi nhóm.',
+5 -1
View File
@@ -948,6 +948,7 @@ const zhHans = {
eventNames: {
wecombot_enter_chat: "用户进入机器人会话",
wecombot_template_card_event: "模板卡片交互",
dingtalk_card_action: "钉钉卡片交互",
wecomcs_enter_session: "客户进入会话",
wecomcs_msg_send_fail: "客服消息发送失败",
wecomcs_servicer_status_change: "接待人员状态变更",
@@ -962,6 +963,7 @@ const zhHans = {
friend_added: '好友添加成功',
group_member_joined: '成员加入群组',
group_member_left: '成员离开群组',
group_info_updated: "群信息更新",
group_member_banned: '成员被封禁',
bot_invited_to_group: '机器人被邀入群',
bot_removed_from_group: '机器人被移出群',
@@ -972,6 +974,7 @@ const zhHans = {
eventDescriptions: {
wecombot_enter_chat: "用户进入机器人会话,可用于欢迎和引导流程。",
wecombot_template_card_event: "用户点击或提交模板卡片;已有交互表单的回调单独处理。",
dingtalk_card_action: "用户点击或提交钉钉卡片;反馈和受管理的表单回调单独处理。",
wecomcs_enter_session: "客户通过客服链接进入会话,可获取来源和欢迎语凭据。",
wecomcs_msg_send_fail: "微信通知客服消息发送失败,包含消息 ID 和失败类型。",
wecomcs_servicer_status_change: "接待人员开始、停止接待或暂时挂起。",
@@ -982,7 +985,7 @@ const zhHans = {
namespace_bot: '匹配机器人入群、退群、禁言和解除禁言等状态事件。',
namespace_feedback: '匹配平台或用户反馈事件。',
namespace_friend: '匹配好友请求、好友添加成功等好友关系事件。',
namespace_group: '匹配成员加入、离开或被移出群组等群组成员事件。',
namespace_group: "匹配群成员变动、群信息更新等群组事件。",
namespace_message: '匹配消息接收、编辑、删除和表态事件。',
namespace_platform: '匹配适配器提供的平台专属事件。',
custom: '自定义或暂未提供说明的事件。',
@@ -995,6 +998,7 @@ const zhHans = {
friend_added: '好友关系建立成功。',
group_member_joined: '有成员加入机器人所在群组。',
group_member_left: '有成员离开机器人所在群组。',
group_info_updated: "群名称、群主等群资料发生变更。",
group_member_banned: '群成员被封禁或移出。',
bot_invited_to_group: '机器人被邀请加入群组。',
bot_removed_from_group: '机器人被移出群组。',
+5 -1
View File
@@ -947,6 +947,7 @@ const zhHant = {
eventNames: {
wecombot_enter_chat: "使用者進入機器人會話",
wecombot_template_card_event: "範本卡片互動",
dingtalk_card_action: "釘釘卡片互動",
wecomcs_enter_session: "客戶進入會話",
wecomcs_msg_send_fail: "客服訊息傳送失敗",
wecomcs_servicer_status_change: "接待人員狀態變更",
@@ -961,6 +962,7 @@ const zhHant = {
friend_added: '新增好友',
group_member_joined: '成員加入群組',
group_member_left: '成員離開群組',
group_info_updated: "群組資訊更新",
group_member_banned: '成員被封禁',
bot_invited_to_group: '機器人被邀請加入群組',
bot_removed_from_group: '機器人被移出群組',
@@ -971,6 +973,7 @@ const zhHant = {
eventDescriptions: {
wecombot_enter_chat: "使用者進入機器人會話,可用於歡迎和引導流程。",
wecombot_template_card_event: "使用者點擊或提交範本卡片;已有互動表單的回呼單獨處理。",
dingtalk_card_action: "使用者點擊或提交釘釘卡片;回饋和受管理的表單回呼單獨處理。",
wecomcs_enter_session: "客戶透過客服連結進入會話,可取得來源和歡迎語憑證。",
wecomcs_msg_send_fail: "微信通知客服訊息傳送失敗,包含訊息 ID 和失敗類型。",
wecomcs_servicer_status_change: "接待人員開始、停止接待或暫時掛起。",
@@ -981,7 +984,7 @@ const zhHant = {
namespace_bot: '匹配機器人入羣、退羣、禁言和解除禁言等狀態事件。',
namespace_feedback: '匹配平臺或使用者反饋事件。',
namespace_friend: '匹配好友請求、好友添加成功等好友關係事件。',
namespace_group: '匹配成員加入、離開或被移出羣組等羣組成員事件。',
namespace_group: "匹配群組成員變動、群組資訊更新等事件。",
namespace_message: '匹配訊息接收、編輯、刪除和表態事件。',
namespace_platform: '匹配適配器提供的平臺專屬事件。',
custom: '自定義或暫未提供說明的事件。',
@@ -994,6 +997,7 @@ const zhHant = {
friend_added: '好友關係建立成功。',
group_member_joined: '有成員加入機器人所在羣組。',
group_member_left: '有成員離開機器人所在羣組。',
group_info_updated: "群組名稱、群主等群組資料發生變更。",
group_member_banned: '羣成員被封禁或移出。',
bot_invited_to_group: '機器人被邀請加入羣組。',
bot_removed_from_group: '機器人被移出羣組。',