
Feishu Voice Assistant
- 2 installs
- 20 repo stars
- Updated April 11, 2026
- autogame-17/feishu-skills
feishu-voice-assistant is a skill that turns text into speech with Duby TTS and delivers it as a native Feishu voice message.
About
This skill generates speech from text using Duby AI and sends it as a native voice message to a Feishu user or chat. A developer runs it as a Node CLI, passing the text and a Feishu user or chat id. It is useful for pushing spoken notifications or replies into Feishu conversations.
- Converts text to speech with Duby AI and posts it as a native Feishu voice message
- Node CLI invoked with --text, --target and optional --voice arguments
- Requires DUBY_API_KEY plus Feishu credentials in .env
Feishu Voice Assistant by the numbers
- 2 all-time installs (skills.sh)
- Ranked #1,842 of 2,719 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
feishu-voice-assistant capabilities & compatibility
Requires a paid/keyed Duby TTS API key plus Feishu app credentials.
- Capabilities
- text to speech · chat notification
- Works with
- slack
- Use cases
- transcription
- Runs
- Runs locally
- Pricing
- Bring your own API key
What feishu-voice-assistant says it does
Generate speech from text using Duby AI and send it as a native voice message (audio) to Feishu.
Requires `DUBY_API_KEY` and Feishu credentials in `.env`.
npx skills add https://github.com/autogame-17/feishu-skills --skill feishu-voice-assistantAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 20 |
| Last updated | April 11, 2026 |
| Repository | autogame-17/feishu-skills ↗ |
What it does
Send a generated voice message to a Feishu user or group chat from an automated workflow.
When should I use this skill?
You need to deliver a spoken audio message into a Feishu chat instead of plain text.
What you get
A TTS audio clip is uploaded and delivered as a native Feishu voice message to a target user or chat.
- A native Feishu voice (audio) message delivered to a target user or chat
By the numbers
- 3 CLI options: --text, --target, --voice
Files
Feishu Voice Assistant
Generate speech from text using Duby AI and send it as a native voice message (audio) to Feishu.
Usage
Send a Voice Message
node skills/feishu-voice-assistant/index.js --text "Hello, this is a voice message!" --target "$TARGET_USER_ID"Options
--text: The text to convert to speech.--target: The Feishu user ID (ou_...) or chat ID (oc_...).--voice: (Optional) Duby Voice ID. Default is Xinduo.
Dependencies
duby: For TTS generation.feishu-common: For API authentication.form-data: For file uploads.
Configuration
Requires DUBY_API_KEY and Feishu credentials in .env.
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { program } = require('commander');
const { getToken, fetchWithRetry } = require('../feishu-common/index.js');
const duby = require('../duby/index.js');
const { Blob } = require('buffer'); // Use native Blob for FormData
// Ensure .env is loaded
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') });
async function uploadAudio(token, filePath) {
const fileName = path.basename(filePath);
const fileBuffer = fs.readFileSync(filePath);
const blob = new Blob([fileBuffer]);
const formData = new FormData();
formData.append('file_type', 'stream'); // Generic stream for audio files
formData.append('file_name', fileName);
formData.append('file', blob, fileName);
formData.append('duration', '10000'); // Optional duration in ms
try {
const response = await fetchWithRetry('https://open.feishu.cn/open-apis/im/v1/files', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
body: formData
});
const data = await response.json();
if (data.code !== 0) {
throw new Error(`Upload failed: ${data.msg} (Code: ${data.code})`);
}
return data.data.file_key;
} catch (error) {
throw new Error(`Upload error: ${error.message}`);
}
}
async function sendAudioMessage(target, fileKey) {
const token = await getToken();
// Determine receive_id_type
const receiveIdType = target.startsWith('oc_') ? 'chat_id' : 'open_id';
const body = {
receive_id: target,
msg_type: 'audio',
content: JSON.stringify({ file_key: fileKey })
};
try {
const response = await fetchWithRetry(
`https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=${receiveIdType}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
}
);
const data = await response.json();
if (data.code !== 0) {
throw new Error(`Send failed: ${data.msg} (Code: ${data.code})`);
}
return data.data;
} catch (error) {
throw new Error(`Send error: ${error.message}`);
}
}
async function main() {
program
.option('--text <text>', 'Text to speak')
.option('--target <id>', 'Target User ID or Chat ID')
.option('--voice <id>', 'Voice ID (optional)')
.parse(process.argv);
const options = program.opts();
if (!options.text || !options.target) {
console.error('Usage: node index.js --text "Hello" --target "ou_xxx" [--voice "id"]');
process.exit(1);
}
try {
console.log(`🎤 Generating audio for: "${options.text}"...`);
// Step 1: Generate TTS
// duby.duby_tts returns "MEDIA:/path/to/file.mp3" string
// We need to parse this string to get the actual path.
const mediaPath = await duby.duby_tts({
text: options.text,
voice_id: options.voice
});
if (!mediaPath || !mediaPath.startsWith('MEDIA:')) {
throw new Error(`Invalid TTS response: ${mediaPath}`);
}
const filePath = mediaPath.replace('MEDIA:', '').trim();
const absolutePath = path.resolve(process.cwd(), filePath);
if (!fs.existsSync(absolutePath)) {
throw new Error(`Generated audio file not found at: ${absolutePath}`);
}
console.log(`📤 Uploading audio: ${filePath}...`);
// Step 2: Upload to Feishu
const token = await getToken();
const fileKey = await uploadAudio(token, absolutePath);
console.log(`📨 Sending audio message to ${options.target}...`);
// Step 3: Send Message
await sendAudioMessage(options.target, fileKey);
console.log('✅ Voice message sent successfully!');
// Cleanup temp file
// fs.unlinkSync(absolutePath); // Optional: keep for cache or delete
} catch (error) {
console.error(`❌ Error: ${error.message}`);
process.exit(1);
}
}
if (require.main === module) {
main();
}
module.exports = { main };
{
"name": "feishu-voice-assistant",
"version": "1.0.0",
"description": "Sends voice messages (audio) to Feishu using Duby TTS",
"main": "index.js",
"dependencies": {
"commander": "^9.0.0",
"form-data": "^4.0.0",
"dotenv": "^16.0.0"
},
"scripts": {
"test": "node index.js --help"
}
}
Related skills
FAQ
What TTS engine does it use?
It uses Duby AI for text-to-speech generation, with the Xinduo voice as the default.
What credentials are needed?
It requires a DUBY_API_KEY and Feishu credentials set in a .env file.