Skip to content

Commit

Permalink
add doTelegramApiAction
Browse files Browse the repository at this point in the history
fix sendTelegramMessage
  • Loading branch information
TimurRin committed Jul 26, 2024
1 parent 8d20704 commit da6053e
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 11 deletions.
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import {
deleteTelegramMessage,
doTelegramApiAction,
sendTelegramDocument,
sendTelegramMessage,
} from "./stateless.js";

export { deleteTelegramMessage, sendTelegramDocument, sendTelegramMessage };
export {
deleteTelegramMessage,
doTelegramApiAction,
sendTelegramDocument,
sendTelegramMessage,
};
36 changes: 32 additions & 4 deletions src/stateless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,58 @@ 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<T extends object>(
token: string,
action: string,
content: T,
): Promise<boolean | null> {
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<null | number> {
const telegramUrl = createTelegramApiUrl(token, "sendMessage");
try {
const messageResponse = await axios.post(telegramUrl, {
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);
Expand Down
95 changes: 89 additions & 6 deletions test/stateless.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -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;
});
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit da6053e

Please sign in to comment.