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:
Inex Code 2021-07-24 03:17:19 +03:00
parent d12455bfd7
commit 443179b286
6 changed files with 426 additions and 262 deletions

View File

@ -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

33
package-lock.json generated
View File

@ -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"
}

View File

@ -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": {

260
src/attachments-handler.ts Normal file
View 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;
}
}

View File

@ -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<string> => {

390
src/vk.ts
View File

@ -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<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 {
private puppets: IEchoPuppets = {};
private converter: Converter = new Converter({
@ -316,6 +141,31 @@ export class VkPuppet {
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
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;
}
}