From 443179b286d6ac4e421ddce2bf9a646edfa4ea17 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Sat, 24 Jul 2021 03:17:19 +0300 Subject: [PATCH] Version 0.6.0 * Fix forwards in user mode (closes #21) * Treat replies as forwards in user mode (closes #20) * Fix posts in user mode (closes #22) * Basic geo support * Initial autopopulate support --- README.md | 1 + package-lock.json | 33 +--- package.json | 2 +- src/attachments-handler.ts | 260 +++++++++++++++++++++++++ src/index.ts | 2 + src/vk.ts | 390 +++++++++++++++---------------------- 6 files changed, 426 insertions(+), 262 deletions(-) create mode 100644 src/attachments-handler.ts diff --git a/README.md b/README.md index bc896bc..6329c4a 100755 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ where the access token is `df89482ba9a19e5a2dee85031612b021a08cd521115e1c7d2cd70 - VK (AS A USER) -> Matrix - [x] Auth as a user instead of group - [x] Text content + - [x] Replies (as forwards) - [x] Forwards - [x] Image content - [ ] Audio content - unavailable via user tokens diff --git a/package-lock.json b/package-lock.json index 3103a12..2a70afc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mx-puppet-vk", - "version": "0.5.1", + "version": "0.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1080,21 +1080,6 @@ "domexception": "2.0.1", "fetch-blob": "2.1.2", "mime-types": "2.1.31" - }, - "dependencies": { - "mime-db": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", - "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" - }, - "mime-types": { - "version": "2.1.31", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", - "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", - "requires": { - "mime-db": "1.48.0" - } - } } }, "forwarded": { @@ -2543,13 +2528,13 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "vk-io": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/vk-io/-/vk-io-4.3.1.tgz", - "integrity": "sha512-ceFgfOHF3uNyy+jBDD4ZZzymcaDbBd3k1UgKWt0L02fzDqJPhQVXjy8x6OKSZXBxqEtQP/M2K3GroEqs/4PqGg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/vk-io/-/vk-io-4.3.2.tgz", + "integrity": "sha512-9L532/3hxkqNhrK3NcHOQ1aiIPxIDExY+D/bwbJX6gE1zT+rVYAyteapXkYR8xuD9qCcgbDBBu8b+h5a1NFVOA==", "requires": { "abort-controller": "^3.0.0", - "debug": "^4.3.1", - "form-data-encoder": "^1.0.0", + "debug": "^4.3.2", + "form-data-encoder": "^1.0.1", "formdata-node": "^3.5.4", "inspectable": "^1.2.0", "middleware-io": "^2.8.0", @@ -2557,9 +2542,9 @@ }, "dependencies": { "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "requires": { "ms": "2.1.2" } diff --git a/package.json b/package.json index 4993855..3ac8007 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mx-puppet-vk", - "version": "0.5.3", + "version": "0.6.0", "description": "Matrix <-> VK bridge based on mx-puppet-bridge and VK-IO.", "main": "index.js", "repository": { diff --git a/src/attachments-handler.ts b/src/attachments-handler.ts new file mode 100644 index 0000000..c1017fb --- /dev/null +++ b/src/attachments-handler.ts @@ -0,0 +1,260 @@ +// first we import a few needed things again +import { + PuppetBridge, + IReceiveParams, + IMessageEvent, + Log, +} from "mx-puppet-bridge"; + +import { VK, AttachmentType, MessageForwardsCollection } from "vk-io"; +import { MessagesForeignMessage, MessagesMessageAttachment } from "vk-io/lib/api/schemas/objects"; +import { VkPuppet } from "./vk"; + +// here we create our log instance +const log = new Log("VKPuppet:attachment-handler"); + +// this interface is to hold all data on a single puppet +interface IEchoPuppet { + // this is usually a client class that connects to the remote protocol + // as we just echo back, unneeded in our case + client: VK; + // tslint:disable-next-line: no-any + data: any; // and let's keep a copy of the data associated with a puppet +} + +export class AttachmentsHandler { + private puppet: IEchoPuppet; + private puppetBridge: PuppetBridge; + constructor(puppet: IEchoPuppet, puppetBridge: PuppetBridge) { + this.puppet = puppet; + this.puppetBridge = puppetBridge; + } + + public getBiggestImage(images: object[]): any { + let maxImageResolution = 0; + let biggestImage: any = null; + images.forEach((image: object) => { + if (maxImageResolution < (image["width"] + image["height"])) { + maxImageResolution = image["width"] + image["height"]; + biggestImage = image; + } + }); + + return biggestImage; + } + + public async handlePhotoAttachment(params: IReceiveParams, attachment: MessagesMessageAttachment) { + try { + if (this.puppet.data.isUserToken) { + // VK API is weird. Very weird. + const biggestImage = this.getBiggestImage( + attachment["photo"]["sizes"], + ); + const url: string = biggestImage["url"] || ""; + + if (url === "") { + log.error(`Image not found in ${attachment["photo"]}`); + } + await this.puppetBridge.sendFileDetect(params, url); + } else { + await this.puppetBridge.sendFileDetect(params, attachment["largeSizeUrl"]); + } + } catch (err) { + const opts: IMessageEvent = { + body: `Image: ${attachment["image"]["largeSizeUrl"]}`, + }; + await this.puppetBridge.sendMessage(params, opts); + } + } + + public async handleStickerAttachment(params: IReceiveParams, attachment: MessagesMessageAttachment) { + try { + if (this.puppet.data.isUserToken) { + await this.puppetBridge.sendFileDetect( + params, attachment["sticker"]["images_with_background"][4]["url"], + ); + } else { + await this.puppetBridge.sendFileDetect(params, attachment["imagesWithBackground"][4]["url"]); + } + + } catch (err) { + const opts: IMessageEvent = { + body: `Sticker: ${attachment["imagesWithBackground"][4]["url"]}`, + }; + await this.puppetBridge.sendMessage(params, opts); + } + } + + public async handleAudioMessage(params: IReceiveParams, attachment: MessagesMessageAttachment) { + const audioUrl: string = attachment["oggUrl"] || attachment["url"] || attachment["link_ogg"]; + if (audioUrl === undefined || audioUrl === "") { + const opts: IMessageEvent = { + body: "Audio messages aren't supported yet", + }; + await this.puppetBridge.sendMessage(params, opts); + } else { + try { + await this.puppetBridge.sendAudio(params, audioUrl); + } catch (err) { + const opts: IMessageEvent = { + body: `Audio message: ${audioUrl}`, + }; + await this.puppetBridge.sendMessage(params, opts); + } + } + if (attachment["transcript"] !== undefined) { + const opts: IMessageEvent = { + body: "[Transcript]" + attachment["transcript"], + }; + await this.puppetBridge.sendMessage(params, opts); + } + } + + public async handleAudio(params: IReceiveParams, attachment: MessagesMessageAttachment) { + const audioUrl: string = attachment["url"]; + if (audioUrl === undefined || audioUrl === "") { + const opts: IMessageEvent = { + body: "Audio in messages aren't supported yet", + }; + await this.puppetBridge.sendMessage(params, opts); + } else { + try { + await this.puppetBridge.sendAudio(params, audioUrl); + } catch (err) { + const opts: IMessageEvent = { + body: `Audio: ${attachment["title"]} by ${attachment["artist"]} ${audioUrl}`, + }; + await this.puppetBridge.sendMessage(params, opts); + } + } + } + + public async handleDocument(params: IReceiveParams, attachment: MessagesMessageAttachment) { + try { + if (this.puppet.data.isUserToken) { + await this.puppetBridge.sendFileDetect(params, attachment["doc"]["url"], attachment["doc"]["title"]); + } else { + const opts: IMessageEvent = { + body: `Document: ${attachment["url"]}`, + }; + await this.puppetBridge.sendMessage(params, opts); + } + } catch (err) { + if (this.puppet.data.isUserToken) { + const opts: IMessageEvent = { + body: `Document: ${attachment["doc"]["url"]}`, + }; + await this.puppetBridge.sendMessage(params, opts); + } else { + const opts: IMessageEvent = { + body: `Document: ${attachment["url"]}`, + }; + await this.puppetBridge.sendMessage(params, opts); + } + } + } + + public async handleForwards( + vkPuppet: VkPuppet, puppetId: number, messageBody: string, + params: IReceiveParams, forwards: MessageForwardsCollection | MessagesForeignMessage[], + ) { + let formatted = `${messageBody}\n`; + + for (const f of forwards) { + const user = await vkPuppet.getRemoteUser(puppetId, Number(f.senderId)); + formatted += `> <[${user.name}](${user.externalUrl})>\n`; + f.text?.split("\n").forEach((element) => { + formatted += `> ${element}\n`; + }); + if (f.attachments !== undefined && f.attachments.length !== 0) { + f.attachments?.forEach(async (attachment) => { + switch (attachment.type) { + case AttachmentType.PHOTO: + formatted += `> 🖼️ [Photo](${attachment["largeSizeUrl"]})\n`; + break; + case AttachmentType.STICKER: + formatted += `> 🖼️ [Sticker](${attachment["imagesWithBackground"][4]["url"]})\n`; + break; + case AttachmentType.AUDIO_MESSAGE: + formatted += `> 🗣️ [Audio message](${attachment["oggUrl"]})\n`; + break; + case AttachmentType.AUDIO: + formatted += `> 🗣️ [Audio message](${attachment["oggUrl"] ?? attachment["url"]})\n`; + break; + case AttachmentType.DOCUMENT: + formatted += `> 📁 [File ${attachment["title"]}](${attachment["url"]})\n`; + break; + case AttachmentType.LINK: + formatted += `> 🔗 [ ${attachment["title"] ? attachment["title"] : attachment["url"]} ](${attachment["url"]})\n`; + break; + default: + formatted += `> ❓️ Unhandled attachment of type ${attachment.type}\n`; + break; + } + }); + } + if (f.hasForwards) { + ( + await this.handleForwards(vkPuppet, puppetId, "", params, f.forwards) + ).trim().split("\n").forEach((element) => { + formatted += `> ${element}\n`; + }, + ); + } + formatted += "\n"; + } + return formatted; + } + + public async handleForwardsAsUser( + vkPuppet: VkPuppet, puppetId: number, messageBody: string, + params: IReceiveParams, forwards: MessageForwardsCollection | MessagesForeignMessage[], + ) { + let formatted = `${messageBody}\n`; + + for (const f of forwards) { + const user = await vkPuppet.getRemoteUser(puppetId, Number(f.from_id)); + formatted += `> <[${user.name}](${user.externalUrl})>\n`; + f.text?.split("\n").forEach((element) => { + formatted += `> ${element}\n`; + }); + if (f.attachments !== undefined && f.attachments.length !== 0) { + f.attachments?.forEach(async (attachment) => { + switch (attachment.type) { + case AttachmentType.PHOTO: + formatted += `> 🖼️ [Photo](${this.getBiggestImage(attachment[attachment.type]["sizes"])["url"]})\n`; + break; + case AttachmentType.STICKER: + formatted += `> 🖼️ [Sticker](${this.getBiggestImage(attachment[attachment.type]["images_with_background"])["url"]})\n`; + break; + case AttachmentType.AUDIO_MESSAGE: + formatted += `> 🗣️ [Audio message](${attachment[attachment.type]["link_ogg"]}) \n`; + if (attachment[attachment.type]["transcript"] !== undefined) { + formatted += `> > [Transcript] ${attachment[attachment.type]["transcript"]}`; + } + break; + case AttachmentType.DOCUMENT: + formatted += `> 📁 [File ${attachment[attachment.type]["title"]}](${attachment[attachment.type]["url"]})\n`; + break; + case AttachmentType.LINK: + formatted += `> 🔗 [ ${attachment["title"] ? attachment["title"] : attachment["url"]} ](${attachment["url"]})\n`; + break; + default: + formatted += `> ❓️ Unhandled attachment of type ${attachment.type}\n`; + break; + } + }); + } + if (f.fwd_messages !== undefined) { + ( + await this.handleForwardsAsUser(vkPuppet, puppetId, "", params, f.fwd_messages) + ).trim().split("\n").forEach((element) => { + formatted += `> ${element}\n`; + }); + } + formatted += "\n"; + } + return formatted; + } + +} diff --git a/src/index.ts b/src/index.ts index 3febece..6dd843f 100755 --- a/src/index.ts +++ b/src/index.ts @@ -98,6 +98,8 @@ async function run() { puppet.on("typing", vk.handleMatrixTyping.bind(vk)); puppet.setCreateRoomHook(vk.createRoom.bind(vk)); + puppet.setGetUserIdsInRoomHook(vk.getUserIdsInRoom.bind(vk)); + puppet.setCreateUserHook(vk.createUser.bind(vk)); // required: get description hook // tslint:disable-next-line: no-any puppet.setGetDescHook(async (puppetId: number, data: any): Promise => { diff --git a/src/vk.ts b/src/vk.ts index 0c861ae..da86920 100755 --- a/src/vk.ts +++ b/src/vk.ts @@ -16,8 +16,10 @@ import { userInfo } from "os"; import { runInThisContext } from "vm"; import { lookup } from "dns"; import { Converter } from "showdown"; -import { MessagesMessageAttachment } from "vk-io/lib/api/schemas/objects"; +import { MessagesMessage, MessagesMessageAttachment } from "vk-io/lib/api/schemas/objects"; import { ElementFlags, OptionalTypeNode } from "typescript"; +import { AttachmentsHandler } from "./attachments-handler"; +import { debug } from "console"; // here we create our log instance const log = new Log("VKPuppet:vk"); @@ -36,183 +38,6 @@ interface IEchoPuppets { [puppetId: number]: IEchoPuppet; } - -export class AttachmentsHandler { - private puppet: IEchoPuppet; - private puppetBridge: PuppetBridge; - constructor (puppet: IEchoPuppet, puppetBridge: PuppetBridge) { - this.puppet = puppet; - this.puppetBridge = puppetBridge; - } - - public getBiggestImage(images: Array): any { - let maxImageResolution = 0; - let biggestImage: any = null; - images.forEach( - function(image: object) { - if (maxImageResolution < (image["width"] + image["height"])) { - maxImageResolution = image["width"] + image["height"]; - biggestImage = image; - } - } - ); - - return biggestImage; - }; - - public async handlePhotoAttachment(params: IReceiveParams, attachment: MessagesMessageAttachment) { - try { - if (this.puppet.data.isUserToken) { - // VK API is weird. Very weird. - let biggestImage = this.getBiggestImage( - attachment["photo"]["sizes"] - ); - let url: string = biggestImage['url'] || ""; - - if (url === "") { - log.error(`Image not found in ${attachment["photo"]}`); - }; - await this.puppetBridge.sendFileDetect(params, url); - } else { - await this.puppetBridge.sendFileDetect(params, attachment["largeSizeUrl"]); - } - } catch (err) { - const opts: IMessageEvent = { - body: `Image: ${attachment["image"]["largeSizeUrl"]}`, - }; - await this.puppetBridge.sendMessage(params, opts); - } - } - - - public async handleStickerAttachment(params: IReceiveParams, attachment: MessagesMessageAttachment) { - try { - if (this.puppet.data.isUserToken) { - await this.puppetBridge.sendFileDetect( - params, attachment["sticker"]["images_with_background"][4]["url"] - ) - } else { - await this.puppetBridge.sendFileDetect(params, attachment["imagesWithBackground"][4]["url"]); - } - - } catch (err) { - const opts: IMessageEvent = { - body: `Sticker: ${attachment["imagesWithBackground"][4]["url"]}`, - }; - await this.puppetBridge.sendMessage(params, opts); - } - } - - public async handleAudioMessage(params: IReceiveParams, attachment: MessagesMessageAttachment) { - let audio_url: string = attachment["oggUrl"] || attachment["url"]; - if (audio_url === undefined || audio_url === "") { - const opts: IMessageEvent = { - body: "Audio messages aren't supported yet", - }; - await this.puppetBridge.sendMessage(params, opts); - } else { - try { - await this.puppetBridge.sendAudio(params, audio_url); - } catch (err) { - const opts: IMessageEvent = { - body: `Audio message: ${audio_url}`, - }; - await this.puppetBridge.sendMessage(params, opts); - } - } - } - - public async handleAudio(params: IReceiveParams, attachment: MessagesMessageAttachment) { - let audio_url: string = attachment["url"]; - if (audio_url === undefined || audio_url === "") { - const opts: IMessageEvent = { - body: "Audio in messages aren't supported yet", - }; - await this.puppetBridge.sendMessage(params, opts); - } else { - try { - await this.puppetBridge.sendAudio(params, audio_url); - } catch (err) { - const opts: IMessageEvent = { - body: `Audio: ${attachment["title"]} by ${attachment["artist"]} ${audio_url}`, - }; - await this.puppetBridge.sendMessage(params, opts); - } - } - } - - public async handleDocument(params: IReceiveParams, attachment: MessagesMessageAttachment) { - try { - if (this.puppet.data.isUserToken) { - await this.puppetBridge.sendFileDetect(params, attachment["doc"]["url"], attachment["doc"]["title"]); - } else { - const opts: IMessageEvent = { - body: `Document: ${attachment["url"]}`, - }; - await this.puppetBridge.sendMessage(params, opts); - } - } catch (err) { - const opts: IMessageEvent = { - body: `Document: ${attachment["url"]}`, - }; - await this.puppetBridge.sendMessage(params, opts); - } - } - - public async handleForwards( - vkPuppet: VkPuppet, puppetId: number, message_body: string, - params: IReceiveParams, forwards: MessageForwardsCollection - ) { - let formatted = `${message_body}\n`; - log.debug("Forawrded messages", forwards); - - for (const f of forwards) { - const user = await vkPuppet.getRemoteUser(puppetId, Number(f.senderId)); - log.debug("Forwarder", user); - formatted += `> <[${user.name}](${user.externalUrl})>\n`; - f.text?.split("\n").forEach((element) => { - formatted += `> ${element}\n`; - }); - if (f.hasAttachments()) { - f.attachments.forEach(async (attachment) => { - switch (attachment.type) { - case AttachmentType.PHOTO: - await this.handlePhotoAttachment(params, attachment); - break; - case AttachmentType.STICKER: - await this.handleStickerAttachment(params, attachment); - break; - case AttachmentType.AUDIO_MESSAGE: - await this.handleAudioMessage(params, attachment); - break; - case AttachmentType.DOCUMENT: - await this.handleDocument(params, attachment); - break; - case AttachmentType.LINK: - formatted += `> 🔗 [ ${attachment["title"] ? attachment["title"] : attachment["url"]} ](${attachment["url"]})\n`; - break; - default: - formatted += `> ❓️ Unhandled attachment of type ${attachment.type}\n`; - break; - } - }); - } - if (f.hasForwards) { - ( - await this.handleForwards(vkPuppet, puppetId, "", params, f.forwards) - ).trim().split("\n").forEach((element) => { - formatted += `> ${element}\n`; - } - ); - } - formatted += "\n"; - } - return formatted; - } - -} - - export class VkPuppet { private puppets: IEchoPuppets = {}; private converter: Converter = new Converter({ @@ -316,6 +141,31 @@ export class VkPuppet { return response; } + public async getUserIdsInRoom(room: IRemoteRoom): Promise | null> { + const p = this.puppets[room.puppetId]; + + const users = new Set(); + if (room.isDirect === false) { + const response = await p.client.api.messages.getConversationMembers({ peer_id: Number(room.roomId) }); + response.items.forEach((element) => { + users.add(element.member_id.toString()); + }); + } + return users; + } + + public async createUser(user: IRemoteUser): Promise { + const p = this.puppets[user.puppetId]; + if (!p) { + return null; + } + const remoteUser = await this.getRemoteUser(user.puppetId, Number(user.userId)); + if (!remoteUser) { + return null; + } + return remoteUser; + } + // tslint:disable-next-line: no-any public async newPuppet(puppetId: number, data: any) { // this is called when we need to create a new puppet @@ -328,7 +178,6 @@ export class VkPuppet { // and listen to incoming messages from it try { const client = new VK({ token: data.token, apiLimit: 20 }); - log.debug("Trying to init listener with", data.token); client.updates.on("message_new", async (context) => { try { @@ -681,42 +530,65 @@ export class VkPuppet { p.data.isUserToken ? context.id.toString() : context.conversationMessageId?.toString() || context.id.toString()); const attachmentHandler = new AttachmentsHandler(p, this.puppet); - if (context.hasText || context.hasForwards) { - let msgText: string = context.text || ""; - if (context.hasForwards) { + let msgText: string = context.text || ""; + let fullContext: MessagesMessage | undefined; + if (p.data.isUserToken) { + fullContext = (await p.client.api.messages.getById({ message_ids: context.id, extended: 1 })).items[0]; + if (fullContext.geo !== undefined) { + msgText += `geo:${fullContext.geo.coordinates.latitude},${fullContext.geo.coordinates.longitude}\n`; + } + } else { + fullContext = undefined; + if (context.geo !== undefined) { + msgText += `geo:${context.geo.coordinates.latitude},${context.geo.coordinates.longitude}\n`; + } + } + if (context.hasForwards) { + if (fullContext !== undefined) { + const forwards = (await p.client.api.messages.getById({ message_ids: context.id, extended: 1 })).items[0]["fwd_messages"]; + if (fullContext.fwd_messages !== undefined) { + try { + msgText = await attachmentHandler.handleForwardsAsUser(this, puppetId, msgText, params, fullContext.fwd_messages); + } catch (err) { + log.error(err); + log.debug(context); + } + } + } else { try { - msgText = await attachmentHandler.handleForwards(this, puppetId, msgText, params, context.forwards); + msgText = await attachmentHandler.handleForwards(this, puppetId, msgText, params, (context.forwards)); } catch (err) { log.error(err); log.debug(context); } } - - // TODO: fix handling of replies somehow because they aren't sending replies in matrix in user mode at all - if (context.hasReplyMessage) { - if (this.puppet.eventSync.getMatrix(params.room, context.replyMessage!.id.toString())) { - const opts: IMessageEvent = { - body: msgText || "Attachment", - formattedBody: this.converter.makeHtml(msgText), - }; - // We got referenced message in room, using matrix reply - await this.puppet.sendReply(params, context.replyMessage!.id.toString(), opts); - } else { - // Using a fallback - const opts: IMessageEvent = { - body: await this.prependReply( - puppetId, msgText || "", - context.replyMessage?.text || "", - context.senderId.toString(), - ), - }; - await this.puppet.sendMessage(params, opts); - } - } else { + } + + if (context.hasReplyMessage) { + if (this.puppet.eventSync.getMatrix(params.room, context.replyMessage!.id.toString())) { const opts: IMessageEvent = { body: msgText || "Attachment", formattedBody: this.converter.makeHtml(msgText), }; + // We got referenced message in room, using matrix reply + await this.puppet.sendReply(params, context.replyMessage!.id.toString(), opts); + } else { + // Using a fallback + const opts: IMessageEvent = { + body: await this.prependReply( + puppetId, msgText || "", + context.replyMessage?.text || "", + context.senderId.toString(), + ), + }; + await this.puppet.sendMessage(params, opts); + } + } else { + if (msgText !== "") { + const opts: IMessageEvent = { + body: msgText, + formattedBody: this.converter.makeHtml(msgText), + }; await this.puppet.sendMessage(params, opts); } } @@ -737,7 +609,7 @@ export class VkPuppet { break; case AttachmentType.AUDIO_MESSAGE: - await attachmentHandler.handleAudioMessage(params, f) + await attachmentHandler.handleAudioMessage(params, f["audio_message"]); break; case AttachmentType.AUDIO: @@ -831,38 +703,82 @@ export class VkPuppet { } public async renderWallPost(puppetId: number, post: MessagesMessageAttachment) { - const user = await this.getRemoteUser(puppetId, Number(post.fromId)); - let formatted = `Forwarded post from [${user.name}](${user.externalUrl})\n`; - post.text?.split("\n").forEach((element) => { - formatted += `> ${element}\n`; - }); - if (post.hasAttachments()) { - post.attachments.forEach((attachment) => { - switch (attachment.type) { - case AttachmentType.PHOTO: - formatted += `> 🖼️ [Photo](${attachment["largeSizeUrl"]})\n`; - break; - case AttachmentType.STICKER: - formatted += `> 🖼️ [Sticker](${attachment["imagesWithBackground"][4]["url"]})\n`; - break; - case AttachmentType.AUDIO_MESSAGE: - formatted += `> 🗣️ [Audio message](${attachment["oggUrl"]})\n`; - break; - case AttachmentType.AUDIO: - formatted += `> 🗣️ [Audio](${attachment["oggUrl"] ?? attachment["url"]})\n`; - break; - case AttachmentType.DOCUMENT: - formatted += `> 📁 [File ${attachment["title"]}](${attachment["url"]})\n`; - break; - case AttachmentType.LINK: - formatted += `> 🔗 [ ${attachment["title"] ? attachment["title"] : attachment["url"]} ](${attachment["url"]})\n`; - break; - default: - formatted += `> ❓️ Unhandled attachment of type ${attachment.type}\n`; - break; - } + + const renderWallPostAsGroup = async () => { + const user = await this.getRemoteUser(puppetId, Number(post.fromId)); + let formatted = `Forwarded post from [${user.name}](${user.externalUrl})\n`; + post.text?.split("\n").forEach((element) => { + formatted += `> ${element}\n`; }); + if (post.hasAttachments()) { + post.attachments.forEach((attachment) => { + switch (attachment.type) { + case AttachmentType.PHOTO: + formatted += `> 🖼️ [Photo](${attachment["largeSizeUrl"]})\n`; + break; + case AttachmentType.STICKER: + formatted += `> 🖼️ [Sticker](${attachment["imagesWithBackground"][4]["url"]})\n`; + break; + case AttachmentType.AUDIO_MESSAGE: + formatted += `> 🗣️ [Audio message](${attachment["oggUrl"]})\n`; + break; + case AttachmentType.AUDIO: + formatted += `> 🗣️ [Audio](${attachment["oggUrl"] ?? attachment["url"]})\n`; + break; + case AttachmentType.DOCUMENT: + formatted += `> 📁 [File ${attachment["title"]}](${attachment["url"]})\n`; + break; + case AttachmentType.LINK: + formatted += `> 🔗 [ ${attachment["title"] ? attachment["title"] : attachment["url"]} ](${attachment["url"]})\n`; + break; + default: + formatted += `> ❓️ Unhandled attachment of type ${attachment.type}\n`; + break; + } + }); + } + return formatted; + }; + + const renderWallPostAsUser = async () => { + const user = await this.getRemoteUser(puppetId, Number(post.fromId)); + let formatted = `Forwarded post from [${user.name}](${user.externalUrl})\n`; + post = post.wall; + post.text?.split("\n").forEach((element) => { + formatted += `> ${element}\n`; + }); + if (post.attachments !== undefined && post.attachments.length !== 0) { + const attachmentHandler = new AttachmentsHandler(p, this.puppet); + post.attachments.forEach((attachment) => { + switch (attachment.type) { + case AttachmentType.PHOTO: + formatted += + `> 🖼️ [Photo](${attachmentHandler.getBiggestImage(attachment[attachment.type]["sizes"])["url"]})\n`; + break; + case AttachmentType.AUDIO: + formatted += `> 🗣️ [Audio] ${attachment[attachment.type]["title"]} by ${attachment[attachment.type]["artist"]} ${attachment[attachment.type]["url"]}\n`; + break; + case AttachmentType.DOCUMENT: + formatted += `> 📁 [File ${attachment[attachment.type]["title"]}](${attachment[attachment.type]["url"]})\n`; + break; + case AttachmentType.LINK: + formatted += `> 🔗 [ ${attachment[attachment.type]["title"] ? attachment[attachment.type]["title"] : attachment[attachment.type]["url"]} ](${attachment[attachment.type]["url"]})\n`; + break; + default: + formatted += `> ❓️ Unhandled attachment of type ${attachment.type}\n`; + break; + } + }); + } + return formatted; + }; + + const p = this.puppets[puppetId]; + if (p.data.isUserToken) { + return await renderWallPostAsUser(); + } else { + return await renderWallPostAsGroup(); } - return formatted; + } }