telegram-bot-api-c
v19.5.0
Published
Lightweight, Simple, Fastest module for Telegram Bot without Dependencies
Downloads
66
Maintainers
Readme
npm -g install telegram-bot-api-c
git clone https://github.com/Daeren/telegram-bot-api-c.git
require("telegram-bot-api-c").call("TK", "sendMessage", [0, "+"])
require("telegram-bot-api-c")("TK").api.sendMessage({chat_id: 0, text: "+"})
require("telegram-bot-api-c")("TK").polling(bot => bot.answer().html("+").send())
> tg-bot --token TK --method sendMessage --chat_id 0 --text "+"
Telegram Bot API, Bot API 2.x, Bot API 3.5
- Proxy: +
- Array and Map as a data source (.call, .callJson, .api[method]): +
- Analytics: tgb-pl-botanio
- Added: tgBot.api[sendMethod] => error.retryAfter
- Added: sendMediaGroup (doesn't support "attach://")
- All methods in the Bot API are case-insensitive (method: .call, .callJson)
- message: buffer, stream, string
- location|venue|contact: buffer, string
- photo|audio|voice|video|document|sticker|video_note: buffer, stream, file_id, path, url
- certificate: buffer, stream, path, url
Goals:
- High stability;
- Low memory usage;
- Maximum performance;
- Flexibility.
Index
- Start
- Proxy
- Polling
- HTTP
- Virtual
- mServer
- Nginx + Node.js
- Response Builder
- Tg Upload
- Plugin
- Goto
- JS Generators
- Render
- Keyboard
- Download
- InlineQuery
- Send file as Buffer
- CLI
- Test
const rTgBot = require("telegram-bot-api-c");
const gBot = rTgBot(process.env.TELEGRAM_BOT_TOKEN),
gApi = gBot.api;
//----------------------------]>
gBot.promise(require("bluebird"));
//----------------------------]>
gApi
.sendMessage(["0", "Hi"])
.then(console.info, console.error);
gApi.sendMessage(["0", "Hi"], (e, data) => console.log(e || data));
// e - Error: request/JSON.parse/response.ok
// data - JSON: response.result or null
//-------]>
gBot.callJson("sendMessage", ["0", "Hi"], (e, data, res) => console.log(e || data));
// e - Error: request/JSON.parse
// data - JSON: response or null
// res - Class: http.IncomingMessage or null
//-------]>
gBot.call("sendMessage", ["0", "Hi"], (e, data, res) => console.log(e || data));
// e - Error: request
// data - Buffer: response or null
// res - Class: http.IncomingMessage or null
//------------]>
/*
e.code - gApi.sendMessage( ...
data.error_code - callJson("sendMessage" ...
rTgBot or gBot
gBot.ERR_INTERNAL_SERVER
gBot.ERR_NOT_FOUND
gBot.ERR_FORBIDDEN
gBot.ERR_MESSAGE_LIMITS
gBot.ERR_USED_WEBHOOK
gBot.ERR_INVALID_TOKEN
gBot.ERR_BAD_REQUEST
gBot.ERR_BAD_PROXY
gBot.ERR_FAILED_PARSE_DATA
*/
//----------------------------]>
gBot
.polling(onDefault)
.catch(onError)
.use(bot => "syncGotoMyMenu")
.use((bot, data, next) => next(new Error("never get")))
.use("/start", bot => { })
.on("/start", onCmdStart_1)
.on("/start", onCmdStart_2)
.on("/start", onCmdStart_3)
.on("enterChat", onEnterChat)
.on("text:syncGotoMyMenu", onText)
.on("photo document", onPhotoOrDoc)
.on("pinnedMessage", onPinnedMessage)
.on(/^id\s+(\d+)/i, onTextRegEx)
.on(/^(id)\s+(\d+)/i, "type id", onTextRegEx)
.on(/^(login)\s+(\w+)/i, ["type", "login"], onTextRegEx);
function onDefault(bot) { }
function onError(error) { }
function onCmdStart_1(bot, params, next) { next(); } // <-- Async
function onCmdStart_2(bot, params) { } // <-- Sync
function onCmdStart_3(bot, params) { } // <-- Sync | end
function onEnterChat(bot, member) { }
function onText(bot, text) { }
function onPhotoOrDoc(bot, data) { }
function onPinnedMessage(bot, message) { }
function onTextRegEx(bot, data) { }
//-----------]>
/*
bot | gBot -> Sugar -> CtxPerRequest
bot instanceof gBot.constructor | true
bot.command.type | common or private
/start [text] -> common
/start@bot [text] -> private
@bot /start [text] -> private
*/
Proxy
const gBot = rBot(process.env.TELEGRAM_BOT_TOKEN);
const gProxyStr = "127.0.0.1:1337", // <-- Only HTTPS
gProxyArr = ["127.0.0.1", "1337"],
gProxyObj = {
"host": "127.0.0.1",
"port": 1337
};
//------------------]>
function getMe(callback) { gBot.api.getMe(callback); }
//------------------]>
gBot.proxy(gProxyObj);
getMe(t => {
objBot.proxy(gProxyStr);
getMe(t => {
objBot.proxy(); // <-- Remove
getMe();
});
});
rBot.callJson({
"token": process.env.TELEGRAM_BOT_TOKEN,
"method": "getMe",
"proxy": gProxyArr
}, (e, d) => {});
rBot.callJson(process.env.TELEGRAM_BOT_TOKEN, "getMe", (e, d) => {}, gProxyObj);
Polling
const gBot = rBot(process.env.TELEGRAM_BOT_TOKEN);
const gOptions = {
"limit": 100,
"timeout": 0,
"interval": 2 // <-- Default / Sec.
};
//------------------]>
const gSrv = gBot
.polling(gOptions, onMsg)
.on("/stop", onCmdStop);
//------------------]>
function onMsg(bot) {
const msg = bot.isGroup && bot.isReply ? ">_>" : "Stop me: /stop";
bot.answer().isReply().text(msg).send();
}
function onCmdStop(bot, params) {
gSrv.stop();
bot.answer().text(JSON.stringify(params)).send();
}
HTTP
const rBot = require("telegram-bot-api-c");
//-----------------------------------------------------
const gSrvOptions = {
// For Self-signed certificate, you need to upload your public key certificate
// "selfSigned": "fullPath/stream/buffer", // <-- If you use Auto-Webhook
"certDir": "/www/site",
"key": "/3_site.xx.key",
"cert": "/2_site.xx.crt",
"ca": [
"/AddTrustExternalCARoot.crt",
"/COMODORSAAddTrustCA.crt",
"/COMODORSADomainValidationSecureServerCA.crt"
],
"host": "site.xx"
};
//------------------]>
const gBotFather = rBot();
const gMyBot = rBot(process.env.TG_BOT_TOKEN_MY),
gOtherBot = rBot(process.env.TG_BOT_TOKEN_OTHER);
const gSrv = gBotFather.http(gSrvOptions);
gSrv
.bot(gMyBot) // <-- Auto-Webhook: "/tg_bot_<sha256(token)>"
.on("/start", onCmdStart)
.on("/stop", onCmdStop);
gSrv
.bot(gOtherBot, "/urlOtherBot", onMsgOtherBot); // <-- Auto-Webhook
//------------------]>
function onMsgOtherBot(bot) { }
function onCmdStart(bot, params) { }
function onCmdStop(bot, params) { }
Virtual
const gBot = rBot(process.env.TELEGRAM_BOT_TOKEN);
const gSrv = gBot
.virtual(function(bot) {
bot.answer().text("Not found!").send();
})
.on("photo", console.log);
//----[Proxy: express]----}>
gBot
.api
.setWebhook({"url": "https://site.xx/dev-bot"})
.then(function(isOk) {
const rExpress = require("express"),
rBodyParser = require("body-parser");
rExpress()
.use(rBodyParser.json())
.post("/dev-bot", gSrv.middleware)
.listen(3000, "localhost");
});
//----[Stress Tests]----}>
gSrv.input(null, {
"update_id": 0,
"message": {
"message_id": 0,
"from": {
"id": 0,
"first_name": "D",
"username": ""
},
"chat": {
"id": 0,
"first_name": "D",
"username": "",
"type": "private"
},
"date": 0,
"text": "Hello"
}
});
mServer
const gBot = rBot(process.env.TELEGRAM_BOT_TOKEN);
gBot
.api
.setWebhook({"url": "https://site.xx/myBot"})
.then(function(isOk) {
if(!isOk) {
throw new Error("Oops...problem with the webhook...");
}
gBot.http(gSrvOptions, cbMsg);
});
NGINX + Node.js
const gBot = rBot();
const gSrvOptions = {
"ssl": false,
"autoWebhook": "site.xx:88", // <-- Default: (host + port); `false` - disable
"host": "localhost",
"port": 1490
};
gBot.http(gSrvOptions, onMsg);
//----[DEFAULT]----}>
gBot.http();
gBot.http(onMsg);
// host: localhost
// port: 1488
// autoWebhook: false
// ssl: false
Response Builder
objSrv
.use(function(bot) {
bot
.answer() // <-- Builder + Queue
.chatAction("typing") // <-- Element
.text("https://google.com", "markdown") // <-- Element
//.parseMode("markdown")
.disableWebPagePreview() // <-- Modifier (for the last element)
.keyboard([["X"], ["Y"]]) // <-- Modifier
.markdown("*text*") // <-- Element
.html("<a>text</a>")
.chatAction("upload_photo")
.photo("https://www.google.ru/images/logos/ps_logo2.png", "myCaption")
.caption("#2EASY") // <-- Modifier
.keyboard("old")
.keyboard("new", "selective") // <-- Uses: bot.mid (selective)
.location(69, 96)
.latitude(13)
.keyboard() // <-- Hide
.send() // <-- Uses: bot.cid
.then(console.log); // <-- Return: array | results
//------[ONE ELEMENT]------}>
const customKb = {
"keyboard": [["1"], ["2"], ["3"]],
"resize_keyboard": true
};
bot
.answer()
.text("Hi")
.keyboard(customKb)
.send((e, r) => console.log(e || r)); // <-- Return: hashTable | result
//------[RENDER]------}>
const template = "Hi, {name}!";
const buttons = [["{btnMenu}", "{btnOptions}"]];
const input = {
"name": "MiElPotato",
"btnMenu": "Menu +",
"btnOptions": "Options"
};
bot
.answer()
.text(template)
.keyboard(buttons, "resize")
.render(input) // <-- text + keyboard
.send();
bot
.answer()
.text("Msg: {0} + {1}")
.render(["H", "i"]) // <-- text
.keyboard([["X: {0}", "Y: {1}"]])
.send();
});
| Name | Args | |-------------------------|---------------------------------------------------------------------------------------------------------------| | | - | | html | text, disable_web_page_preview, disable_notification, reply_to_message_id, reply_markup | | markdown | text, disable_web_page_preview, disable_notification, reply_to_message_id, reply_markup | | | - | | text | text, parse_mode, disable_web_page_preview, disable_notification, reply_to_message_id, reply_markup | | photo | photo, caption, disable_notification, reply_to_message_id, reply_markup | | audio | audio, performer, title, duration, caption, disable_notification, reply_to_message_id, reply_markup | | document | document, caption, disable_notification, reply_to_message_id, reply_markup | | sticker | sticker, disable_notification, reply_to_message_id, reply_markup | | video | video, width, height, duration, caption, disable_notification, reply_to_message_id, reply_markup | | voice | voice, duration, caption, disable_notification, reply_to_message_id, reply_markup | | videoNote | videoNote, duration, length, disable_notification, reply_to_message_id, reply_markup | | location | latitude, longitude, disable_notification, reply_to_message_id, reply_markup | | venue | latitude, longitude, title, address, foursquare_id, disable_notification, reply_to_message_id, reply_markup | | contact | phone_number, first_name, last_name, disable_notification, reply_to_message_id, reply_markup | | chatAction | action | | game | game_short_name, disable_notification, reply_to_message_id, reply_markup | | invoice | title ... ... reply_markup | | | - | | inlineQuery | results, next_offset, is_personal, cache_time, switch_pm_text, switch_pm_parameter | | callbackQuery | text, show_alert | | shippingQuery | ok, shipping_options, error_message | | preCheckoutQuery | ok, error_message |
Tg Upload
gBot.enable("tgUrlUpload");
gBot
.polling()
.on("text", function(bot, url) {
bot.answer().photo(url).send();
});
/*
Added the option to specify an HTTP URL for a file in all methods where InputFile or file_id can be used (except voice messages).
Telegram will get the file from the specified URL and send it to the user.
Files must be smaller than 5 MB for photos and smaller than 20 MB for all other types of content.
*/
Plugin
gSrv
.use(function(bot, data, next) {
console.log("Async | Type: any");
if(data === "next") {
next();
}
})
.use("text", function(bot) {
console.log("F:Sync | Type: text");
bot.user = {};
})
.use(function(bot) {
bot.user.id = 1;
});
gSrv
.on("text", function(bot, data) {
bot.user.id;
});
Goto
gSrv
.use(function(bot, data, next) {
next(data === "room" ? "room.menu" : "");
})
.use(function(bot) {
console.log("If not the room");
// return "room.menu";
})
.on("text", function(bot, data) { })
.on("text:room.menu", function(bot, data) { });
JS Generators
gBot
.polling(function* (bot) {
const result = yield send(bot);
console.info(result);
yield error();
})
.catch(function* (error) {
console.error(error);
})
.use(function* (bot) {
yield auth("D", "13");
})
.use("text", function* (bot, data) {
yield save();
if(data === "key") {
return "eventYield";
}
})
.on("text:eventYield", function* (bot, data) {
console.log("eventYield:", data);
});
//----------------]>
function auth(login, password) {
return new Promise(x => setTimeout(x, 1000));
}
function send(bot) {
return bot.answer().text("Ok, let's go...").send();
}
Render
//-----[EJS]-----}>
gBot.engine(require("ejs"))
data = {"x": "H", "y": "i"};
bot.render("EJS | Text: <%= x %> + <%= y %>", data);
//-----[DEFAULT]-----}>
data = ["H", "i"];
bot.render("Array | Text: {0} + {1}", data);
data = {"x": "H", "y": "i"};
bot.render("Hashtable | Text: {x} + {y}", data);
Keyboard
const rBot = require("telegram-bot-api-c");
function onMsg(bot) {
const data = {};
data.chat_id = bot.cid;
data.text = "Hell Word!";
data.reply_markup = bot.keyboard(); // Or: bot.keyboard.hide()
data.reply_markup = bot.keyboard([["1", "2"], ["3"]]);
data.reply_markup = bot.keyboard.hOx();
data.reply_markup = bot.keyboard.inline.hOx();
bot.api.sendMessage(data);
}
rBot.keyboard.numpad(true); // <-- Once
rBot.keyboard.numpad(false, true); // <-- Selective
rBot.keyboard.inline.numpad();
//------------------------------
rBot.keyboard(buttons[, params])
rBot.keyboard.inline(inlButtons, isVertically)
/*
buttons: `string`, `array of array` or `false`
inlButtons: `string`, `array of array` or `object`
params: "resize once selective"
v - vertically; h - horizontally;
vOx, hOx, vPn, hPn, vLr, hLr, vGb, hGb
abcd, numpad, hide
Normal keyboard:
vOx(once, selective)
numpad(once, selective)
*/
| Name | Note | |-------------------|--------------------------------------| | | - | | _Ox | O / X | | _Pn | + / - | | _Ud | Upwards / Downwards arrow | | _Lr | Leftwards / Rightwards arrow | | _Gb | Like / Dislike | | | - | | abcd | ABCD | | numpad | 0-9 | | | - | | hide | |
Download
gBot.download("file_id", "dir"/*, callback*/);
gBot.download("file_id", "dir", "name.mp3"/*, callback*/);
gBot
.download("file_id")
.then(function(info) {
info.stream.pipe(require("fs").createWriteStream("./" + info.name));
});
gBot
.download("file_id", function(error, info) {
info.stream.pipe(require("fs").createWriteStream("./myFile"));
});
InlineQuery
https://core.telegram.org/bots/inline
gBot
.polling()
.on("inlineQuery", function(bot, data) {
const idx = Date.now().toString(32) + Math.random().toString(24);
const results = [
{
"type": "article",
"title": "Title #1",
"message_text": "Text...",
"thumb_url": "https://pp.vk.me/c627530/v627530230/2fce2/PF9loxF4ick.jpg"
},
{
"type": "article",
"title": "Title #2: " + data.query,
"message_text": "Text...yeah"
},
{
"type": "photo",
"photo_width": 128,
"photo_height": 128,
"photo_url": "https://pp.vk.me/c627530/v627530230/2fce2/PF9loxF4ick.jpg",
"thumb_url": "https://pp.vk.me/c627530/v627530230/2fce2/PF9loxF4ick.jpg"
}
]
.map((t, i) => { t.id = idx + i; return t; });
// results = {results};
bot
.answer()
.inlineQuery(results)
.send()
.then(console.info, console.error);
});
//------------]>
bot
.api
.answerInlineQuery({
"inline_query_id": 0,
"results": results
})
.then(console.info, console.error);
Send file as Buffer
const imgBuffer = require("fs").readFileSync(__dirname + "/MiElPotato.jpg");
//------------]>
objSrv
.use(function(bot, next) {
bot
.answer()
.photo(imgBuffer)
.filename("MiElPotato.jpg") // <-- It is important
.filename("/path/MiElPotato.jpg") // <-- Same as above
.send();
});
//------------]>
api.sendPhoto({
"chat_id": 0,
"photo": imgBuffer,
"filename": "MiElPotato.jpg" // <-- It is important
});
api.sendDocument({
"chat_id": 0,
"document": imgBuffer
});
CLI
| Key | Note | |-------------------|-------------------------------------------------------------------------------| | | - | | -j | insert white space into the output JSON string for readability purposes | | | - | | --token | high priority | | --method | high priority | | --proxy | "ip:port" |
// Environment variables: low priority
> set TELEGRAM_BOT_TOKEN=X
> set TELEGRAM_BOT_METHOD=X
> set TELEGRAM_BOT_PROXY=X
...
> tg-bot --token X --method sendMessage --key val -bool
> node telegram-bot-api-c --token X --method sendMessage --key val -bool
...
> tg-bot --token X --method sendMessage --chat_id 0 --text "Hi" -disable_web_page_preview
> tg-bot --token X --method sendMessage < "./examples/msg.json"
> tg-bot --token X --method sendPhoto --chat_id 0 --photo "/path/MiElPotato.jpg"
> tg-bot --token X --method sendPhoto --chat_id 0 --photo "https://www.google.ru/images/logos/ps_logo2.png"
...
> tg-bot
> {"token": "", "method": "sendMessage", "chat_id": 0, "text": "1"}
> <enter>
(result)
> {"chat_id": 0, "text": "2", "j": true, "proxy": "ip:port"}
> <enter>
(result)
Test
npm -g install mocha
npm install chai
set TELEGRAM_BOT_TOKEN=X
set TELEGRAM_CHAT_ID=X
set TELEGRAM_MSG_ID=X
cd <module>
npm test
Module
| Method | Arguments | Note | |-------------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------| | | - | | | keyboard | buttons[, params] | return: object; buttons: string/array; params: "resize once selective" | | parseCmd | text[, strict] | return: {type, name, text, cmd}; strict: maxLen32 + alphanum + underscore | | | - | | | call | token, method[, data][, callback(error, buffer, response)][, proxy][, tgUrlUpload] | | | call | options{token, method, proxy, tgUrlUpload}[, data][, callback] | | | callJson | token, method[, data][, callback(error, json, response)][, proxy][, tgUrlUpload] | | | callJson | options{token, method, proxy, tgUrlUpload}[, data][, callback] | |
Instance
| Attribute | Type | Note | |-------------------|----------------|--------------------------------------| | | - | | | api | object | See Telegram Bot API | | | - | | | keyboard | function | | | parseCmd | function | |
| Method | Arguments | Return | |-------------------|-----------------------------------------------------------------------|-----------------------------------| | | - | | | | - | | | enable | key | this | | disable | key | this | | enabled | key | true/false | | disabled | key | true/false | | | - | | | engine | instance | this | | promise | instance | this | | token | [token] | this or token | | proxy | [proxy] | this | | | - | | | call | method[, data][, callback(error, buffer, response)] | | | callJson | method[, data][, callback(error, json, response)] | | | | - | | | render | template, data | string | | download | fid[, dir][, name][, callback(error, info {id,size,file,stream})] | promise or undefined | | | - | | | http | [options][, callback(bot, cmd)] | object | | polling | [options][, callback(bot, cmd)] | object | | virtual | [callback(bot, cmd)] | object |
Methods: Response Builder
| Name | Args | Note | |---------------|---------------------------------------|-------------------------------------------------------------------| | | - | | | inlineQuery | (results) | | | callbackQuery | ([message]) | | | | - | | | render | (data) | | | keyboard | (buttons[, params]) | | | inlineKeyboard| (buttons[, isVertically]) | | | | - | | | isReply | ([flag]) | | | send | ([callback]) | | | | - | | | text | | | | photo | | Ext: jpg, jpeg, gif, tif, png, bmp | | audio | | Ext: mp3 | | document | | | | sticker | | Ext: webp [, jpg, jpeg, gif, tif, png, bmp] | | video | | Ext: mp4 | | voice | | Ext: ogg | | location | | | | venue | | | | contact | | | | chatAction | | | | game | | |
Methods: Server
| Name | Arguments | Return | |---------------|-----------------------------------------------|-------------------------------------------| | | - | | | | POLLING | | | | - | | | start | | this | | stop | | this | | | HTTP | | | | - | | | bot | bot[, path][, onMsg(json, request)] | new srvInstance | | | - | | | | VIRTUAL | | | | - | | | input | error, data | | | middleware | | | | | - | | | | ALL | | | | - | | | catch | callback(error) | this | | use | [type], [params], callback(bot[, data, next]) | this | | on | type[, params], callback(data, params[, next])| this | | off | [type][, callback] | this |
Fields: bot | srv.on('', bot => 0)
| Name | Type | Note | |-------------------|-----------------------|--------------------------------------------------------| | | - | | | isGroup | boolean | bot.isGroup = bot.message.chat.type === [super]group | | isReply | boolean | bot.isReply = !!bot.message.reply_to_message | | | - | | | cid | number | bot.cid = bot.message.chat.id | | mid | number | bot.mid = bot.message.message_id | | qid | string | bot.qid = bot.inlineQuery.id | | cqid | string | bot.cqid = bot.callbackQuery.id | | sid | string | bot.sid = bot.shipping_query.id | | pqid | string | bot.pqid = bot.pre_checkout_query.id | | | - | | | command | object | Incoming command | | | - | | | updateType | string | | | updateSubType | string | | | eventType | string | | | eventSubType | string | | | gotoState | string | | | | - | | | from | object | Persistent | | | - | | | message | object | Incoming message | | inlineQuery | object | Incoming inline query | | chosenInlineResult| object | The result of an inline query that was chosen | | callbackQuery | object | Incoming callback query | | | - | | | answer | function() | Response Builder; message; Uses: cid, mid | | answer | function() | Response Builder; inlineQuery; Uses: qid | | answer | function() | Response Builder; callbackQuery; Uses: cqid |
Events: use / on
| Name | Args | Note | |-------------------|---------------------------------------|-------------------------------------------| | | - | | | message | bot, message[, next] | | | editedMessage | bot, message[, next] | | | | - | | | channelPost | bot, post[, next] | | | editedChannelPost | bot, post[, next] | | | | - | | | inlineQuery | bot, data[, next] | | | chosenInlineResult| bot, data[, next] | | | callbackQuery | bot, data[, next] | | | | - | | | pinnedMessage | bot, message[, next] | | | | - | | | invoice | bot, data[, next] | | | successfulPayment | bot, data[, next] | | | | - | | | enterChat | bot, data[, next] | | | leftChat | bot, data[, next] | | | | - | | | chatTitle | bot, data[, next] | | | chatNewPhoto | bot, data[, next] | | | chatDeletePhoto | bot, data[, next] | | | | - | | | chatCreated | bot, data[, next] | | | superChatCreated | bot, data[, next] | | | channelChatCreated| bot, data[, next] | | | | - | | | migrateToChatId | bot, data[, next] | | | migrateFromChatId | bot, data[, next] | | | | - | | | text | bot, data[, next] | | | photo | bot, data[, next] | | | audio | bot, data[, next] | | | document | bot, data[, next] | | | sticker | bot, data[, next] | | | video | bot, data[, next] | | | voice | bot, data[, next] | | | videoNote | bot, data[, next] | | | location | bot, data[, next] | | | venue | bot, data[, next] | | | contact | bot, data[, next] | | | game | bot, data[, next] | | | | - | | | * | bot, data[, next] | | | /[name] | bot, params[, next] | CMD | | | - | | | (regexp) | bot, params[, next] | |
License
MIT