Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add /editMessageText, /answerCallbackQuery, fix client.getUpdates #56

Merged
merged 6 commits into from
Nov 16, 2021
Merged
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
4 changes: 3 additions & 1 deletion src/modules/telegramClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { GetUpdatesResponse } from '../routes/client/getUpdates';
import type { GetUpdatesHistoryResponse } from '../routes/client/getUpdatesHistory';

export interface CommonMessage {
message_id?: number;
text?: string;
from: User;
chat: Chat;
}
Expand Down Expand Up @@ -214,7 +216,7 @@ export class TelegramClient {
}

async getUpdates(): Promise<GetUpdatesResponse> {
const data = { token: this.botToken };
const data = { token: this.botToken, chatId: this.chatId };
const update = await request({
url: `${this.url}/getUpdates`,
method: 'POST',
Expand Down
15 changes: 15 additions & 0 deletions src/routes/bot/answerCallbackQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { handle } from './utils';
import type { Route } from '../route';
/**
* This route does nothing, it just stops spinner on keyboard button
* @see https://core.telegram.org/bots/api#answercallbackquery
*/
export const answerCallbackQuery: Route = (app) => {
// @ts-expect-error TS6133: 'unusedTestBot' is declared but its value is never read.
handle(app, '/bot:token/answerCallbackQuery', (req, res) => {
res.sendResult({
ok: true,
result: null,
});
});
};
10 changes: 10 additions & 0 deletions src/routes/bot/editMessageText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { handle } from './utils';
import type { Route } from '../route';

export const editMessageText: Route = (app, telegramServer) => {
handle(app, '/bot:token/editMessageText', (req, res, _next) => {
telegramServer.editMessageText(req.body);
const data = {ok: true, result: null};
res.sendResult(data);
});
};
4 changes: 4 additions & 0 deletions src/routes/bot/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import { deleteMessage } from './deleteMessage';
import { getUpdates } from './getUpdates';
import { getMe } from './getMe';
import { answerCallbackQuery } from './answerCallbackQuery';
import { sendMessage } from './sendMessage';
import { editMessageText } from './editMessageText';
import { setWebhook } from './setWebhook';
import { deleteWebhook } from './deleteWebhook';
import { unknownMethod } from './unknownMethod';

export const botRoutes = [
deleteMessage,
getUpdates,
answerCallbackQuery,
getMe,
editMessageText,
sendMessage,
setWebhook,
deleteWebhook,
Expand Down
6 changes: 5 additions & 1 deletion src/routes/client/getUpdates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ export const getUpdates: Route = (app, telegramServer) => {
app.post('/getUpdates', (req, res) => {
const botToken = req.body.token;
const messages = telegramServer.storage.botMessages.filter(
(update) => update.botToken === botToken && !update.isRead,
(update) => (
update.botToken === botToken
&& String(update.message.chat_id) === String(req.body.chatId)
&& !update.isRead
),
);
// mark updates as read
messages.forEach((update) => {
Expand Down
28 changes: 25 additions & 3 deletions src/telegramServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ interface StoredUpdate {
isRead: boolean;
}

// TODO instead of `any` here must be `InputFile` whatever that is
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type BotIncommingMessage = Params<'sendMessage', any>[0] & {
type BotIncommingMessage = Params<'sendMessage', never>[0] & {
// TODO parse reply_markup to its actual type, see https://git.io/J1kiM
reply_markup?: string;
};

type BotEditTextIncommingMessage = Params<'editMessageText', never>[0] & {
reply_markup?: string;
};

export interface StoredBotUpdate extends StoredUpdate {
message: BotIncommingMessage;
}
Expand Down Expand Up @@ -175,6 +178,25 @@ export class TelegramServer extends EventEmitter {
this.emit('AddedBotMessage');
}

editMessageText(message: BotEditTextIncommingMessage) {
const update = this.storage.botMessages.find(
(u) =>(
String(u.messageId) === String(message.message_id)
&& String(u.message.chat_id) === String(message.chat_id)
),
);
if (update) {
update.message = {...update.message, ...message };
this.emit('EditedMessageText');
}
}

async waitBotEdits() {
return new Promise<void>((resolve) => {
this.on('EditedMessageText', () => resolve());
});
}

async waitBotMessage() {
return new Promise<void>((resolve) => {
this.on('AddedBotMessage', () => resolve());
Expand Down
49 changes: 49 additions & 0 deletions src/test/generic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import debug from 'debug';
import { assert } from 'chai';
import TelegramBot from 'node-telegram-bot-api';
import { getServerAndClient, assertEventuallyTrue, delay } from './utils';
import {
TelegramBotEx,
Expand Down Expand Up @@ -127,6 +128,23 @@ describe('Telegram Server', () => {
);
});

it('should get updates only for respective client', async () => {
const { server, client } = await getServerAndClient(token);
const botOptions = {polling: true, baseApiUrl: server.config.apiURL};
const telegramBot = new TelegramBotEx(token, botOptions);
// @ts-expect-error TS6133: 'unusedTestBot' is declared but its value is never read.
const unusedTestBot = new TestBot(telegramBot);
const client2 = server.getClient(token, {chatId: 2, firstName: 'Second User'});
await client.sendMessage(client.makeMessage('/start'));
await client2.sendMessage(client2.makeMessage('/start'));
const updates = await client.getUpdates();
const updates2 = await client2.getUpdates();
assert.equal(updates.result.length, 1);
assert.equal(updates2.result.length, 1);
await telegramBot.stopPolling();
await server.stop();
});

it('should get updates history', async () => {
const { server, client } = await getServerAndClient(token);
let message = client.makeMessage('/start');
Expand Down Expand Up @@ -220,6 +238,37 @@ describe('Telegram Server', () => {
assert.equal('somedata', res2.data);
});

it('should handle message editing', async () => {
const { server, client } = await getServerAndClient(token);
const bot = new TelegramBot(token, {baseApiUrl: server.config.apiURL, polling: true});
bot.onText(/\/start/, (msg) => {
const chatId = msg.from!.id;
bot.sendMessage(chatId, 'Greetings');
});
bot.on('callback_query', (query) => {
if (query.data === 'edit') {
bot.editMessageText(
'Edited',
{chat_id: query.message!.chat.id, message_id: query.message!.message_id},
);
}
});
await client.sendCommand(client.makeCommand('/start'));
const startUpdates = await client.getUpdates();
const botReply = startUpdates.result[0];
assert.exists(botReply);
assert.equal(botReply.message.text, 'Greetings');

const cb = client.makeCallbackQuery('edit', {message: {message_id: botReply.messageId}});
await client.sendCallback(cb);
await server.waitBotEdits();
const allUpdates = await client.getUpdatesHistory();
const targetUpdte = allUpdates.find((update) => update.messageId === botReply.messageId);
assert.equal(targetUpdte && 'message' in targetUpdte && targetUpdte.message.text, 'Edited');
await bot.stopPolling();
await server.stop();
});

it('should remove messages on storeTimeout', async () => {
const { server, client } = await getServerAndClient(token, {
storeTimeout: 1,
Expand Down
8 changes: 4 additions & 4 deletions src/test/testBots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export class Logger {
export class TestBot {
constructor(bot: TelegramBot) {
bot.onText(/\/ping/, (msg) => {
const chatId = msg.from?.id;
const chatId = msg.chat.id;
if (!chatId) return;
const opts = {
reply_to_message_id: msg.message_id,
Expand All @@ -33,7 +33,7 @@ export class TestBot {
});

bot.onText(/\/start/, (msg) => {
const chatId = msg.from?.id;
const chatId = msg.chat.id;
if (!chatId) return;
const opts = {
reply_to_message_id: msg.message_id,
Expand All @@ -45,7 +45,7 @@ export class TestBot {
});

bot.onText(/Masha/, (msg) => {
const chatId = msg.from?.id;
const chatId = msg.chat.id;
if (!chatId) return;
const opts = {
reply_to_message_id: msg.message_id,
Expand All @@ -57,7 +57,7 @@ export class TestBot {
});

bot.onText(/Sasha/, (msg) => {
const chatId = msg.from?.id;
const chatId = msg.chat.id;
if (!chatId) return;
const opts = {
reply_to_message_id: msg.message_id,
Expand Down