soumyadeep/TgDocsXBotPublic · Bot Template
TgDocsXBot is a Telegram bot template designed to help developers instantly look up Telegram Bot API documentation directly inside any chat using Inline Queries.
7 commands3 envUpdated 5d agoCreated Jun 10, 2026
commands/_inline_query.js
javascript · 913 lines · click line # to share
1/**#command2name: /inline_query3answer: 4keyboard: 5parse_mode: markdown6aliases: 7allow_only_group: false8need_reply: false9is_web: 010#command**/11 12let query = request.query || "message";13if (request && query) {14 let isProp = query.includes(".");15 let isTypeSearch = query.startsWith("!");16 let isErrorSearch = query.startsWith("error:");17 let methodName = "";18 let fieldNames = [];19 let typeFilter = "";20 let searchQ = query;21 let results = [];22 let offset = parseInt(request.offset) || 0;23 const LIMIT = 50;24 25 if (isErrorSearch) {26 const {27 searchErrors,28 getErrorByCode,29 getErrorsByLetter,30 getAllErrors31 } = require("errorUtils");32 33 let searchTerm = query.substring(6).trim();34 35 // Helper function to create full error details text36 const createErrorText = (error) => {37 let text = `<b>Error ${error.code}</b>\n\n`;38 text += `<b>Description:</b> ${error.description}\n\n`;39 40 if (error.common_causes) {41 text += `<b>Common Causes:</b>\n`;42 error.common_causes.forEach(cause => {43 text += `• ${cause}\n`;44 });45 text += `\n`;46 }47 48 if (error.examples) {49 text += `<b>Examples:</b>\n<code>${error.examples.join('</code>\n<code>')}</code>\n\n`;50 }51 52 if (error.solution) {53 text += `<b>Solution:</b> ${error.solution}\n`;54 }55 56 return text;57 };58 59 // Helper function to create reply markup60 const createReplyMarkup = (searchTerm) => {61 if (searchTerm) {62 return {63 inline_keyboard: [64 [{65 text: "All Errors",66 switch_inline_query_current_chat: "error:"67 }]68 ]69 };70 } else {71 return {72 inline_keyboard: [73 [{74 text: "Search Another Error",75 switch_inline_query_current_chat: "error:"76 }]77 ]78 };79 }80 };81 82 if (!searchTerm) {83 // Show all errors with pagination84 let allErrors = getAllErrors();85 let paginatedErrors = allErrors.slice(offset, offset + LIMIT);86 87 paginatedErrors.forEach((error, idx) => {88 let text = createErrorText(error);89 90 results.push({91 type: "article",92 id: `error_${error.code}_${idx}`,93 title: `${error.code}`,94 description: error.description.substring(0, 60),95 input_message_content: {96 message_text: text,97 parse_mode: "HTML"98 },99 reply_markup: createReplyMarkup(searchTerm)100 });101 });102 103 let nextOffset = offset + LIMIT < allErrors.length ? offset + LIMIT : "";104 105 Api.answerInlineQuery({106 inline_query_id: request.id,107 results: results,108 cache_time: 0,109 next_offset: nextOffset110 });111 return;112 } else if (searchTerm.match(/^\d{3}$/)) {113 // Search by error code114 let error = getErrorByCode(parseInt(searchTerm));115 if (error) {116 let text = createErrorText(error);117 118 results.push({119 type: "article",120 id: `error_${error.code}`,121 title: `${error.code}`,122 description: error.description.substring(0, 60),123 input_message_content: {124 message_text: text,125 parse_mode: "HTML"126 },127 reply_markup: createReplyMarkup(searchTerm)128 });129 } else {130 results.push({131 type: "article",132 id: "error_not_found",133 title: "Error Code Not Found",134 description: `Code ${searchTerm} not recognized`,135 input_message_content: {136 message_text: `Error code <code>${searchTerm}</code> not found in Telegram Bot API.\n\nUse "error:" to see all available error codes.`,137 parse_mode: "HTML"138 }139 });140 }141 } else if (searchTerm.match(/^[a-z]$/i)) {142 // Search by letter143 let errors = getErrorsByLetter(searchTerm);144 let paginatedErrors = errors.slice(offset, offset + LIMIT);145 146 if (paginatedErrors.length > 0) {147 paginatedErrors.forEach((error, idx) => {148 let text = createErrorText(error);149 150 results.push({151 type: "article",152 id: `error_${error.code}_${idx}`,153 title: `${error.code}`,154 description: error.description.substring(0, 60),155 input_message_content: {156 message_text: text,157 parse_mode: "HTML"158 },159 reply_markup: createReplyMarkup(searchTerm)160 });161 });162 163 let nextOffset = offset + LIMIT < errors.length ? offset + LIMIT : "";164 165 Api.answerInlineQuery({166 inline_query_id: request.id,167 results: results,168 cache_time: 86400,169 next_offset: nextOffset170 });171 return;172 } else {173 results.push({174 type: "article",175 id: "no_errors_letter",176 title: "No Errors Found",177 description: `No errors starting with ${searchTerm}`,178 input_message_content: {179 message_text: `No Telegram Bot API errors found starting with "${searchTerm}".\n\nUse "error:" to see all available error codes.`,180 parse_mode: "HTML"181 }182 });183 }184 } else {185 // Search by keyword186 let foundErrors = searchErrors(searchTerm);187 let paginatedErrors = foundErrors.slice(offset, offset + LIMIT);188 189 if (paginatedErrors.length > 0) {190 paginatedErrors.forEach((error, idx) => {191 let text = createErrorText(error);192 193 results.push({194 type: "article",195 id: `error_${error.code}_${idx}`,196 title: `${error.code}`,197 description: error.description.substring(0, 60),198 input_message_content: {199 message_text: text,200 parse_mode: "HTML"201 },202 reply_markup: createReplyMarkup(searchTerm)203 });204 });205 206 let nextOffset = offset + LIMIT < foundErrors.length ? offset + LIMIT : "";207 208 Api.answerInlineQuery({209 inline_query_id: request.id,210 results: results,211 cache_time: 0,212 next_offset: nextOffset213 });214 return;215 } else {216 results.push({217 type: "article",218 id: "no_errors",219 title: "No Errors Found",220 description: "Try different search terms",221 input_message_content: {222 message_text: `No Telegram Bot API errors found for: <code>${searchTerm}</code>\n\nTry:\n• Error codes: 400, 401, 403, etc.\n• Error names: BAD_REQUEST, UNAUTHORIZED\n• Or just "error:" for all errors`,223 parse_mode: "HTML"224 }225 });226 }227 }228 229 Api.answerInlineQuery({230 inline_query_id: request.id,231 results: results,232 cache_time: 86400233 });234 return;235 }236 let customText = "";237 let textMatch = query.match(/@([^\n]+)\n(.+)/s);238 if (textMatch) {239 searchQ = textMatch[1].trim();240 customText = textMatch[2].trim();241 }242 243 if (isTypeSearch) {244 let parts = query.substring(1).split(" ");245 typeFilter = parts[0].toLowerCase();246 searchQ = parts.slice(1).join(" ").trim();247 } else if (isProp) {248 let p = query.split(".");249 methodName = p[0].trim().toLowerCase();250 let fieldsPart = (p[1] || "").trim().toLowerCase();251 fieldNames = fieldsPart.split(",").map(f => f.trim()).filter(f => f);252 searchQ = methodName;253 }254 255 let apiUrl = "https://docs-bot-ycm4.onrender.com/api/search?format=html&limit=100&q=" + encodeURIComponent(searchQ);256 if (isTypeSearch && typeFilter) {257 apiUrl += "&type=" + encodeURIComponent(typeFilter);258 }259 260 let res = await HTTP.get({261 url: apiUrl262 });263 let data = res.data;264 265 function sanitize(t) {266 if (!t) return "";267 268 t = t.replace(/^\n+/, "");269 270 const allowed = [271 "b", "strong", "i", "em", "u", "s", "strike",272 "del", "code", "pre", "a", "tg-spoiler"273 ];274 275 t = t276 // Convert <br>, <br/>, <br /> to newline277 .replace(/<br\s*\/?>/gi, "\n")278 279 // Remove HTML comments280 .replace(/<!--[\s\S]*?-->/g, "")281 282 // Allow only specific tags283 .replace(/<(\/?)([A-Za-z0-9_-]+)([^>]*)>/g, (m, s, tg, at) =>284 allowed.includes(tg.toLowerCase()) ?285 `<${s}${tg}${at}>` :286 ""287 );288 289 return t;290 }291 292 function truncateHTML(text, maxLength) {293 if (text.length <= maxLength) return text;294 295 let result = "";296 let stack = [];297 let length = 0;298 let i = 0;299 300 while (i < text.length && length < maxLength - 50) {301 let char = text[i];302 303 if (char === '<') {304 let tagEnd = text.indexOf('>', i);305 if (tagEnd === -1) break;306 307 let tag = text.substring(i, tagEnd + 1);308 result += tag;309 310 if (tag.startsWith('</')) {311 stack.pop();312 } else if (!tag.endsWith('/>')) {313 let tagNameMatch = tag.match(/<(\w+)/);314 if (tagNameMatch) {315 stack.push(tagNameMatch[1]);316 }317 }318 319 i = tagEnd + 1;320 } else {321 result += char;322 length++;323 i++;324 }325 }326 327 while (stack.length > 0) {328 result += `</${stack.pop()}>`;329 }330 331 return result + "...\n\n<i>More details in full docs</i>";332 }333 334 function createNoResultsArticle(query) {335 return {336 type: "article",337 id: "none",338 title: "No Results Found",339 description: "Try another term",340 input_message_content: {341 message_text: `<b>No documentation found for:</b> <code>${query}</code>`,342 parse_mode: "HTML"343 },344 reply_markup: {345 inline_keyboard: [346 [{347 text: "Search Again",348 switch_inline_query_current_chat: ""349 }]350 ]351 }352 };353 }354 355 function createMethodNotFoundArticle(methodName) {356 return {357 type: "article",358 id: "nomethod",359 title: "Method Not Found",360 description: `No method named "${methodName}"`,361 input_message_content: {362 message_text: `Method "<b>${methodName}</b>" not found.`,363 parse_mode: "HTML"364 }365 };366 }367 368 function createTypeSuggestions() {369 let types = [{370 id: "object",371 name: "Object",372 count: 304373 },374 {375 id: "method",376 name: "Method",377 count: 158378 },379 {380 id: "webapp",381 name: "Webapp",382 count: 66383 },384 {385 id: "feature",386 name: "Feature",387 count: 56388 }389 ];390 391 let text = `<b>Available Types:</b>\n\n`;392 types.forEach(t => {393 text += `• <b>${t.name}</b> (${t.count} items)\n Use: <code>!${t.id} search_term</code>\n\n`;394 });395 text += `Examples:\n<code>!method sendMessage</code>\n<code>!object User</code>\n<code>!webapp init</code>`;396 397 return {398 type: "article",399 id: "types_help",400 title: "Available Types",401 description: "Object, Method, Webapp, Feature",402 input_message_content: {403 message_text: text,404 parse_mode: "HTML",405 link_preview_options: {406 is_disabled: true407 }408 },409 reply_markup: {410 inline_keyboard: [411 [{412 text: "Search Again",413 switch_inline_query_current_chat: ""414 }]415 ]416 }417 };418 }419 420 function createInvalidTypeArticle(typeFilter) {421 return {422 type: "article",423 id: "invalid_type",424 title: "Invalid Type",425 description: `Type "${typeFilter}" not found`,426 input_message_content: {427 message_text: `Invalid type: <code>${typeFilter}</code>\n\nUse one of: object, method, webapp, feature\n\nExample: <code>!method sendMessage</code>`,428 parse_mode: "HTML"429 },430 reply_markup: {431 inline_keyboard: [432 [{433 text: "Show All Types",434 switch_inline_query_current_chat: "!"435 }]436 ]437 }438 };439 }440 441 function createAllFieldsArticle(method, fields, currentOffset) {442 const MAX_LENGTH = 3500;443 let paginatedFields = fields.slice(currentOffset, currentOffset + 15);444 let baseText = `<b>${method.name}</b>\n\n`;445 baseText += `<b>Fields (${fields.length} total, showing ${paginatedFields.length}):</b>\n`;446 447 paginatedFields.forEach(f => {448 let desc = sanitize(f.description_html || f.description || "No description available");449 let req = f.required ? "required" : "optional";450 let type = f.type || "";451 let tag = (type === "method") ? req : type;452 baseText += `• <b>${f.name}</b> <i>(${sanitize(tag)})</i> - ${desc}\n`;453 });454 455 let nextOffset = currentOffset + 15 < fields.length ? currentOffset + 15 : "";456 457 if (nextOffset) {458 baseText += `\n<i>Use next page to see more fields...</i>`;459 }460 461 return {462 type: "article",463 id: method.id + "_fields_" + currentOffset,464 title: `${method.name}.* (page ${Math.floor(currentOffset/15) + 1})`,465 description: `Fields ${currentOffset + 1}-${currentOffset + paginatedFields.length} of ${fields.length}`,466 input_message_content: {467 message_text: baseText,468 parse_mode: "HTML",469 link_preview_options: {470 is_disabled: true471 }472 },473 reply_markup: {474 inline_keyboard: [475 nextOffset ? [{476 text: "Next Page",477 switch_inline_query_current_chat: `${method.name}.*`478 }] : [],479 [{480 text: "Full Docs",481 url: method.reference482 }],483 [{484 text: "Search Again",485 switch_inline_query_current_chat: ""486 }]487 ].filter(arr => arr.length > 0)488 }489 };490 }491 492 function createMultipleFieldsArticle(method, fields, requestedFields) {493 let baseText = `<b>${method.name} : </b>${sanitize(method.description)}\n`;494 baseText += `<b>Requested Fields (${requestedFields.length}):</b>\n`;495 496 requestedFields.forEach(fieldName => {497 let field = fields.find(f => f.name.toLowerCase() === fieldName);498 if (field) {499 let desc = sanitize(field.description_html || field.description || "No description available");500 let type = sanitize((field.type || "").replace(/<\/?td>/g, ""));501 let req = field.required ? "True" : "False";502 503 baseText += `\n<b>${field.name}</b>: `;504 baseText += `${desc}\n`;505 baseText += `<b>Type:</b> <i>${type}</i>\n`;506 baseText += `<b>Required:</b> <code>${req}</code>\n`;507 }508 });509 510 if (baseText.length > 4000) {511 baseText = truncateHTML(baseText, 4000);512 }513 514 return {515 type: "article",516 id: method.id + "_multi_" + requestedFields.join("_"),517 title: `${method.name}.${requestedFields.join(",")}`,518 description: `View ${requestedFields.length} specific fields`,519 input_message_content: {520 message_text: baseText,521 parse_mode: "HTML",522 link_preview_options: {523 is_disabled: true524 }525 },526 reply_markup: {527 inline_keyboard: [528 [{529 text: "Full Docs",530 url: method.reference531 }],532 [{533 text: "Search Again",534 switch_inline_query_current_chat: ""535 }]536 ]537 }538 };539 }540 541 if (isTypeSearch) {542 if (!typeFilter) {543 results.push(createTypeSuggestions());544 Api.answerInlineQuery({545 inline_query_id: request.id,546 results,547 cache_time: 3600548 });549 return;550 }551 552 let validTypes = ["object", "method", "webapp", "feature"];553 if (!validTypes.includes(typeFilter)) {554 results.push(createInvalidTypeArticle(typeFilter));555 Api.answerInlineQuery({556 inline_query_id: request.id,557 results,558 cache_time: 3600559 });560 return;561 }562 563 if (!searchQ) {564 results.push({565 type: "article",566 id: "no_search_term",567 title: "Enter Search Term",568 description: `Search in ${typeFilter} type`,569 input_message_content: {570 message_text: `Enter a search term after the type.\n\nExample: <code>!${typeFilter} search_term</code>`,571 parse_mode: "HTML"572 },573 reply_markup: {574 inline_keyboard: [575 [{576 text: "Search Again",577 switch_inline_query_current_chat: ""578 }]579 ]580 }581 });582 Api.answerInlineQuery({583 inline_query_id: request.id,584 results,585 cache_time: 0586 });587 return;588 }589 }590 591 if (isProp) {592 if (!data.results || data.results.length === 0) {593 Api.answerInlineQuery({594 inline_query_id: request.id,595 results: [createMethodNotFoundArticle(methodName)],596 cache_time: 3600597 });598 return;599 }600 601 let m = data.results.find(x => x.name.toLowerCase() === methodName);602 if (!m) {603 Api.answerInlineQuery({604 inline_query_id: request.id,605 results: [createMethodNotFoundArticle(methodName)],606 cache_time: 3600607 });608 return;609 }610 611 if (fieldNames.length === 1 && fieldNames[0] === "*") {612 if (!Array.isArray(m.fields) || m.fields.length === 0) {613 Api.answerInlineQuery({614 inline_query_id: request.id,615 results: [{616 type: "article",617 id: "nofields",618 title: "No Fields",619 description: "This method has no fields.",620 input_message_content: {621 message_text: `Method "<b>${m.name}</b>" has no fields.`,622 parse_mode: "HTML"623 }624 }],625 cache_time: 3600626 });627 return;628 }629 630 results.push(createAllFieldsArticle(m, m.fields, offset));631 Api.answerInlineQuery({632 inline_query_id: request.id,633 results,634 cache_time: 3600,635 next_offset: offset + 15 < m.fields.length ? offset + 15 : ""636 });637 return;638 }639 640 if (!Array.isArray(m.fields) || m.fields.length === 0) {641 Api.answerInlineQuery({642 inline_query_id: request.id,643 results: [{644 type: "article",645 id: "nofields",646 title: "No Fields",647 description: "This method has no fields.",648 input_message_content: {649 message_text: `Method "<b>${m.name}</b>" has no fields.`,650 parse_mode: "HTML"651 }652 }],653 cache_time: 3600654 });655 return;656 }657 658 if (fieldNames.length === 0) {659 let fieldList = m.fields.slice(offset, offset + LIMIT);660 fieldList.forEach(f => {661 let desc = sanitize(f.description_html || f.clean_desc || f.description || "");662 let descMain = sanitize(f.clean_desc || f.description || "");663 let type = sanitize((f.type || "").replace(/<\/?td>/g, ""));664 let req = f.required ? "True" : "False";665 let text = `<b>${m.name}.${f.name} :</b> ${desc}\n\n<b>Type:</b> <i>${type}</i>\n<b>Required:</b> <code>${req}</code>`;666 667 results.push({668 type: "article",669 id: m.id + "_" + f.name + "_" + offset,670 title: `${m.name}.${f.name}`,671 description: descMain.substring(0, 120),672 input_message_content: {673 message_text: text,674 parse_mode: "HTML",675 link_preview_options: {676 is_disabled: true677 }678 },679 reply_markup: {680 inline_keyboard: [681 [{682 text: "Docs",683 url: m.reference684 }],685 [{686 text: "Search Again",687 switch_inline_query_current_chat: ""688 }]689 ]690 }691 });692 });693 694 let nextOffset = offset + LIMIT < m.fields.length ? offset + LIMIT : "";695 696 Api.answerInlineQuery({697 inline_query_id: request.id,698 results,699 cache_time: 3600,700 next_offset: nextOffset701 });702 return;703 } else if (fieldNames.length === 1) {704 let fieldName = fieldNames[0];705 let fieldList = m.fields.filter(f => f.name.toLowerCase().startsWith(fieldName));706 let paginatedFields = fieldList.slice(offset, offset + LIMIT);707 708 if (paginatedFields.length === 0) {709 Api.answerInlineQuery({710 inline_query_id: request.id,711 results: [{712 type: "article",713 id: "nofieldmatch",714 title: "Field Not Found",715 description: `No field "${fieldName}" in ${m.name}`,716 input_message_content: {717 message_text: `Field "<b>${fieldName}</b>" not found in <b>${m.name}</b>.`,718 parse_mode: "HTML"719 }720 }],721 cache_time: 3600722 });723 return;724 }725 726 paginatedFields.forEach(f => {727 let desc = sanitize(f.description_html || f.clean_desc || f.description || "");728 let descMain = sanitize(f.clean_desc || f.description || "");729 let type = sanitize((f.type || "").replace(/<\/?td>/g, ""));730 let req = f.required ? "True" : "False";731 let text = `<b>${m.name}.${f.name} : </b>${desc}\n\n<b>Type:</b> <i>${type}</i>\n<b>Required:</b> <code>${req}</code>`;732 733 results.push({734 type: "article",735 id: m.id + "_" + f.name + "_" + offset,736 title: `${m.name}.${f.name}`,737 description: descMain.substring(0, 120),738 input_message_content: {739 message_text: text,740 parse_mode: "HTML",741 link_preview_options: {742 is_disabled: true743 }744 },745 reply_markup: {746 inline_keyboard: [747 [{748 text: "Docs",749 url: m.reference750 }],751 [{752 text: "Search Again",753 switch_inline_query_current_chat: ""754 }]755 ]756 }757 });758 });759 760 let nextOffset = offset + LIMIT < fieldList.length ? offset + LIMIT : "";761 762 Api.answerInlineQuery({763 inline_query_id: request.id,764 results,765 cache_time: 3600,766 next_offset: nextOffset767 });768 return;769 } else {770 let foundFields = [];771 let notFoundFields = [];772 773 fieldNames.forEach(fieldName => {774 let field = m.fields.find(f => f.name.toLowerCase() === fieldName);775 if (field) {776 foundFields.push(field);777 } else {778 notFoundFields.push(fieldName);779 }780 });781 782 if (foundFields.length === 0) {783 Api.answerInlineQuery({784 inline_query_id: request.id,785 results: [{786 type: "article",787 id: "nofields_multi",788 title: "Fields Not Found",789 description: `None of the fields found in ${m.name}`,790 input_message_content: {791 message_text: `None of these fields found in <b>${m.name}</b>: <code>${fieldNames.join(", ")}</code>`,792 parse_mode: "HTML"793 }794 }],795 cache_time: 3600796 });797 return;798 }799 800 if (notFoundFields.length > 0) {801 results.push({802 type: "article",803 id: "partial_multi",804 title: "Some Fields Not Found",805 description: `${foundFields.length} of ${fieldNames.length} fields found`,806 input_message_content: {807 message_text: `Found ${foundFields.length} of ${fieldNames.length} fields in <b>${m.name}</b>\n\nFound: <code>${foundFields.map(f => f.name).join(", ")}</code>\nNot found: <code>${notFoundFields.join(", ")}</code>`,808 parse_mode: "HTML"809 },810 reply_markup: {811 inline_keyboard: [812 [{813 text: "View Found Fields",814 switch_inline_query_current_chat: `${m.name}.${foundFields.map(f => f.name).join(",")}`815 }],816 [{817 text: "Search Again",818 switch_inline_query_current_chat: ""819 }]820 ]821 }822 });823 }824 825 results.push(createMultipleFieldsArticle(m, m.fields, foundFields.map(f => f.name)));826 }827 828 Api.answerInlineQuery({829 inline_query_id: request.id,830 results,831 cache_time: 3600832 });833 return;834 }835 836 if (!data.results || data.results.length === 0) {837 results.push(createNoResultsArticle(query));838 Api.answerInlineQuery({839 inline_query_id: request.id,840 results,841 cache_time: 3600842 });843 return;844 }845 846 let paginatedResults = data.results.slice(offset, offset + LIMIT);847 848 paginatedResults.forEach(item => {849 let n = sanitize(item.name);850 let d = sanitize(item.description || item.clean_desc || "");851 let msg = `<b>${n} :</b> ${d}`;852 853 if (customText) {854 msg += `\n\n${customText}`;855 }856 857 if (item.notes && item.notes.length > 0) {858 msg += `\n<b>Notes:</b>\n`;859 item.notes.forEach(nt => {860 msg += `• ${sanitize(nt.content)}\n`;861 });862 }863 864 if (msg.length > 4000) {865 msg = truncateHTML(msg, 4000);866 }867 868 results.push({869 type: "article",870 id: String(item.id) + "_" + offset,871 title: n,872 description: sanitize(item.clean_desc || item.description || ""),873 input_message_content: {874 message_text: msg,875 parse_mode: "HTML",876 link_preview_options: {877 is_disabled: true878 }879 },880 reply_markup: {881 inline_keyboard: [882 [{883 text: "Docs",884 url: item.reference885 }],886 [{887 text: "Search Again",888 switch_inline_query_current_chat: ""889 }]890 ]891 }892 });893 });894 895 let nextOffset = offset + LIMIT < data.results.length ? offset + LIMIT : "";896 897 Api.answerInlineQuery({898 inline_query_id: request.id,899 results,900 cache_time: 3600,901 next_offset: nextOffset902 });903 return;904}905 906Api.answerInlineQuery({907 inline_query_id: request.id,908 results: [],909 cache_time: 0,910 is_personal: true,911 switch_pm_text: "Enter a search term",912 switch_pm_parameter: "start"913});