fix: urgent providers fix
This commit is contained in:
parent
7eeb1cb044
commit
e29812501e
|
|
@ -10,7 +10,6 @@ import dayjs from 'dayjs';
|
|||
import { Integration } from '@prisma/client';
|
||||
import { AuthService } from '@gitroom/helpers/auth/auth.service';
|
||||
import { LemmySettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/lemmy.dto';
|
||||
import { groupBy } from 'lodash';
|
||||
import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator';
|
||||
|
||||
export class LemmyProvider extends SocialAbstract implements SocialProvider {
|
||||
|
|
@ -121,14 +120,7 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider {
|
|||
}
|
||||
}
|
||||
|
||||
async post(
|
||||
id: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails<LemmySettingsDto>[],
|
||||
integration: Integration
|
||||
): Promise<PostResponse[]> {
|
||||
const [firstPost, ...restPosts] = postDetails;
|
||||
|
||||
private async getJwtAndService(integration: Integration): Promise<{ jwt: string; service: string }> {
|
||||
const body = JSON.parse(
|
||||
AuthService.fixedDecryption(integration.customInstanceDetails!)
|
||||
);
|
||||
|
|
@ -146,6 +138,18 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider {
|
|||
})
|
||||
).json();
|
||||
|
||||
return { jwt, service: body.service };
|
||||
}
|
||||
|
||||
async post(
|
||||
id: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails<LemmySettingsDto>[],
|
||||
integration: Integration
|
||||
): Promise<PostResponse[]> {
|
||||
const [firstPost] = postDetails;
|
||||
const { jwt, service } = await this.getJwtAndService(integration);
|
||||
|
||||
const valueArray: PostResponse[] = [];
|
||||
|
||||
for (const lemmy of firstPost.settings.subreddit) {
|
||||
|
|
@ -159,8 +163,8 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider {
|
|||
: {}),
|
||||
nsfw: false,
|
||||
});
|
||||
const { post_view, ...all } = await (
|
||||
await fetch(body.service + '/api/v3/post', {
|
||||
const { post_view } = await (
|
||||
await fetch(service + '/api/v3/post', {
|
||||
body: JSON.stringify({
|
||||
community_id: +lemmy.value.id,
|
||||
name: lemmy.value.title,
|
||||
|
|
@ -188,41 +192,68 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider {
|
|||
|
||||
valueArray.push({
|
||||
postId: post_view.post.id,
|
||||
releaseURL: body.service + '/post/' + post_view.post.id,
|
||||
releaseURL: service + '/post/' + post_view.post.id,
|
||||
id: firstPost.id,
|
||||
status: 'published',
|
||||
});
|
||||
|
||||
for (const comment of restPosts) {
|
||||
const { comment_view } = await (
|
||||
await fetch(body.service + '/api/v3/comment', {
|
||||
body: JSON.stringify({
|
||||
post_id: post_view.post.id,
|
||||
content: comment.message,
|
||||
}),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
).json();
|
||||
|
||||
valueArray.push({
|
||||
postId: comment_view.post.id,
|
||||
releaseURL: body.service + '/comment/' + comment_view.comment.id,
|
||||
id: comment.id,
|
||||
status: 'published',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(groupBy(valueArray, (p) => p.id)).map((p) => ({
|
||||
id: p[0].id,
|
||||
postId: p.map((p) => String(p.postId)).join(','),
|
||||
releaseURL: p.map((p) => p.releaseURL).join(','),
|
||||
status: 'published',
|
||||
}));
|
||||
return [
|
||||
{
|
||||
id: firstPost.id,
|
||||
postId: valueArray.map((p) => String(p.postId)).join(','),
|
||||
releaseURL: valueArray.map((p) => p.releaseURL).join(','),
|
||||
status: 'published',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async comment(
|
||||
id: string,
|
||||
postId: string,
|
||||
lastCommentId: string | undefined,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails<LemmySettingsDto>[],
|
||||
integration: Integration
|
||||
): Promise<PostResponse[]> {
|
||||
const [commentPost] = postDetails;
|
||||
const { jwt, service } = await this.getJwtAndService(integration);
|
||||
|
||||
// postId can be comma-separated if posted to multiple communities
|
||||
const postIds = postId.split(',');
|
||||
const valueArray: PostResponse[] = [];
|
||||
|
||||
for (const singlePostId of postIds) {
|
||||
const { comment_view } = await (
|
||||
await fetch(service + '/api/v3/comment', {
|
||||
body: JSON.stringify({
|
||||
post_id: +singlePostId,
|
||||
content: commentPost.message,
|
||||
}),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
).json();
|
||||
|
||||
valueArray.push({
|
||||
postId: String(comment_view.comment.id),
|
||||
releaseURL: service + '/comment/' + comment_view.comment.id,
|
||||
id: commentPost.id,
|
||||
status: 'published',
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: commentPost.id,
|
||||
postId: valueArray.map((p) => p.postId).join(','),
|
||||
releaseURL: valueArray.map((p) => p.releaseURL).join(','),
|
||||
status: 'published',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@Tool({
|
||||
|
|
@ -241,27 +272,11 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider {
|
|||
id: string,
|
||||
integration: Integration
|
||||
) {
|
||||
const body = JSON.parse(
|
||||
AuthService.fixedDecryption(integration.customInstanceDetails!)
|
||||
);
|
||||
|
||||
const { jwt } = await (
|
||||
await fetch(body.service + '/api/v3/user/login', {
|
||||
body: JSON.stringify({
|
||||
username_or_email: body.identifier,
|
||||
password: body.password,
|
||||
}),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
})
|
||||
).json();
|
||||
const { jwt, service } = await this.getJwtAndService(integration);
|
||||
|
||||
const { communities } = await (
|
||||
await fetch(
|
||||
body.service +
|
||||
`/api/v3/search?type_=Communities&sort=Active&q=${data.word}`,
|
||||
service + `/api/v3/search?type_=Communities&sort=Active&q=${data.word}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${jwt}`,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { getPublicKey, Relay, finalizeEvent, SimplePool } from 'nostr-tools';
|
|||
|
||||
import WebSocket from 'ws';
|
||||
import { AuthService } from '@gitroom/helpers/auth/auth.service';
|
||||
import { Integration } from '@prisma/client';
|
||||
|
||||
// @ts-ignore
|
||||
global.WebSocket = WebSocket;
|
||||
|
|
@ -158,43 +159,77 @@ export class NostrProvider extends SocialAbstract implements SocialProvider {
|
|||
}
|
||||
}
|
||||
|
||||
private buildContent(post: PostDetails): string {
|
||||
const mediaContent = post.media?.map((m) => m.path).join('\n\n') || '';
|
||||
return mediaContent
|
||||
? `${post.message}\n\n${mediaContent}`
|
||||
: post.message;
|
||||
}
|
||||
|
||||
async post(
|
||||
id: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[]
|
||||
): Promise<PostResponse[]> {
|
||||
const { password } = AuthService.verifyJWT(accessToken) as any;
|
||||
const [firstPost] = postDetails;
|
||||
|
||||
let lastId = '';
|
||||
const ids: PostResponse[] = [];
|
||||
for (const post of postDetails) {
|
||||
const textEvent = finalizeEvent(
|
||||
{
|
||||
kind: 1, // Text note
|
||||
content:
|
||||
post.message + '\n\n' + post.media?.map((m) => m.path).join('\n\n'),
|
||||
tags: [
|
||||
...(lastId
|
||||
? [
|
||||
['e', lastId, '', 'reply'],
|
||||
['p', id],
|
||||
]
|
||||
: []),
|
||||
], // Include delegation token in the event
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
},
|
||||
password
|
||||
);
|
||||
const textEvent = finalizeEvent(
|
||||
{
|
||||
kind: 1, // Text note
|
||||
content: this.buildContent(firstPost),
|
||||
tags: [],
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
},
|
||||
password
|
||||
);
|
||||
|
||||
lastId = await this.publish(id, textEvent);
|
||||
ids.push({
|
||||
id: post.id,
|
||||
postId: String(lastId),
|
||||
releaseURL: `https://primal.net/e/${lastId}`,
|
||||
const eventId = await this.publish(id, textEvent);
|
||||
|
||||
return [
|
||||
{
|
||||
id: firstPost.id,
|
||||
postId: String(eventId),
|
||||
releaseURL: `https://primal.net/e/${eventId}`,
|
||||
status: 'completed',
|
||||
});
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return ids;
|
||||
async comment(
|
||||
id: string,
|
||||
postId: string,
|
||||
lastCommentId: string | undefined,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[],
|
||||
integration: Integration
|
||||
): Promise<PostResponse[]> {
|
||||
const { password } = AuthService.verifyJWT(accessToken) as any;
|
||||
const [commentPost] = postDetails;
|
||||
const replyToId = lastCommentId || postId;
|
||||
|
||||
const textEvent = finalizeEvent(
|
||||
{
|
||||
kind: 1, // Text note
|
||||
content: this.buildContent(commentPost),
|
||||
tags: [
|
||||
['e', replyToId, '', 'reply'],
|
||||
['p', id],
|
||||
],
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
},
|
||||
password
|
||||
);
|
||||
|
||||
const eventId = await this.publish(id, textEvent);
|
||||
|
||||
return [
|
||||
{
|
||||
id: commentPost.id,
|
||||
postId: String(eventId),
|
||||
releaseURL: `https://primal.net/e/${eventId}`,
|
||||
status: 'completed',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,118 +140,173 @@ export class TelegramProvider extends SocialAbstract implements SocialProvider {
|
|||
: {};
|
||||
}
|
||||
|
||||
private processMedia(mediaFiles: PostDetails['media']) {
|
||||
return (mediaFiles || []).map((media) => {
|
||||
let mediaUrl = media.path;
|
||||
if (mediaStorage === 'local' && mediaUrl.startsWith(frontendURL)) {
|
||||
mediaUrl = mediaUrl.replace(frontendURL, '');
|
||||
}
|
||||
//get mime type to pass contentType to telegram api.
|
||||
//some photos and videos might not pass telegram api restrictions, so they are sent as documents instead of returning errors
|
||||
const mimeType = mime.getType(mediaUrl); // Detect MIME type
|
||||
let mediaType: 'photo' | 'video' | 'document';
|
||||
|
||||
if (mimeType?.startsWith('image/')) {
|
||||
mediaType = 'photo';
|
||||
} else if (mimeType?.startsWith('video/')) {
|
||||
mediaType = 'video';
|
||||
} else {
|
||||
mediaType = 'document';
|
||||
}
|
||||
|
||||
return {
|
||||
type: mediaType,
|
||||
media: mediaUrl,
|
||||
fileOptions: {
|
||||
filename: media.path.split('/').pop(),
|
||||
contentType: mimeType || 'application/octet-stream',
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private async sendMessage(
|
||||
accessToken: string,
|
||||
message: PostDetails,
|
||||
replyToMessageId?: number
|
||||
): Promise<number | null> {
|
||||
let messageId: number | null = null;
|
||||
const mediaFiles = message.media || [];
|
||||
const text = striptags(message.message || '', ['u', 'strong', 'p'])
|
||||
.replace(/<strong>/g, '<b>')
|
||||
.replace(/<\/strong>/g, '</b>')
|
||||
.replace(/<p>(.*?)<\/p>/g, '$1\n');
|
||||
|
||||
console.log(text);
|
||||
const processedMedia = this.processMedia(mediaFiles);
|
||||
|
||||
// if there's no media, bot sends a text message only
|
||||
if (processedMedia.length === 0) {
|
||||
const response = await telegramBot.sendMessage(accessToken, text, {
|
||||
parse_mode: 'HTML',
|
||||
...(replyToMessageId ? { reply_to_message_id: replyToMessageId } : {}),
|
||||
});
|
||||
messageId = response.message_id;
|
||||
}
|
||||
// if there's only one media, bot sends the media with the text message as caption
|
||||
else if (processedMedia.length === 1) {
|
||||
const media = processedMedia[0];
|
||||
const options = {
|
||||
caption: text,
|
||||
parse_mode: 'HTML' as const,
|
||||
...(replyToMessageId ? { reply_to_message_id: replyToMessageId } : {}),
|
||||
};
|
||||
const response =
|
||||
media.type === 'video'
|
||||
? await telegramBot.sendVideo(
|
||||
accessToken,
|
||||
media.media,
|
||||
options,
|
||||
media.fileOptions
|
||||
)
|
||||
: media.type === 'photo'
|
||||
? await telegramBot.sendPhoto(
|
||||
accessToken,
|
||||
media.media,
|
||||
options,
|
||||
media.fileOptions
|
||||
)
|
||||
: await telegramBot.sendDocument(
|
||||
accessToken,
|
||||
media.media,
|
||||
options,
|
||||
media.fileOptions
|
||||
);
|
||||
messageId = response.message_id;
|
||||
}
|
||||
// if there are multiple media, bot sends them as a media group - max 10 media per group - with the text as a caption (if there are more than 1 group, the caption will only be sent with the first group)
|
||||
else {
|
||||
const mediaGroups = this.chunkMedia(processedMedia, 10);
|
||||
for (let i = 0; i < mediaGroups.length; i++) {
|
||||
const mediaGroup = mediaGroups[i].map((m, index) => ({
|
||||
type: m.type === 'document' ? 'document' : m.type, // Documents are not allowed in media groups
|
||||
media: m.media,
|
||||
caption: i === 0 && index === 0 ? text : undefined,
|
||||
parse_mode: 'HTML',
|
||||
}));
|
||||
|
||||
const response = await telegramBot.sendMediaGroup(
|
||||
accessToken,
|
||||
mediaGroup as any[],
|
||||
{
|
||||
...(replyToMessageId && i === 0
|
||||
? { reply_to_message_id: replyToMessageId }
|
||||
: {}),
|
||||
}
|
||||
);
|
||||
if (i === 0) {
|
||||
messageId = response[0].message_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messageId;
|
||||
}
|
||||
|
||||
async post(
|
||||
id: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[]
|
||||
): Promise<PostResponse[]> {
|
||||
const ids: PostResponse[] = [];
|
||||
const [firstPost] = postDetails;
|
||||
|
||||
for (const message of postDetails) {
|
||||
let messageId: number | null = null;
|
||||
const mediaFiles = message.media || [];
|
||||
const text = striptags(message.message || '', ['u', 'strong', 'p'])
|
||||
.replace(/<strong>/g, '<b>')
|
||||
.replace(/<\/strong>/g, '</b>')
|
||||
.replace(/<p>(.*?)<\/p>/g, '$1\n');
|
||||
const messageId = await this.sendMessage(accessToken, firstPost);
|
||||
|
||||
console.log(text);
|
||||
// check if media is local to modify url
|
||||
const processedMedia = mediaFiles.map((media) => {
|
||||
let mediaUrl = media.path;
|
||||
if (mediaStorage === 'local' && mediaUrl.startsWith(frontendURL)) {
|
||||
mediaUrl = mediaUrl.replace(frontendURL, '');
|
||||
}
|
||||
//get mime type to pass contentType to telegram api.
|
||||
//some photos and videos might not pass telegram api restrictions, so they are sent as documents instead of returning errors
|
||||
const mimeType = mime.getType(mediaUrl); // Detect MIME type
|
||||
let mediaType: 'photo' | 'video' | 'document';
|
||||
|
||||
if (mimeType?.startsWith('image/')) {
|
||||
mediaType = 'photo';
|
||||
} else if (mimeType?.startsWith('video/')) {
|
||||
mediaType = 'video';
|
||||
} else {
|
||||
mediaType = 'document';
|
||||
}
|
||||
|
||||
return {
|
||||
type: mediaType,
|
||||
media: mediaUrl,
|
||||
fileOptions: {
|
||||
filename: media.path.split('/').pop(),
|
||||
contentType: mimeType || 'application/octet-stream',
|
||||
},
|
||||
};
|
||||
});
|
||||
// if there's no media, bot sends a text message only
|
||||
if (processedMedia.length === 0) {
|
||||
const response = await telegramBot.sendMessage(accessToken, text, {
|
||||
parse_mode: 'HTML',
|
||||
});
|
||||
messageId = response.message_id;
|
||||
}
|
||||
// if there's only one media, bot sends the media with the text message as caption
|
||||
else if (processedMedia.length === 1) {
|
||||
const media = processedMedia[0];
|
||||
const response =
|
||||
media.type === 'video'
|
||||
? await telegramBot.sendVideo(
|
||||
accessToken,
|
||||
media.media,
|
||||
{ caption: text, parse_mode: 'HTML' },
|
||||
media.fileOptions
|
||||
)
|
||||
: media.type === 'photo'
|
||||
? await telegramBot.sendPhoto(
|
||||
accessToken,
|
||||
media.media,
|
||||
{ caption: text, parse_mode: 'HTML' },
|
||||
media.fileOptions
|
||||
)
|
||||
: await telegramBot.sendDocument(
|
||||
accessToken,
|
||||
media.media,
|
||||
{ caption: text, parse_mode: 'HTML' },
|
||||
media.fileOptions
|
||||
);
|
||||
messageId = response.message_id;
|
||||
}
|
||||
// if there are multiple media, bot sends them as a media group - max 10 media per group - with the text as a caption (if there are more than 1 group, the caption will only be sent with the first group)
|
||||
else {
|
||||
const mediaGroups = this.chunkMedia(processedMedia, 10);
|
||||
for (let i = 0; i < mediaGroups.length; i++) {
|
||||
const mediaGroup = mediaGroups[i].map((m, index) => ({
|
||||
type: m.type === 'document' ? 'document' : m.type, // Documents are not allowed in media groups
|
||||
media: m.media,
|
||||
caption: i === 0 && index === 0 ? text : undefined,
|
||||
parse_mode: 'HTML',
|
||||
}));
|
||||
|
||||
const response = await telegramBot.sendMediaGroup(
|
||||
accessToken,
|
||||
mediaGroup as any[]
|
||||
);
|
||||
if (i === 0) {
|
||||
messageId = response[0].message_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
// for private groups/channels message.id is undefined so the link generated by Postiz will be unusable "https://t.me/c/undefined/16"
|
||||
// to avoid that, we use accessToken instead of message.id and we generate the link manually removing the -100 from the start.
|
||||
if (messageId) {
|
||||
ids.push({
|
||||
id: message.id,
|
||||
// for private groups/channels message.id is undefined so the link generated by Postiz will be unusable "https://t.me/c/undefined/16"
|
||||
// to avoid that, we use accessToken instead of message.id and we generate the link manually removing the -100 from the start.
|
||||
if (messageId) {
|
||||
return [
|
||||
{
|
||||
id: firstPost.id,
|
||||
postId: String(messageId),
|
||||
releaseURL: `https://t.me/${
|
||||
id !== 'undefined' ? id : `c/${accessToken.replace('-100', '')}`
|
||||
}/${messageId}`,
|
||||
status: 'completed',
|
||||
});
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return ids;
|
||||
return [];
|
||||
}
|
||||
|
||||
async comment(
|
||||
id: string,
|
||||
postId: string,
|
||||
lastCommentId: string | undefined,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[],
|
||||
integration: Integration
|
||||
): Promise<PostResponse[]> {
|
||||
const [commentPost] = postDetails;
|
||||
const replyToId = Number(lastCommentId || postId);
|
||||
|
||||
const messageId = await this.sendMessage(accessToken, commentPost, replyToId);
|
||||
|
||||
if (messageId) {
|
||||
return [
|
||||
{
|
||||
id: commentPost.id,
|
||||
postId: String(messageId),
|
||||
releaseURL: `https://t.me/${
|
||||
id !== 'undefined' ? id : `c/${accessToken.replace('-100', '')}`
|
||||
}/${messageId}`,
|
||||
status: 'completed',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
// chunkMedia is used to split media into groups of "size". 10 is used here because telegram api allows a maximum of 10 media per group
|
||||
private chunkMedia(media: { type: string; media: string }[], size: number) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { createHash, randomBytes } from 'crypto';
|
|||
import axios from 'axios';
|
||||
import FormDataNew from 'form-data';
|
||||
import mime from 'mime-types';
|
||||
import { Integration } from '@prisma/client';
|
||||
|
||||
export class VkProvider extends SocialAbstract implements SocialProvider {
|
||||
override maxConcurrentJob = 2; // VK has moderate API limits
|
||||
|
|
@ -158,123 +159,153 @@ export class VkProvider extends SocialAbstract implements SocialProvider {
|
|||
};
|
||||
}
|
||||
|
||||
private async uploadMedia(
|
||||
userId: string,
|
||||
accessToken: string,
|
||||
post: PostDetails
|
||||
): Promise<{ id: string; type: string }[]> {
|
||||
return await Promise.all(
|
||||
(post?.media || []).map(async (media) => {
|
||||
const all = await (
|
||||
await this.fetch(
|
||||
media.path.indexOf('mp4') > -1
|
||||
? `https://api.vk.com/method/video.save?access_token=${accessToken}&v=5.251`
|
||||
: `https://api.vk.com/method/photos.getWallUploadServer?owner_id=${userId}&access_token=${accessToken}&v=5.251`
|
||||
)
|
||||
).json();
|
||||
|
||||
const { data } = await axios.get(media.path!, {
|
||||
responseType: 'stream',
|
||||
});
|
||||
|
||||
const slash = media.path.split('/').at(-1);
|
||||
|
||||
const formData = new FormDataNew();
|
||||
formData.append('photo', data, {
|
||||
filename: slash,
|
||||
contentType: mime.lookup(slash!) || '',
|
||||
});
|
||||
const value = (
|
||||
await axios.post(all.response.upload_url, formData, {
|
||||
headers: {
|
||||
...formData.getHeaders(),
|
||||
},
|
||||
})
|
||||
).data;
|
||||
|
||||
if (media.path.indexOf('mp4') > -1) {
|
||||
return {
|
||||
id: all.response.video_id,
|
||||
type: 'video',
|
||||
};
|
||||
}
|
||||
|
||||
const formSend = new FormData();
|
||||
formSend.append('photo', value.photo);
|
||||
formSend.append('server', value.server);
|
||||
formSend.append('hash', value.hash);
|
||||
|
||||
const { id } = (
|
||||
await (
|
||||
await fetch(
|
||||
`https://api.vk.com/method/photos.saveWallPhoto?access_token=${accessToken}&v=5.251`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: formSend,
|
||||
}
|
||||
)
|
||||
).json()
|
||||
).response[0];
|
||||
|
||||
return {
|
||||
id,
|
||||
type: 'photo',
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async post(
|
||||
userId: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[]
|
||||
): Promise<PostResponse[]> {
|
||||
let replyTo = '';
|
||||
const values: PostResponse[] = [];
|
||||
const [firstPost] = postDetails;
|
||||
|
||||
const uploading = await Promise.all(
|
||||
postDetails.map(async (post) => {
|
||||
return await Promise.all(
|
||||
(post?.media || []).map(async (media) => {
|
||||
const all = await (
|
||||
await this.fetch(
|
||||
media.path.indexOf('mp4') > -1
|
||||
? `https://api.vk.com/method/video.save?access_token=${accessToken}&v=5.251`
|
||||
: `https://api.vk.com/method/photos.getWallUploadServer?owner_id=${userId}&access_token=${accessToken}&v=5.251`
|
||||
)
|
||||
).json();
|
||||
// Upload media for the first post
|
||||
const mediaList = await this.uploadMedia(userId, accessToken, firstPost);
|
||||
|
||||
const { data } = await axios.get(media.path!, {
|
||||
responseType: 'stream',
|
||||
});
|
||||
const body = new FormData();
|
||||
body.append('message', firstPost.message);
|
||||
|
||||
const slash = media.path.split('/').at(-1);
|
||||
|
||||
const formData = new FormDataNew();
|
||||
formData.append('photo', data, {
|
||||
filename: slash,
|
||||
contentType: mime.lookup(slash!) || '',
|
||||
});
|
||||
const value = (
|
||||
await axios.post(all.response.upload_url, formData, {
|
||||
headers: {
|
||||
...formData.getHeaders(),
|
||||
},
|
||||
})
|
||||
).data;
|
||||
|
||||
if (media.path.indexOf('mp4') > -1) {
|
||||
return {
|
||||
id: all.response.video_id,
|
||||
type: 'video',
|
||||
};
|
||||
}
|
||||
|
||||
const formSend = new FormData();
|
||||
formSend.append('photo', value.photo);
|
||||
formSend.append('server', value.server);
|
||||
formSend.append('hash', value.hash);
|
||||
|
||||
const { id } = (
|
||||
await (
|
||||
await fetch(
|
||||
`https://api.vk.com/method/photos.saveWallPhoto?access_token=${accessToken}&v=5.251`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: formSend,
|
||||
}
|
||||
)
|
||||
).json()
|
||||
).response[0];
|
||||
|
||||
return {
|
||||
id,
|
||||
type: 'photo',
|
||||
};
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
let i = 0;
|
||||
for (const post of postDetails) {
|
||||
const list = uploading?.[i] || [];
|
||||
|
||||
const body = new FormData();
|
||||
body.append('message', post.message);
|
||||
if (replyTo) {
|
||||
body.append('post_id', replyTo);
|
||||
}
|
||||
|
||||
if (list.length) {
|
||||
body.append(
|
||||
'attachments',
|
||||
list.map((p) => `${p.type}${userId}_${p.id}`).join(',')
|
||||
);
|
||||
}
|
||||
|
||||
const { response, ...all } = await (
|
||||
await this.fetch(
|
||||
`https://api.vk.com/method/${
|
||||
replyTo ? 'wall.createComment' : 'wall.post'
|
||||
}?v=5.251&access_token=${accessToken}&client_id=${process.env.VK_ID}`,
|
||||
{
|
||||
method: 'POST',
|
||||
body,
|
||||
}
|
||||
)
|
||||
).json();
|
||||
|
||||
values.push({
|
||||
id: post.id,
|
||||
postId: String(response?.post_id || response?.comment_id),
|
||||
releaseURL: `https://vk.com/feed?w=wall${userId}_${
|
||||
response?.post_id || replyTo
|
||||
}`,
|
||||
status: 'completed',
|
||||
});
|
||||
|
||||
if (!replyTo) {
|
||||
replyTo = response.post_id;
|
||||
}
|
||||
|
||||
i++;
|
||||
if (mediaList.length) {
|
||||
body.append(
|
||||
'attachments',
|
||||
mediaList.map((p) => `${p.type}${userId}_${p.id}`).join(',')
|
||||
);
|
||||
}
|
||||
|
||||
return values;
|
||||
const { response } = await (
|
||||
await this.fetch(
|
||||
`https://api.vk.com/method/wall.post?v=5.251&access_token=${accessToken}&client_id=${process.env.VK_ID}`,
|
||||
{
|
||||
method: 'POST',
|
||||
body,
|
||||
}
|
||||
)
|
||||
).json();
|
||||
|
||||
return [
|
||||
{
|
||||
id: firstPost.id,
|
||||
postId: String(response?.post_id),
|
||||
releaseURL: `https://vk.com/feed?w=wall${userId}_${response?.post_id}`,
|
||||
status: 'completed',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
async comment(
|
||||
userId: string,
|
||||
postId: string,
|
||||
lastCommentId: string | undefined,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[],
|
||||
integration: Integration
|
||||
): Promise<PostResponse[]> {
|
||||
const [commentPost] = postDetails;
|
||||
|
||||
// Upload media for the comment
|
||||
const mediaList = await this.uploadMedia(userId, accessToken, commentPost);
|
||||
|
||||
const body = new FormData();
|
||||
body.append('message', commentPost.message);
|
||||
body.append('post_id', postId);
|
||||
|
||||
if (mediaList.length) {
|
||||
body.append(
|
||||
'attachments',
|
||||
mediaList.map((p) => `${p.type}${userId}_${p.id}`).join(',')
|
||||
);
|
||||
}
|
||||
|
||||
const { response } = await (
|
||||
await this.fetch(
|
||||
`https://api.vk.com/method/wall.createComment?v=5.251&access_token=${accessToken}&client_id=${process.env.VK_ID}`,
|
||||
{
|
||||
method: 'POST',
|
||||
body,
|
||||
}
|
||||
)
|
||||
).json();
|
||||
|
||||
return [
|
||||
{
|
||||
id: commentPost.id,
|
||||
postId: String(response?.comment_id),
|
||||
releaseURL: `https://vk.com/feed?w=wall${userId}_${postId}`,
|
||||
status: 'completed',
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue