feat: support for select menus

This commit is contained in:
xhyrom 2022-04-14 12:41:23 +02:00
parent 1f23b2536e
commit 4ee047e41d
11 changed files with 135 additions and 74 deletions

View file

@ -1,33 +1,23 @@
import { APIApplicationCommandInteraction, APIInteractionResponse, APIMessageComponentInteraction, APIPingInteraction, InteractionResponseType, InteractionType, MessageFlags, RouteBases, Routes } from 'discord-api-types/v9'; import { APIApplicationCommandInteraction, APIMessageComponentInteraction, APIPingInteraction, ApplicationCommandType, ComponentType, InteractionResponseType, InteractionType, MessageFlags, RouteBases, Routes } from 'discord-api-types/v9';
import { badFormatting } from './utils/badFormatting';
import { isJSON } from './utils/isJson'; import { isJSON } from './utils/isJson';
import { resolvePartialEmoji } from './utils/resolveEmoji'; import { resolveButtonComponents, resolveSelectMenuComponents } from './utils/resolveComponents';
import { respond } from './utils/respond';
import { verify } from './utils/verify'; import { verify } from './utils/verify';
const respond = (response: APIInteractionResponse) => new Response(JSON.stringify(response), {headers: {'content-type': 'application/json'}});
const badFormatting = (rolesMax?: boolean) => {
return respond({
type: InteractionResponseType.ChannelMessageWithSource,
data: {
flags: 64,
content: `${rolesMax ? 'You can have maximum 25 buttons. (5x5)' : 'Bad formatting, generate [here](https://xhyrom.github.io/roles-bot)'}`
}
});
};
export const handleRequest = async(request: Request): Promise<Response> => { export const handleRequest = async(request: Request): Promise<Response> => {
if (!request.headers.get('X-Signature-Ed25519') || !request.headers.get('X-Signature-Timestamp')) return Response.redirect('https://www.youtube.com/watch?v=dQw4w9WgXcQ'); if (!request.headers.get('X-Signature-Ed25519') || !request.headers.get('X-Signature-Timestamp')) return Response.redirect('https://www.youtube.com/watch?v=dQw4w9WgXcQ');
if (!await verify(request)) return new Response('', { status: 401 }); if (!await verify(request)) return new Response('', { status: 401 });
const interaction = await request.json() as APIPingInteraction | APIApplicationCommandInteraction | APIMessageComponentInteraction; const interaction = await request.json() as APIPingInteraction | APIApplicationCommandInteraction | APIMessageComponentInteraction;
console.log(interaction);
if (interaction.type === InteractionType.Ping) if (interaction.type === InteractionType.Ping)
return respond({ return respond({
type: InteractionResponseType.Pong type: InteractionResponseType.Pong
}); });
if (interaction.type === InteractionType.ApplicationCommand && interaction.data.name === 'setup') { if (interaction.type === InteractionType.ApplicationCommand && interaction.data.type === ApplicationCommandType.ChatInput && interaction.data.name === 'setup') {
if ((Number(interaction.member?.permissions) & 0x10) !== 0x10) return respond({ if ((Number(interaction.member?.permissions) & 0x10) !== 0x10) return respond({
type: InteractionResponseType.ChannelMessageWithSource, type: InteractionResponseType.ChannelMessageWithSource,
data: { data: {
@ -36,10 +26,8 @@ export const handleRequest = async(request: Request): Promise<Response> => {
} }
}); });
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error No typings for value
// @ts-ignore const json = isJSON(interaction.data.options?.[0]?.value) ? JSON.parse(interaction.data.options?.[0]?.value) : null;
const json = isJSON(interaction.data.options[0].value) ? JSON.parse(interaction.data.options[0].value) : null;
if (!json) return badFormatting(); if (!json) return badFormatting();
const channelId = json.channel; const channelId = json.channel;
@ -50,34 +38,7 @@ export const handleRequest = async(request: Request): Promise<Response> => {
if (!message) message = ''; if (!message) message = '';
if (!roles || Object.values(json.roles).filter((role: any) => role.id && role.label).length === 0 || roles.length === 0 || roles.length > 25) return badFormatting(roles.length > 25); if (!roles || Object.values(json.roles).filter((role: any) => role.id && role.label).length === 0 || roles.length === 0 || roles.length > 25) return badFormatting(roles.length > 25);
roles = roles.map((r: any) => { const finalComponents = json.type === 1 ? resolveButtonComponents(roles) : resolveSelectMenuComponents(roles, json.placeholder?.toString());
const o: any = {
type: 2,
style: r.style || 2,
label: r.label,
custom_id: r.id
};
if (r.emoji) o.emoji = resolvePartialEmoji(r.emoji);
return o;
});
let finalComponents = [];
for (let i = 0; i <= roles.length; i += 5) {
const row: any = {
type: 1,
components: []
};
const btnslice: any = roles.slice(i, i + 5);
for (let y = 0; y < btnslice.length; y++) row.components.push(btnslice[y]);
finalComponents.push(row);
}
finalComponents = finalComponents.filter(a => a.components.length > 0);
const fetched = await fetch(`${RouteBases.api}/channels/${channelId}/messages`, { const fetched = await fetch(`${RouteBases.api}/channels/${channelId}/messages`, {
method: 'POST', method: 'POST',
@ -99,7 +60,7 @@ export const handleRequest = async(request: Request): Promise<Response> => {
} }
}); });
} else if (interaction.type === InteractionType.MessageComponent) { } else if (interaction.type === InteractionType.MessageComponent) {
const roleId = interaction.data.custom_id; const roleId = interaction.data.component_type === ComponentType.Button ? interaction.data.custom_id : interaction.data.values[0];
const url = `${RouteBases.api}${Routes.guildMemberRole(interaction.guild_id || '', interaction.member?.user.id || '', roleId)}`; const url = `${RouteBases.api}${Routes.guildMemberRole(interaction.guild_id || '', interaction.member?.user.id || '', roleId)}`;
let method = ''; let method = '';
@ -128,8 +89,7 @@ export const handleRequest = async(request: Request): Promise<Response> => {
allowed_mentions: { parse: [] } allowed_mentions: { parse: [] }
} }
}); });
} } else {
return respond({ return respond({
type: InteractionResponseType.ChannelMessageWithSource, type: InteractionResponseType.ChannelMessageWithSource,
data: { data: {
@ -137,4 +97,5 @@ export const handleRequest = async(request: Request): Promise<Response> => {
content: 'Beep boop, boop beep?' content: 'Beep boop, boop beep?'
} }
}); });
}
}; };

View file

@ -0,0 +1,12 @@
import { InteractionResponseType } from 'discord-api-types/v9';
import { respond } from './respond';
export const badFormatting = (rolesMax?: boolean) => {
return respond({
type: InteractionResponseType.ChannelMessageWithSource,
data: {
flags: 64,
content: `${rolesMax ? 'You can have maximum 25 buttons. (5x5)' : 'Bad formatting, generate [here](https://xhyrom.github.io/roles-bot)'}`
}
});
};

View file

@ -0,0 +1,73 @@
import { ButtonStyle, ComponentType, Snowflake } from 'discord-api-types/v9';
import { resolvePartialEmoji } from './resolveEmoji';
interface Role {
id: Snowflake;
style?: ButtonStyle;
label: string;
description?: string;
emoji: string | null;
}
export const resolveButtonComponents = (roles: Array<Role>) => {
roles = roles.map((r: Role) => {
const o: any = {
type: ComponentType.Button,
style: r.style || ButtonStyle.Secondary,
label: r.label,
custom_id: r.id
};
if (r.emoji) o.emoji = resolvePartialEmoji(r.emoji);
return o;
});
let finalComponents = [];
for (let i = 0; i <= roles.length; i += 5) {
const row: any = {
type: ComponentType.ActionRow,
components: []
};
const btnslice: any = roles.slice(i, i + 5);
for (let y = 0; y < btnslice.length; y++) row.components.push(btnslice[y]);
finalComponents.push(row);
}
finalComponents = finalComponents.filter(a => a.components.length > 0);
return finalComponents;
}
export const resolveSelectMenuComponents = (roles: Array<Role>, placeholder?: string) => {
roles = roles.map((r: Role) => {
const o: any = {
label: r.label,
value: r.id,
description: r.description,
};
if (r.emoji) o.emoji = resolvePartialEmoji(r.emoji);
return o;
});
const actionRow = [
{
type: ComponentType.ActionRow,
components: [
{
type: ComponentType.SelectMenu,
custom_id: 'role_select',
options: roles,
}
]
}
];
// @ts-expect-error No typings
if (placeholder) actionRow[0].components[0].placeholder = placeholder;
return actionRow;
}

View file

@ -0,0 +1,3 @@
import { APIInteractionResponse } from 'discord-api-types/v9';
export const respond = (response: APIInteractionResponse) => new Response(JSON.stringify(response), {headers: {'content-type': 'application/json'}});

View file

@ -5,7 +5,7 @@
function hex2bin(hex: string) { function hex2bin(hex: string) {
const buf = new Uint8Array(Math.ceil(hex.length / 2)); const buf = new Uint8Array(Math.ceil(hex.length / 2));
for (let i = 0; i < buf.length; i++) { for (let i = 0; i < buf.length; i++) {
buf[i] = parseInt(hex.substr(i * 2, 2), 16); buf[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
} }
return buf; return buf;
} }

View file

@ -1,7 +1,7 @@
const { createSecureHeaders } = require('next-secure-headers'); const { createSecureHeaders } = require('next-secure-headers');
module.exports = { module.exports = {
assetPrefix: '/roles-bot/', assetPrefix: '/',
async headers() { async headers() {
return [{ return [{
source: '/(.*)', source: '/(.*)',

View file

@ -18,6 +18,11 @@ export default function Home() {
<h1>Generate</h1> <h1>Generate</h1>
<form> <form>
<input placeholder="Your Message" name="message" id="message"/><br /> <input placeholder="Your Message" name="message" id="message"/><br />
<select placeholder="Message Type" name="type" id="type" required>
<option value="" disabled={true}>Select a type</option>
<option value="1">Buttons</option>
<option value="2">Select Menu</option>
</select><br />
<input placeholder="Channel Id*" name="channel" id="channel" required/> <input placeholder="Channel Id*" name="channel" id="channel" required/>
</form> </form>
<button id="addRole">Add Role</button> <button id="addRole">Add Role</button>

View file

@ -1,28 +1,34 @@
const json = { const json = {
roles: [] roles: [],
type: "1",
}; };
document.getElementById('json').innerHTML = hljs.highlight(JSON.stringify(json), { language: 'json' }).value;
$('input').change((e) => { for (const tag of ['input', 'select']) {
$(tag).change((e) => {
json[e.currentTarget.id] = e.currentTarget.value.replaceAll('\\n', '\n'); json[e.currentTarget.id] = e.currentTarget.value.replaceAll('\\n', '\n');
document.getElementById('json').innerHTML = hljs.highlight(JSON.stringify(json), { language: 'json' }).value; document.getElementById('json').innerHTML = hljs.highlight(JSON.stringify(json), { language: 'json' }).value;
}); });
}
$('button[id=addRole]').click((e) => { $('button[id=addRole]').click((e) => {
Swal.fire({ Swal.fire({
title: 'Add Role', title: 'Add Role',
html: html:
'<input id="swal-input1" class="swal2-input" placeholder="Button Label*" required />' + `<input id="swal-input1" class="swal2-input" placeholder="${json.type === "1" ? 'Button' : 'Option'} Label*" required />` +
'<input id="swal-input2" class="swal2-input" placeholder="Role Id*" required />' + '<input id="swal-input2" class="swal2-input" placeholder="Role Id*" required />' +
'<input id="swal-input3" class="swal2-input" placeholder="Emoji" />' + '<input id="swal-input3" class="swal2-input" placeholder="Emoji" />' +
'<select id="swal-input4" class="swal2-input" style="display: flex;"><option value="" disabled="">Select a style</option><option value="1">Primary</option><option value="2">Secondary</option><option value="3">Success</option><option value="4">Danger</option></select>', `${json.type === "2" ? '<input id="swal-input4" class="swal2-input" placeholder="Option Placeholder" />' : ''} ` +
`${json.type === "1" ? '<select id="swal-input5" class="swal2-input" style="display: flex;"><option value="" disabled="">Select a style</option><option value="1">Primary</option><option value="2">Secondary</option><option value="3">Success</option><option value="4">Danger</option></select>' : ''}`,
preConfirm: function () { preConfirm: function () {
return new Promise(function (resolve) { return new Promise(function (resolve) {
resolve([ resolve([
$('#swal-input1').val(), $('#swal-input1').val(),
$('#swal-input2').val(), $('#swal-input2').val(),
$('#swal-input3').val(), $('#swal-input3').val(),
$('#swal-input4').val() $('#swal-input4').val(),
$('#swal-input5').val()
]); ]);
}); });
} }
@ -32,7 +38,8 @@ $('button[id=addRole]').click((e) => {
id: result.value[1], id: result.value[1],
label: result.value[0], label: result.value[0],
emoji: result.value[2] || null, emoji: result.value[2] || null,
style: parseInt(result.value[3]) || 2 placeholder: result.value[3] || null,
style: parseInt(result.value[4]) || 2
}); });
document.getElementById('json').innerHTML = hljs.highlight(JSON.stringify(json), { language: 'json' }).value; document.getElementById('json').innerHTML = hljs.highlight(JSON.stringify(json), { language: 'json' }).value;

View file

@ -128,7 +128,7 @@ button:hover {
background-color: var(--yellow-dark); background-color: var(--yellow-dark);
} }
input { input, select {
color: white; color: white;
padding: .2rem .5rem; padding: .2rem .5rem;
background-color: var(--codeblock); background-color: var(--codeblock);
@ -154,7 +154,7 @@ input {
} }
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {
input { input, select {
font-size: 1rem; font-size: 1rem;
} }
button { button {

View file

@ -1,6 +1,6 @@
{ {
"version": 3, "version": 3,
"mappings": "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", "mappings": "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",
"sources": [ "sources": [
"../scss/style.scss", "../scss/style.scss",
"../scss/_globals.scss", "../scss/_globals.scss",

View file

@ -14,7 +14,7 @@ button {
} }
} }
input { input, select {
color: white; color: white;
padding: .2rem .5rem; padding: .2rem .5rem;
background-color: var(--codeblock); background-color: var(--codeblock);
@ -40,7 +40,7 @@ max-width: 900px;
} }
@media screen and (max-width:600px) { @media screen and (max-width:600px) {
input { input, select {
font-size: 1rem; font-size: 1rem;
} }
button { button {