Files
amane-tanikaze-dcbot/commands/images/image.ts
2024-10-13 14:50:19 +00:00

222 lines
7.4 KiB
TypeScript

import discord, {
CommandInteraction,
CommandInteractionOptionResolver,
SlashCommandBuilder,
SlashCommandStringOption,
SlashCommandAttachmentOption,
SlashCommandSubcommandBuilder,
EmbedBuilder,
InteractionResponse,
SlashCommandSubcommandsOnlyBuilder,
Attachment
} from 'discord.js';
import { HydratedDocument } from 'mongoose';
import fs from 'fs';
import path from 'path';
import axios from 'axios';
import { Client } from 'minio';
import { Command } from '../../classes/command';
import { logger } from '../../logger';
import { config } from '../../config';
import { reactPreprocess } from '../../functions/react-preprocess';
import { Image, imageModel } from '../../models/Image';
import { Alias, aliasModel } from '../../models/Alias';
import { Guild, guildModel } from '../../models/Guild';
import { Token, tokenModel } from '../../models/Token';
type CIOR = CommandInteractionOptionResolver;
const client = new Client(config.minio.client);
async function download(url: string, file: string): Promise<void>{
const saveFile = await axios({
url: url, method: 'GET', responseType: 'arraybuffer'
});
if(!await client.bucketExists(config.minio.bucket))
await client.makeBucket(config.minio.bucket);
await client.putObject(config.minio.bucket, file, saveFile.data);
}
async function upload(interaction: CommandInteraction): Promise<void>{
if(!interaction.guildId)
throw Error('guildId not exist');
const file: discord.Attachment | null =
(interaction.options as CIOR).getAttachment('file');
if(!file)
throw Error('file not exist');
const fileExt: string = path.extname(file.name);
const image: HydratedDocument<Image> | null =
await (new imageModel({extension: fileExt})).save();
if(!image)
throw Error('image not exist');
await download(file.url, `${String(image._id)}${fileExt}`);
const publicUrl: string = `${config.httpServer.external.url}/${String(image._id)}${fileExt}`;
await interaction.reply({
content: logger.log(`Image saved with id: ${image._id}.`),
});
if(interaction.channel)
await interaction.channel.send({content: publicUrl});
}
async function link(interaction: CommandInteraction): Promise<void>{
if(!interaction.guildId)
throw Error('guildId not exist');
let aliasText: string | null =
(interaction.options as CIOR).getString('alias');
if(!aliasText)
throw Error('aliasText not exist');
const imageId: string | null =
(interaction.options as CIOR).getString('image');
if(!imageId)
throw Error('imageId not exist');
aliasText = reactPreprocess(aliasText);
if(!interaction.guild || !interaction.guild.id || !interaction.guild.name)
throw Error('guild not exist');
const guild: HydratedDocument<Guild> | null =
await guildModel.findOneAndUpdate(
{id: interaction.guild.id},
{$set: {name: interaction.guild.name}},
{new: true, upsert: true}
);
if(!guild)
throw Error('guild not exist');
const image: HydratedDocument<Image> | null =
await imageModel.findById(imageId);
if(!image)
throw Error('image not exist');
const alias: HydratedDocument<Alias> | null =
await aliasModel.findOneAndUpdate(
{guild: guild, text: aliasText, images: {$nin: image}},
{$push: {images: image}},
{new: true, upsert: true}
);
if(!alias)
throw Error('alias update failed');
await interaction.reply({
content: logger.log(`Alias ${aliasText} has linked to image id: ${imageId}.`)
});
}
async function unlink(interaction: CommandInteraction): Promise<void>{
if(!interaction.guildId)
throw Error('guildId not exist');
let aliasText: string | null =
(interaction.options as CIOR).getString('alias');
if(!aliasText)
throw Error('aliasText not exist');
aliasText = reactPreprocess(aliasText);
if(!interaction.guild || !interaction.guild.id)
throw Error('guild not exist');
const guild: HydratedDocument<Guild> | null =
await guildModel.findOne({id: interaction.guild.id});
if(!guild)
throw Error('guild not exist');
const res = await aliasModel.deleteOne(
{guild: guild, text: aliasText}
);
if(!res)
logger.warning('alias not exist');
await interaction.reply({
content: logger.log(`All the image have been unlinked to alias ${aliasText}.`)
});
}
async function list(interaction: CommandInteraction): Promise<void>{
const token: string = String(Math.floor(Math.random() * 100000000));
if(!interaction.guild || !interaction.guild.id)
throw Error('guild not exist');
await tokenModel.create({
token: token,
guildId: interaction.guild.id,
exp: Date.now() + config.tokenTimeLimit
});
const embed = new EmbedBuilder()
.setColor(0x1f1e33)
.setTitle('Autoreact Image List')
.setDescription('experimental function')
.addFields({
name: 'One Time Link:',
value: `[open in browser](https://amane.konchin.com/auth?token=${token})`
});
await interaction.reply({embeds: [embed], ephemeral: true});
}
class ImageCmd extends Command{
get name(){return "image";}
get description(){return "Modify the reacting image database.";}
async execute(interaction: CommandInteraction): Promise<void>{
try{
const subcommand: string | null =
(interaction.options as CIOR).getSubcommand();
switch(subcommand){
case 'upload':
await upload(interaction); break;
case 'link':
await link(interaction); break;
case 'unlink':
await unlink(interaction); break;
case 'list':
await list(interaction); break;
default:
throw Error('subcommand not exist');
}
}catch(err: unknown){
let message;
if(err instanceof Error) message = err.message;
else message = String(message);
logger.error(`While executing "/image", ${message}`);
await interaction.reply({content: `While executing "/image", ${message}`});
}
}
override build(): SlashCommandSubcommandsOnlyBuilder{
return new SlashCommandBuilder()
.setName(this.name)
.setDescription(this.description)
.addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand
.setName('upload')
.setDescription('Upload an image.')
.addAttachmentOption((option: SlashCommandAttachmentOption) => option
.setName('file')
.setDescription('The image file that you want to upload.')
.setRequired(true)
)
)
.addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand
.setName('link')
.setDescription('Link an alias to an image')
.addStringOption((option: SlashCommandStringOption) => option
.setName('alias')
.setDescription('The alias to be linked.')
.setRequired(true)
)
.addStringOption((option: SlashCommandStringOption) => option
.setName('image')
.setDescription('The image to be linked, as id.')
.setRequired(true)
)
)
.addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand
.setName('unlink')
.setDescription('Unlink an alias to an image')
.addStringOption((option: SlashCommandStringOption) => option
.setName('alias')
.setDescription('The alias to be unlinked.')
.setRequired(true)
)
)
.addSubcommand((subcommand: SlashCommandSubcommandBuilder) => subcommand
.setName('list')
.setDescription('Show the image list')
)
}
};
export const command = new ImageCmd();