From a7d051cc0871afd5cea5389381715fcd94a34310 Mon Sep 17 00:00:00 2001 From: kutuk Date: Wed, 9 Aug 2023 10:54:00 +0200 Subject: [PATCH] + Discord msg limit = 2000 chars. Use embeds for big messages. + For text with inline links in text use embeds. --- package.json | 2 +- src/telegram2discord/endwares.ts | 102 ++++++++++++++----------- src/telegram2discord/handleEntities.ts | 48 ++++++------ src/telegram2discord/middlewares.ts | 9 ++- 4 files changed, 89 insertions(+), 72 deletions(-) diff --git a/package.json b/package.json index 51fb8342..db595344 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tedicross", - "version": "0.12.2", + "version": "0.12.3", "description": "Bridging Telegram and Discord", "license": "MIT", "repository": { diff --git a/src/telegram2discord/endwares.ts b/src/telegram2discord/endwares.ts index e629c618..5d1c3b10 100644 --- a/src/telegram2discord/endwares.ts +++ b/src/telegram2discord/endwares.ts @@ -5,9 +5,15 @@ import { fetchDiscordChannel } from "../fetchDiscordChannel"; import { Context } from "telegraf"; import { deleteMessage, ignoreAlreadyDeletedError } from "./helpers"; import { createFromObjFromUser } from "./From"; -import { MessageEditOptions } from "discord.js"; +import { MessageEditOptions, EmbedBuilder } from "discord.js"; import { Message, User } from "telegraf/typings/core/types/typegram"; +interface DiscordMessage { + embeds?: any[]; + content?: string; + files?: any[]; +} + export interface TediCrossContext extends Context { TediCross: any; tediCross: { @@ -155,6 +161,9 @@ const parseMediaGroup = (ctx: TediCrossContext, byTimer: boolean = false) => { if (lPrepared.header) { prepared.header = lPrepared.header; } + if (lPrepared.hasLinks) { + prepared.hasLinks = lPrepared.hasLinks; + } if (lPrepared.text) { prepared.text = lPrepared.text; } @@ -209,47 +218,37 @@ export const relayMessage = (ctx: TediCrossContext) => { const messageToReply = prepared.messageToReply; const replyId = prepared.replyId; - // Discord doesn't handle messages longer than 2000 characters. Split it up into chunks that big const messageText = prepared.header + "\n" + prepared.text; - let chunks = R.splitEvery(2000, messageText); - // Send the attachment first, if there is one + // NOTE: using EMBED when over 2000 symbols length + + const sendObject: DiscordMessage = {}; + if ((messageText.length > 2000) || prepared.hasLinks) { + const text = prepared.text.length > 4096 ? prepared.text.substring(0, 4090) + "..." : prepared.text; + const embed = new EmbedBuilder().setTitle(prepared.header).setDescription(text); + sendObject.embeds = [embed]; + } else { + sendObject.content = messageText; + } + if (!R.isNil(prepared.file)) { - try { - if (replyId === "0" || replyId === undefined || messageToReply === undefined) { - dcMessage = await channel.send({ - content: R.head(chunks), - files: prepared.files || [prepared.file] - }); - } else { - dcMessage = await messageToReply.reply({ - content: R.head(chunks), - files: prepared.files || [prepared.file] - }); - } - chunks = R.tail(chunks); - } catch (err: any) { - 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*` - ); - } else { - throw err; - } - } + sendObject.files = prepared.files || [prepared.file]; } - if (replyId === "0" || replyId === undefined || messageToReply === undefined) { - dcMessage = await R.reduce( - (p, chunk) => p.then(() => channel.send(chunk)), - Promise.resolve(dcMessage), - chunks - ); - } else { - dcMessage = await R.reduce( - (p, chunk) => p.then(() => messageToReply.reply(chunk)), - Promise.resolve(dcMessage), - chunks - ); + + try { + if (replyId === "0" || replyId === undefined || messageToReply === undefined) { + dcMessage = await channel.send(sendObject); + } else { + dcMessage = await messageToReply.reply(sendObject); + } + } catch (err: any) { + 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*` + ); + } else { + throw err; + } } // Make the mapping so future edits can work XXX Only the last chunk is considered @@ -325,16 +324,31 @@ export const handleEdits = createMessageHandler(async (ctx: TediCrossContext, br R.forEach(async (prepared: any) => { // Discord doesn't handle messages longer than 2000 characters. Take only the first 2000 - const messageText = R.slice(0, 2000, prepared.header + "\n" + prepared.text); + const messageText = prepared.header + "\n" + prepared.text; // R.slice(0, 2000, + + const sendObject: DiscordMessage = {}; + if ((messageText.length > 2000) || prepared.hasLinks) { + const text = prepared.text.length > 4096 ? prepared.text.substring(0, 4090) + "..." : prepared.text; + const embed = new EmbedBuilder().setTitle(prepared.header).setDescription(text); + sendObject.embeds = [embed]; + } else { + sendObject.content = messageText; + } + + if (!R.isNil(prepared.file)) { + sendObject.files = prepared.files || [prepared.file]; + } // Send them in serial, with the attachment first, if there is one if (typeof dcMessage.edit !== "function") { - console.error("dcMessage.edit is not a function"); + ctx.TediCross.logger.error("dcMessage.edit is not a function"); } else { - await dcMessage.edit({ - content: messageText, - attachment: prepared.attachment - } as MessageEditOptions); + await dcMessage.edit(sendObject as MessageEditOptions); + // { + // content: messageText, + // attachment: prepared.attachment + // } + // as MessageEditOptions); } })(ctx.tediCross.prepared); } catch (err: any) { diff --git a/src/telegram2discord/handleEntities.ts b/src/telegram2discord/handleEntities.ts index 64a8bd95..28c8c52f 100644 --- a/src/telegram2discord/handleEntities.ts +++ b/src/telegram2discord/handleEntities.ts @@ -28,9 +28,10 @@ const findFn = (prop: string, regexp: RegExp) => R.compose(R.not, R.isEmpty, R.m export async function handleEntities(text: string, entities: MessageEntity[], dcBot: Client, bridge: Bridge) { // Don't mess up the original const substitutedText = text !== undefined ? text.split("") : [""]; + let hasLinks = false; // Markdown links to put on the message - const markdownLinks = []; + // const markdownLinks = []; // Make sure messages without entities don't crash the thing if (!Array.isArray(entities)) { @@ -86,13 +87,14 @@ export async function handleEntities(text: string, entities: MessageEntity[], dc } case "text_link": { // Markdown style link. 'part' is the text, 'e.url' is the URL - // substitute = "[" + part + "](" + e.url + ")"; + substitute = "[" + part + "](" + e.url + ")"; + hasLinks = true; // Discord appears to not be able to handle this type of links. Make the substitution an object which can be found and properly substituted later - markdownLinks.unshift(e.url); - substitute = { - type: "mdlink", - text: part - }; + // markdownLinks.unshift(e.url); + // substitute = { + // type: "mdlink", + // text: part + // }; break; } case "bold": { @@ -162,22 +164,22 @@ export async function handleEntities(text: string, entities: MessageEntity[], dc } // Put the markdown links on the end, if there are any - if (!R.isEmpty(markdownLinks)) { - substitutedText.push("\n\n"); - for (let i = 0; i < markdownLinks.length; i++) { - // Find out where the corresponding text is - const index = substitutedText.findIndex((e: any) => e instanceof Object && e.type === "mdlink"); - const obj = substitutedText[index]; - - // Replace the object with the proper text and reference - //@ts-ignore - substitutedText[index] = `${obj.text}[${i + 1}]`; - - // Push the link to the end - substitutedText.push(`[${i + 1}]: ${markdownLinks[i]}\n`); - } - } + // if (!R.isEmpty(markdownLinks)) { + // substitutedText.push("\n\n"); + // for (let i = 0; i < markdownLinks.length; i++) { + // // Find out where the corresponding text is + // const index = substitutedText.findIndex((e: any) => e instanceof Object && e.type === "mdlink"); + // const obj = substitutedText[index]; + // + // // Replace the object with the proper text and reference + // //@ts-ignore + // substitutedText[index] = `${obj.text}[${i + 1}]`; + // + // // Push the link to the end + // substitutedText.push(`[${i + 1}]: ${markdownLinks[i]}\n`); + // } + // } // Return the converted string - return substitutedText.join(""); + return [substitutedText.join(""), hasLinks]; } diff --git a/src/telegram2discord/middlewares.ts b/src/telegram2discord/middlewares.ts index 06b2d2b9..5a37a580 100644 --- a/src/telegram2discord/middlewares.ts +++ b/src/telegram2discord/middlewares.ts @@ -652,14 +652,14 @@ async function addPreparedObj(ctx: TediCrossContext, next: () => void) { )(tc); // Make the text to send - const text = await (async () => { - let text = await handleEntities(tc.text.raw, tc.text.entities, ctx.TediCross.dcBot, bridge); + const [text, hasLinks] = await (async () => { + let [text, hasLinks] = await handleEntities(tc.text.raw, tc.text.entities, ctx.TediCross.dcBot, bridge); if (!R.isNil(replyQuote) && !tc.hasActualReference) { text = replyQuote + "\n" + text; } - return text; + return [text, hasLinks]; })(); return { @@ -669,7 +669,8 @@ async function addPreparedObj(ctx: TediCrossContext, next: () => void) { file, text, messageToReply, - replyId + replyId, + hasLinks }; }, tc.bridges) );