Files
2024-04-05 20:41:55 +11:00

167 lines
4.4 KiB
TypeScript

import { Router } from 'itty-router'
/**
* demo
* https://home-tts.junv.workers.dev/tts?text=%E4%BD%A0%E7%9C%9F%E7%9A%84%E6%98%AF%E5%BE%88%E8%AE%A8%E5%8E%8C%E5%91%A2&haha=hahaJunv&voice=zh-CN-XiaoxiaoNeural&voiceStyle=angry
*
* to publish
* wrangler publish tts-bot.ts --compatibility-date 2024-04-05
*
*
*/
const router = Router()
const botToken = BOT_TOKEN;
const chatId = CHAT_ID;
const ttsKey = TTS_KEY
const format = "audio-16khz-128kbitrate-mono-mp3";
// const defaultVoice = 'zh-CN-XiaoxiaoNeural'; //female
const defaultVoice = 'zh-CN-XiaoxiaoNeural'
// male
// zh-CN-YunfengNeural
// zh-CN-YunyangNeural
// 'zh-CN-XiaoxiaoNeural'; //female
const defaultVoiceStyle = "cheerful"
// "assistant",
// "chat",
// "customerservice",
// "newscast",
// "affectionate",
// "angry",
// "calm",
// "cheerful",
// "disgruntled",
// "fearful",
// "gentle",
// "lyrical",
// "sad",
// "serious",
// "poetry-reading",
// "friendly",
// "chat-casual",
// "whisper",
// "sorry"
async function sendToAzure(text: string, voice: string, voiceStyle:string, format: string) {
const endpoint = `https://australiaeast.tts.speech.microsoft.com/cognitiveservices/v1`
const body = `<speak version="1.0" xml:lang="${voice}"><voice xml:lang="${voice}" xml:gender="Female" name="${voice}" style="${voiceStyle}" >${text}</voice></speak>`
const response = await fetch(`${endpoint}`, {
method: 'POST',
headers: {
'Ocp-Apim-Subscription-Key': ttsKey,
'Content-Type': 'application/ssml+xml',
'X-Microsoft-OutputFormat': format,
'User-Agent': 'curl'
},
body,
})
if (!response.ok) {
throw new Error(`Failed to generate speech: ${response.statusText}`)
}
return response.arrayBuffer()
}
async function sendVoice(chatId: number | string, voice: ArrayBuffer, caption?: string): Promise<boolean> {
const apiUrl = `https://api.telegram.org/bot${botToken}/sendVoice`;
const formData = new FormData();
formData.append('chat_id', chatId.toString());
formData.append('voice', new Blob([voice], { type: 'audio/mpeg' }), 'voice.mp3');
if (caption) {
formData.append('caption', caption);
}
const response = await fetch(apiUrl, {
method: 'POST',
body: formData,
});
const responseBody = await response.json();
if (responseBody.ok) {
return true;
} else {
console.error(`Telegram API returned error ${responseBody.error_code}: ${responseBody.description}`);
return false;
}
}
// Define the request handler
async function handleBotRequest(request) {
// Parse the request body as JSON
const requestBody = await request.json();
if (!requestBody.message) {
return new Response('Error: No valid message founded', { status: 400 });
}
const text = requestBody.message.text;
var audioData;
try {
// Generate the audio file from the input text
audioData = await sendToAzure(text, defaultVoice, defaultVoiceStyle, format);
} catch (err) {
await new Response('Error: something went wrong', { status: 500 });
}
if (await sendVoice(chatId, audioData)) {
return new Response(audioData, {
headers: {
'Content-Type': 'audio/mpeg',
'Content-Disposition': 'attachment; filename="audio.mp3"',
},
});
}
return new Response('Error: something went wrong', { status: 500 });
}
async function handleRequest(request: Request): Promise<Response> {
try {
const params = request.query;
if (!params) {
return new Response('Error: No valid params provided', { status: 400 });
}
var voice = params.voice
var voiceStyle = params.voiceStyle
const haha = params.haha
const text = params.text
if (haha != "hahaJunv") {
return new Response('Error: something went wrong, haha', { status: 500 });
}
if (voice == null) {
voice = defaultVoice
}
if (voiceStyle == null) {
voiceStyle = defaultVoiceStyle
}
const audioData = await sendToAzure(text, voice, voiceStyle, format)
return new Response(audioData, {
headers: {
'Content-Type': 'audio/mpeg',
},
})
} catch (error) {
console.log(error)
return new Response(error.message || 'Internal Server Error', {
status: 500,
})
}
}
// Define the Cloudflare Worker entry point
// addEventListener('fetch', event => {
// event.respondWith(handleRequest(event.request));
// });
router.post('/bot', handleBotRequest)
router.get('/tts', handleRequest)
addEventListener('fetch', event => {
event.respondWith(router.handle(event.request))
})