postiz/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts

223 lines
6.1 KiB
TypeScript

import {
AuthTokenDetails,
PostDetails,
PostResponse,
SocialProvider,
} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract';
export class DiscordProvider extends SocialAbstract implements SocialProvider {
identifier = 'discord';
name = 'Discord';
isBetweenSteps = false;
scopes = ['identify', 'guilds'];
async refreshToken(refreshToken: string): Promise<AuthTokenDetails> {
const { access_token, expires_in, refresh_token } = await (
await this.fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
body: new URLSearchParams({
refresh_token: refreshToken,
grant_type: 'refresh_token',
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(
process.env.DISCORD_CLIENT_ID +
':' +
process.env.DISCORD_CLIENT_SECRET
).toString('base64')}`,
},
})
).json();
const { application } = await (
await fetch('https://discord.com/api/oauth2/@me', {
headers: {
Authorization: `Bearer ${access_token}`,
},
})
).json();
return {
refreshToken: refresh_token,
expiresIn: expires_in,
accessToken: access_token,
id: '',
name: application.name,
picture: '',
username: '',
};
}
async generateAuthUrl() {
const state = makeId(6);
return {
url: `https://discord.com/oauth2/authorize?client_id=${
process.env.DISCORD_CLIENT_ID
}&permissions=377957124096&response_type=code&redirect_uri=${encodeURIComponent(
`${process.env.FRONTEND_URL}/integrations/social/discord`
)}&integration_type=0&scope=bot+identify+guilds&state=${state}`,
codeVerifier: makeId(10),
state,
};
}
async authenticate(params: {
code: string;
codeVerifier: string;
refresh?: string;
}) {
const { access_token, expires_in, refresh_token, scope, guild } = await (
await this.fetch('https://discord.com/api/oauth2/token', {
method: 'POST',
body: new URLSearchParams({
code: params.code,
grant_type: 'authorization_code',
redirect_uri: `${process.env.FRONTEND_URL}/integrations/social/discord`,
}),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(
process.env.DISCORD_CLIENT_ID +
':' +
process.env.DISCORD_CLIENT_SECRET
).toString('base64')}`,
},
})
).json();
this.checkScopes(this.scopes, scope.split(' '));
const { application } = await (
await fetch('https://discord.com/api/oauth2/@me', {
headers: {
Authorization: `Bearer ${access_token}`,
},
})
).json();
return {
id: guild.id,
name: application.name,
accessToken: access_token,
refreshToken: refresh_token,
expiresIn: expires_in,
picture: `https://cdn.discordapp.com/avatars/${application.bot.id}/${application.bot.avatar}.png`,
username: application.bot.username,
};
}
async channels(accessToken: string, params: any, id: string) {
const list = await (
await fetch(`https://discord.com/api/guilds/${id}/channels`, {
headers: {
Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`,
},
})
).json();
return list
.filter((p: any) => p.type === 0 || p.type === 5 || p.type === 15)
.map((p: any) => ({
id: String(p.id),
name: p.name,
}));
}
async post(
id: string,
accessToken: string,
postDetails: PostDetails[]
): Promise<PostResponse[]> {
let channel = postDetails[0].settings.channel;
if (postDetails.length > 1) {
const { id: threadId } = await (
await fetch(
`https://discord.com/api/channels/${postDetails[0].settings.channel}/threads`,
{
method: 'POST',
headers: {
Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: postDetails[0].message,
auto_archive_duration: 1440,
type: 11, // Public thread type
}),
}
)
).json();
channel = threadId;
}
const finalData = [];
for (const post of postDetails) {
const form = new FormData();
form.append(
'payload_json',
JSON.stringify({
content: post.message,
attachments: post.media?.map((p, index) => ({
id: index,
description: `Picture ${index}`,
filename: p.url.split('/').pop(),
})),
})
);
let index = 0;
for (const media of post.media || []) {
const loadMedia = await fetch(media.url);
form.append(
`files[${index}]`,
await loadMedia.blob(),
media.url.split('/').pop()
);
index++;
}
const data = await (
await fetch(`https://discord.com/api/channels/${channel}/messages`, {
method: 'POST',
headers: {
Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`,
},
body: form,
})
).json();
finalData.push({
id: post.id,
releaseURL: `https://discord.com/channels/${id}/${channel}/${data.id}`,
postId: data.id,
status: 'success',
});
}
return finalData;
}
async changeNickname(
id: string,
accessToken: string,
name: string,
) {
await (await fetch(`https://discord.com/api/guilds/${id}/members/@me`, {
method: 'PATCH',
headers: {
Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
nick: name,
})
})).json();
return {
name,
}
}
}