diff --git a/package.json b/package.json index 3e60175a..b8685f33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tedicross", - "version": "0.12.0", + "version": "0.12.1", "description": "Bridging Telegram and Discord", "license": "MIT", "repository": { diff --git a/src/discord2telegram/setup.ts b/src/discord2telegram/setup.ts index 7bafdfea..6705e336 100644 --- a/src/discord2telegram/setup.ts +++ b/src/discord2telegram/setup.ts @@ -230,130 +230,217 @@ export function setup( // For now: ignoring captions - always send as standalone message // Check for attachments and pass them on - // TODO: limit = 10 files (if more - split) - const images = []; // size limit = 10 mb - const videos = []; // size limit = 20 mb - const audios = []; - const documents = []; + const images: InputMediaPhoto[] = []; + const videos: InputMediaVideo[] = []; + const audios: InputMediaAudio[] = []; + const documents: InputMediaDocument[] = []; + + const handleMediaFile = (attachment: any, type: "video" | "photo" | "audio" | "document") => { + const maxFileSize = type === "video" ? 20000000 : 10000000; + + if (attachment.size < maxFileSize) { + const mediaFile = { media: { url: attachment.url, filename: attachment.name }, type }; + switch (type) { + case "video": + videos.push(mediaFile as InputMediaVideo); + break; + case "photo": + images.push(mediaFile as InputMediaPhoto); + break; + case "audio": + audios.push(mediaFile as InputMediaAudio); + break; + case "document": + documents.push(mediaFile as InputMediaDocument); + break; + } + } else { + logger.error(`[${bridge.name}] Too big attachment ${type} File: ${attachment.name}`); + } + }; for (const attachment of message.attachments.values()) { const fileType: string = attachment.contentType || ""; if (fileType.indexOf("video") >= 0) { - if (attachment.size < 20000000) { - videos.push({ - media: { url: attachment.url }, - type: "video" - } as InputMediaVideo); - } else { - logger.error(`[${bridge.name}] Too big attachment Video File: ${attachment.name}`); - } + handleMediaFile(attachment, "video"); } else if (fileType.indexOf("image") >= 0) { - if (attachment.size < 10000000) { - images.push({ media: { url: attachment.url }, type: "photo" } as InputMediaPhoto); - } else { - logger.error(`[${bridge.name}] Too big attachment Image File: ${attachment.name}`); - } + handleMediaFile(attachment, "photo"); } else if (fileType.indexOf("audio") >= 0) { - if (attachment.size < 20000000) { - audios.push({ media: { url: attachment.url }, type: "audio" } as InputMediaAudio); - } else { - logger.error(`[${bridge.name}] Too big attachment Audio File: ${attachment.name}`); - } + handleMediaFile(attachment, "audio"); } else { - if (attachment.size < 20000000) { - documents.push({ media: { url: attachment.url }, type: "document" } as InputMediaDocument); - } else { - logger.error(`[${bridge.name}] Too big attachment Document File: ${attachment.name}`); - } + handleMediaFile(attachment, "document"); } } - // TODO refactoring - combine in one cycle - // let tgMessage; - if (images.length) { - try { - if (images.length > 1) { - // tgMessage = - await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, images, { - reply_to_message_id: +replyId - }); - } else { - // tgMessage = - await tgBot.telegram.sendPhoto(bridge.telegram.chatId, images[0].media, { - reply_to_message_id: +replyId - }); - } - // NOTE: disabled message mapping for galleries - // messageMap.insert( - // MessageMap.DISCORD_TO_TELEGRAM, - // bridge, - // message.id, - // tgMessage.message_id.toString() - // ); - } catch (err) { - logger.error( - `[${bridge.name}] Telegram did not accept an Image attachment:`, - (err as Error).toString() - ); - } - } + const mediaArray = []; + if (videos.length) mediaArray.push([...videos]); + if (audios.length) mediaArray.push([...audios]); + if (images.length) mediaArray.push([...images]); + if (documents.length) mediaArray.push([...documents]); - if (videos.length) { + for (const oneArray of mediaArray) { + const type = oneArray[0].type; try { - if (videos.length > 1) { - await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, videos, { + if (oneArray.length > 1) { + await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, oneArray, { reply_to_message_id: +replyId }); } else { - await tgBot.telegram.sendVideo(bridge.telegram.chatId, videos[0].media, { - reply_to_message_id: +replyId - }); + switch (type) { + case "video": + await tgBot.telegram.sendVideo(bridge.telegram.chatId, oneArray[0].media, { + reply_to_message_id: +replyId + }); + break; + case "audio": + await tgBot.telegram.sendAudio(bridge.telegram.chatId, oneArray[0].media, { + reply_to_message_id: +replyId + }); + break; + case "photo": + await tgBot.telegram.sendPhoto(bridge.telegram.chatId, oneArray[0].media, { + reply_to_message_id: +replyId + }); + break; + case "document": + await tgBot.telegram.sendDocument(bridge.telegram.chatId, oneArray[0].media, { + reply_to_message_id: +replyId + }); + break; + } } } catch (err) { logger.error( - `[${bridge.name}] Telegram did not accept Video attachment:`, + `[${bridge.name}] Telegram did not accept ${type} attachment:`, (err as Error).toString() ); } } - if (audios.length) { - try { - if (videos.length > 1) { - await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, audios, { - reply_to_message_id: +replyId - }); - } else { - await tgBot.telegram.sendAudio(bridge.telegram.chatId, audios[0].media, { - reply_to_message_id: +replyId - }); - } - } catch (err) { - logger.error( - `[${bridge.name}] Telegram did not accept Audio attachment:`, - (err as Error).toString() - ); - } - } + // const images = []; // size limit = 10 mb + // const videos = []; // size limit = 20 mb + // const audios = []; + // const documents = []; + // const mediaArrays = []; + // + // for (const attachment of message.attachments.values()) { + // const fileType: string = attachment.contentType || ""; + // if (fileType.indexOf("video") >= 0) { + // if (attachment.size < 20000000) { + // videos.push({ + // media: { url: attachment.url }, + // type: "video" + // } as InputMediaVideo); + // } else { + // logger.error(`[${bridge.name}] Too big attachment Video File: ${attachment.name}`); + // } + // } else if (fileType.indexOf("image") >= 0) { + // if (attachment.size < 10000000) { + // images.push({ media: { url: attachment.url }, type: "photo" } as InputMediaPhoto); + // } else { + // logger.error(`[${bridge.name}] Too big attachment Image File: ${attachment.name}`); + // } + // } else if (fileType.indexOf("audio") >= 0) { + // if (attachment.size < 20000000) { + // audios.push({ media: { url: attachment.url }, type: "audio" } as InputMediaAudio); + // } else { + // logger.error(`[${bridge.name}] Too big attachment Audio File: ${attachment.name}`); + // } + // } else { + // if (attachment.size < 20000000) { + // documents.push({ media: { url: attachment.url }, type: "document" } as InputMediaDocument); + // } else { + // logger.error(`[${bridge.name}] Too big attachment Document File: ${attachment.name}`); + // } + // } + // } - if (documents.length) { - try { - if (videos.length > 1) { - await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, documents, { - reply_to_message_id: +replyId - }); - } else { - await tgBot.telegram.sendDocument(bridge.telegram.chatId, documents[0].media, { - reply_to_message_id: +replyId - }); - } - } catch (err) { - logger.error( - `[${bridge.name}] Telegram did not accept Document attachment:`, - (err as Error).toString() - ); - } - } + // let tgMessage; + + // if (images.length) { + // try { + // if (images.length > 1) { + // // tgMessage = + // await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, images, { + // reply_to_message_id: +replyId + // }); + // } else { + // // tgMessage = + // await tgBot.telegram.sendPhoto(bridge.telegram.chatId, images[0].media, { + // reply_to_message_id: +replyId + // }); + // } + // // NOTE: disabled message mapping for galleries + // // messageMap.insert( + // // MessageMap.DISCORD_TO_TELEGRAM, + // // bridge, + // // message.id, + // // tgMessage.message_id.toString() + // // ); + // } catch (err) { + // logger.error( + // `[${bridge.name}] Telegram did not accept an Image attachment:`, + // (err as Error).toString() + // ); + // } + // } + // + // if (videos.length) { + // try { + // if (videos.length > 1) { + // await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, videos, { + // reply_to_message_id: +replyId + // }); + // } else { + // await tgBot.telegram.sendVideo(bridge.telegram.chatId, videos[0].media, { + // reply_to_message_id: +replyId + // }); + // } + // } catch (err) { + // logger.error( + // `[${bridge.name}] Telegram did not accept Video attachment:`, + // (err as Error).toString() + // ); + // } + // } + // + // if (audios.length) { + // try { + // if (videos.length > 1) { + // await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, audios, { + // reply_to_message_id: +replyId + // }); + // } else { + // await tgBot.telegram.sendAudio(bridge.telegram.chatId, audios[0].media, { + // reply_to_message_id: +replyId + // }); + // } + // } catch (err) { + // logger.error( + // `[${bridge.name}] Telegram did not accept Audio attachment:`, + // (err as Error).toString() + // ); + // } + // } + // + // if (documents.length) { + // try { + // if (videos.length > 1) { + // await tgBot.telegram.sendMediaGroup(bridge.telegram.chatId, documents, { + // reply_to_message_id: +replyId + // }); + // } else { + // await tgBot.telegram.sendDocument(bridge.telegram.chatId, documents[0].media, { + // reply_to_message_id: +replyId + // }); + // } + // } catch (err) { + // logger.error( + // `[${bridge.name}] Telegram did not accept Document attachment:`, + // (err as Error).toString() + // ); + // } + // } // Check the message for embeds message.embeds.forEach(embed => { diff --git a/src/telegram2discord/endwares.ts b/src/telegram2discord/endwares.ts index 9a09aebf..e629c618 100644 --- a/src/telegram2discord/endwares.ts +++ b/src/telegram2discord/endwares.ts @@ -26,6 +26,7 @@ export interface TediCrossContext extends Context { forwardFrom: any; from: any; hasActualReference: boolean; + hasMediaGroup?: boolean; }; } @@ -100,7 +101,7 @@ export const newChatMembers = createMessageHandler((ctx: TediCrossContext, bridg ctx.TediCross.dcBot.ready .then(() => fetchDiscordChannel(ctx.TediCross.dcBot, bridge).then((channel: any) => channel.send(text))) .catch((err: any) => - console.error(`Could not tell Discord about a new chat member on bridge ${bridge.name}: ${err.message}`) + ctx.TediCross.logger.error(`Could not tell Discord about a new chat member on bridge ${bridge.name}: ${err.message}`) ); })(ctx.tediCross.message.new_chat_members) ); @@ -126,12 +127,59 @@ export const leftChatMember = createMessageHandler((ctx: TediCrossContext, bridg ctx.TediCross.dcBot.ready .then(() => fetchDiscordChannel(ctx.TediCross.dcBot, bridge).then(channel => channel.send(text))) .catch((err: any) => - console.error( + ctx.TediCross.logger.error( `Could not tell Discord about a chat member who left on bridge ${bridge.name}: ${err.message}` ) ); }); +const parseMediaGroup = (ctx: TediCrossContext, byTimer: boolean = false) => { + // ctx.TediCross.logger.info(ctx.tediCross.message.media_group_id, byTimer); + + const groupIdMap = ctx.TediCross.groupIdMap; + const groupId = ctx.tediCross.message.media_group_id; + + if (byTimer) { + if (groupIdMap.has(groupId)) { + const ctxArray = groupIdMap.get(groupId); + groupIdMap.delete(groupId); + if (ctxArray) { + // ctx.TediCross.logger.info(`Array Length: ${ctxArray.length}`); + const comboCtx: TediCrossContext = ctxArray[0]; + comboCtx.tediCross.hasMediaGroup = true; + const prepared = comboCtx.tediCross.prepared[0]; + prepared.files = []; + + for (const lCtx of ctxArray) { + const lPrepared = lCtx.tediCross.prepared[0]; + if (lPrepared.header) { + prepared.header = lPrepared.header; + } + if (lPrepared.text) { + prepared.text = lPrepared.text; + } + if (lPrepared.file.attachment) { + prepared.files.push(lPrepared.file); + } + } + + // ctx.TediCross.logger.info(`Files Array Length: ${prepared.files.length}`); + + relayMessage(comboCtx); + } + } else { + // ctx.TediCross.logger.info(`No groupId: ${groupId}`); + return; + } + } else { + let ctxArray: TediCrossContext[] | undefined; + ctxArray = groupIdMap.get(groupId); + if (!ctxArray) ctxArray = []; + ctxArray.push(ctx); + groupIdMap.set(groupId, ctxArray); + } +}; + /** * Relays a message from Telegram to Discord * @@ -140,6 +188,15 @@ export const leftChatMember = createMessageHandler((ctx: TediCrossContext, bridg * @param ctx.TediCross The global TediCross context of the message */ export const relayMessage = (ctx: TediCrossContext) => { + // group mediaGroup objects - and delay them (each object comes in separate message) + if (ctx.tediCross.message?.media_group_id) { + if (!ctx.tediCross.hasMediaGroup) { + parseMediaGroup(ctx); + setTimeout(parseMediaGroup, 5000, ctx, true); + return; + } + } + R.forEach(async (prepared: any) => { try { // Wait for the Discord bot to become ready @@ -162,17 +219,16 @@ export const relayMessage = (ctx: TediCrossContext) => { if (replyId === "0" || replyId === undefined || messageToReply === undefined) { dcMessage = await channel.send({ content: R.head(chunks), - files: [prepared.file] + files: prepared.files || [prepared.file] }); } else { dcMessage = await messageToReply.reply({ content: R.head(chunks), - files: [prepared.file] + files: prepared.files || [prepared.file] }); } chunks = R.tail(chunks); } catch (err: any) { - console.dir(`relayMessage err: ${err.message}`); if (err.message === "Request entity too large") { dcMessage = await channel.send( `***${prepared.senderName}** on Telegram sent a file, but it was too large for Discord. If you want it, ask them to send it some other way*` @@ -204,7 +260,7 @@ export const relayMessage = (ctx: TediCrossContext) => { dcMessage?.id ); } catch (err: any) { - console.error(`Could not relay a message to Discord on bridge ${prepared.bridge.name}: ${err.message}`); + ctx.TediCross.logger.error(`Could not relay a message to Discord on bridge ${prepared.bridge.name}: ${err.message}`); } })(ctx.tediCross.prepared); }; @@ -240,7 +296,7 @@ export const handleEdits = createMessageHandler(async (ctx: TediCrossContext, br await Promise.all([dp, tp]); } catch (err: any) { - console.error( + ctx.TediCross.logger.error( `Could not cross-delete message from Telegram to Discord on bridge ${bridge.name}: ${err.message}` ); } @@ -283,7 +339,7 @@ export const handleEdits = createMessageHandler(async (ctx: TediCrossContext, br })(ctx.tediCross.prepared); } catch (err: any) { // Log it - console.error( + ctx.TediCross.logger.error( `Could not cross-edit message from Telegram to Discord on bridge ${bridge.name}: ${err.message}` ); } diff --git a/src/telegram2discord/setup.ts b/src/telegram2discord/setup.ts index 351776c8..f8e1acb9 100644 --- a/src/telegram2discord/setup.ts +++ b/src/telegram2discord/setup.ts @@ -85,6 +85,8 @@ export function setup( // Set keeping track of where the "This is an instance of TediCross..." has been sent the last minute const antiInfoSpamSet = new Set(); + const groupIdMap: Map = new Map(); + // Add some global context tgBot.context.TediCross = { me, @@ -93,7 +95,8 @@ export function setup( settings, messageMap, logger, - antiInfoSpamSet + antiInfoSpamSet, + groupIdMap }; // Apply middlewares and endwares