adityajat/SaarvamAI_botPublic · Bot Template

ProfileTelegram

AISarvam AI is a multi‑purpose Telegram bot that lets users ask questions, get explanations, convert text to speech, and translate messages using the Sarvam AI API. It also offers group administration commands such as warn, mute, ban, pin, delete, and announcements, and can clear personal or group AI memory. The bot uses the TeleBotHost TBL framework with Bot, Api, db, and HTTP calls, and provides inline keyboards for language and voice selection.

Ai Assistantaivoicetranslatemoderationadminsarvam
34 commands4 envUpdated 15d agoCreated Sep 14, 2026
Back to folder

commands/_handle_callback_query.js

javascript · 929 lines

Raw
1/**#command2name: /handle_callback_query3answer: 4keyboard: 5parse_mode: markdown6aliases: 7allow_only_group: false8need_reply: false9is_web: 010#command**/11 12// ========================================13// VOICE CALLBACK HANDLER14// ========================================15 16if (!update.callback_query) {17  return;18}19 20let cq = update.callback_query;21let data = cq.data || "";22 23if (!cq.message) {24  return;25}26 27let chatId = cq.message.chat.id;28let messageId = cq.message.message_id;29 30// Remove Telegram's loading spinner31await Api.answerCallbackQuery({32  callback_query_id: cq.id33});34 35 36// ========================================37// LANGUAGE SELECTED38// ========================================39 40if (data.startsWith("vl_")) {41 42  let language = data.substring(3);43 44  let request = await db.user.get("voice_request");45 46  if (!request || !request.text) {47    await Api.editMessageText({48      chat_id: chatId,49      message_id: messageId,50      text: "❌ Voice request expired. Please use /voice again."51    });52    return;53  }54 55  // Save selected output language56  await db.user.set("voice_language", language);57 58  await Api.editMessageText({59    chat_id: chatId,60    message_id: messageId,61    text: "🎙️ Choose a voice:"62  });63 64  // 37 Bulbul v3 voices65  let keyboard = [66    [67      { text: "👨 Shubh", callback_data: "vs_shubh" },68      { text: "👨 Aditya", callback_data: "vs_aditya" }69    ],70    [71      { text: "👨 Rahul", callback_data: "vs_rahul" },72      { text: "👨 Rohan", callback_data: "vs_rohan" }73    ],74    [75      { text: "👨 Amit", callback_data: "vs_amit" },76      { text: "👨 Dev", callback_data: "vs_dev" }77    ],78    [79      { text: "👨 Ratan", callback_data: "vs_ratan" },80      { text: "👨 Varun", callback_data: "vs_varun" }81    ],82    [83      { text: "👨 Manan", callback_data: "vs_manan" },84      { text: "👨 Sumit", callback_data: "vs_sumit" }85    ],86    [87      { text: "👨 Kabir", callback_data: "vs_kabir" },88      { text: "👨 Aayan", callback_data: "vs_aayan" }89    ],90    [91      { text: "👨 Ashutosh", callback_data: "vs_ashutosh" },92      { text: "👨 Advait", callback_data: "vs_advait" }93    ],94    [95      { text: "👨 Anand", callback_data: "vs_anand" },96      { text: "👨 Tarun", callback_data: "vs_tarun" }97    ],98    [99      { text: "👨 Sunny", callback_data: "vs_sunny" },100      { text: "👨 Mani", callback_data: "vs_mani" }101    ],102    [103      { text: "👨 Gokul", callback_data: "vs_gokul" },104      { text: "👨 Vijay", callback_data: "vs_vijay" }105    ],106    [107      { text: "👨 Mohit", callback_data: "vs_mohit" },108      { text: "👨 Rehan", callback_data: "vs_rehan" }109    ],110    [111      { text: "👨 Soham", callback_data: "vs_soham" },112      { text: "👩 Ritu", callback_data: "vs_ritu" }113    ],114    [115      { text: "👩 Priya", callback_data: "vs_priya" },116      { text: "👩 Neha", callback_data: "vs_neha" }117    ],118    [119      { text: "👩 Pooja", callback_data: "vs_pooja" },120      { text: "👩 Simran", callback_data: "vs_simran" }121    ],122    [123      { text: "👩 Kavya", callback_data: "vs_kavya" },124      { text: "👩 Ishita", callback_data: "vs_ishita" }125    ],126    [127      { text: "👩 Shreya", callback_data: "vs_shreya" },128      { text: "👩 Roopa", callback_data: "vs_roopa" }129    ],130    [131      { text: "👩 Tanya", callback_data: "vs_tanya" },132      { text: "👩 Shruti", callback_data: "vs_shruti" }133    ],134    [135      { text: "👩 Suhani", callback_data: "vs_suhani" },136      { text: "👩 Kavitha", callback_data: "vs_kavitha" }137    ],138    [139      { text: "👩 Rupali", callback_data: "vs_rupali" }140    ],141    [142      { text: "🎧 Voice Demos", callback_data: "vd" }143    ],144    [145      { text: "🌐 Change Language", callback_data: "backlang" }146    ]147  ];148 149  await Api.editMessageReplyMarkup({150    chat_id: chatId,151    message_id: messageId,152    reply_markup: {153      inline_keyboard: keyboard154    }155  });156 157  return;158}159 160 161// ========================================162// CHANGE LANGUAGE163// ========================================164 165if (data === "backlang") {166 167  await Api.editMessageText({168    chat_id: chatId,169    message_id: messageId,170    text: "🌐 Choose the language:"171  });172 173  await Api.editMessageReplyMarkup({174    chat_id: chatId,175    message_id: messageId,176    reply_markup: {177      inline_keyboard: [178        [179          { text: "🇮🇳 Hindi", callback_data: "vl_hi-IN" },180          { text: "🇬🇧 English", callback_data: "vl_en-IN" }181        ],182        [183          { text: "🇧🇩 Bengali", callback_data: "vl_bn-IN" },184          { text: "🇮🇳 Tamil", callback_data: "vl_ta-IN" }185        ],186        [187          { text: "🇮🇳 Telugu", callback_data: "vl_te-IN" },188          { text: "🇮🇳 Gujarati", callback_data: "vl_gu-IN" }189        ],190        [191          { text: "🇮🇳 Kannada", callback_data: "vl_kn-IN" },192          { text: "🇮🇳 Malayalam", callback_data: "vl_ml-IN" }193        ],194        [195          { text: "🇮🇳 Marathi", callback_data: "vl_mr-IN" },196          { text: "🇮🇳 Punjabi", callback_data: "vl_pa-IN" }197        ],198        [199          { text: "🇮🇳 Odia", callback_data: "vl_od-IN" }200        ]201      ]202    }203  });204 205  return;206}207 208 209// ========================================210// VOICE SELECTED211// ========================================212 213if (data.startsWith("vs_")) { 214 215  216const voiceCreditBalance = await db.user.get("voice_credits", 0);217 218if (voiceCreditBalance < 1) {219  await Api.sendMessage({220    text:221      "⚠️ You have no voice credits left.\n\n" +222      "Use /buyvoice to purchase 10 credits, " +223      "or /credits to check your balance."224  });225  return;226}227 228const debitResult = await db.user.set(229  "voice_credits",230  voiceCreditBalance - 1231);232 233if (!debitResult || debitResult.ok === false) {234  await Api.sendMessage({235    text: "Could not reserve a voice credit. Please retry."236  });237  return;238}239 240 241  let voice = data.substring(3);242 243  let request = await db.user.get("voice_request");244  let language = await db.user.get("voice_language");245 246  if (!request || !request.text || !language) {247    await Api.sendMessage({248      chat_id: chatId,249      text: "❌ Voice request expired. Please use /voice again."250    });251    return;252  }253 254  // Sarvam Mayura has a 10000-character limit255  if (request.text.length > 10000) {256    await Api.editMessageText({257      chat_id: chatId,258      message_id: messageId,259      text: "❌ The text is too long.\n\nPlease keep the /voice text within 1000 characters."260    });261    return;262  }263 264  await Api.editMessageText({265    chat_id: chatId,266    message_id: messageId,267    text: "⏳ Processing...\n\n🌐 Translating text..."268  });269 270 271  // ========================================272  // TRANSLATION273  // Automatically detects source language274  // ========================================275 276  let translation = await HTTP.post(277    "https://api.sarvam.ai/translate",278    {279      headers: {280        "api-subscription-key": process.env.SARVAM_API_KEY,281        "Content-Type": "application/json"282      },283      body: {284        input: request.text,285        source_language_code: "auto",286        target_language_code: language,287        model: "mayura:v1",288        mode: "modern-colloquial"289      },290      responseType: "json",291      timeout: 30000292    }293  );294 295  if (296    !translation.ok ||297    !translation.data ||298    !translation.data.translated_text299  ) {300 301    await Api.editMessageText({302      chat_id: chatId,303      message_id: messageId,304      text:305        "❌ Translation failed.\n\n" +306        JSON.stringify(translation.data || translation)307    });308 309    return;310  }311 312  let translatedText = translation.data.translated_text;313 314 315  // ========================================316  // TEXT TO SPEECH317  // ========================================318 319  await Api.editMessageText({320    chat_id: chatId,321    message_id: messageId,322    text:323      "⏳ Generating voice...\n\n" +324      "🌐 Language: " + language + "\n" +325      "🎙️ Voice: " + voice326  });327 328  let tts = await HTTP.post(329    "https://api.sarvam.ai/text-to-speech",330    {331      headers: {332        "api-subscription-key": process.env.SARVAM_API_KEY,333        "Content-Type": "application/json"334      },335      body: {336        text: translatedText,337        language_code: language,338        speaker: voice,339        model: "bulbul:v3",340        output_audio_codec: "mp3"341      },342      responseType: "json",343      timeout: 30000344    }345  );346 347  if (348    !tts.ok ||349    !tts.data ||350    !tts.data.audios ||351    !tts.data.audios[0]352  ) {353 354    await Api.editMessageText({355      chat_id: chatId,356      message_id: messageId,357      text:358        "❌ Sarvam TTS error:\n\n" +359        JSON.stringify(tts.data || tts)360    });361 362    return;363  }364 365  let audioBase64 = tts.data.audios[0];366 367  let audioBuffer = Buffer.from(audioBase64, "base64");368 369 370  // ========================================371  // TELEGRAM MP3 UPLOAD372  // ========================================373 374  let boundary = "----TBLVoice" + Date.now();375 376  let head =377    "--" + boundary + "\r\n" +378    'Content-Disposition: form-data; name="chat_id"\r\n\r\n' +379    chatId + "\r\n" +380 381    "--" + boundary + "\r\n" +382    'Content-Disposition: form-data; name="audio"; filename="voice.mp3"\r\n' +383    "Content-Type: audio/mpeg\r\n\r\n";384 385  let tail =386    "\r\n--" + boundary + "--\r\n";387 388  let body = Buffer.concat([389    Buffer.from(head),390    audioBuffer,391    Buffer.from(tail)392  ]);393 394  let telegram = await HTTP.post(395    "https://api.telegram.org/bot" +396      process.env.BOT_TOKEN +397      "/sendAudio",398    {399      headers: {400        "Content-Type":401          "multipart/form-data; boundary=" + boundary402      },403      body: body,404      timeout: 20000,405      responseType: "json"406    }407  );408 409 410  // ========================================411  // TELEGRAM UPLOAD ERROR412  // ========================================413 414  if (415    !telegram.ok ||416    !telegram.data ||417    !telegram.data.ok418  ) {419 420    await Api.editMessageText({421      chat_id: chatId,422      message_id: messageId,423      text:424        "❌ Telegram could not upload the audio.\n\n" +425        JSON.stringify(telegram.data || telegram)426    });427 428    return;429  }430 431 432  // ========================================433  // SUCCESS434  // ========================================435 436  await Api.deleteMessage({437    chat_id: chatId,438    message_id: messageId439  });440 441  return;442}443 444 445// ========================================446// VOICE DEMOS MENU447// ========================================448 449if (data === "vd") {450 451  let language = await db.user.get("voice_language");452 453  if (!language) {454    await Api.editMessageText({455      chat_id: chatId,456      message_id: messageId,457      text: "❌ Choose a language first."458    });459    return;460  }461 462  await Api.editMessageText({463    chat_id: chatId,464    message_id: messageId,465    text: "🎧 Choose a voice for the demo:"466  });467 468  await Api.editMessageReplyMarkup({469    chat_id: chatId,470    message_id: messageId,471    reply_markup: {472      inline_keyboard: [473        [474          { text: "Shubh", callback_data: "demo_shubh" },475          { text: "Aditya", callback_data: "demo_aditya" }476        ],477        [478          { text: "Ratan", callback_data: "demo_ratan" },479          { text: "Priya", callback_data: "demo_priya" }480        ],481        [482          { text: "Ishita", callback_data: "demo_ishita" },483          { text: "Suhani", callback_data: "demo_suhani" }484        ],485        [486          { text: "Ritu", callback_data: "demo_ritu" },487          { text: "Rupali", callback_data: "demo_rupali" }488        ],489        [490          { text: "⬅️ Back", callback_data: "backvoices" }491        ]492      ]493    }494  });495 496  return;497}498 499 500// ========================================501// BACK TO VOICE LIST502// ========================================503 504if (data === "backvoices") {505 506  await Api.editMessageText({507    chat_id: chatId,508    message_id: messageId,509    text: "🎙️ Choose a voice:"510  });511 512  await Api.editMessageReplyMarkup({513    chat_id: chatId,514    message_id: messageId,515    reply_markup: {516      inline_keyboard: [517        [518          { text: "🎧 Voice Demos", callback_data: "vd" }519        ],520        [521          { text: "🌐 Change Language", callback_data: "backlang" }522        ]523      ]524    }525  });526 527  return;528}529 530 531// ========================================532// DEMO SELECTED533// ========================================534 535if (data.startsWith("demo_")) {536 537  let voice = data.substring(5);538  let language = await db.user.get("voice_language");539 540  if (!language) {541    return;542  }543 544  await Api.editMessageText({545    chat_id: chatId,546    message_id: messageId,547    text: "⏳ Generating " + voice + " demo..."548  });549 550  let demoText = "Hello! This is a voice demonstration.";551 552  // Translate demo text into selected language553  let demoTranslation = await HTTP.post(554    "https://api.sarvam.ai/translate",555    {556      headers: {557        "api-subscription-key": process.env.SARVAM_API_KEY,558        "Content-Type": "application/json"559      },560      body: {561        input: demoText,562        source_language_code: "en-IN",563        target_language_code: language,564        model: "mayura:v1",565        mode: "modern-colloquial"566      },567      responseType: "json",568      timeout: 30000569    }570  );571 572  if (573    !demoTranslation.ok ||574    !demoTranslation.data ||575    !demoTranslation.data.translated_text576  ) {577 578    await Api.editMessageText({579      chat_id: chatId,580      message_id: messageId,581      text: "❌ Demo translation failed."582    });583 584    return;585  }586 587  let translatedDemo =588    demoTranslation.data.translated_text;589 590 591  // Generate MP3 demo592  let tts = await HTTP.post(593    "https://api.sarvam.ai/text-to-speech",594    {595      headers: {596        "api-subscription-key": process.env.SARVAM_API_KEY,597        "Content-Type": "application/json"598      },599      body: {600        text: translatedDemo,601        language_code: language,602        speaker: voice,603        model: "bulbul:v3",604        output_audio_codec: "mp3"605      },606      responseType: "json",607      timeout: 30000608    }609  );610 611  if (612    !tts.ok ||613    !tts.data ||614    !tts.data.audios ||615    !tts.data.audios[0]616  ) {617 618    await Api.editMessageText({619      chat_id: chatId,620      message_id: messageId,621      text: "❌ Demo generation failed."622    });623 624    return;625  }626 627  let audioBuffer =628    Buffer.from(tts.data.audios[0], "base64");629 630 631  // ========================================632  // DEMO MP3 UPLOAD633  // ========================================634 635  let boundary = "----TBLDemo" + Date.now();636 637  let head =638    "--" + boundary + "\r\n" +639    'Content-Disposition: form-data; name="chat_id"\r\n\r\n' +640    chatId + "\r\n" +641 642    "--" + boundary + "\r\n" +643    'Content-Disposition: form-data; name="audio"; filename="demo.mp3"\r\n' +644    "Content-Type: audio/mpeg\r\n\r\n";645 646  let tail =647    "\r\n--" + boundary + "--\r\n";648 649  let body = Buffer.concat([650    Buffer.from(head),651    audioBuffer,652    Buffer.from(tail)653  ]);654 655  let telegram = await HTTP.post(656    "https://api.telegram.org/bot" +657      process.env.BOT_TOKEN +658      "/sendAudio",659    {660      headers: {661        "Content-Type":662          "multipart/form-data; boundary=" + boundary663      },664      body: body,665      timeout: 20000,666      responseType: "json"667    }668  );669 670  if (671    !telegram.ok ||672    !telegram.data ||673    !telegram.data.ok674  ) {675 676    await Api.editMessageText({677      chat_id: chatId,678      message_id: messageId,679      text: "❌ Demo upload failed."680    });681 682    return;683  }684 685  await Api.editMessageText({686    chat_id: chatId,687    message_id: messageId,688    text: "🎧 Demo sent!"689  });690 691  return;692}693 694 695/* ========================================696   TEXT TRANSLATION CALLBACK697======================================== */698 699if (data.startsWith("tr_")) {700  let targetLanguage = data.substring(3);701  let inputText = await db.user.get("translate_request");702 703  if (!inputText || typeof inputText !== "string") {704    await Api.sendMessage({705      chat_id: chatId,706      text: "❌ Translation request expired. Please use /translate again."707    });708    return;709  }710 711  if (!process.env.SARVAM_API_KEY) {712    await Api.sendMessage({713      chat_id: chatId,714      text: "❌ Sarvam API key is missing from the environment."715    });716    return;717  }718 719  await Api.answerCallbackQuery({720    callback_query_id: cq.id,721    text: "Translating..."722  });723 724  try {725    await Api.editMessageText({726      chat_id: chatId,727      message_id: messageId,728      text: "⏳ Translating your text..."729    });730 731    // Detect the source language732    let detected = await HTTP.post(733      "https://api.sarvam.ai/text-lid",734      {735        headers: {736          "api-subscription-key": process.env.SARVAM_API_KEY,737          "Content-Type": "application/json"738        },739        body: {740          input: inputText.substring(0, 1000)741        },742        responseType: "json",743        timeout: 30000744      }745    );746 747    let sourceLanguage =748      detected.data &&749      (detected.data.language_code ||750       detected.data.language);751 752    if (!detected.ok || !sourceLanguage) {753      throw new Error(754        "Language detection failed: " +755        JSON.stringify(detected.data || detected)756      );757    }758 759    // Translate the original text760    let translated = await HTTP.post(761      "https://api.sarvam.ai/translate",762      {763        headers: {764          "api-subscription-key": process.env.SARVAM_API_KEY,765          "Content-Type": "application/json"766        },767        768 769body: {770  input: inputText,771  source_language_code: sourceLanguage,772  target_language_code: targetLanguage,773  model: "sarvam-translate:v1",774  mode: "formal"775},776responseType: "json",777timeout: 30000778}779);780 781 782    if (783      !translated.ok ||784      !translated.data ||785      !translated.data.translated_text786    ) {787      throw new Error(788        "Translation API error: " +789        JSON.stringify(translated.data || translated)790      );791    }792 793    await Api.sendMessage({794      chat_id: chatId,795      text: "🌐 Translation (" + targetLanguage + "):\n\n" +796        translated.data.translated_text797    });798 799    await Api.editMessageText({800      chat_id: chatId,801      message_id: messageId,802      text: "✅ Translation completed."803    });804 805  } catch (error) {806    await Api.editMessageText({807      chat_id: chatId,808      message_id: messageId,809      text: "❌ Translation failed.\n\n" +810        String(error.message || error).substring(0, 2500)811    });812  }813 814  return;815}816 817 818let translatedText = translated.data.translated_text;819 820 821const languageNames = {822  "as-IN": "Assamese",823  "bn-IN": "Bengali",824  "brx-IN": "Bodo",825  "doi-IN": "Dogri",826  "en-IN": "English",827  "gu-IN": "Gujarati",828  "hi-IN": "Hindi",829  "kn-IN": "Kannada",830  "ks-IN": "Kashmiri",831  "kok-IN": "Konkani",832  "mai-IN": "Maithili",833  "ml-IN": "Malayalam",834  "mni-IN": "Manipuri",835  "mr-IN": "Marathi",836  "ne-IN": "Nepali",837  "od-IN": "Odia",838  "pa-IN": "Punjabi",839  "sa-IN": "Sanskrit",840  "sat-IN": "Santali",841  "sd-IN": "Sindhi",842  "ta-IN": "Tamil",843  "te-IN": "Telugu",844  "ur-IN": "Urdu"845};846 847const targetName = languageNames[targetLanguage];848 849if (!targetName) {850  throw new Error("Unsupported target language: " + targetLanguage);851}852 853// Use 105B for these languages; use the dedicated854// translation API for other languages.855const chatLanguages = [856  "bn-IN", "en-IN", "gu-IN", "hi-IN",857  "kn-IN", "ml-IN", "mr-IN", "od-IN",858  "pa-IN", "ta-IN", "te-IN"859];860 861if (chatLanguages.includes(targetLanguage)) {862  const result = await HTTP.post(863    "https://api.sarvam.ai/v1/chat/completions",864    {865      headers: {866        "api-subscription-key": process.env.SARVAM_API_KEY,867        "Content-Type": "application/json"868      },869      body: {870        model: "sarvam-105b",871        messages: [872          {873            role: "system",874            content:875              "Translate the user's entire message into " +876              targetName +877              ". Preserve its meaning, names, numbers, tone, and formatting. " +878              "Do not summarize, omit content, or add commentary. " +879              "Return only the translation."880          },881          {882            role: "user",883            content: inputText884          }885        ],886        temperature: 0.2,887        max_tokens: 4096888      },889      responseType: "json",890      timeout: 60000891    }892  );893 894  translatedText =895    result.data?.choices?.[0]?.message?.content;896 897} else {898  const result = await HTTP.post(899    "https://api.sarvam.ai/translate",900    {901      headers: {902        "api-subscription-key": process.env.SARVAM_API_KEY,903        "Content-Type": "application/json"904      },905      body: {906        input: inputText,907        source_language_code: sourceLanguage,908        target_language_code: targetLanguage,909        model: "sarvam-translate:v1",910        mode: "formal"911      },912      responseType: "json",913      timeout: 30000914    }915  );916 917  translatedText = result.data?.translated_text;918}919 920if (921  typeof translatedText !== "string" ||922  !translatedText.trim()923) {924  throw new Error("Translation returned an empty response.");925}926 927translatedText = translatedText.trim();928 929