Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 21 additions & 40 deletions telegram/README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,38 @@
# Telegram Bot Command
# Telegram Bot

A simple Telegram Bot Command.
A simple Telegram bot built with [grammY](https://grammy.dev).

## Tutorial

1. Follow the
[official Telegram guide](https://core.telegram.org/bots#3-how-do-i-create-a-bot)
for creating a Bot.
2. Deploy the Bot by clicking on this button:
[![Deploy this example](https://deno.com/deno-deploy-button.svg)](https://dash.deno.com/new?url=https://raw.githubusercontent.com/denoland/deploy_examples/main/telegram/mod.ts&env=TOKEN,BOT_NAME)
3. Input `TOKEN` and `BOT_NAME` env variable fields. The token value should be
available from the BotFather and the value `BOT_NAME` is the bot username
that ends with either `_bot` or `Bot`.
4. Click on **Create** to create the project, then on **Deploy** to deploy the
1. Deploy the Bot.

[![Deploy this example](https://deno.com/deno-deploy-button.svg)](https://dash.deno.com/new?url=https://raw.githubusercontent.com/denoland/deploy_examples/main/telegram/mod.ts&env=BOT_TOKEN)
2. Open Telegram, talk to [BotFather](https://telegram.me/BotFather) and grab a
bot token. Set it as the `BOT_TOKEN` environment variable value.
3. Click on **Create** to create the project, then on **Deploy** to deploy the
script.
5. Grab the URL that's displayed under Domains in the Production Deployment
card.
6. Visit the following URL (make sure to replace the template fields):
4. Visit the following URL (make sure to replace the template fields):
```
https://api.telegram.org/bot<YOUR_TOKEN>/setWebhook?url=<DOMAIN_NAME>/<YOUR_TOKEN>
https://api.telegram.org/bot<YOUR_TOKEN>/setWebhook?url=<APP_URL>/<YOUR_TOKEN>
```

> Replace <YOUR_TOKEN> with the token from the BotFather and `<DOMAIN_NAME>`
> with the URL from the previous step.
- Replace `<YOUR_TOKEN>` with the token you got earlier.
- Replace `<APP_URL>` with the the URL that is displayed under the
**Production Deployment** card at your project dashboard page.
5. Now send the bot a `/start` or `/ping` command.

7. Add a command to the bot by visiting the following URL:
<div align="center">

```
https://api.telegram.org/bot<YOUR_TOKEN>/setMyCommands?commands=[{"command":"ping","description":"Should return a 'pong' from the Bot."}]
```
8. Now you can invite the bot to a Group Chat or just PM the bot with the
following command "/ping".
![preview](https://raw.githubusercontent.com/dcdunkan/deploy_examples/main/telegram/preview.png)

<img align="center" src="demo.png" alt="demo of Telegram Bot Command" />
</div>

## Run Offline
## Run Locally

You can run the example program on your machine using
[`deno`](https://github.com/denoland/deno):
You can run the example on your machine using [Deno CLI](https://deno.land).

```sh
TOKEN=<your_telegram_bot_token> BOT_NAME=<bot_username> deno run --allow-env --allow-net https://raw.githubusercontent.com/denoland/deploy_examples/main/telegram/mod.ts
BOT_TOKEN="<YOUR_TOKEN>" deno run --allow-env --allow-net https://raw.githubusercontent.com/denoland/deploy_examples/main/telegram/local.ts
```

You need to use a tool like [ngrok](https://ngrok.com) to tunnel Telegram
requests to the app running on your machine.

1. Run `ngrok http 8080` (assuming that the application is running on port
`8080`)
2. While registering the bot, use the https URL output by ngrok for **url**
query.

> Example:
> `https://api.telegram.org/bot<YOUR_TOKEN>/setWebhook?url=<ngrok_url>/<YOUR_TOKEN>`

That's it.
Remember to replace the `<YOUR_TOKEN>` with your own bot token.
11 changes: 11 additions & 0 deletions telegram/bot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Bot } from "https://deno.land/x/[email protected]/mod.ts";

export const bot = new Bot(Deno.env.get("BOT_TOKEN") ?? "");

bot.command("start", async (ctx) => {
await ctx.reply("Hello!");
});

bot.command("ping", async (ctx) => {
await ctx.reply("Pong!");
});
Binary file removed telegram/demo.png
Binary file not shown.
3 changes: 3 additions & 0 deletions telegram/local.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { bot } from "./bot.ts";

bot.start();
149 changes: 22 additions & 127 deletions telegram/mod.ts
Original file line number Diff line number Diff line change
@@ -1,130 +1,25 @@
import {
json,
PathParams,
serve,
validateRequest,
} from "https://deno.land/x/[email protected]/mod.ts";

// For all requests to "/<TOKEN>" endpoint, we want to invoke handleTelegram() handler.
// Recommend using a secret path in the URL, e.g. https://www.example.com/<token>.
serve({
"/": () => new Response("Welcome to the Telegram Bot site."),
"/:slug": handleTelegram,
});

// The main logic of the Telegram bot is defined in this function.
async function handleTelegram(request: Request, params?: PathParams) {
// Gets the environment variable TOKEN
const TOKEN = Deno.env.get("TOKEN")!;
// Gets the environment variable BOT_NAME
const BOT_NAME = Deno.env.get("BOT_NAME")!;

// If the environment variable TOKEN is not found, throw an error.
if (!TOKEN) {
throw new Error("environment variable TOKEN is not set");
}

// For using a secret path in the URL, e.g. https://www.example.com/<token>. If wrong url return "invalid request".
if (params?.slug != TOKEN) {
return json(
{ error: "invalid request" },
{
status: 401,
},
);
}

// Make sure the request is a POST request.
const { error } = await validateRequest(request, {
POST: {},
});

// validateRequest populates the error if the request doesn't meet
// the schema we defined.
if (error) {
return json({ error: error.message }, { status: error.status });
}

// Get the body of the request
const body = await request.text();
// Parse the raw JSON body from Telegrams webhook.
const data = await JSON.parse(body);

// Check if the method is a POST request and that there was somthing in the body.
if (request.method === "POST") {
// Cheack if the command was "/ping".
if (
data && data["message"] && data["message"]["text"] &&
(data["message"]["text"].toLowerCase() == "/ping" ||
data["message"]["text"].toLowerCase() ==
"/ping@" + BOT_NAME.toLowerCase())
) {
// Store the chat id of the Group Chat, Channel or PM.
const chatId: number = data["message"]["chat"]["id"];

// Calls the API service to Telegram for sending a message.
const { dataTelegram, errors } = await sendMessage(
chatId,
"Pong",
TOKEN,
);

if (errors) {
console.error(errors.map((error) => error.message).join("\n"));
return json({ error: "couldn't create the message" }, {
status: 500,
});
import { serve } from "https://deno.land/[email protected]/http/mod.ts";
import { webhookCallback } from "https://deno.land/x/[email protected]/mod.ts";
import { bot } from "./bot.ts";

await bot.init();

const handleUpdate = webhookCallback(bot, "std/http");
serve(async (req) => {
const pathname = new URL(req.url).pathname;
switch (pathname) {
case `/${bot.token}`:
if (req.method === "POST") {
try {
return await handleUpdate(req);
} catch (err) {
console.error(err);
return new Response();
}
}
break;

// Returns the answer and set status code 201.
return json({ dataTelegram }, { status: 201 });
}
// Returns empty object and set status code 200.
return json({}, { status: 200 });
}

// We will return a bad request error as a valid Telegram request
// shouldn't reach here.
return json({ error: "bad request" }, { status: 400 });
}

/** What to store for an error message. */
type TelegramError = {
message?: string;
};

/** Sending a POST request to Telegram's API to send a message. */
async function sendMessage(
chatId: number,
text: string,
token: string,
): Promise<{
dataTelegram?: unknown;
errors?: TelegramError[];
}> {
try {
const res = await fetch(
`https://api.telegram.org/bot${token}/sendMessage`,
{
method: "POST",
headers: {
"content-type": "application/json",
},
body: JSON.stringify({
chat_id: chatId,
text: text,
}),
},
);
const { dataTelegram, errors } = await res.json();

if (errors) {
return { dataTelegram, errors };
}

return { dataTelegram };
} catch (error) {
console.error(error);
return { errors: [{ message: "failed to fetch data from Telegram" }] };
default:
return Response.redirect(`https://telegram.me/${bot.botInfo.username}`);
}
}
});
Binary file added telegram/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.