Emoji 模式是一个全新的功能,允许服务端根据对话内容自动匹配并发送相应的 emoji 表情标识符。客户端可以根据这些标识符显示自定义的表情动画、图片或其他视觉效果。
EMOJI (类型值 = 9)false、true、dimi在 TCP 认证消息中添加 emoji_mode 参数:
##START[AUTH][task_id][seq]your_jwt_token##emoji_mode:true##END
或者在 HTTP API 中(如果您使用的是 HTTP 接口):
{
"emoji_mode": "true"
}
| 参数值 | 说明 | Prompt匹配 | Answer匹配 | 匹配算法 |
|---|---|---|---|---|
false 或不传 | 关闭emoji功能 | ❌ | ❌ | - |
true | 仅对回答匹配 | ❌ | ✅ | 情感分析 |
dimi | 对问题和回答都匹配 | ✅ | ✅ | 关键词匹配 |
falsetrue/false 与 "true"/"false" 均支持MessageType.EMOJI = 9
##START + Header + JSON_Data + ##END
| 字段 | 长度 | 说明 |
|---|---|---|
| msg_type | 1 byte | 消息类型 = 9 |
| task_id | 8 bytes | 任务ID(UTF-8编码) |
| seq | 4 bytes | 序列号(固定为 "0000") |
{
"emoji": "emoji_key"
}
假设 task_id = abc12345,匹配到的 emoji 为 happy:
##START[0x09][abc12345][0000]{"emoji":"happy"}##END
二进制表示:
0x23 0x23 0x53 0x54 0x41 0x52 0x54 // ##START (7 bytes)
0x09 // msg_type = 9 (1 byte)
0x61 0x62 0x63 0x31 0x32 0x33 0x34 0x35 // task_id = "abc12345" (8 bytes)
0x30 0x30 0x30 0x30 // seq = "0000" (4 bytes)
{"emoji":"happy"} // JSON数据 (可变长度)
0x23 0x23 0x45 0x4E 0x44 // ##END (5 bytes)
基于情感分析算法,返回以下20种情感标识符:
| Emoji Key | 中文说明 | 触发示例 |
|---|---|---|
happy | 开心 | "太好了!"、"真棒" |
laughing | 大笑 | "哈哈哈"、"笑死我了" |
sad | 伤心 | "好难过"、"失望" |
angry | 生气 | "气死了"、"讨厌" |
crying | 哭泣 | "想哭"、"呜呜" |
loving | 喜爱 | "爱你"、"喜欢" |
surprised | 惊讶 | "天啊"、"哇塞" |
shocked | 震惊 | "不敢相信"、"吓到" |
thinking | 思考 | "?"、"考虑一下" |
embarrassed | 尴尬 | "不好意思"、"害羞" |
winking | 眨眼 | "你懂的"、"调皮" |
cool | 酷 | "厉害"、"牛逼" |
relaxed | 放松 | "舒服"、"惬意" |
delicious | 美味 | "好吃"、"香" |
kissy | 亲亲 | "么么哒"、"mua" |
confident | 自信 | "当然"、"肯定" |
sleepy | 困倦 | "晚安"、"好累" |
silly | 傻笑 | "傻乎乎"、"呆萌" |
confused | 困惑 | "不明白"、"疑惑" |
funny | 搞笑 | "幽默"、"滑稽" |
基于关键词匹配,支持大量场景化表情,例如:
| Emoji Key | 关键词示例 |
|---|---|
hu_die | "go for a walk", "春天散步" |
sheng_bing | "sore throat", "喉咙痛" |
xia_yu | "after the rain", "下雨" |
bi_xin | "比心"、"heart gesture" |
kai_hua | "peach blossoms", "开花" |
| ... | ... |
💡 提示: Dimi 模式支持中英文混合匹配,优先匹配长关键词。
import struct
import json
START_MARK = b'##START'
END_MARK = b'##END'
def parse_emoji_message(data):
"""解析 EMOJI 消息"""
if data.startswith(START_MARK) and data.endswith(END_MARK):
# 移除标记
frame = data[len(START_MARK):-len(END_MARK)]
# 解析 header
msg_type = frame[0]
task_id = frame[1:9].decode('utf-8')
seq = frame[9:13].decode('utf-8')
# 检查是否是 EMOJI 消息
if msg_type == 9:
# 解析 JSON 数据
json_data = frame[13:].decode('utf-8')
emoji_info = json.loads(json_data)
return {
'type': 'emoji',
'task_id': task_id,
'emoji_key': emoji_info['emoji']
}
return None
# 使用示例
data = b'##START\x09abc123450000{"emoji":"happy"}##END'
result = parse_emoji_message(data)
print(result)
# 输出: {'type': 'emoji', 'task_id': 'abc12345', 'emoji_key': 'happy'}
using System;
using System.Text;
using Newtonsoft.Json.Linq;
public class EmojiMessage
{
public string Type { get; set; }
public string TaskId { get; set; }
public string EmojiKey { get; set; }
}
public class MessageParser
{
private static readonly byte[] START_MARK = Encoding.UTF8.GetBytes("##START");
private static readonly byte[] END_MARK = Encoding.UTF8.GetBytes("##END");
public static EmojiMessage ParseEmojiMessage(byte[] data)
{
// 检查起始和结束标记
if (!StartsWith(data, START_MARK) || !EndsWith(data, END_MARK))
return null;
// 提取帧数据
int frameStart = START_MARK.Length;
int frameEnd = data.Length - END_MARK.Length;
// 解析消息类型
byte msgType = data[frameStart];
if (msgType != 9) return null; // 不是 EMOJI 消息
// 解析 task_id (8 bytes)
string taskId = Encoding.UTF8.GetString(data, frameStart + 1, 8);
// 解析 JSON 数据
int jsonStart = frameStart + 13; // 1 + 8 + 4
int jsonLength = frameEnd - jsonStart;
string jsonData = Encoding.UTF8.GetString(data, jsonStart, jsonLength);
// 解析 JSON
JObject json = JObject.Parse(jsonData);
return new EmojiMessage
{
Type = "emoji",
TaskId = taskId,
EmojiKey = json["emoji"].ToString()
};
}
private static bool StartsWith(byte[] data, byte[] prefix)
{
if (data.Length < prefix.Length) return false;
for (int i = 0; i < prefix.Length; i++)
if (data[i] != prefix[i]) return false;
return true;
}
private static bool EndsWith(byte[] data, byte[] suffix)
{
if (data.Length < suffix.Length) return false;
int offset = data.Length - suffix.Length;
for (int i = 0; i < suffix.Length; i++)
if (data[offset + i] != suffix[i]) return false;
return true;
}
}
public class EmojiDisplay : MonoBehaviour
{
[SerializeField] private Dictionary<string, AnimationClip> emojiAnimations;
[SerializeField] private Animator characterAnimator;
public void ShowEmoji(string emojiKey)
{
if (emojiAnimations.ContainsKey(emojiKey))
{
// 播放对应的表情动画
characterAnimator.Play(emojiKey);
// 或者显示表情图标
ShowEmojiIcon(emojiKey);
}
else
{
Debug.LogWarning($"未找到 emoji: {emojiKey}");
}
}
private void ShowEmojiIcon(string emojiKey)
{
// 在角色头顶显示表情气泡
// 实现您自己的显示逻辑
}
}
import socket
import threading
class TCPClient:
def __init__(self, host, port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port))
self.buffer = b""
def receive_loop(self):
"""接收消息循环"""
while True:
data = self.sock.recv(1024)
if not data:
break
self.buffer += data
# 处理缓冲区中的完整消息
while b'##END' in self.buffer:
frame, self.buffer = self.buffer.split(b'##END', 1)
if b'##START' in frame:
frame = frame[frame.index(b'##START'):]
frame += b'##END'
# 解析消息
self.handle_message(frame)
def handle_message(self, frame):
"""处理单条消息"""
if len(frame) < 20:
return
msg_type = frame[7] # 跳过 ##START
if msg_type == 4: # TEXT
# 处理文本消息
self.handle_text(frame)
elif msg_type == 2: # AUDIO_FRAME
# 处理音频消息
self.handle_audio(frame)
elif msg_type == 9: # EMOJI
# 处理 emoji 消息
emoji_msg = parse_emoji_message(frame)
if emoji_msg:
print(f"收到 emoji: {emoji_msg['emoji_key']}")
self.show_emoji(emoji_msg['emoji_key'])
def show_emoji(self, emoji_key):
"""显示 emoji(由您实现)"""
pass
客户端 服务端
| |
|----(1) 发送认证------------->|
| emoji_mode: true |
| |
|<---(2) 发送认证成功---------|
| |
|----(3) 发送用户输入-------->|
| "你好呀!" |
| |
|<---(4) TEXT: "你好!"-------|
|<---(5) EMOJI: "happy"-------| ← 只对回答匹配
|<---(6) TEXT: "很高兴见到你" |
|<---(7) EMOJI: "happy"-------|
|<---(8) END_FRAME------------|
客户端 服务端
| |
|----(1) 发送认证------------->|
| emoji_mode: dimi |
| |
|<---(2) 发送认证成功---------|
| |
|----(3) 发送用户输入-------->|
| "今天下雨了" |
| |
|<---(4) EMOJI: "xia_yu"------| ← 对问题匹配
|<---(5) TEXT: "记得带伞哦"---|
|<---(6) EMOJI: "关心"--------| ← 对回答匹配
|<---(7) END_FRAME------------|
try:
emoji_msg = parse_emoji_message(frame)
if emoji_msg:
show_emoji(emoji_msg['emoji_key'])
except Exception as e:
logging.warning(f"解析 emoji 消息失败: {e}")
# 忽略错误,继续处理其他消息
# 测试用例
test_cases = [
("false", "你好", 0), # 期望收到 0 个 emoji
("true", "哈哈哈,太好笑了!", 1), # 期望收到 1 个 emoji
("dimi", "今天666啊", 2), # 期望收到 2 个 emoji(问题+回答)
]
for emoji_mode, input_text, expected_emoji_count in test_cases:
# 发送认证(设置 emoji_mode)
# 发送输入
# 统计收到的 EMOJI 消息数量
# 验证是否符合预期
A: 检查以下几点:
emoji_mode 参数且不为 falseA:
A:
A:
客户端维护一个 emoji_key → 动画/图片 的映射表:
{
"happy": "happy_animation.anim",
"laughing": "laugh_animation.anim",
"xia_yu": "rain_icon.png"
}
A:
happy、xia_yu、tai_hao_xiao如有问题,请联系技术支持团队或查阅完整协议文档。