From da6053e8a0f46d0c9b1d866a4c3b30b4b9066a7e Mon Sep 17 00:00:00 2001 From: Timur Moziev Date: Fri, 26 Jul 2024 10:21:34 +0000 Subject: [PATCH] add doTelegramApiAction fix sendTelegramMessage --- src/index.ts | 8 +++- src/stateless.ts | 36 ++++++++++++++-- test/stateless.test.ts | 95 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index f09f300..8073be8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,13 @@ import { deleteTelegramMessage, + doTelegramApiAction, sendTelegramDocument, sendTelegramMessage, } from "./stateless.js"; -export { deleteTelegramMessage, sendTelegramDocument, sendTelegramMessage }; +export { + deleteTelegramMessage, + doTelegramApiAction, + sendTelegramDocument, + sendTelegramMessage, +}; diff --git a/src/stateless.ts b/src/stateless.ts index 4106b84..bcd0301 100644 --- a/src/stateless.ts +++ b/src/stateless.ts @@ -11,18 +11,43 @@ function createTelegramApiUrl(token: string, method: string) { return `https://api.telegram.org/bot${token}/${method}`; } +/** + * Do any API action + * @param token + * @param action + * @param content + */ +export async function doTelegramApiAction( + token: string, + action: string, + content: T, +): Promise { + const telegramUrl = createTelegramApiUrl(token, action); + try { + const messageResponse = await axios.post(telegramUrl, content); + if (messageResponse.status === 200 && messageResponse.data?.ok) { + return true; + } + } catch (error) { + console.error(`Failed to to action ${action}:`, error); + } + return null; +} + /** * Send a message to a Telegram chat * @param token * @param chatId * @param text * @param mode + * @param options */ export async function sendTelegramMessage( token: string, chatId: number, text: string, mode: "html" | "markdown" | "markdownV2", + options?: object, ): Promise { const telegramUrl = createTelegramApiUrl(token, "sendMessage"); try { @@ -30,11 +55,14 @@ export async function sendTelegramMessage( chat_id: chatId, parse_mode: mode, text, + ...options, }); - if (messageResponse.status === 200 && messageResponse.data?.message_id) { - return messageResponse.data.message_id; - } else { - return null; + if ( + messageResponse.status === 200 && + messageResponse.data?.ok && + messageResponse.data?.result + ) { + return messageResponse.data.result.message_id; } } catch (error) { console.error(`Failed to send message:`, error); diff --git a/test/stateless.test.ts b/test/stateless.test.ts index 07c479f..0bb8f1f 100644 --- a/test/stateless.test.ts +++ b/test/stateless.test.ts @@ -5,10 +5,83 @@ import fs from "fs"; import { deleteTelegramMessage, + doTelegramApiAction, sendTelegramDocument, sendTelegramMessage, } from "../src/stateless.js"; +describe("doTelegramApiAction", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("should return true on successful API action", async () => { + const mockResponse = { status: 200, data: { ok: true } }; + const axiosPostStub = sandbox.stub(axios, "post").resolves(mockResponse); + const result = await doTelegramApiAction("dummy_token", "dummy_action", { + key: "value", + }); + expect(result).to.be.true; + expect(axiosPostStub.calledOnce).to.be.true; + }); + + it("should return null on API failure", async () => { + sandbox.stub(axios, "post").rejects(new Error("Network error")); + const result = await doTelegramApiAction("dummy_token", "dummy_action", { + key: "value", + }); + expect(result).to.be.null; + }); + + it("should call axios.post with correct URL and payload", async () => { + const mockResponse = { status: 200, data: { ok: true } }; + const axiosPostStub = sandbox.stub(axios, "post").resolves(mockResponse); + await doTelegramApiAction("dummy_token", "dummy_action", { key: "value" }); + expect( + axiosPostStub.calledWith( + sinon.match.string, + sinon.match.has("key", "value"), + ), + ).to.be.true; + }); + + it("should log error on failure", async () => { + const consoleErrorStub = sandbox.stub(console, "error"); + sandbox.stub(axios, "post").rejects(new Error("Failed to send")); + await doTelegramApiAction("dummy_token", "dummy_action", { key: "value" }); + expect( + consoleErrorStub.calledWith( + sinon.match.string, + sinon.match.instanceOf(Error), + ), + ).to.be.true; + }); + + it("should return null if response status is not 200", async () => { + const mockResponse = { status: 404, data: {} }; + sandbox.stub(axios, "post").resolves(mockResponse); + const result = await doTelegramApiAction("dummy_token", "dummy_action", { + key: "value", + }); + expect(result).to.be.null; + }); + + it("should return null if response data does not contain ok", async () => { + const mockResponse = { status: 200, data: {} }; + sandbox.stub(axios, "post").resolves(mockResponse); + const result = await doTelegramApiAction("dummy_token", "dummy_action", { + key: "value", + }); + expect(result).to.be.null; + }); +}); + describe("sendTelegramMessage", () => { let sandbox: sinon.SinonSandbox; @@ -21,13 +94,17 @@ describe("sendTelegramMessage", () => { }); it("should return message_id on successful message send", async () => { - const mockResponse = { status: 200, data: { message_id: 123456 } }; + const mockResponse = { + status: 200, + data: { ok: true, result: { message_id: 123456 } }, + }; const axiosPostStub = sandbox.stub(axios, "post").resolves(mockResponse); const result = await sendTelegramMessage( "dummy_token", 12345, "Hello, World!", "markdown", + { disable_notification: true }, ); expect(result).to.equal(123456); expect(axiosPostStub.calledOnce).to.be.true; @@ -45,15 +122,21 @@ describe("sendTelegramMessage", () => { }); it("should call axios.post with correct URL and payload", async () => { - const mockResponse = { status: 200, data: { message_id: 123456 } }; + const mockResponse = { + status: 200, + data: { ok: true, result: { message_id: 123456 } }, + }; const axiosPostStub = sandbox.stub(axios, "post").resolves(mockResponse); - await sendTelegramMessage("dummy_token", 12345, "Test Message", "html"); + await sendTelegramMessage("dummy_token", 12345, "Test Message", "html", { + disable_notification: true, + }); expect( axiosPostStub.calledWith( sinon.match.string, sinon.match.has("chat_id", 12345) && sinon.match.has("text", "Test Message") && - sinon.match.has("parse_mode", "html"), + sinon.match.has("parse_mode", "html") && + sinon.match.has("disable_notification", true), ), ).to.be.true; }); @@ -87,8 +170,8 @@ describe("sendTelegramMessage", () => { expect(result).to.be.null; }); - it("should return null if response data does not contain message_id", async () => { - const mockResponse = { status: 200, data: {} }; + it("should return null if response data does not contain ok or result", async () => { + const mockResponse = { status: 200, data: { ok: false } }; sandbox.stub(axios, "post").resolves(mockResponse); const result = await sendTelegramMessage( "dummy_token",