Move from global message IDs to local IDs

This commit is contained in:
Inex Code 2020-11-13 06:42:45 +00:00
parent 757badde67
commit 50e27b2e7d
3 changed files with 77 additions and 32 deletions

View file

@ -1,9 +1,9 @@
# mx-puppet-vk # 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). 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 ## Installation
```bash ```bash
@ -25,7 +25,7 @@ Register that one with synapse and start the bridge with `npm run start`.
- [ ] Presence - not possible yet - [ ] Presence - not possible yet
- [ ] Read notifications - not possible yet - [ ] Read notifications - not possible yet
- [x] Message edits - [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 - VK -> Matrix
- [x] Text content - [x] Text content
- [ ] Forwards - [ ] 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. 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. 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)

View file

@ -52,6 +52,7 @@ const protocol: IProtocolInformation = {
presence: false, presence: false,
reply: true, reply: true,
edit: true, edit: true,
advancedRelay: true,
}, },
id: "vk", // an internal ID for the protocol, all lowercase id: "vk", // an internal ID for the protocol, all lowercase
displayname: "VK", // a human-readable name of the protocol displayname: "VK", // a human-readable name of the protocol

View file

@ -81,7 +81,7 @@ export class VkPuppet {
const info = await p.client.api.messages.getConversationsById({ peer_ids: peerId, fields: ["photo_max"] }); const info = await p.client.api.messages.getConversationsById({ peer_ids: peerId, fields: ["photo_max"] });
// log.info(info.items[0]); // log.info(info.items[0]);
let response: IRemoteRoom; let response: IRemoteRoom;
switch (info.items[0].peer.type) { switch (info.items[0]?.peer.type || "chat") {
case "user": case "user":
// tslint:disable-next-line: no-shadowed-variable // 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"] }); 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 = { response = {
puppetId, puppetId,
roomId: peerId.toString(), roomId: peerId.toString(),
name: info.items[0].chat_settings.title, name: info.items[0]?.chat_settings.title || `VK chat №${(peerId - 2000000000).toString()}`,
avatarUrl: info.items[0].chat_settings.photo.photo_200, 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; break;
@ -177,7 +178,7 @@ export class VkPuppet {
////////////////////////// //////////////////////////
// tslint:disable-next-line: no-any // 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 // this is called every time we receive a message from matrix and need to
// forward it to the remote protocol. // forward it to the remote protocol.
@ -186,30 +187,43 @@ export class VkPuppet {
if (!p) { if (!p) {
return; 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 // usually you'd send it here to the remote protocol via the client object
try { try {
const response = await p.client.api.messages.send({ const response = await p.client.api.messages.send({
peer_id: Number(room.roomId), peer_ids: Number(room.roomId),
message: data.body, message: asUser ? `${asUser.displayname}: ${data.body}` : data.body,
random_id: new Date().getTime(), 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) { } catch (err) {
log.error("Error sending to vk", err.error || err.body || 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]; const p = this.puppets[room.puppetId];
if (!p) { if (!p) {
return; 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 // usually you'd send it here to the remote protocol via the client object
try { try {
const response = await p.client.api.messages.edit({ const response = await p.client.api.messages.edit({
peer_id: Number(room.roomId), peer_id: Number(room.roomId),
message: data.body, conversation_message_id: Number(eventId),
message_id: Number(eventId), message: asUser ? `${asUser.displayname}: ${data.body}` : data.body,
random_id: new Date().getTime(), random_id: new Date().getTime(),
}); });
await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); 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]; const p = this.puppets[room.puppetId];
if (!p) { if (!p) {
return; 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 // usually you'd send it here to the remote protocol via the client object
try { 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, spam: 0,
delete_for_all: 1, delete_for_all: 1,
message_ids: Number(eventId), message_ids: Number(eventId),
}); });*/
} catch (err) { } catch (err) {
log.error("Error sending edit to vk", err.error || err.body || err); log.error("Error sending edit to vk", err.error || err.body || err);
} }
@ -239,6 +261,7 @@ export class VkPuppet {
room: IRemoteRoom, room: IRemoteRoom,
eventId: string, eventId: string,
data: IMessageEvent, data: IMessageEvent,
asUser: ISendingUser | null,
// tslint:disable-next-line: no-any // tslint:disable-next-line: no-any
event: any, event: any,
) { ) {
@ -246,15 +269,22 @@ export class VkPuppet {
if (!p) { if (!p) {
return; 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 { try {
// log.info("Sending reply", Number(eventId));
const response = await p.client.api.messages.send({ const response = await p.client.api.messages.send({
peer_id: Number(room.roomId), peer_ids: Number(room.roomId),
message: await this.stripReply(data.body), message: asUser ? `${asUser.displayname}: ${await this.stripReply(data.body)}` : await this.stripReply(data.body),
random_id: new Date().getTime(), 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) { } catch (err) {
log.error("Error sending to vk", err.error || err.body || err); log.error("Error sending to vk", err.error || err.body || err);
} }
@ -274,22 +304,28 @@ export class VkPuppet {
const MAXFILESIZE = 50000000; const MAXFILESIZE = 50000000;
const size = data.info ? data.info.size || 0 : 0; 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) { if (size < MAXFILESIZE) {
try { try {
// log.info("Sending image...");
const attachment = await p.client.upload.messagePhoto({ const attachment = await p.client.upload.messagePhoto({
peer_id: Number(room.roomId), peer_id: Number(room.roomId),
source: { source: {
value: data.url, value: data.url,
}, },
}); });
// log.info("Image sent", attachment);
const response = await p.client.api.messages.send({ const response = await p.client.api.messages.send({
peer_id: Number(room.roomId), peer_ids: Number(room.roomId),
random_id: new Date().getTime(), random_id: new Date().getTime(),
message: asUser ? `${asUser.displayname} sent a photo:` : undefined,
attachment: [`photo${attachment.ownerId}_${attachment.id}`], 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) { } catch (err) {
log.error("Error sending to vk", err.error || err.body || err); log.error("Error sending to vk", err.error || err.body || err);
} }
@ -323,7 +359,6 @@ export class VkPuppet {
if (size < MAXFILESIZE) { if (size < MAXFILESIZE) {
try { try {
// log.info("Sending file...");
const attachment = await p.client.upload.messageDocument({ const attachment = await p.client.upload.messageDocument({
peer_id: Number(room.roomId), peer_id: Number(room.roomId),
source: { source: {
@ -331,21 +366,22 @@ export class VkPuppet {
filename: data.filename, filename: data.filename,
}, },
}); });
// log.info("File sent", attachment);
const response = await p.client.api.messages.send({ const response = await p.client.api.messages.send({
peer_id: Number(room.roomId), peer_id: Number(room.roomId),
random_id: new Date().getTime(), random_id: new Date().getTime(),
message: asUser ? `${asUser.displayname} sent a file:` : undefined,
attachment: [`doc${attachment.ownerId}_${attachment.id}`], attachment: [`doc${attachment.ownerId}_${attachment.id}`],
}); });
await this.puppet.eventSync.insert(room, data.eventId!, response.toString()); await this.puppet.eventSync.insert(room, data.eventId!, response.toString());
} catch (err) { } catch (err) {
try { try {
const response = await p.client.api.messages.send({ 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}`, 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(), 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) { } catch (err) {
log.error("Error sending to vk", err.error || err.body || err); log.error("Error sending to vk", err.error || err.body || err);
} }
@ -353,11 +389,12 @@ export class VkPuppet {
} else { } else {
try { try {
const response = await p.client.api.messages.send({ 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}`, 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(), 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) { } catch (err) {
log.error("Error sending to vk", err.error || err.body || err); log.error("Error sending to vk", err.error || err.body || err);
} }
@ -388,7 +425,8 @@ export class VkPuppet {
return; // Deduping 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.hasText) {
if (context.hasReplyMessage) { if (context.hasReplyMessage) {