Adds deployment for icons
This commit is contained in:
parent
154002f6f3
commit
0e10ea3cab
14 changed files with 1449 additions and 53 deletions
|
|
@ -7,6 +7,8 @@ import {GroupRepository} from "../Repositories/GroupRepository";
|
|||
import {PlaydateRepository} from "../Repositories/PlaydateRepository";
|
||||
import {GuildEmojiRoleManager} from "discord.js";
|
||||
import {GroupConfigurationRepository} from "../Repositories/GroupConfigurationRepository";
|
||||
import {DiscordClient} from "../Discord/DiscordClient";
|
||||
import {IconCache} from "../Icons/IconCache";
|
||||
|
||||
export enum ServiceHint {
|
||||
App,
|
||||
|
|
@ -22,6 +24,12 @@ export class Services {
|
|||
const database = new DatabaseConnection(env.database);
|
||||
container.set<DatabaseConnection>(database);
|
||||
|
||||
const discordClient = new DiscordClient(env.discord.clientId);
|
||||
container.set<DiscordClient>(discordClient);
|
||||
|
||||
const iconCache = new IconCache(discordClient);
|
||||
container.set<IconCache>(iconCache);
|
||||
|
||||
// @ts-ignore
|
||||
configure({
|
||||
appenders: {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
ChatInputCommandInteraction,
|
||||
MessageFlags,
|
||||
Activity,
|
||||
ActivityType
|
||||
ActivityType, REST
|
||||
} from "discord.js";
|
||||
import Commands from "./Commands/Commands";
|
||||
import {Container} from "../Container/Container";
|
||||
|
|
@ -16,17 +16,33 @@ import {UserError} from "./UserError";
|
|||
export class DiscordClient {
|
||||
private readonly client: Client;
|
||||
private commands: Commands;
|
||||
private readonly restClient: REST;
|
||||
|
||||
public get Client (): Client {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
public get Commands(): Commands {
|
||||
return this.commands
|
||||
}
|
||||
|
||||
public get RESTClient(): REST {
|
||||
return this.restClient;
|
||||
}
|
||||
|
||||
public get ApplicationId(): string {
|
||||
return this.applicationId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly applicationId: string
|
||||
) {
|
||||
this.client = new Client({
|
||||
intents: [GatewayIntentBits.Guilds]
|
||||
})
|
||||
|
||||
this.commands = new Commands();
|
||||
this.restClient = new REST();
|
||||
}
|
||||
|
||||
applyEvents() {
|
||||
|
|
@ -52,6 +68,10 @@ export class DiscordClient {
|
|||
this.client.login(token);
|
||||
}
|
||||
|
||||
connectRESTClient(token: string) {
|
||||
this.restClient.setToken(token);
|
||||
}
|
||||
|
||||
private findCommandMethod(interaction: Interaction) {
|
||||
if (interaction.isChatInputCommand()) {
|
||||
const command = this.commands.getCommand(interaction.commandName);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {unwatchFile} from "node:fs";
|
|||
import {UserError} from "../Discord/UserError";
|
||||
import {RuntimeGroupConfiguration} from "./RuntimeGroupConfiguration";
|
||||
import {ChannelId} from "../types/DiscordTypes";
|
||||
import {IconCache} from "../Icons/IconCache";
|
||||
|
||||
type UIElementCollection = Record<string, UIElement>;
|
||||
type UIElement = {
|
||||
|
|
@ -227,7 +228,8 @@ export class GroupConfigurationRenderer {
|
|||
|
||||
private createActionRowBuildersForMenu() : ActionRowBuilder<MessageActionRowComponentBuilder>[] {
|
||||
const {currentCollection, currentElement} = this.findCurrentUI();
|
||||
|
||||
const icons = Container.get<IconCache>(IconCache.name);
|
||||
|
||||
if (currentElement?.isConfiguration ?? false) {
|
||||
return [
|
||||
new ActionRowBuilder<ChannelSelectMenuBuilder | MentionableSelectMenuBuilder | RoleSelectMenuBuilder | StringSelectMenuBuilder | UserSelectMenuBuilder>()
|
||||
|
|
@ -239,9 +241,10 @@ export class GroupConfigurationRenderer {
|
|||
new ActionRowBuilder<ButtonBuilder>()
|
||||
.setComponents(
|
||||
...Object.values(currentCollection).map(elem => new ButtonBuilder()
|
||||
.setLabel(elem.label)
|
||||
.setLabel(` ${elem.label}`)
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
.setCustomId(GroupConfigurationRenderer.MOVETO_COMMAND + elem.key)
|
||||
.setEmoji(icons.get("folder_tree_solid") ?? '')
|
||||
)
|
||||
)
|
||||
]
|
||||
|
|
|
|||
8
source/Icons/DiscordIcons.d.ts
vendored
Normal file
8
source/Icons/DiscordIcons.d.ts
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
type DiscordIcon = {
|
||||
id: string,
|
||||
name: string,
|
||||
}
|
||||
|
||||
type DiscordIconRequest = {
|
||||
items: DiscordIcon[]
|
||||
}
|
||||
52
source/Icons/IconCache.ts
Normal file
52
source/Icons/IconCache.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import {Routes} from "discord.js";
|
||||
import {DiscordClient} from "../Discord/DiscordClient";
|
||||
|
||||
export class IconCache {
|
||||
private existingIcons: Map<string, string>|null;
|
||||
|
||||
constructor(
|
||||
private readonly client: DiscordClient
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
public get(iconName: string): string | null {
|
||||
if (!this.existingIcons?.has(iconName) ?? false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.existingIcons?.get(iconName) ?? null;
|
||||
}
|
||||
|
||||
public async set(iconName: string, pngBuffer: Buffer) {
|
||||
const pngBase64 = pngBuffer.toString("base64");
|
||||
const iconDataUrl = `data:image/png;base64,${pngBase64}`;
|
||||
|
||||
await this.client.RESTClient.post(
|
||||
Routes.applicationEmojis(this.client.ApplicationId),
|
||||
{
|
||||
body: {
|
||||
name: iconName,
|
||||
image: iconDataUrl
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public async populate() {
|
||||
if (this.existingIcons != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const existingEmojis: DiscordIconRequest = await this.client.RESTClient.get(
|
||||
Routes.applicationEmojis(this.client.ApplicationId)
|
||||
)
|
||||
|
||||
this.existingIcons = new Map<string, string>(
|
||||
existingEmojis.items.map((item) => {
|
||||
return [ item.name, item.id ]
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
56
source/Icons/IconDeployer.ts
Normal file
56
source/Icons/IconDeployer.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import {REST, Routes} from "discord.js";
|
||||
import path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
import svg2img from "svg2img";
|
||||
import {IconCache} from "./IconCache";
|
||||
|
||||
export class IconDeployer {
|
||||
static ICON_PATH = path.resolve('public/icons')
|
||||
|
||||
constructor(
|
||||
private readonly iconCache: IconCache
|
||||
) {}
|
||||
|
||||
public async ensureExistance() {
|
||||
const directory = await fs.promises.opendir(IconDeployer.ICON_PATH);
|
||||
const addIconPromises: Promise<void>[] = [];
|
||||
for await (let dirname of directory) {
|
||||
const iconName = path.basename(dirname.name, '.svg').replaceAll('-','_');
|
||||
|
||||
if (this.iconCache.get(iconName) !== null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
addIconPromises.push(
|
||||
this.addIcon(path.resolve(dirname.parentPath, dirname.name), iconName)
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(addIconPromises);
|
||||
}
|
||||
|
||||
private async addIcon(iconPath: string, iconName: string) {
|
||||
const svgBuffer = await fs.promises.readFile(iconPath, 'utf-8');
|
||||
const pngBuffer = await new Promise<Buffer>(resolve => {
|
||||
svg2img(
|
||||
svgBuffer,
|
||||
{
|
||||
format: "png",
|
||||
resvg: {
|
||||
fitTo: {
|
||||
mode: "width",
|
||||
value: 128
|
||||
}
|
||||
}
|
||||
},
|
||||
function (err, buffer) {
|
||||
resolve(buffer);
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
await this.iconCache.set(iconName, pngBuffer);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -7,16 +7,23 @@ import {Container} from "./Container/Container";
|
|||
import {ServiceHint, Services} from "./Container/Services";
|
||||
import {Logger} from "log4js";
|
||||
|
||||
const { REST, Routes } = require('discord.js');
|
||||
import {REST, Routes} from 'discord.js';
|
||||
import {IconDeployer} from "./Icons/IconDeployer";
|
||||
import {DiscordClient} from "./Discord/DiscordClient";
|
||||
import {IconCache} from "./Icons/IconCache";
|
||||
|
||||
const container = Container.getInstance();
|
||||
Services.setup(container, ServiceHint.Deploy)
|
||||
|
||||
const commands = new Commands().allCommands;
|
||||
|
||||
const environment = container.get<Environment>(Environment.name);
|
||||
const logger = container.get<Logger>("logger");
|
||||
// Construct and prepare an instance of the REST module
|
||||
const rest = new REST().setToken(environment.discord.token);
|
||||
|
||||
const client = container.get<DiscordClient>(DiscordClient.name);
|
||||
client.connectRESTClient(environment.discord.token)
|
||||
|
||||
const commands = client.Commands.allCommands;
|
||||
|
||||
// and deploy your commands!
|
||||
(async () => {
|
||||
|
|
@ -29,7 +36,7 @@ const rest = new REST().setToken(environment.discord.token);
|
|||
logger.log(`Started refreshing ${commandInfos.length} application (/) commands.`);
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
const data = await rest.put(
|
||||
const data = await client.RESTClient.put(
|
||||
Routes.applicationGuildCommands(environment.discord.clientId, environment.discord.guildId),
|
||||
{ body: commandInfos },
|
||||
);
|
||||
|
|
@ -43,4 +50,16 @@ const rest = new REST().setToken(environment.discord.token);
|
|||
|
||||
logger.log("Ensuring Database...");
|
||||
const updater = new DatabaseUpdater(container.get<DatabaseConnection>(DatabaseConnection.name));
|
||||
updater.ensureAvaliablity(Definitions);
|
||||
updater.ensureAvaliablity(Definitions);
|
||||
|
||||
logger.log("Ensuring icons...");
|
||||
(async () => {
|
||||
const iconCache = container.get<IconCache>(IconCache.name);
|
||||
await iconCache.populate();
|
||||
|
||||
const deployer = new IconDeployer(
|
||||
iconCache
|
||||
);
|
||||
|
||||
deployer.ensureExistance()
|
||||
})()
|
||||
|
|
@ -3,10 +3,18 @@ import {Environment} from "./Environment";
|
|||
import {Container} from "./Container/Container";
|
||||
import {DatabaseConnection} from "./Database/DatabaseConnection";
|
||||
import {ServiceHint, Services} from "./Container/Services";
|
||||
import {IconCache} from "./Icons/IconCache";
|
||||
|
||||
const container = Container.getInstance();
|
||||
Services.setup(container, ServiceHint.App);
|
||||
(async () => {
|
||||
const env = container.get<Environment>(Environment.name);
|
||||
|
||||
const client = new DiscordClient()
|
||||
client.applyEvents()
|
||||
client.connect(container.get<Environment>(Environment.name).discord.token)
|
||||
const client = container.get<DiscordClient>(DiscordClient.name);
|
||||
client.connectRESTClient(env.discord.token);
|
||||
|
||||
await container.get<IconCache>(IconCache.name).populate()
|
||||
|
||||
client.applyEvents()
|
||||
client.connect(env.discord.token)
|
||||
})()
|
||||
Loading…
Add table
Add a link
Reference in a new issue