You Jun
You Jun
Jun 10, 2018 · 8 min read

TeleCast v1.0: https://t.me/telecast_v1_0_bot
Go have a whirl, let me know if you find any bugs, find me on Telegram: @tsangyoujun


1. Installing Node.js

  1. Windows: https://nodejs.org/dist/v6.6.0/node-v6.6.0-x64.msi
  2. Mac OS: https://nodejs.org/dist/v6.6.0/node-v6.6.0.pkg
  3. Linux: https://nodejs.org/en/download/package-manager/

I used Mac OS to code and test, and Windows to host (had a server just lying around). Linux works just fine too.

2. Install other useful Node.js libraries

  1. npm install -save node-telegram-bot-api
  2. npm install got@7.1.0
  3. npm install -S moment
  4. npm install forever -g
  5. npm install forever-monitor (https://stackoverflow.com/questions/20445599/auto-start-node-js-server-on-boot )

Do remember that you may need other libraries depending on whatever you’re attempting to acheive, Google them.

3. Get an api-token from the Telegram BotFather

BotFather: https://t.me/BotFather

Type “/newbot”, then give it a “name”, a “bot name”, then grab the api-key highlighted in red. You can also edit your bot’s picture, description, about text, using BotFather. Use “/mybots” for that.

4. Coding the Bot Program

const BotToken = “619081104:AAH47fQ1PNufjL1F-3_mFbWfedbLAUy8gdE”;

Simply store that red-coloured api-key into a const.

var TelegramBot = require(‘node-telegram-bot-api’),
telegram = new TelegramBot(BotToken, {polling: true});

Here’s where you tell the bot to listen to Telegram, via that api-key, for events, such as when a message is typed by a user. If you don’t do this, your bot will be “dead”, it won’t respond to anything from Telegram.

var moment = require(‘moment’); //it's nice to have clock sometimes
var got = require(‘got’);
//i use this for web requests

Add in whatever extra libraries you need here…

function telegram_user() {
this.telegram_id = 0;
this.telegram_username = null;
this.telegram_firstname = null;
this.telegram_lastname = null;
this.some_array = [];
this.some_other_array = [];
//yes you can store arrays in objects
this.store_some_message = null;
}

This object will store the details (state) about your user, so the bot can provide a customised experience for each user, every time a message is received.

var user_array = [];
user_array.push(new telegram_user());

Stick all user-objects into an array, so you can fetch them easily. We have to do it this way, as the bot runs as a single thread for all users. If you don’t store the state of each user, then actions from one user will bleed into other users!

I like to push a blank user into the array[0], so my first user already starts on array[1]. Yes I’m a little nuts about the numbers, but heck its convenient.

function auth_usr(telegram_id) {
for(var i = 0; i < user_array.length; i++)
{
if (user_array[i].telegram_id === telegram_id) {
return i;
//return the user's position in the user_array
}
}
return 0;
//otherwise just return 0;
}

When the Telegram webhook sends our bot an event, the bot is essentially in a new “cycle” or loop. So before it does anything, it has to figure out who sent it the message “message.from.id” = “telegram_id”, and then recall that user’s “state object” from the “global user array”.

//when bot receives a text message event, do something
telegram.on("text", (message) => {
//create a new user state object, if this user is new
if(auth_usr(message.from.id) === 0)
{
user_array.push(new telegram_user());
user_array[user_array.length-1].telegram_id = message.from.id;
user_array[user_array.length-1].telegram_username = message.from.username;
user_array[user_array.length-1].telegram_firstname = message.from.first_name;
user_array[user_array.length-1].telegram_lastname = message.from.last_name;
//send a welcome message to the user PRIVATELY, so we don't pollute any group chats with spam (message.from.id)
telegram.sendMessage(message.from.id, "Welcome Message", {parse_mode: "Markdown"});
return;
}
//some more of your program logic
if(some_user_state or some_user_message)
{
//do something
return;
}
}

This is the main program body, where your bot will act on incoming message events. The <if — return> statements are basically the little tumblers in the lock, that respond to changes in the “user state object”, and return different answers to the user’s messages.

I found this structure to be the simplest to program, and the quickest to maintain. Yes you can always do if{ } else if{ } else{ }, but what’s the point, when you have a return;

5. Some code for you Sir, or Ma’am?

To ease your transition into the often confusing (and poorly documented) world of Telegram Bots, here’s a boiler-plate code template to get you started.

//required packages
//windows: https://nodejs.org/dist/v6.6.0/node-v6.6.0-x64.msi
//macos: https://nodejs.org/dist/v6.6.0/node-v6.6.0.pkg
//npm init
//npm install --save node-telegram-bot-api
//npm install got@7.1.0
//npm install -S moment
//npm install forever -g
//npm install forever-monitor
//https://stackoverflow.com/questions/20445599/auto-start-node-js-server-on-boot
//how to control how the bot replies to users
//send to message.from.id to restrict messaging between the user and bot only
//send to message.chat.id to allow the bot to respond to any user via any group or channel
//initialise telegram bot
const BotToken = "YOUR_API_KEY";
var TelegramBot = require('node-telegram-bot-api'),
telegram = new TelegramBot(BotToken, { polling: true });
//declare user object
function telegram_user() {
this.telegram_id = 0; //integer for storing the id
this.telegram_username = null; //null for storing some string
this.telegram_firstname = null;
this.telegram_lastname = null;
this.some_state = 0;
this.some_other_state = 0;
this.some_array = [];
this.some_other_array = []; //yes you can store arrays in objects
this.store_some_message = null;
}
//initialise global user array
var user_array = [];
user_array.push(new telegram_user()); //i just like to fill the [0] with a blank user
//scan array for user
function auth_usr(telegram_id) {
for(var i = 0; i < user_array.length; i++)
{
if (user_array[i].tele_id === telegram_id) {
return i;
}
}
return 0;
}
//declare bot logic, on text event
telegram.on("text", (message) => {
//instantiate new user state object
if(auth_usr(message.from.id) === 0)
{
user_array.push(new telegram_user());
user_array[user_array.length-1].telegram_id = message.from.id;
user_array[user_array.length-1].telegram_username = message.from.username;
user_array[user_array.length-1].telegram_firstname = message.from.first_name;
user_array[user_array.length-1].telegram_lastname = message.from.last_name;
telegram.sendMessage(message.from.id, "Welcome Message!", {parse_mode: "Markdown"});
return;
}
//listen for a bot command
if(message.text.toLowerCase().indexOf("/some_command") === 0)
{
//then change some state variable
user_array[auth_usr(message.from.id)].some_state = 1;
//do some other weird stuff
return;
}
//listen for some other bot command
if(message.text.toLowerCase().indexOf("/some_other_command") === 0)
{
//then reply to the user with a message
telegram.sendMessage(message.from.id, "you sent me some_other_command", {parse_mode: "Markdown"});
//do some other weird stuff
return;
}
//listen for a command with some text at the end of it
if(message.text.toLowerCase().indexOf("/text_input") === 0)
{
//strip the command from the string, to get the user_text
var user_text = message.text.replace("/text_input ", "");
if(user_text === "/text_input")
{
//respond with "you didn't send me any text"
telegram.sendMessage(message.from.id, "you didn't send me any text", {parse_mode: "Markdown"});
return;
}
else
{
//respond with the text the user sent, user_text
telegram.sendMessage(message.from.id, "you sent: " + user_text, {parse_mode: "Markdown"});
return;
}
}
//return a message to the user when some_state has been changed
if(user_array[auth_usr(message.from.id)].some_state === 1)
{
//send this message privately
telegram.sendMessage(message.from.id, "some_state has been changed to 1!", {parse_mode: "Markdown"});

//or send it publicly, if user is interacting with this bot from a group / supergroup
telegram.sendMessage(message.chat.id, "some_state has been changed to 1!", {parse_mode: "Markdown"});
return;
}
});//some other event handlers for you to consider/*
//respond when user sends anything, not very safe, as you can imagine
telegram.on("message", (message) => {
//do something here, message is the variable that contains the JSON, say message.text, or message.photo, etc.
}
//receive image content
telegram.on("photo", (message) => {
telegram.sendMessage(message.from.id, "*Photograph Received*\nYou sent a photo to me.", {parse_mode: "Markdown"});
});
//receive audio content
telegram.on("audio", (message) => {
telegram.sendMessage(message.from.id, "*Audio Received*\nYou sent audio to me.", {parse_mode: "Markdown"});
});
//receive document content
telegram.on("document", (message) => {
telegram.sendMessage(message.from.id, "*Document Received*\nYou sent a document to me.", {parse_mode: "Markdown"});
});
//receive sticker content
telegram.on("sticker", (message) => {
telegram.sendMessage(message.from.id, "*Sticker Received*\nYou sent a sticker to me.", {parse_mode: "Markdown"});
});
//receive video content
telegram.on("video", (message) => {
telegram.sendMessage(message.from.id, "*Video Received*\nYou sent a video to me.", {parse_mode: "Markdown"});
});
//receive voice content
telegram.on("voice", (message) => {
telegram.sendMessage(message.from.id, "*Voice Received*\nYou sent a voice to me.", {parse_mode: "Markdown"});
});
//receive contact content
telegram.on("contact", (message) => {
telegram.sendMessage(message.from.id, "*Contact Received*\nYou sent a contact to me.", {parse_mode: "Markdown"});
});
//receive location content
telegram.on("location", (message) => {
telegram.sendMessage(message.from.id, "*Location Received*\nYou sent a location to me.", {parse_mode: "Markdown"});
});
*/

Useful Tips & Tricks

1. Sending Messages to the User Privately or Publicly

Now here’s what the basic tutorials don’t teach you. Well it’s pretty obvious if you think about it, but it took me a while to realise, because the Telegram API is a little dense, and you don’t know enough unless you mess with it.

If you wish to send a message PRIVATELY to a user, even if that user interacts with your bot PUBLICLY in a group or supergroup, address the return-message to his or her message.from.id. In this way, the message from your bot is not sent into the group or supergroup, but directly to the user from your bot!

Private Message:

telegram.sendMessage(message.from.id, “Message Content”, {parse_mode: “Markdown”});

If you wish to send a message PUBLICLY or PRIVATELY, meaning you don’t care where your user is messaging your bot from…be it directly from your bot’s page, or from a group or supergroup, use message.chat.id. This means your bot will reply to whichever chat page your user is on right now. Be careful! If you’re sending sensitive user-specific information, you don’t want it to be displayed in a group chat!

Public Message:

telegram.SendMessage(message.chat.id, “Message Content”, {parse_mode: “Markdown”});

“{parse_mode: “Markdown”}” is just so that I can use fancy markdown for the text, like *BOLD* is BOLD. And so on, fancy text stuff, no big deal.

2. Deconstructing the JSON

One of the toughest parts of being a beginner at Telegram API, was figuring out what the heck was in the JSON that Telegram was sending to me…thankfully, there’s a very useful bot for that.

Telegram Bot Raw: https://t.me/RawDataBot

3. URL-Safe Web Requests

If you are using the got() library like me for doing web requests, for whatever reason, ensure that the messages you pass into the Telegram API are URL-safe. Telegram likes to use the & _ @ characters for some weird stuff, so they have to be properly escaped. There’s code for this too…

var URL_SAFE_MESSAGE = require('querystring').escape(UNSAFE_MESSAGE);

4. Primitive Sleep(); Function

For some weird reason, node.js doesn’t have a sleep(); function like other languages, yes this really annoys me. Thankfully, you can just use a loop to do this, an occupy some CPU-time. You could probably also call time(); or moment();, and get some time, but I couldn’t be bothered. Lame I know.

//primitive sleep function
function sleep(num) {
var k = 0;
for (i = 0; i < num; i++)
{
k++;
}
}

5. Run your bot forever, with Forever

If you run your bot with the default,

node my_bot.js

It won’t necessarily restart if something goes wrong…you’ll need to install another node.js library, aptly named “Forever”, so your bot will run “Forever”, or until the datacenter burns down to the ground.

npm install forever -g
npm install forever-monitor

Then just do this to start,

forever start my_bot.js

Do this to stop,

forever stop my_bot.js

Do this to list all running forever processes,

forever list

6. Telegram Node.js API Reference

And of course, you wouldn’t be able to get anywhere without this!

Telegram Node.js API Reference: https://github.com/yagop/node-telegram-bot-api

Stackoverflow is your friend: https://stackoverflow.com

tsangyoujun

Email: tsangyoujun@gmail.com, Twitter: @tsangyoujun, Telegram: https://t.me/cryptocircle_analysis

You Jun

Written by

You Jun

tsangyoujun

Email: tsangyoujun@gmail.com, Twitter: @tsangyoujun, Telegram: https://t.me/cryptocircle_analysis

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade