-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
106 lines (90 loc) · 3.37 KB
/
index.js
File metadata and controls
106 lines (90 loc) · 3.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// telegram-voice-group 实现
// 文本格式清洗函数
function cleanTextForTTS(rawText) {
// 移除 Markdown 标记
let cleaned = rawText
// 移除加粗标记
.replace(/\*\*/g, '')
// 移除代码标记
.replace(/`/g, '')
// 移除标题标记
.replace(/^#+\s*/gm, '')
// 移除链接
.replace(/https?:\/\/[^\s]+/g, '')
// 移除特殊分隔符
.replace(/---/g, '')
.replace(/\*\*\*/g, '')
.replace(/>>>/g, '')
// 清理多余的空白字符
.replace(/\s+/g, ' ')
.trim();
return cleaned;
}
async function sendVoiceToTelegramGroup({ text, groupId, voice = "zh-CN-XiaoxiaoNeural", rate = "+5%", threadId = null }) {
// 验证参数
if (!text || !groupId) {
throw new Error("缺少必需参数: text 和 groupId");
}
const fs = require('fs').promises;
const { exec } = require('child_process');
const util = require('util');
const execAsync = util.promisify(exec);
// 生成临时文件名
const tempId = Date.now().toString();
const tempMp3 = `/tmp/voice_${tempId}.mp3`;
const tempOgg = `/tmp/voice_${tempId}.ogg`;
try {
// 1. 清洗文本格式,避免朗读出标记符号
const cleanedText = cleanTextForTTS(text);
console.log(`清洗后的文本: ${cleanedText.substring(0, 50)}...`);
// 2. 使用 edge-tts 生成语音
console.log(`正在生成语音: ${cleanedText.substring(0, 50)}...`);
await execAsync(`edge-tts --voice "${voice}" --rate="${rate}" --text "${cleanedText.replace(/"/g, '')}" --write-media "${tempMp3}"`);
// 3. 使用 ffmpeg 转换为 Telegram 兼容格式
console.log("正在转换为Telegram兼容格式...");
await execAsync(`ffmpeg -y -i "${tempMp3}" -c:a libopus -b:a 48k -ac 1 -ar 48000 -application voip "${tempOgg}"`);
// 4. 构造群组ID(如果提供了完整会话键,则提取群组ID部分)
let telegramGroupId;
if (groupId.startsWith('agent:main:telegram:group:')) {
// 如果是完整会话键,提取群组ID部分
telegramGroupId = groupId.split(':')[3];
} else if (groupId.startsWith('-100')) {
// 如果已经是群组ID格式
telegramGroupId = groupId;
} else {
throw new Error("群组ID格式无效,应为 -100xxx 格式或 agent:main:telegram:group:-100xxx 格式");
}
// 5. 使用 message 工具发送语音消息(使用 asVoice 参数确保显示为语音气泡)
const { message } = require('@openclaw/core');
const messageOptions = {
action: 'send',
channel: 'telegram',
to: telegramGroupId,
message: text, // 作为消息内容
asVoice: true, // 重要:确保显示为语音气泡
media: tempOgg // 指定媒体文件
};
// 如果提供了话题ID,则添加到选项中
if (threadId !== null) {
messageOptions.threadId = threadId;
}
const result = await message(messageOptions);
console.log("语音消息发送成功");
return result;
} catch (error) {
console.error("发送语音消息时发生错误:", error);
throw error;
} finally {
// 清理临时文件
try {
await fs.unlink(tempMp3).catch(() => {});
await fs.unlink(tempOgg).catch(() => {});
console.log("临时文件已清理");
} catch (cleanupError) {
console.warn("清理临时文件时出错:", cleanupError);
}
}
}
module.exports = {
sendVoiceToTelegramGroup
};