From 0cf2253fe766fcf47f72fb7e232af70f49d1ac52 Mon Sep 17 00:00:00 2001 From: Timur Moziev Date: Wed, 24 Jul 2024 07:20:08 +0000 Subject: [PATCH] add sendTelegramDocument function add deleteTelegramMessage function --- src/index.ts | 8 +- src/stateless.ts | 64 +++++++++++++++- test/index.test.ts | 70 ------------------ test/stateless.test.ts | 161 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+), 76 deletions(-) delete mode 100644 test/index.test.ts create mode 100644 test/stateless.test.ts diff --git a/src/index.ts b/src/index.ts index 9250844..f09f300 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,7 @@ -import { sendTelegramMessage } from "./stateless.js"; +import { + deleteTelegramMessage, + sendTelegramDocument, + sendTelegramMessage, +} from "./stateless.js"; -export { sendTelegramMessage }; +export { deleteTelegramMessage, sendTelegramDocument, sendTelegramMessage }; diff --git a/src/stateless.ts b/src/stateless.ts index 8cd9cb5..8bdf19f 100644 --- a/src/stateless.ts +++ b/src/stateless.ts @@ -1,22 +1,24 @@ import axios from "axios"; +import fs from "fs"; +import path from "path"; /** - * Send a message to a Telegram recipient + * Send a message to a Telegram chat * @param token - * @param recipientId + * @param chatId * @param text * @param mode */ export async function sendTelegramMessage( token: string, - recipientId: number, + chatId: number, text: string, mode: "html" | "markdown", ): Promise { const telegramUrl = `https://api.telegram.org/bot${token}/sendMessage`; try { await axios.post(telegramUrl, { - chat_id: recipientId, + chat_id: chatId, parse_mode: mode, text, }); @@ -26,3 +28,57 @@ export async function sendTelegramMessage( } return false; } + +/** + * Send a document to a Telegram chat + * @param token + * @param chatId + * @param filePath + * @param customFileName + */ +export async function sendTelegramDocument( + token: string, + chatId: number, + filePath: string, + customFileName?: string, +): Promise { + const telegramUrl = `https://api.telegram.org/bot${token}/sendDocument`; + try { + const fileContent = await fs.promises.readFile(path.resolve(filePath)); + const fileName = customFileName || path.basename(filePath); + // eslint-disable-next-line n/no-unsupported-features/node-builtins + const form = new FormData(); + form.append("chat_id", chatId.toString()); + form.append("document", new Blob([fileContent]), fileName); + + await axios.post(telegramUrl, form); + return true; + } catch (error) { + console.error(`Failed to send document:`, error); + } + return false; +} + +/** + * Delete a message from a Telegram chat + * @param token + * @param chatId + * @param messageId + */ +export async function deleteTelegramMessage( + token: string, + chatId: number, + messageId: number, +): Promise { + const telegramUrl = `https://api.telegram.org/bot${token}/deleteMessage`; + try { + await axios.post(telegramUrl, { + chat_id: chatId, + message_id: messageId, + }); + return true; + } catch (error) { + console.error(`Failed to delete message:`, error); + } + return false; +} diff --git a/test/index.test.ts b/test/index.test.ts deleted file mode 100644 index bea48f9..0000000 --- a/test/index.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { expect } from "chai"; -import sinon from "sinon"; -import axios from "axios"; - -import { sendTelegramMessage } from "../src/stateless.js"; - -describe("sendTelegramMessage", () => { - let sandbox: sinon.SinonSandbox; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - it("should return true on successful message send", async () => { - const axiosPostStub = sandbox.stub(axios, "post").resolves(); - const result = await sendTelegramMessage( - "dummy_token", - 12345, - "Hello, World!", - "markdown", - ); - expect(result).to.be.true; - expect(axiosPostStub.calledOnce).to.be.true; - }); - - it("should return false on API failure", async () => { - sandbox.stub(axios, "post").rejects(new Error("Network error")); - const result = await sendTelegramMessage( - "dummy_token", - 12345, - "Hello, World!", - "markdown", - ); - expect(result).to.be.false; - }); - - it("should call axios.post with correct URL and payload", async () => { - const axiosPostStub = sandbox.stub(axios, "post").resolves(); - await sendTelegramMessage("dummy_token", 12345, "Test Message", "html"); - expect( - axiosPostStub.calledWith( - sinon.match.string, - sinon.match.has("chat_id", 12345) && - sinon.match.has("text", "Test Message") && - sinon.match.has("parse_mode", "html"), - ), - ).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 sendTelegramMessage( - "dummy_token", - 12345, - "Hello, World!", - "markdown", - ); - expect( - consoleErrorStub.calledWith( - sinon.match.string, - sinon.match.instanceOf(Error), - ), - ).to.be.true; - }); -}); diff --git a/test/stateless.test.ts b/test/stateless.test.ts new file mode 100644 index 0000000..1eab615 --- /dev/null +++ b/test/stateless.test.ts @@ -0,0 +1,161 @@ +import { expect } from "chai"; +import sinon from "sinon"; +import axios from "axios"; +import fs from "fs"; + +import { + deleteTelegramMessage, + sendTelegramDocument, + sendTelegramMessage, +} from "../src/stateless.js"; + +describe("sendTelegramMessage", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("should return true on successful message send", async () => { + const axiosPostStub = sandbox.stub(axios, "post").resolves(); + const result = await sendTelegramMessage( + "dummy_token", + 12345, + "Hello, World!", + "markdown", + ); + expect(result).to.be.true; + expect(axiosPostStub.calledOnce).to.be.true; + }); + + it("should return false on API failure", async () => { + sandbox.stub(axios, "post").rejects(new Error("Network error")); + const result = await sendTelegramMessage( + "dummy_token", + 12345, + "Hello, World!", + "markdown", + ); + expect(result).to.be.false; + }); + + it("should call axios.post with correct URL and payload", async () => { + const axiosPostStub = sandbox.stub(axios, "post").resolves(); + await sendTelegramMessage("dummy_token", 12345, "Test Message", "html"); + expect( + axiosPostStub.calledWith( + sinon.match.string, + sinon.match.has("chat_id", 12345) && + sinon.match.has("text", "Test Message") && + sinon.match.has("parse_mode", "html"), + ), + ).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 sendTelegramMessage( + "dummy_token", + 12345, + "Hello, World!", + "markdown", + ); + expect( + consoleErrorStub.calledWith( + sinon.match.string, + sinon.match.instanceOf(Error), + ), + ).to.be.true; + }); +}); + +describe("sendTelegramDocument", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("should return true on successful document send", async () => { + const axiosPostStub = sandbox.stub(axios, "post").resolves(); + const fsReadFileStub = sandbox + .stub(fs.promises, "readFile") + .resolves(Buffer.from("dummy content")); + const result = await sendTelegramDocument( + "dummy_token", + 12345, + "path/to/document", + ); + expect(result).to.be.true; + expect(axiosPostStub.calledOnce).to.be.true; + expect(fsReadFileStub.calledOnceWith(sinon.match.string)).to.be.true; + }); + + it("should return false on API failure", async () => { + sandbox.stub(axios, "post").rejects(new Error("Network error")); + const result = await sendTelegramDocument( + "dummy_token", + 12345, + "path/to/document", + ); + expect(result).to.be.false; + }); + + it("should log error on failure", async () => { + const consoleErrorStub = sandbox.stub(console, "error"); + sandbox.stub(axios, "post").rejects(new Error("Failed to send document")); + await sendTelegramDocument("dummy_token", 12345, "path/to/document"); + expect( + consoleErrorStub.calledWith( + sinon.match.string, + sinon.match.instanceOf(Error), + ), + ).to.be.true; + }); +}); + +describe("deleteTelegramMessage", () => { + let sandbox: sinon.SinonSandbox; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + it("should return true on successful message deletion", async () => { + const axiosPostStub = sandbox.stub(axios, "post").resolves(); + const result = await deleteTelegramMessage("dummy_token", 12345, 67890); + expect(result).to.be.true; + expect(axiosPostStub.calledOnce).to.be.true; + }); + + it("should return false on API failure", async () => { + sandbox.stub(axios, "post").rejects(new Error("Network error")); + const result = await deleteTelegramMessage("dummy_token", 12345, 67890); + expect(result).to.be.false; + }); + + it("should log error on failure", async () => { + const consoleErrorStub = sandbox.stub(console, "error"); + sandbox.stub(axios, "post").rejects(new Error("Failed to delete message")); + await deleteTelegramMessage("dummy_token", 12345, 67890); + expect( + consoleErrorStub.calledWith( + sinon.match.string, + sinon.match.instanceOf(Error), + ), + ).to.be.true; + }); +});