Skip to content

Commit

Permalink
feat: emote management
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldietzler committed Nov 25, 2024
1 parent 4967327 commit 600f7af
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import { DiscordHelpDesk } from 'src/discord/help-desk';
import { providers } from 'src/repositories';
import { services } from 'src/services';
import { DatabaseService } from 'src/services/database.service';
import { DiscordContextMenus } from './discord/context-menus';

const middleware = [{ provide: APP_PIPE, useValue: new ValidationPipe({ transform: true, whitelist: true }) }];
const discord = [DiscordCommands, DiscordEvents, DiscordHelpDesk];
const discord = [DiscordCommands, DiscordEvents, DiscordHelpDesk, DiscordContextMenus];

@Module({
imports: [ScheduleModule.forRoot()],
Expand Down
38 changes: 37 additions & 1 deletion src/discord/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
ThreadChannel,
type CommandInteraction,
} from 'discord.js';
import { Discord, ModalComponent, Slash, SlashOption } from 'discordx';
import { Discord, ModalComponent, Slash, SlashChoice, SlashOption } from 'discordx';
import { Constants, DiscordField, DiscordModal } from 'src/constants';
import { DiscordChannel } from 'src/interfaces/discord.interface';
import { DiscordService } from 'src/services/discord.service';
Expand Down Expand Up @@ -327,4 +327,40 @@ export class DiscordCommands {

await interaction.deferUpdate();
}

@Slash({ name: 'emote-add', description: 'Add new emotes to the server' })
async handleEmoteAdd(
@SlashChoice('7tv', 'bttv')
@SlashOption({
name: 'source',
description: 'Where the emote is from',
type: ApplicationCommandOptionType.String,
required: true,
})
source: '7tv' | 'bttv',
@SlashOption({
name: 'id',
description: 'ID of the emote',
type: ApplicationCommandOptionType.String,
required: true,
})
id: string,
interaction: CommandInteraction,
) {
let emote;
switch (source) {
case '7tv':
emote = await this.service.create7TvEmote(id, interaction.guildId);
break;
case 'bttv':
emote = await this.service.createBttvEmote(id, interaction.guildId);
}

if (!emote) {
await interaction.reply({ content: `Could not find ${source.toUpperCase()} emote with id ${id}` });
return;
}

await interaction.reply({ content: `Emote successfully added! ${emote.toString()}` });
}
}
50 changes: 50 additions & 0 deletions src/discord/context-menus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Injectable } from '@nestjs/common';
import { Discord } from 'discordx';
import { DiscordService } from 'src/services/discord.service';

@Discord()
@Injectable()
export class DiscordContextMenus {
constructor(private service: DiscordService) {}

// @ContextMenu({
// name: 'Save as emote',
// type: ApplicationCommandType.Message,
// defaultMemberPermissions: 'ManageEmojisAndStickers',
// })
// async onSaveEmote(interaction: MessageContextMenuCommandInteraction) {
// const modal = new ModalBuilder({
// customId: 'emote_create_label',
// title: 'Create Emote',
// components: [
// new ActionRowBuilder<TextInputBuilder>({
// components: [
// new TextInputBuilder({
// customId: 'name',
// label: 'Emote Name',
// style: TextInputStyle.Short,
// required: true,
// }),
// ],
// }),
// ],
// });
// await interaction.showModal(modal);
// const modalResponse = await interaction.awaitModalSubmit({ time: 9999999 });

// const emoteUrl = interaction.targetMessage.attachments.first()?.url;

// if (!emoteUrl) {
// await interaction.reply({ content: 'Could not find emote.' });
// return;
// }

// const emote = await this.service.createEmote(
// modalResponse.fields.getTextInputValue('name'),
// emoteUrl,
// interaction.guildId,
// );
// console.log(emote);
// await interaction.reply({ content: emote?.identifier });
// }
}
3 changes: 2 additions & 1 deletion src/interfaces/discord.interface.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MessageCreateOptions } from 'discord.js';
import { GuildEmoji, MessageCreateOptions } from 'discord.js';

export const IDiscordInterface = 'IDiscordInterface';

Expand Down Expand Up @@ -34,4 +34,5 @@ export interface IDiscordInterface {
message: string | MessageCreateOptions;
crosspost?: boolean;
}): Promise<void>;
createEmote(name: string, emote: string | Buffer, guildId: string): Promise<GuildEmoji | undefined>;
}
4 changes: 4 additions & 0 deletions src/repositories/discord.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,8 @@ export class DiscordRepository implements IDiscordInterface {
}
}
}

async createEmote(name: string, emote: string | Buffer, guildId: string) {
return bot.guilds.cache.get(guildId)?.emojis.create({ name, attachment: emote });
}
}
1 change: 1 addition & 0 deletions src/services/discord.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const newGithubMockRepository = (): Mocked<IGithubInterface> => ({
const newDiscordMockRepository = (): Mocked<IDiscordInterface> => ({
login: vitest.fn(),
sendMessage: vitest.fn(),
createEmote: vitest.fn(),
});

const newDatabaseMockRepository = (): Mocked<IDatabaseRepository> => ({
Expand Down
66 changes: 66 additions & 0 deletions src/services/discord.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,32 @@ type GithubLink = {
};
type LinkType = 'issue' | 'pull' | 'discussion';

type SevenTVResponse = {
id: string;
name: string;
host: {
url: string;
files: [
{
name: string;
static_name: string;
width: number;
height: number;
frame_count: number;
size: number;
format: string;
},
];
};
};

type BetterTTVResponse = {
id: string;
code: string;
imageType: string;
animated: string;
};

@Injectable()
export class DiscordService {
private logger = new Logger(DiscordService.name);
Expand Down Expand Up @@ -325,4 +351,44 @@ export class DiscordService {
}
return this.database.addDiscordMessage({ name, content, lastEditedBy: author });
}

async createEmote(name: string, emote: string | Buffer, guildId: string | null) {
if (!guildId) {
return;
}

return this.discord.createEmote(name, emote, guildId);
}

async create7TvEmote(id: string, guildId: string | null) {
if (!guildId) {
return;
}

const rawResponse = await fetch(`https://7tv.io/v3/emotes/${id}`);
if (rawResponse.status !== 200) {
return;
}

const response = (await rawResponse.json()) as SevenTVResponse;
const gif = response.host.files.findLast((file) => file.format === 'GIF' && file.size < 256_000);
const file = gif || response.host.files.findLast((file) => file.format === 'WEBP' && file.size < 256_000)!;
this.logger.log(`https:${response.host.url}/${file.name}`);
return this.discord.createEmote(response.name, `https:${response.host.url}/${file.name}`, guildId);
}

async createBttvEmote(id: string, guildId: string | null) {
if (!guildId) {
return;
}

const rawResponse = await fetch(`https://api.betterttv.net/3/emotes/${id}`);
if (rawResponse.status !== 200) {
return;
}

const response = (await rawResponse.json()) as BetterTTVResponse;

return this.discord.createEmote(response.code, `https://cdn.betterttv.net/emote/${id}/3x`, guildId);
}
}

0 comments on commit 600f7af

Please sign in to comment.