How to use Telegram Bot for Contact Form

I have deleted my Facebook account for 3 months. Now I use Telegram as the main contact point. When building this “small universe”, I find out that I can use Telegram API for sending messages. A light bulb goes off in my head with an idea, using Telegram Bot for the Contact Form.

How it works

This feature may be too overkill for a simple contact form, I do it for the sake of learning and creating things. Let’s jump into building progress.

Telegram Settings

Bot

Creating a Telegram Bot is very simple. Telegram has another bot handling that process. Its name is BotFather. All I have to do is type some commands.

Using /newbot command to create a new bot
Create a Bot with BotFather

After creating Bot, I receive an authentication token for using the Bots API. It looks like 992219941:AAEEoFKKPN1qJWYZkVoujOFcHysITXa8uAY. I’ll call <token> from now on.

Group

According to the API documentation, I need a chat_id of a group or a channel. The main reason is that a Bot cannot send direct messages to a User.

chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)

https://core.telegram.org/bots/api

When I have created a group, the first thing to do is give Administrator permission to the Bot.

Permission options
Set permission for the Bot

To find chat_id, I go to Group Type. A group is set to Private by default. It has a sharable link to join the group below, something like https://t.me/joinchat/<id>.

At first, I’m quite sure that the <id> here is chat_id, but when I put it into the API call, it fails immediately. After a lot of attempts, the only way to get the format @channelusername is to set Group Type to Public. So that I can have the group username as purpose. For me, chat_id will be @TheSmallUniverse.

Group Type configuration
Group Type in Public

Telegram API

Now I already have <token> and chat_id, the next step is to connect the API. Following the documentation, Send Message API is the only API that I need.

Terminal
curl --location --request POST 'https://api.telegram.org/bot<token>/sendMessage' \ --header 'Content-Type: application/json' \ --data-raw '{ "chat_id": "@TheSmallUniverse", "text": "Hello Darkness, my old friend!" }'

And here is the response I get when running the command. There is a different detail, chat_id has changed to an Integer -1001258275042.

{ "ok": true, "result": { "message_id": 5, "from": { "id": 992219941, "is_bot": true, "first_name": "The Small Bot", "username": "TheSmallBot" }, "chat": { "id": -1001258275042, "title": "The Small Universe", "username": "TheSmallUniverse", "type": "supergroup" }, "date": 1578890703, "text": "Hello Darkness, my old friend!" } }

Contact Form

The configuration for Telegram is done. I need an UI to apply that functionality, and nothing better than a Contact Form.

User Interface

A meaningful message needs to have sender name, email and message content. As Telegram supports message formatting (Markdown & HTML), I come up with this interface.

On the client-side, I use Formik – a form validation library. When submitting the form, it calls an internal API, which is a simple serverless function.

Serverless Function

I use Next.js, which supports API Routes out of the box. If I don’t use Next.js, I could do this functionality with Zeit Serverless Functions or Netlify Functions.

The function looks like this:

messages.js
export default async (req, res) => { const { name, message, email } = req.body if (!name || !message || !email) { res.status(400).json({ ok: false, status: 400, message: "Missing required fields", }) return } // https://core.telegram.org/bots/api#markdownv2-style const escapedEmail = email.replace(/_/g, "\\_") fetch(`https://api.telegram.org/bot${process.env.botToken}/sendMessage`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ chat_id: process.env.chatId, parse_mode: "Markdown", text: `“${message}”\n— ${name} <${escapedEmail}>`, }), }) .then((response) => response.json()) .then((data) => res.status(200).json(data)) .catch((err) => res.status(err.status).json(err)) }

During local development, I create a .env file at the root of the project directory. It contains environment variables values.

.env
CHAT_ID="@TheSmallUniverse" BOT_TOKEN="992219941:AAEEoFKKPN1qJWYZkVoujOFcHysITXa8uAY"

For production, I add these values to Now Secrets. They will be available at Build Time. As I use Zeit Now service, all I have to do is config now.json file.

now.json
{ "version": 2, "name": "The Small Things", "build": { "env": { "CHAT_ID": "@thesmallthings.chat-id", "BOT_TOKEN": "@thesmallthings.bot-token" } } }

Then declare botToken and chatId in the next.config.js as environment variables.

next.config.js
// Use `dotenv` for local .env file require("dotenv").config() const nextConfig = { env: { chatId: process.env.CHAT_ID, botToken: process.env.BOT_TOKEN, }, }

Private Group

In my case, I don’t want to expose those messages for anyone in Telegram, so I set the group to Private.

At first, the Send Message API doesn’t accept @TheSmallUniverse as chat_id. Because when moving group to Private, its sharable link has changed. So the old chat_id won’t be available anymore. Luckily, I have the Integer chat_id from the previous API call.

{ "chat": { "id": -1001258275042, "title": "The Small Universe", "username": "TheSmallUniverse", "type": "supergroup" } }

Replace that value to .env file, the function works as expected.

.env
CHAT_ID="-1001258275042" BOT_TOKEN="992219941:AAEEoFKKPN1qJWYZkVoujOFcHysITXa8uAY"

But then, when I try to update Now Secrets for chat_id, I get this error

Terminal
now secret add thesmallthings.chat-id -1001258275042 > Error! Invalid number of arguments. Usage: `now secret add <name> <value>`

That's because the secret value cannot contain the dash character. After searching for a while, I come across this solution.

At the root of the project directory, I create a chat-id.txt file, its content is the chat_id value. Then update the command to use that file:

Terminal
now secret add -- thesmallthings.chat-id "`cat chat-id.txt`" > Success! Secret thesmallthings.chat-id added [762ms]

The cat chat-id.txt takes the content of the chat-id.txt file and pastes it into the command line for me.

And, what’s done is done. Now I have an helpful way to receive messages from anyone.