dissonity
Version:
Extension of the Discord Embedded App SDK that allows you to create activities alongside the Dissonity v1 Unity package
307 lines (306 loc) • 10.7 kB
JavaScript
// src/functions.ts
import { DiscordSDK } from "@discord/embedded-app-sdk";
import fetch from "cross-fetch";
var PACKAGE_VERSION = "1.2.0";
var initialized = false;
function stringifyBigInt(obj) {
const str = JSON.stringify(obj, (key, value) => {
if (typeof value == "bigint") {
return value.toString();
}
return value;
});
return str;
}
function getChildIframe() {
const iframe = document.getElementById("dissonity-child");
if (iframe == null) {
throw new Error("No iframe with id 'dissonity-child' found");
}
return iframe;
}
async function initializeSdk(options) {
const discordSdk = new DiscordSDK(options.clientId);
await discordSdk.ready();
const { code } = await discordSdk.commands.authorize({
client_id: options.clientId,
response_type: "code",
state: "",
prompt: "none",
scope: options.scope
});
const response = await fetch(`/.proxy${options.tokenRoute}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ code })
});
const data = await response.json();
if (!data.access_token) {
throw new Error("No access_token field found in response data");
}
const { user } = await discordSdk.commands.authenticate({
access_token: data.access_token
});
user.flags = user.public_flags;
user.bot = false;
return { discordSdk, user };
}
async function receiveMessage(discordSdk, user, messageData) {
const { nonce, event, command } = messageData;
let args = messageData.args ?? {};
function handleSubscribeEvent(eventData) {
getChildIframe().contentWindow?.postMessage(
{
event,
command: "DISPATCH",
data: eventData
},
"*"
);
}
switch (command) {
case "SUBSCRIBE": {
if (event == null) {
throw new Error("SUBSCRIBE event is undefined");
}
if (args.channel_id)
args = { channel_id: discordSdk.channelId };
try {
discordSdk.subscribe(event, handleSubscribeEvent, args);
} catch (_err) {
console.error(`Dissonity NPM: Error attempting to subscribe to: ${event}. You may need some scope?`);
}
break;
}
case "UNSUBSCRIBE": {
if (event == null) {
throw new Error("UNSUBSCRIBE event is undefined");
}
if (args.channel_id)
args = { channel_id: discordSdk.channelId };
discordSdk.unsubscribe(event, handleSubscribeEvent);
break;
}
case "SET_ACTIVITY": {
if (!args.activity) {
throw new Error("No activity provided for SET_ACTIVITY");
}
if (args.activity.assets?.large_image == "")
delete args.activity.assets;
if (args.activity.party?.id == "")
delete args.activity.party;
if (args.activity.emoji?.id == "")
delete args.activity.emoji;
if (args.activity.secrets?.match == "")
delete args.activity.secrets;
try {
const data = await discordSdk.commands.setActivity(args);
getChildIframe().contentWindow?.postMessage({ nonce, event, command, data }, "*");
} catch (_err) {
console.error("Dissonity NPM: Error attempting to set the activity. You may need the 'rpc.activities.write' scope.");
}
break;
}
case "GET_APPLICATION_ID": {
const { clientId } = discordSdk;
getChildIframe().contentWindow?.postMessage({ nonce, command, data: clientId, args }, "*");
break;
}
case "GET_INSTANCE_ID": {
const { instanceId } = discordSdk;
getChildIframe().contentWindow?.postMessage({ nonce, command, data: instanceId, args }, "*");
break;
}
case "GET_CHANNEL_ID": {
const { channelId } = discordSdk;
getChildIframe().contentWindow?.postMessage({ nonce, command, data: channelId, args }, "*");
break;
}
case "GET_GUILD_ID": {
const { guildId } = discordSdk;
getChildIframe().contentWindow?.postMessage({ nonce, command, data: guildId, args }, "*");
break;
}
case "GET_USER_ID": {
if (user == null)
throw new Error("You need to be authenticated to get the current user id");
getChildIframe().contentWindow?.postMessage({ nonce, command, data: user.id, args }, "*");
break;
}
case "GET_USER": {
if (user == null)
throw new Error("You need to be authenticated to get the current user");
getChildIframe().contentWindow?.postMessage({ nonce, command, data: user, args }, "*");
break;
}
case "GET_INSTANCE_PARTICIPANTS": {
const data = await discordSdk.commands.getInstanceConnectedParticipants();
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
break;
}
case "HARDWARE_ACCELERATION": {
const data = await discordSdk.commands.encourageHardwareAcceleration();
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
break;
}
case "GET_CHANNEL": {
if (!args.channel_id) {
throw new Error("No channel id provided for GET_CHANNEL");
}
try {
const data = await discordSdk.commands.getChannel(args);
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
} catch (_err) {
console.error("Dissonity NPM: Error attempting to get the channel. You may need the 'guilds' scope.");
}
break;
}
case "GET_CHANNEL_PERMISSIONS": {
if (!args.channel_id) {
throw new Error("No channel id provided for GET_CHANNEL_PERMISSIONS");
}
try {
const data = await discordSdk.commands.getChannelPermissions(args);
getChildIframe().contentWindow?.postMessage({ nonce, command, data: stringifyBigInt(data), args }, "*");
} catch (_err) {
console.error("Dissonity NPM: Error attempting to get the channel permissions. You may need the 'guilds.members.read' scope.");
}
break;
}
case "GET_ENTITLEMENTS": {
throw new Error("Entitlements are not supported in Dissonity v1");
const data = await discordSdk.commands.getEntitlements();
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
break;
}
case "GET_PLATFORM_BEHAVIORS": {
const data = await discordSdk.commands.getPlatformBehaviors();
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
break;
}
case "GET_SKUS": {
throw new Error("Skus are not supported in Dissonity v1");
const data = await discordSdk.commands.getSkus();
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
break;
}
case "IMAGE_UPLOAD": {
try {
const data = await discordSdk.commands.initiateImageUpload();
getChildIframe().contentWindow?.postMessage({ nonce, command, data: { image_url: data.image_url, canceled: false }, args }, "*");
} catch (_err) {
getChildIframe().contentWindow?.postMessage({ nonce, command, data: { image_url: "", canceled: true }, args }, "*");
}
break;
}
case "EXTERNAL_LINK": {
if (!args.url) {
throw new Error("No url provided for EXTERNAL_LINK");
}
discordSdk.commands.openExternalLink(args);
break;
}
case "INVITE_DIALOG": {
discordSdk.commands.openInviteDialog();
break;
}
case "SHARE_MOMENT_DIALOG": {
if (!args.mediaUrl) {
throw new Error("No media url provided for SHARE_MOMENT_DIALOG");
}
discordSdk.commands.openShareMomentDialog(args);
break;
}
case "SET_ORIENTATION_LOCK_STATE": {
if (!args.lock_state) {
throw new Error("No lock state provided for SET_ORIENTATION_LOCK_STATE");
}
try {
discordSdk.commands.setOrientationLockState(args);
} catch (_err) {
console.error("Dissonity NPM: Error attempting to set the orientation lock state. You may need the 'guilds.members.read' scope.");
}
break;
}
case "START_PURCHASE": {
throw new Error("Purchases are not supported in Dissonity v1");
discordSdk.commands.startPurchase(args);
break;
}
case "GET_LOCALE": {
try {
const data = await discordSdk.commands.userSettingsGetLocale();
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
} catch (_err) {
console.error("Dissonity NPM: Error attempting to get the user locale. You may need the 'identify' scope.");
}
break;
}
case "SET_CONFIG": {
if (!args.use_interactive_pip) {
throw new Error("No 'use interactive pip' provided for SET_CONFIG");
}
const data = await discordSdk.commands.setConfig(args);
getChildIframe().contentWindow?.postMessage({ nonce, command, data, args }, "*");
break;
}
case "PING_LOAD": {
getChildIframe().contentWindow?.postMessage({ command: "LOADED", data: PACKAGE_VERSION }, "*");
break;
}
}
}
async function setupSdk(options) {
if (initialized)
throw new Error("Already initialized");
initialized = true;
const dataPromise = initializeSdk(options);
let discordSdk = null;
let user = null;
async function handleMessage({ data: messageData }) {
if (typeof messageData !== "object" || Array.isArray(messageData) || messageData === null) {
return;
}
if (!discordSdk || !user) {
await dataPromise;
}
receiveMessage(discordSdk, user, messageData);
}
window.addEventListener("message", handleMessage);
const resolvedData = await dataPromise;
discordSdk = resolvedData.discordSdk;
user = resolvedData.user;
getChildIframe().contentWindow?.postMessage({ command: "LOADED", data: PACKAGE_VERSION }, "*");
}
async function useSdk(dataPromise) {
if (initialized)
throw new Error("Already initialized");
initialized = true;
let discordSdk = null;
let user = null;
async function handleMessage({ data: messageData }) {
if (typeof messageData !== "object" || Array.isArray(messageData) || messageData === null) {
return;
}
if (!discordSdk || !user) {
await dataPromise;
}
receiveMessage(discordSdk, user, messageData);
}
window.addEventListener("message", handleMessage);
const resolvedData = await dataPromise;
discordSdk = resolvedData.discordSdk;
if (resolvedData.user != null) {
user = { ...resolvedData.user };
user.flags = user.public_flags;
user.bot = false;
}
getChildIframe().contentWindow?.postMessage({ command: "LOADED", data: PACKAGE_VERSION }, "*");
}
export {
DiscordSDK,
setupSdk,
useSdk
};