222 lines
7.4 KiB
TypeScript
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();
|