Skip to content

Commit

Permalink
fix(interaction): enable message component delivery
Browse files Browse the repository at this point in the history
  • Loading branch information
BastiDood committed Dec 8, 2024
1 parent d25277c commit 8b8ea8a
Show file tree
Hide file tree
Showing 17 changed files with 177 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function handleError({ error, event }) {
if (typeof event.locals.ctx !== 'undefined') {
if (isValiError(error)) {
const valibotErrorPaths = error.issues.map(issue => getDotPath(issue)).filter(path => path !== null);
event.locals.ctx.logger.fatal({ valibotError: error, valibotErrorPaths }, 'valibot validation failed');
event.locals.ctx.logger.fatal({ valibotErrorPaths }, 'valibot validation failed');
} else if (error instanceof AssertionError) {
event.locals.ctx.logger.fatal({ nodeAssertionError: error }, 'assertion error encountered');
} else if (error instanceof Error) {
Expand Down
12 changes: 6 additions & 6 deletions src/lib/server/api/discord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ async function sendMessage(logger: Logger, channelId: Snowflake, data: CreateMes
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': body.length.toString(),
Authorization: `Bot ${botToken}`,
},
});
Expand Down Expand Up @@ -132,16 +131,16 @@ export async function logPendingConfessionViaHttp(
{
type: MessageComponentType.Button,
style: MessageComponentButtonStyle.Success,
emoji: { id: null, name: '️🖊️' },
label: 'Publish',
custom_id: customId,
emoji: { name: '\u{2712}\u{fe0f}' },
custom_id: `publish:${customId}`,
},
{
type: MessageComponentType.Button,
style: MessageComponentButtonStyle.Danger,
emoji: { id: null, name: '🗑️' },
label: 'Delete',
custom_id: customId,
emoji: { name: '\u{1f5d1}\u{fe0f}' },
custom_id: `delete:${customId}`,
},
],
},
Expand Down Expand Up @@ -254,7 +253,6 @@ async function editMessage(
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'Content-Length': body.length.toString(),
Authorization: `Bot ${botToken}`,
},
});
Expand Down Expand Up @@ -290,6 +288,7 @@ export async function approveConfessionLog(
channelId,
messageId,
{
components: [],
embeds: [
{
type: EmbedType.Rich,
Expand Down Expand Up @@ -337,6 +336,7 @@ export async function rejectConfessionLog(
logChannelId,
logMessageId,
{
components: [],
embeds: [
{
type: EmbedType.Rich,
Expand Down
2 changes: 1 addition & 1 deletion src/lib/server/models/discord/emoji.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type InferOutput, boolean, nullable, object, optional, string } from 'v
import { Snowflake } from '$lib/server/models/discord/snowflake';

export const Emoji = object({
id: nullable(Snowflake),
id: optional(nullable(Snowflake)),
name: nullable(string()),
require_colons: optional(boolean()),
managed: optional(boolean()),
Expand Down
11 changes: 10 additions & 1 deletion src/lib/server/models/discord/interaction/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type InferOutput, variant } from 'valibot';

import { DeserializedInteractionMessageComponent, InteractionMessageComponent } from './message-component';
import { InteractionApplicationCommand } from './application-command';
import { InteractionMessageComponent } from './message-component';
import { InteractionModalSubmit } from './modal-submit';
import { InteractionPing } from './ping';

Expand All @@ -13,3 +13,12 @@ export const Interaction = variant('type', [
]);

export type Interaction = InferOutput<typeof Interaction>;

export const DeserializedInteraction = variant('type', [
InteractionPing,
InteractionApplicationCommand,
DeserializedInteractionMessageComponent,
InteractionModalSubmit,
]);

export type DeserializedInteraction = InferOutput<typeof DeserializedInteraction>;
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { type InferOutput, variant } from 'valibot';

import { InteractionDataMessageComponentButtonLink } from './link';
import { InteractionDataMessageComponentButtonNormal } from './normal';
import { InteractionDataMessageComponentButtonPremium } from './premium';
import {
DeserializedInteractionDataMessageComponentButtonLink,
InteractionDataMessageComponentButtonLink,
} from './link';
import {
DeserializedInteractionDataMessageComponentButtonNormal,
InteractionDataMessageComponentButtonNormal,
} from './normal';
import {
DeserializedInteractionDataMessageComponentButtonPremium,
InteractionDataMessageComponentButtonPremium,
} from './premium';

export const InteractionDataMessageComponentButton = variant('style', [
InteractionDataMessageComponentButtonLink,
Expand All @@ -11,3 +20,13 @@ export const InteractionDataMessageComponentButton = variant('style', [
]);

export type InteractionMessageComponentButton = InferOutput<typeof InteractionDataMessageComponentButton>;

export const DeserializedInteractionDataMessageComponentButton = variant('component_type', [
DeserializedInteractionDataMessageComponentButtonLink,
DeserializedInteractionDataMessageComponentButtonNormal,
DeserializedInteractionDataMessageComponentButtonPremium,
]);

export type DeserializedInteractionMessageComponentButton = InferOutput<
typeof DeserializedInteractionDataMessageComponentButton
>;
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { type InferOutput, object } from 'valibot';

import {
DeserializedMessageComponentButtonLink,
MessageComponentButtonLink,
} from '$lib/server/models/discord/message/component/button/link';
import { InteractionDataMessageComponentBase } from '$lib/server/models/discord/interaction/message-component/base';
import { MessageComponentButtonLink } from '$lib/server/models/discord/message/component/button/link';

export const InteractionDataMessageComponentButtonLink = object({
...InteractionDataMessageComponentBase.entries,
...MessageComponentButtonLink.entries,
});

export type InteractionDataMessageComponentButtonLink = InferOutput<typeof InteractionDataMessageComponentButtonLink>;

export const DeserializedInteractionDataMessageComponentButtonLink = object({
...InteractionDataMessageComponentBase.entries,
...DeserializedMessageComponentButtonLink.entries,
});

export type DeserializedInteractionDataMessageComponentButtonLink = InferOutput<
typeof DeserializedInteractionDataMessageComponentButtonLink
>;
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { type InferOutput, object } from 'valibot';

import {
DeserializedMessageComponentButtonNormal,
MessageComponentButtonNormal,
} from '$lib/server/models/discord/message/component/button/normal';
import { InteractionDataMessageComponentBase } from '$lib/server/models/discord/interaction/message-component/base';
import { MessageComponentButtonNormal } from '$lib/server/models/discord/message/component/button/normal';

export const InteractionDataMessageComponentButtonNormal = object({
...InteractionDataMessageComponentBase.entries,
Expand All @@ -11,3 +14,12 @@ export const InteractionDataMessageComponentButtonNormal = object({
export type InteractionDataMessageComponentButtonNormal = InferOutput<
typeof InteractionDataMessageComponentButtonNormal
>;

export const DeserializedInteractionDataMessageComponentButtonNormal = object({
...InteractionDataMessageComponentBase.entries,
...DeserializedMessageComponentButtonNormal.entries,
});

export type DeserializedInteractionDataMessageComponentButtonNormal = InferOutput<
typeof DeserializedInteractionDataMessageComponentButtonNormal
>;
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { type InferOutput, object } from 'valibot';

import {
DeserializedMessageComponentButtonPremium,
MessageComponentButtonPremium,
} from '$lib/server/models/discord/message/component/button/premium';
import { InteractionDataMessageComponentBase } from '$lib/server/models/discord/interaction/message-component/base';
import { MessageComponentButtonPremium } from '$lib/server/models/discord/message/component/button/premium';

export const InteractionDataMessageComponentButtonPremium = object({
...InteractionDataMessageComponentBase.entries,
Expand All @@ -11,3 +14,12 @@ export const InteractionDataMessageComponentButtonPremium = object({
export type InteractionDataMessageComponentButtonLink = InferOutput<
typeof InteractionDataMessageComponentButtonPremium
>;

export const DeserializedInteractionDataMessageComponentButtonPremium = object({
...InteractionDataMessageComponentBase.entries,
...DeserializedMessageComponentButtonPremium.entries,
});

export type DeserializedInteractionDataMessageComponentButtonLink = InferOutput<
typeof DeserializedInteractionDataMessageComponentButtonPremium
>;
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ import { type InferOutput, literal, object, variant } from 'valibot';

import { InteractionBase, InteractionType } from '$lib/server/models/discord/interaction/base';

import { InteractionDataMessageComponentButton } from './button';
import { InteractionDataMessageComponentSnowflakeSelect } from './snowflake-select';
import { InteractionDataMessageComponentStringSelect } from './string-select';
import { DeserializedInteractionDataMessageComponentButton, InteractionDataMessageComponentButton } from './button';
import {
DeserializedInteractionDataMessageComponentSnowflakeSelect,
InteractionDataMessageComponentSnowflakeSelect,
} from './snowflake-select';
import {
DeserializedInteractionDataMessageComponentStringSelect,
InteractionDataMessageComponentStringSelect,
} from './string-select';

export const InteractionMessageComponent = object({
...InteractionBase.entries,
Expand All @@ -17,3 +23,15 @@ export const InteractionMessageComponent = object({
});

export type InteractionMessageComponent = InferOutput<typeof InteractionMessageComponent>;

export const DeserializedInteractionMessageComponent = object({
...InteractionBase.entries,
type: literal(InteractionType.MessageComponent),
data: variant('component_type', [
DeserializedInteractionDataMessageComponentButton,
DeserializedInteractionDataMessageComponentStringSelect,
DeserializedInteractionDataMessageComponentSnowflakeSelect,
]),
});

export type DeserializedInteractionMessageComponent = InferOutput<typeof DeserializedInteractionMessageComponent>;
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,18 @@ export const InteractionDataMessageComponentSnowflakeSelect = object({
export type InteractionDataMessageComponentSnowflakeSelect = InferOutput<
typeof InteractionDataMessageComponentSnowflakeSelect
>;

export const DeserializedInteractionDataMessageComponentSnowflakeSelect = object({
...InteractionDataMessageComponentBase.entries,
component_type: union([
literal(MessageComponentType.UserSelect),
literal(MessageComponentType.RoleSelect),
literal(MessageComponentType.MentionableSelect),
literal(MessageComponentType.ChannelSelect),
]),
values: array(Snowflake),
});

export type DeserializedInteractionDataMessageComponentSnowflakeSelect = InferOutput<
typeof DeserializedInteractionDataMessageComponentSnowflakeSelect
>;
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,13 @@ export const InteractionDataMessageComponentStringSelect = object({
export type InteractionDataMessageComponentStringSelect = InferOutput<
typeof InteractionDataMessageComponentStringSelect
>;

export const DeserializedInteractionDataMessageComponentStringSelect = object({
...InteractionDataMessageComponentBase.entries,
component_type: literal(MessageComponentType.StringSelect),
values: array(string()),
});

export type DeserializedInteractionDataMessageComponentStringSelect = InferOutput<
typeof InteractionDataMessageComponentStringSelect
>;
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,12 @@ export const MessageComponentButtonLink = object({
});

export type MessageComponentButtonLink = InferOutput<typeof MessageComponentButtonLink>;

// HACK: Deserializing requires `component_type` instead of `type`. Wtf Discord?
export const DeserializedMessageComponentButtonLink = object({
...MessageComponentButtonBase.entries,
component_type: literal(MessageComponentType.Button),
url: Url,
});

export type DeserializedMessageComponentButtonLink = InferOutput<typeof DeserializedMessageComponentButtonLink>;
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ export const MessageComponentButtonNormal = object({
});

export type MessageComponentButtonNormal = InferOutput<typeof MessageComponentButtonNormal>;

// HACK: Deserializing requires `component_type` instead of `type`. Wtf Discord?
export const DeserializedMessageComponentButtonNormal = object({
...MessageComponentButtonBase.entries,
component_type: literal(MessageComponentType.Button),
custom_id: string(),
});

export type DeserializedMessageComponentButtonNormal = InferOutput<typeof DeserializedMessageComponentButtonNormal>;
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ export const MessageComponentButtonPremium = object({
});

export type MessageComponentButtonPremium = InferOutput<typeof MessageComponentButtonPremium>;

// HACK: Deserializing requires `component_type` instead of `type`. Wtf Discord?
export const DeserializedMessageComponentButtonPremium = object({
...MessageComponentButtonBase.entries,
component_type: literal(MessageComponentType.Button),
sku_id: string(),
});

export type DeserializedMessageComponentButtonPremium = InferOutput<typeof DeserializedMessageComponentButtonPremium>;
13 changes: 6 additions & 7 deletions src/routes/webhook/discord/interaction/+server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Buffer } from 'node:buffer';

import { DISCORD_PUBLIC_KEY } from '$lib/server/env/discord';

import { Interaction } from '$lib/server/models/discord/interaction';
import { DeserializedInteraction } from '$lib/server/models/discord/interaction';
import { InteractionApplicationCommandType } from '$lib/server/models/discord/interaction/application-command/base';
import type { InteractionCallback } from '$lib/server/models/discord/interaction-callback';
import { InteractionCallbackType } from '$lib/server/models/discord/interaction-callback/base';
Expand Down Expand Up @@ -35,7 +35,7 @@ async function handleInteraction(
db: Database,
logger: Logger, // TODO: Fine-grained database-level performance logs.
timestamp: Date,
interaction: Interaction,
interaction: DeserializedInteraction,
): Promise<InteractionCallback> {
// eslint-disable-next-line default-case
switch (interaction.type) {
Expand Down Expand Up @@ -157,17 +157,16 @@ async function handleInteraction(
assert(typeof interaction.message !== 'undefined');
assert(typeof interaction.member?.user !== 'undefined');
assert(typeof interaction.member.permissions !== 'undefined');
strictEqual(interaction.data.type, MessageComponentType.Button);
strictEqual(interaction.data.component_type, MessageComponentType.Button);
return {
type: InteractionCallbackType.ChannelMessageWithSource,
data: await handleApproval(
db,
logger,
timestamp,
interaction.data.style,
interaction.data.custom_id,
interaction.message.channel_id,
interaction.message.id,
BigInt(interaction.data.custom_id),
interaction.member.user.id,
interaction.member.permissions,
),
Expand Down Expand Up @@ -223,8 +222,8 @@ export async function POST({ locals: { ctx }, request }) {
const interaction = JSON.parse(text);
assert(typeof ctx !== 'undefined');
const logger = ctx.logger.child({ interaction });
ctx.logger.info('interaction received');
const parsed = parse(Interaction, interaction);
logger.info('interaction received');
const parsed = parse(DeserializedInteraction, interaction);

const start = performance.now();
const response = await handleInteraction(ctx.db, logger, datetime, parsed);
Expand Down
Loading

0 comments on commit 8b8ea8a

Please sign in to comment.