From 50e27b2e7de6753a72bf61785785d82e7126bae3 Mon Sep 17 00:00:00 2001 From: Inex Code Date: Fri, 13 Nov 2020 06:42:45 +0000 Subject: [PATCH] Move from global message IDs to local IDs --- README.md | 12 +++++-- src/index.ts | 1 + src/vk.ts | 96 ++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 77 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 0f5f7ff..4b48371 100755 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # mx-puppet-vk This is a Matrix <-> VK bridge based on [mx-puppet-bridge](https://github.com/Sorunome/mx-puppet-bridge) and [VK-IO](https://github.com/negezor/vk-io). -It is in early development and should be considered as proof-of-concept. Right now it logs message data when log level includes "info" level. +It is in early development. Right now it logs message data when log level includes "info" level. -Group chats are not properly tested. +Relay mode works too, but we don't recommend it. ## Installation ```bash @@ -25,7 +25,7 @@ Register that one with synapse and start the bridge with `npm run start`. - [ ] Presence - not possible yet - [ ] Read notifications - not possible yet - [x] Message edits - - [x] Message redacts - only works in 24 hours after message is sent + - [x] Message redacts - works as edit, real redact unavailable without being admin in chat - VK -> Matrix - [x] Text content - [ ] Forwards @@ -49,3 +49,9 @@ Register that one with synapse and start the bridge with `npm run start`. 5. Now, if someone contacts your community, you will be invited to the corresponding room on Matrix. Plese note: when community is invited to the group chat as a bot, make sure it has message access. Only chat admins can change bot permissions. + +Bridge doesn't handle being kicked from chat yet. + +### Relay usage + +See [mx-puppet-bridge docs](https://github.com/Sorunome/mx-puppet-bridge#relay-mode) diff --git a/src/index.ts b/src/index.ts index e46f9e2..bf84764 100755 --- a/src/index.ts +++ b/src/index.ts @@ -52,6 +52,7 @@ const protocol: IProtocolInformation = { presence: false, reply: true, edit: true, + advancedRelay: true, }, id: "vk", // an internal ID for the protocol, all lowercase displayname: "VK", // a human-readable name of the protocol diff --git a/src/vk.ts b/src/vk.ts index ca70542..5312b43 100755 --- a/src/vk.ts +++ b/src/vk.ts @@ -81,7 +81,7 @@ export class VkPuppet { const info = await p.client.api.messages.getConversationsById({ peer_ids: peerId, fields: ["photo_max"] }); // log.info(info.items[0]); let response: IRemoteRoom; - switch (info.items[0].peer.type) { + switch (info.items[0]?.peer.type || "chat") { case "user": // tslint:disable-next-line: no-shadowed-variable const userInfo = await p.client.api.users.get({ user_ids: info.items[0].peer.id, fields: ["photo_max"] }); @@ -98,8 +98,9 @@ export class VkPuppet { response = { puppetId, roomId: peerId.toString(), - name: info.items[0].chat_settings.title, - avatarUrl: info.items[0].chat_settings.photo.photo_200, + name: info.items[0]?.chat_settings.title || `VK chat №${(peerId - 2000000000).toString()}`, + topic: info.count === 0 ? "To recieve chat name and avatar, puppet needs admin rights on VK side" : null, + avatarUrl: info.items[0]?.chat_settings.photo?.photo_200, }; break; @@ -177,7 +178,7 @@ export class VkPuppet { ////////////////////////// // tslint:disable-next-line: no-any - public async handleMatrixMessage(room: IRemoteRoom, data: IMessageEvent, event: any) { + public async handleMatrixMessage(room: IRemoteRoom, data: IMessageEvent, asUser: ISendingUser | null, event: any) { // this is called every time we receive a message from matrix and need to // forward it to the remote protocol. @@ -186,30 +187,43 @@ export class VkPuppet { if (!p) { return; } + + if (asUser) { + const MAX_NAME_LENGTH = 80; + const displayname = (new TextEncoder().encode(asUser.displayname)); + asUser.displayname = (new TextDecoder().decode(displayname.slice(0, MAX_NAME_LENGTH))); + } // usually you'd send it here to the remote protocol via the client object try { const response = await p.client.api.messages.send({ - peer_id: Number(room.roomId), - message: data.body, + peer_ids: Number(room.roomId), + message: asUser ? `${asUser.displayname}: ${data.body}` : data.body, random_id: new Date().getTime(), }); - await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); + await this.puppet.eventSync.insert(room, data.eventId!, + response[0]["conversation_message_id"].toString()); } catch (err) { log.error("Error sending to vk", err.error || err.body || err); } } - public async handleMatrixEdit(room: IRemoteRoom, eventId: string, data: IMessageEvent) { + public async handleMatrixEdit(room: IRemoteRoom, eventId: string, data: IMessageEvent, asUser: ISendingUser | null) { const p = this.puppets[room.puppetId]; if (!p) { return; } + + if (asUser) { + const MAX_NAME_LENGTH = 80; + const displayname = (new TextEncoder().encode(asUser.displayname)); + asUser.displayname = (new TextDecoder().decode(displayname.slice(0, MAX_NAME_LENGTH))); + } // usually you'd send it here to the remote protocol via the client object try { const response = await p.client.api.messages.edit({ peer_id: Number(room.roomId), - message: data.body, - message_id: Number(eventId), + conversation_message_id: Number(eventId), + message: asUser ? `${asUser.displayname}: ${data.body}` : data.body, random_id: new Date().getTime(), }); await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); @@ -218,18 +232,26 @@ export class VkPuppet { } } - public async handleMatrixRedact(room: IRemoteRoom, eventId: string) { + public async handleMatrixRedact(room: IRemoteRoom, eventId: string, asUser: ISendingUser | null) { const p = this.puppets[room.puppetId]; if (!p) { return; } + + if (asUser) { + const MAX_NAME_LENGTH = 80; + const displayname = (new TextEncoder().encode(asUser.displayname)); + asUser.displayname = (new TextDecoder().decode(displayname.slice(0, MAX_NAME_LENGTH))); + } // usually you'd send it here to the remote protocol via the client object try { - await p.client.api.messages.delete({ + await this.handleMatrixEdit(room, eventId, { body: "[ДАННЫЕ УДАЛЕНЫ]", eventId }, asUser); + // broken in chats without admin access + /*await p.client.api.messages.delete({ spam: 0, delete_for_all: 1, message_ids: Number(eventId), - }); + });*/ } catch (err) { log.error("Error sending edit to vk", err.error || err.body || err); } @@ -239,6 +261,7 @@ export class VkPuppet { room: IRemoteRoom, eventId: string, data: IMessageEvent, + asUser: ISendingUser | null, // tslint:disable-next-line: no-any event: any, ) { @@ -246,15 +269,22 @@ export class VkPuppet { if (!p) { return; } + + if (asUser) { + const MAX_NAME_LENGTH = 80; + const displayname = (new TextEncoder().encode(asUser.displayname)); + asUser.displayname = (new TextDecoder().decode(displayname.slice(0, MAX_NAME_LENGTH))); + } + try { - // log.info("Sending reply", Number(eventId)); const response = await p.client.api.messages.send({ - peer_id: Number(room.roomId), - message: await this.stripReply(data.body), + peer_ids: Number(room.roomId), + message: asUser ? `${asUser.displayname}: ${await this.stripReply(data.body)}` : await this.stripReply(data.body), random_id: new Date().getTime(), - reply_to: Number(eventId), + forward: `{"peer_id":${Number(room.roomId)},"conversation_message_ids":${Number(eventId)},"is_reply": true}`, }); - await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); + await this.puppet.eventSync.insert(room, data.eventId!, + response[0]["conversation_message_id"].toString()); } catch (err) { log.error("Error sending to vk", err.error || err.body || err); } @@ -274,22 +304,28 @@ export class VkPuppet { const MAXFILESIZE = 50000000; const size = data.info ? data.info.size || 0 : 0; + if (asUser) { + const MAX_NAME_LENGTH = 80; + const displayname = (new TextEncoder().encode(asUser.displayname)); + asUser.displayname = (new TextDecoder().decode(displayname.slice(0, MAX_NAME_LENGTH))); + } + if (size < MAXFILESIZE) { try { - // log.info("Sending image..."); const attachment = await p.client.upload.messagePhoto({ peer_id: Number(room.roomId), source: { value: data.url, }, }); - // log.info("Image sent", attachment); const response = await p.client.api.messages.send({ - peer_id: Number(room.roomId), + peer_ids: Number(room.roomId), random_id: new Date().getTime(), + message: asUser ? `${asUser.displayname} sent a photo:` : undefined, attachment: [`photo${attachment.ownerId}_${attachment.id}`], }); - await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); + await this.puppet.eventSync.insert(room, data.eventId!, + response[0]["conversation_message_id"].toString()); } catch (err) { log.error("Error sending to vk", err.error || err.body || err); } @@ -323,7 +359,6 @@ export class VkPuppet { if (size < MAXFILESIZE) { try { - // log.info("Sending file..."); const attachment = await p.client.upload.messageDocument({ peer_id: Number(room.roomId), source: { @@ -331,21 +366,22 @@ export class VkPuppet { filename: data.filename, }, }); - // log.info("File sent", attachment); const response = await p.client.api.messages.send({ peer_id: Number(room.roomId), random_id: new Date().getTime(), + message: asUser ? `${asUser.displayname} sent a file:` : undefined, attachment: [`doc${attachment.ownerId}_${attachment.id}`], }); await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); } catch (err) { try { const response = await p.client.api.messages.send({ - peer_id: Number(room.roomId), + peer_ids: Number(room.roomId), message: `File ${data.filename} was sent, but VK refused to recieve it. You may download it there:\n${data.url}`, random_id: new Date().getTime(), }); - await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); + await this.puppet.eventSync.insert(room, data.eventId!, + response[0]["conversation_message_id"].toString()); } catch (err) { log.error("Error sending to vk", err.error || err.body || err); } @@ -353,11 +389,12 @@ export class VkPuppet { } else { try { const response = await p.client.api.messages.send({ - peer_id: Number(room.roomId), + peer_ids: Number(room.roomId), message: `File ${data.filename} was sent, but it is too big for VK. You may download it there:\n${data.url}`, random_id: new Date().getTime(), }); - await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); + await this.puppet.eventSync.insert(room, data.eventId!, + response[0]["conversation_message_id"].toString()); } catch (err) { log.error("Error sending to vk", err.error || err.body || err); } @@ -388,7 +425,8 @@ export class VkPuppet { return; // Deduping } - const params = await this.getSendParams(puppetId, context.peerId, context.senderId, context.id.toString()); + const params = await this.getSendParams(puppetId, context.peerId, context.senderId, + context.conversationMessageId?.toString() || context.id.toString()); if (context.hasText) { if (context.hasReplyMessage) {