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
This commit is contained in:
parent
d12455bfd7
commit
443179b286
|
@ -120,6 +120,7 @@ where the access token is `df89482ba9a19e5a2dee85031612b021a08cd521115e1c7d2cd70
|
||||||
- VK (AS A USER) -> Matrix
|
- VK (AS A USER) -> Matrix
|
||||||
- [x] Auth as a user instead of group
|
- [x] Auth as a user instead of group
|
||||||
- [x] Text content
|
- [x] Text content
|
||||||
|
- [x] Replies (as forwards)
|
||||||
- [x] Forwards
|
- [x] Forwards
|
||||||
- [x] Image content
|
- [x] Image content
|
||||||
- [ ] Audio content - unavailable via user tokens
|
- [ ] Audio content - unavailable via user tokens
|
||||||
|
|
33
package-lock.json
generated
33
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mx-puppet-vk",
|
"name": "mx-puppet-vk",
|
||||||
"version": "0.5.1",
|
"version": "0.6.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1080,21 +1080,6 @@
|
||||||
"domexception": "2.0.1",
|
"domexception": "2.0.1",
|
||||||
"fetch-blob": "2.1.2",
|
"fetch-blob": "2.1.2",
|
||||||
"mime-types": "2.1.31"
|
"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": {
|
"forwarded": {
|
||||||
|
@ -2543,13 +2528,13 @@
|
||||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
||||||
},
|
},
|
||||||
"vk-io": {
|
"vk-io": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/vk-io/-/vk-io-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/vk-io/-/vk-io-4.3.2.tgz",
|
||||||
"integrity": "sha512-ceFgfOHF3uNyy+jBDD4ZZzymcaDbBd3k1UgKWt0L02fzDqJPhQVXjy8x6OKSZXBxqEtQP/M2K3GroEqs/4PqGg==",
|
"integrity": "sha512-9L532/3hxkqNhrK3NcHOQ1aiIPxIDExY+D/bwbJX6gE1zT+rVYAyteapXkYR8xuD9qCcgbDBBu8b+h5a1NFVOA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abort-controller": "^3.0.0",
|
"abort-controller": "^3.0.0",
|
||||||
"debug": "^4.3.1",
|
"debug": "^4.3.2",
|
||||||
"form-data-encoder": "^1.0.0",
|
"form-data-encoder": "^1.0.1",
|
||||||
"formdata-node": "^3.5.4",
|
"formdata-node": "^3.5.4",
|
||||||
"inspectable": "^1.2.0",
|
"inspectable": "^1.2.0",
|
||||||
"middleware-io": "^2.8.0",
|
"middleware-io": "^2.8.0",
|
||||||
|
@ -2557,9 +2542,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
|
||||||
"integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==",
|
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "2.1.2"
|
"ms": "2.1.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "mx-puppet-vk",
|
"name": "mx-puppet-vk",
|
||||||
"version": "0.5.3",
|
"version": "0.6.0",
|
||||||
"description": "Matrix <-> VK bridge based on mx-puppet-bridge and VK-IO.",
|
"description": "Matrix <-> VK bridge based on mx-puppet-bridge and VK-IO.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
260
src/attachments-handler.ts
Normal file
260
src/attachments-handler.ts
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -98,6 +98,8 @@ async function run() {
|
||||||
puppet.on("typing", vk.handleMatrixTyping.bind(vk));
|
puppet.on("typing", vk.handleMatrixTyping.bind(vk));
|
||||||
|
|
||||||
puppet.setCreateRoomHook(vk.createRoom.bind(vk));
|
puppet.setCreateRoomHook(vk.createRoom.bind(vk));
|
||||||
|
puppet.setGetUserIdsInRoomHook(vk.getUserIdsInRoom.bind(vk));
|
||||||
|
puppet.setCreateUserHook(vk.createUser.bind(vk));
|
||||||
// required: get description hook
|
// required: get description hook
|
||||||
// tslint:disable-next-line: no-any
|
// tslint:disable-next-line: no-any
|
||||||
puppet.setGetDescHook(async (puppetId: number, data: any): Promise<string> => {
|
puppet.setGetDescHook(async (puppetId: number, data: any): Promise<string> => {
|
||||||
|
|
388
src/vk.ts
388
src/vk.ts
|
@ -16,8 +16,10 @@ import { userInfo } from "os";
|
||||||
import { runInThisContext } from "vm";
|
import { runInThisContext } from "vm";
|
||||||
import { lookup } from "dns";
|
import { lookup } from "dns";
|
||||||
import { Converter } from "showdown";
|
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 { ElementFlags, OptionalTypeNode } from "typescript";
|
||||||
|
import { AttachmentsHandler } from "./attachments-handler";
|
||||||
|
import { debug } from "console";
|
||||||
|
|
||||||
// here we create our log instance
|
// here we create our log instance
|
||||||
const log = new Log("VKPuppet:vk");
|
const log = new Log("VKPuppet:vk");
|
||||||
|
@ -36,183 +38,6 @@ interface IEchoPuppets {
|
||||||
[puppetId: number]: IEchoPuppet;
|
[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<object>): 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 {
|
export class VkPuppet {
|
||||||
private puppets: IEchoPuppets = {};
|
private puppets: IEchoPuppets = {};
|
||||||
private converter: Converter = new Converter({
|
private converter: Converter = new Converter({
|
||||||
|
@ -316,6 +141,31 @@ export class VkPuppet {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getUserIdsInRoom(room: IRemoteRoom): Promise<Set<string> | null> {
|
||||||
|
const p = this.puppets[room.puppetId];
|
||||||
|
|
||||||
|
const users = new Set<string>();
|
||||||
|
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<IRemoteUser | null> {
|
||||||
|
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
|
// tslint:disable-next-line: no-any
|
||||||
public async newPuppet(puppetId: number, data: any) {
|
public async newPuppet(puppetId: number, data: any) {
|
||||||
// this is called when we need to create a new puppet
|
// 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
|
// and listen to incoming messages from it
|
||||||
try {
|
try {
|
||||||
const client = new VK({ token: data.token, apiLimit: 20 });
|
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) => {
|
client.updates.on("message_new", async (context) => {
|
||||||
try {
|
try {
|
||||||
|
@ -681,42 +530,65 @@ export class VkPuppet {
|
||||||
p.data.isUserToken ? context.id.toString() : context.conversationMessageId?.toString() || context.id.toString());
|
p.data.isUserToken ? context.id.toString() : context.conversationMessageId?.toString() || context.id.toString());
|
||||||
const attachmentHandler = new AttachmentsHandler(p, this.puppet);
|
const attachmentHandler = new AttachmentsHandler(p, this.puppet);
|
||||||
|
|
||||||
if (context.hasText || context.hasForwards) {
|
let msgText: string = context.text || "";
|
||||||
let msgText: string = context.text || "";
|
let fullContext: MessagesMessage | undefined;
|
||||||
if (context.hasForwards) {
|
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 {
|
try {
|
||||||
msgText = await attachmentHandler.handleForwards(this, puppetId, msgText, params, context.forwards);
|
msgText = await attachmentHandler.handleForwards(this, puppetId, msgText, params, (context.forwards));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log.error(err);
|
log.error(err);
|
||||||
log.debug(context);
|
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 (context.hasReplyMessage) {
|
if (this.puppet.eventSync.getMatrix(params.room, context.replyMessage!.id.toString())) {
|
||||||
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 {
|
|
||||||
const opts: IMessageEvent = {
|
const opts: IMessageEvent = {
|
||||||
body: msgText || "Attachment",
|
body: msgText || "Attachment",
|
||||||
formattedBody: this.converter.makeHtml(msgText),
|
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);
|
await this.puppet.sendMessage(params, opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -737,7 +609,7 @@ export class VkPuppet {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AttachmentType.AUDIO_MESSAGE:
|
case AttachmentType.AUDIO_MESSAGE:
|
||||||
await attachmentHandler.handleAudioMessage(params, f)
|
await attachmentHandler.handleAudioMessage(params, f["audio_message"]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AttachmentType.AUDIO:
|
case AttachmentType.AUDIO:
|
||||||
|
@ -831,38 +703,82 @@ export class VkPuppet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async renderWallPost(puppetId: number, post: MessagesMessageAttachment) {
|
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`;
|
const renderWallPostAsGroup = async () => {
|
||||||
post.text?.split("\n").forEach((element) => {
|
const user = await this.getRemoteUser(puppetId, Number(post.fromId));
|
||||||
formatted += `> ${element}\n`;
|
let formatted = `Forwarded post from [${user.name}](${user.externalUrl})\n`;
|
||||||
});
|
post.text?.split("\n").forEach((element) => {
|
||||||
if (post.hasAttachments()) {
|
formatted += `> ${element}\n`;
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue