Skip to content

Commit

Permalink
feat: new context menus
Browse files Browse the repository at this point in the history
  • Loading branch information
didinele committed Oct 10, 2023
1 parent 483edf5 commit 740e0dc
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 7 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (C) 2022 didinele
Copyright (C) 2023 ChatSift

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
Expand Down
104 changes: 104 additions & 0 deletions packages/bot/src/commands/add-answer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { ms } from '@naval-base/ms';
import { PrismaClient } from '@prisma/client';
import type { Embed, MessageContextMenuCommandInteraction, ModalActionRowComponentBuilder } from 'discord.js';
import {
ActionRowBuilder,
ApplicationCommandType,
EmbedBuilder,
ModalBuilder,
TextInputBuilder,
TextInputStyle,
} from 'discord.js';
import { nanoid } from 'nanoid';
import { singleton } from 'tsyringe';
import type { Command, CommandBody } from '../struct/Command.js';
import { Colors } from '../util/colors.js';

@singleton()
export default class implements Command<ApplicationCommandType.Message> {
public readonly interactionOptions: CommandBody<ApplicationCommandType.Message> = {
name: 'Add Answer',
type: ApplicationCommandType.Message,
default_member_permissions: '0',
dm_permission: false,
};

public constructor(private readonly prisma: PrismaClient) {}

public async handle(interaction: MessageContextMenuCommandInteraction<'cached'>) {
const question = await this.prisma.amaQuestion.findFirst({
where: {
answerMessageId: interaction.targetId,
},
});

if (!question) {
return interaction.reply({
content: 'This message is not an AMA question.',
ephemeral: true,
});
}

const id = nanoid();

const modal = new ModalBuilder()
.setTitle('Add an answer')
.setCustomId(id)
.addComponents(
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new TextInputBuilder()
.setCustomId('answer')
.setLabel('Answer to the question')
.setPlaceholder('Yes! I love ramen!')
.setMinLength(2)
.setMaxLength(4_000)
.setStyle(TextInputStyle.Paragraph)
.setRequired(true),
),
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new TextInputBuilder()
.setCustomId('image-url')
.setLabel('(Optional) Image URL to use')
.setStyle(TextInputStyle.Short)
.setRequired(false),
),
);

await interaction.showModal(modal);
const modalInteraction = await interaction
.awaitModalSubmit({ time: ms('5m'), filter: (interaction) => interaction.customId === id })
.catch(() => null);

if (!modalInteraction) {
return;
}

const text = modalInteraction.fields.getTextInputValue('answer');
const imageUrl = modalInteraction.fields.getTextInputValue('image-url');

const embeds: (Embed | EmbedBuilder)[] = interaction.targetMessage.embeds;
const answerEmbed = new EmbedBuilder()
.setDescription(text)
.setImage(imageUrl.length ? imageUrl : null)
.setAuthor({
name: `${interaction.user.tag} (${interaction.user.id})`,
iconURL: interaction.user.displayAvatarURL(),
})
.setColor(Colors.Blurple);

if (embeds.length >= 2) {
embeds.splice(1, 1, answerEmbed);
} else {
embeds.push(answerEmbed);
}

await interaction.targetMessage.edit({
embeds,
});

await modalInteraction.reply({
content: 'Answer added!',
ephemeral: true,
});
}
}
94 changes: 94 additions & 0 deletions packages/bot/src/commands/add-timestamp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { ms } from '@naval-base/ms';
import { PrismaClient } from '@prisma/client';
import type { MessageContextMenuCommandInteraction, ModalActionRowComponentBuilder } from 'discord.js';
import {
ActionRowBuilder,
ApplicationCommandType,
Embed,
EmbedBuilder,
ModalBuilder,
TextInputBuilder,
TextInputStyle,
} from 'discord.js';
import { nanoid } from 'nanoid';
import { singleton } from 'tsyringe';
import type { Command, CommandBody } from '../struct/Command';

@singleton()
export default class implements Command<ApplicationCommandType.Message> {
public readonly interactionOptions: CommandBody<ApplicationCommandType.Message> = {
name: 'Add Timestamp',
type: ApplicationCommandType.Message,
default_member_permissions: '0',
dm_permission: false,
};

public constructor(private readonly prisma: PrismaClient) {}

public async handle(interaction: MessageContextMenuCommandInteraction<'cached'>) {
const question = await this.prisma.amaQuestion.findFirst({
where: {
answerMessageId: interaction.targetId,
},
});

if (!question) {
return interaction.reply({
content: 'This message is not an AMA question.',
ephemeral: true,
});
}

const id = nanoid();

const modal = new ModalBuilder()
.setTitle('Indicate when the question was answered')
.setCustomId(id)
.addComponents(
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new TextInputBuilder()
.setCustomId('text')
.setLabel('(Text) Visual representation of the timestamp')
.setPlaceholder('e.g. 1:23:45')
.setMinLength(1)
.setMaxLength(20)
.setStyle(TextInputStyle.Short)
.setRequired(true),
),
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new TextInputBuilder()
.setCustomId('url')
.setLabel('(Optional) Link for the timestamp')
.setPlaceholder('e.g. https://youtu.be/Siqi_yunMV0')
.setStyle(TextInputStyle.Short)
.setRequired(false),
),
);

await interaction.showModal(modal);
const modalInteraction = await interaction
.awaitModalSubmit({ time: ms('5m'), filter: (interaction) => interaction.customId === id })
.catch(() => null);

if (!modalInteraction) {
return;
}

const text = modalInteraction.fields.getTextInputValue('text');
const url = modalInteraction.fields.getTextInputValue('url');

const final = url.length ? `[${text}](${url})` : text;

const [toUpdate, ...rest] = interaction.targetMessage.embeds;
const updated = new EmbedBuilder(toUpdate!.toJSON()).setFields([{ name: 'Answered at', value: final }]);

await interaction.targetMessage.edit({
embeds: [updated, ...rest],
});

await modalInteraction.reply({
content: 'Timestamp added!',
ephemeral: true,
});
}
}
20 changes: 17 additions & 3 deletions packages/bot/src/struct/AmaManager.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { PrismaClient } from '@prisma/client';
import type { AmaQuestion } from '@prisma/client';
import { Result } from '@sapphire/result';
import type { MessageActionRowComponentBuilder, TextChannel, User } from 'discord.js';
import { ActionRowBuilder, ButtonBuilder, EmbedBuilder, ButtonStyle, Client, Colors } from 'discord.js';
import { ActionRowBuilder, ButtonBuilder, EmbedBuilder, ButtonStyle, Client } from 'discord.js';
import { singleton } from 'tsyringe';
import { Colors } from '../util/colors.js';

export interface EmbedData {
content: string;
Expand Down Expand Up @@ -37,7 +39,10 @@ export type PostToAnswerChannelData = PostData & {

@singleton()
export class AmaManager {
public constructor(private readonly client: Client) {}
public constructor(
private readonly client: Client,
private readonly prisma: PrismaClient,
) {}

private getBaseEmbed({ content, imageUrl, user }: EmbedData): EmbedBuilder {
return new EmbedBuilder()
Expand Down Expand Up @@ -145,11 +150,20 @@ export class AmaManager {
return Result.err(new Error('The answers channel no longer exists - please contact an admin.'));
}

await channel.send({
const message = await channel.send({
allowedMentions: { parse: [] },
embeds: [embed],
});

await this.prisma.amaQuestion.update({
where: {
id: question.id,
},
data: {
answerMessageId: message.id,
},
});

return Result.ok();
}
}
2 changes: 0 additions & 2 deletions prisma/migrations/20231010155620_remove_stages/migration.sql

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "Ama" ALTER COLUMN "stageOnly" SET DEFAULT false;

-- AlterTable
ALTER TABLE "AmaQuestion" ADD COLUMN "answerMessageId" TEXT;
2 changes: 2 additions & 0 deletions prisma/migrations/20231010163926_add_created_at/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Ama" ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
3 changes: 2 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["interactiveTransactions"]
}

datasource db {
Expand All @@ -20,6 +19,7 @@ model Ama {
promptMessageId String @unique
stageOnly Boolean @default(false) // deprecated, hence the defualt.
ended Boolean @default(false)
createdAt DateTime @default(now())
questions AmaQuestion[]
}
Expand All @@ -31,4 +31,5 @@ model AmaQuestion {
authorId String
content String
imageUrl String?
answerMessageId String?
}

0 comments on commit 740e0dc

Please sign in to comment.