diff --git a/.gitignore b/.gitignore index 7d8f8cd..021b136 100644 --- a/.gitignore +++ b/.gitignore @@ -67,4 +67,5 @@ typings/ *.js .idea/ -.vscode/settings.json \ No newline at end of file +.vscode/settings.json +tmp/ diff --git a/src/danktimesbot-controller/danktimesbot-controller-mock.ts b/src/danktimesbot-controller/danktimesbot-controller-mock.ts index be0b64c..ff0c479 100644 --- a/src/danktimesbot-controller/danktimesbot-controller-mock.ts +++ b/src/danktimesbot-controller/danktimesbot-controller-mock.ts @@ -1,4 +1,4 @@ -import TelegramBot from "node-telegram-bot-api"; +import TelegramBot, { File } from "node-telegram-bot-api"; import { Chat } from "../chat/chat"; import { CustomEventArguments } from "../plugin-host/plugin-events/event-arguments/custom-event-arguments"; import { AbstractPlugin } from "../plugin-host/plugin/plugin"; @@ -65,4 +65,12 @@ export class DankTimesBotControllerMock implements IDankTimesBotController { public onPluginWantsToGetOtherPlugins(callingPlugin: AbstractPlugin): AbstractPlugin[] { return []; } + + onPluginWantsToRetrieveFile(chatId: number, fileId: string): Promise { + return Promise.resolve(); + } + + onPluginWantsToSendFile(chatId: number, filePath: string, replyToMessageId: number, forceReply: boolean): Promise { + return Promise.resolve(); + } } diff --git a/src/danktimesbot-controller/danktimesbot-controller.ts b/src/danktimesbot-controller/danktimesbot-controller.ts index b223799..3d13320 100644 --- a/src/danktimesbot-controller/danktimesbot-controller.ts +++ b/src/danktimesbot-controller/danktimesbot-controller.ts @@ -1,5 +1,5 @@ import moment from "moment"; -import TelegramBot from "node-telegram-bot-api"; +import TelegramBot, { File } from "node-telegram-bot-api"; import { IChatRegistry } from "../chat-registry/i-chat-registry"; import { Chat } from "../chat/chat"; import { IDankTimeScheduler } from "../dank-time-scheduler/i-dank-time-scheduler"; @@ -115,10 +115,24 @@ export class DankTimesBotController implements IDankTimesBotController { /** * From IPluginListener. */ - public onPluginWantsToParseScoreInput(input: string): number | null { + public onPluginWantsToParseScoreInput(input: string): number | null { return this.util.parseScoreInput(input); } + /** + * From IPluginListener. + */ + onPluginWantsToRetrieveFile(chatId: number, fileId: string): Promise { + return this.telegramClient.retrieveFile(chatId, fileId); + } + + /** + * From IPluginListener. + */ + onPluginWantsToSendFile(chatId: number, filePath: string, replyToMessageId: number, forceReply: boolean): Promise { + return this.telegramClient.sendFile(chatId, filePath, replyToMessageId, forceReply); + } + /** * From IPluginListener. */ diff --git a/src/plugin-host/plugin/plugin-listener.ts b/src/plugin-host/plugin/plugin-listener.ts index b7e9119..1c751f5 100644 --- a/src/plugin-host/plugin/plugin-listener.ts +++ b/src/plugin-host/plugin/plugin-listener.ts @@ -1,4 +1,4 @@ -import TelegramBot from "node-telegram-bot-api"; +import TelegramBot, { File, PhotoSize } from "node-telegram-bot-api"; import { Chat } from "../../chat/chat"; import { CustomEventArguments } from "../plugin-events/event-arguments/custom-event-arguments"; import { AbstractPlugin } from "./plugin"; @@ -18,6 +18,15 @@ export interface IPluginListener { onPluginWantsToSendChatMessage(chatId: number, htmlMessage: string, replyToMessageId: number, forceReply: boolean): Promise; + /** + * Fired when a plugin wants to send a file / photo to a chat. + * @param chatId Chat to send photo to + * @param filePath Path to the file on disk to send + * @param replyToMessageId Message to respond to + * @param forceReply Whether to force the replied-to or tagged user to reply to this message + */ + onPluginWantsToSendFile(chatId: number, filePath: string, replyToMessageId: number, forceReply: boolean): Promise; + /** * Fired when a plugin wants to delete a chat message. * @param chatId The id of the chat to delete a message in. @@ -25,6 +34,13 @@ export interface IPluginListener { */ onPluginWantsToDeleteChatMessage(chatId: number, messageId: number): Promise; + /** + * Fired when a plugin wants to delete a file. This is likely to be a photo. + * @param chatId The id of the chat to retrieve a file from. + * @param fileId Id of the file to retrieve. + */ + onPluginWantsToRetrieveFile(chatId: number, fileId: string): Promise; + /** * Fired when a plugin wants to edit a chat message. * @param chatId The id of the chat to edit a message in. diff --git a/src/plugin-host/plugin/plugin.ts b/src/plugin-host/plugin/plugin.ts index 0675746..3f816ff 100644 --- a/src/plugin-host/plugin/plugin.ts +++ b/src/plugin-host/plugin/plugin.ts @@ -1,4 +1,4 @@ -import TelegramBot from "node-telegram-bot-api"; +import TelegramBot, { File, PhotoSize } from "node-telegram-bot-api"; import { BotCommand } from "../../bot-commands/bot-command"; import { Chat } from "../../chat/chat"; import { ChatSettingTemplate } from "../../chat/settings/chat-setting-template"; @@ -173,6 +173,17 @@ export abstract class AbstractPlugin { return this.listener.onPluginWantsToSendChatMessage(chatId, htmlMessage, replyToMessageId, forceReply); } + /** + * Sends a file to the Telegram Bot API. + * @param chatId The id of the chat to send a message to. + * @param filePath The path to the file we want to send. + * @param replyToMessageId The (optional) id of the message to reply to. + * @param forceReply Whether to force the replied-to or tagged user to reply to this message. + */ + protected sendFile(chatId: number, filePath: string, replyToMessageId = -1, forceReply = false): Promise { + return this.listener.onPluginWantsToSendFile(chatId, filePath, replyToMessageId, forceReply); + } + /** * Deletes a message via the Telegram Bot API. * @param chatId The id of the chat to delete a message in. @@ -182,6 +193,15 @@ export abstract class AbstractPlugin { return this.listener.onPluginWantsToDeleteChatMessage(chatId, messageId); } + /** + * Retrieves a file from the Telegram Bot API. + * @param chatId The id of the chat to retrieve a file from. + * @param fileId Id of the file to retrieve. + */ + protected retrieveFile(chatId: number, fileId: string): Promise { + return this.listener.onPluginWantsToRetrieveFile(chatId, fileId); + } + /** * Edits a message via the Telegram Bot API. * @param chatId The id of the chat to edit a message in. @@ -200,7 +220,7 @@ export abstract class AbstractPlugin { return this.listener.onPluginWantsToGetChat(chatId); } - /** + /** * Parses the score input, returning a number if a number could be determined, * otherwise returns null. * @param input The string input to cleanse to a number. diff --git a/src/telegram-client/i-telegram-client.ts b/src/telegram-client/i-telegram-client.ts index 7ac9c0d..5a7b143 100644 --- a/src/telegram-client/i-telegram-client.ts +++ b/src/telegram-client/i-telegram-client.ts @@ -1,4 +1,4 @@ -import TelegramBot from "node-telegram-bot-api"; +import TelegramBot, { File } from "node-telegram-bot-api"; import { ITelegramClientListener } from "./i-telegram-client-listener"; /** @@ -61,4 +61,20 @@ export interface ITelegramClient { * @return The administrators of the specified chat. */ getChatAdministrators(chatId: number): Promise; + + /** + * retrieves a file with given id from a chat. + * @param chatId Id of the chat to retrieve the file from. + * @param fileId Id of the file. + */ + retrieveFile(chatId: number, fileId: string): Promise; + + /** + * Send a file or photo to a chat. + * @param chatId Id of the chat to send the file to. + * @param filePath Local path to the file on disk. + * @param replyToMessageId The (optional) id of the message to reply to. + * @param forceReply Whether to force the replied-to or tagged user to reply to this message. False by default. + */ + sendFile(chatId: number, filePath: string, replyToMessageId: number, forceReply: boolean): Promise; } diff --git a/src/telegram-client/telegram-client-mock.ts b/src/telegram-client/telegram-client-mock.ts index 3bac762..0a75d64 100644 --- a/src/telegram-client/telegram-client-mock.ts +++ b/src/telegram-client/telegram-client-mock.ts @@ -1,4 +1,4 @@ -import TelegramBot from "node-telegram-bot-api"; +import TelegramBot, { File } from "node-telegram-bot-api"; import { ITelegramClient } from "./i-telegram-client"; import { ITelegramClientListener } from "./i-telegram-client-listener"; @@ -45,4 +45,12 @@ export class TelegramClientMock implements ITelegramClient { }, }]); } + + public async retrieveFile(chatId: number, fileId: string): Promise { + // Don't do anything, this is a mock. + } + + public async sendFile(chatId: number, filePath: string, replyToMessageId: number, forceReply: boolean): Promise { + // Don't do anything, this is a mock + } } diff --git a/src/telegram-client/telegram-client.ts b/src/telegram-client/telegram-client.ts index 34e722d..de04c1a 100644 --- a/src/telegram-client/telegram-client.ts +++ b/src/telegram-client/telegram-client.ts @@ -1,4 +1,5 @@ -import TelegramBot from "node-telegram-bot-api"; +import fs from "fs"; +import TelegramBot, { File, PhotoSize } from "node-telegram-bot-api"; import { ITelegramClient } from "./i-telegram-client"; import { ITelegramClientListener } from "./i-telegram-client-listener"; @@ -52,6 +53,31 @@ export class TelegramClient implements ITelegramClient { }); } + public retrieveFile(chatId: number, fileId: string): Promise { + if (!fs.existsSync("./tmp/dtb")) { + fs.mkdir("./tmp/dtb", {recursive: true}, err => { + this.listeners.forEach((listener) => listener.onErrorFromApi(chatId, "Could not create file location")); + }); + } + return this.bot.downloadFile(fileId, "./tmp/dtb") + .catch((reason: void | TelegramBot.Message) => { + this.listeners.forEach((listener) => listener.onErrorFromApi(chatId, reason)); + }); + } + + public sendFile(chatId: number, filePath: string, replyToMessageId: number, forceReply: boolean): Promise { + if (!fs.existsSync(filePath)) { + this.listeners.forEach((listener) => listener.onErrorFromApi(chatId, "File does not exist!")); + return new Promise(() => { + return; + }); + } else { + return this.bot.sendPhoto(chatId, filePath, { + reply_to_message_id: replyToMessageId, + }).catch((reason: void | TelegramBot.Message) => { this.listeners.forEach((listener) => listener.onErrorFromApi(chatId, reason)); }); + } + } + public subscribe(subscriber: ITelegramClientListener): void { if (this.listeners.indexOf(subscriber) === -1) { this.listeners.push(subscriber);