diff --git a/.gitignore b/.gitignore index 1e0aeee..56e862c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,5 @@ yarn-error.log* /.pnp src/**/*.js .pnp.js - +/shitposts .vscode/* \ No newline at end of file diff --git a/src/command.ts b/src/command.ts index c114239..1ae4e61 100644 --- a/src/command.ts +++ b/src/command.ts @@ -14,8 +14,8 @@ export abstract class ContextCommand extends ICommand } export abstract class Command extends ICommand { - abstract run(interaction: ChatInputCommandInteraction, config: Config, s3: S3Client): Promise; - autoComplete(interaction: AutocompleteInteraction, config: Config, option: AutocompleteFocusedOption, s3: S3Client): Promise { + abstract run(interaction: ChatInputCommandInteraction, config: Config): Promise; + autoComplete(interaction: AutocompleteInteraction, config: Config, option: AutocompleteFocusedOption): Promise { throw new Error("Autocompletion called on command that does not have #autoComplete implemented."); } abstract slashCommand: SharedSlashCommand diff --git a/src/commands/addtoshitpost.ts b/src/commands/addtoshitpost.ts index 2e4a1bc..3584b51 100644 --- a/src/commands/addtoshitpost.ts +++ b/src/commands/addtoshitpost.ts @@ -5,9 +5,14 @@ import { Message } from "discord.js"; import { ContextCommand } from "../command.ts"; -import {PutObjectCommand, type S3Client} from "@aws-sdk/client-s3"; import type {Config} from "../config.ts"; import {BUCKETNAME} from "./shitpost.ts"; +import fs from "node:fs/promises"; +import path from "node:path"; +import {fileURLToPath} from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); export default class Mock extends ContextCommand { targetType: ApplicationCommandType.Message = ApplicationCommandType.Message; @@ -16,26 +21,40 @@ export default class Mock extends ContextCommand { .setName('AddToShitposts') .setType(ApplicationCommandType.Message) async run(interaction: ContextMenuCommandInteraction, target: Message, config:Config): Promise { - await interaction.deferReply() - await interaction.followUp({content: "uploading..."}) - for (const [_, attachment] of target.attachments) { + await interaction.deferReply(); + await interaction.followUp({content: "uploading..."}); - const response = await fetch(attachment.proxyURL); + const downloadFolderPath = path.join(__dirname, '..', '..', 'shitposts'); + + try { + await fs.mkdir(downloadFolderPath, { recursive: true }); + } catch (error) { + console.error("Error creating download folder:", error); + await interaction.editReply({ content: "the fucking posix file system failed me (download foler couldnt be made)" }); + return; + } + + for (const [_, attachment] of target.attachments) { + const response = await fetch(attachment.url); if (!response.ok) { - await interaction.reply({ content: "discord shat itself??????" }); + await interaction.editReply({ content: "discord shat itself while fetching an attachment!?" }); return; } const buffer = await response.arrayBuffer(); + const fileName = attachment.name || `attachment_${attachment.id}`; + const filePath = path.join(downloadFolderPath, fileName); - const command = new PutObjectCommand({ - Bucket: BUCKETNAME, - Key: attachment.name, - Body: Buffer.from(buffer), - }); - await config.s3.send(command) + try { + await fs.writeFile(filePath, Buffer.from(buffer)); + console.log(`Downloaded: ${fileName}`); + } catch (error) { + console.error(`Error downloading ${fileName}:`, error); + await interaction.editReply({ content: `Failed to download ${fileName}.` }); + return; + } } - await interaction.editReply({content: "shits have been posted"}) + await interaction.editReply({content: "shits have been posted!"}); } } \ No newline at end of file diff --git a/src/commands/nowplaying.ts b/src/commands/nowplaying.ts index 9fe0b92..d226799 100644 --- a/src/commands/nowplaying.ts +++ b/src/commands/nowplaying.ts @@ -29,7 +29,7 @@ function keepV(url: string): string { } export default class PingCommand extends Command { - async run(interaction: ChatInputCommandInteraction, config: Config, s3: S3Client): Promise { + async run(interaction: ChatInputCommandInteraction, config: Config): Promise { await interaction.deferReply() const user = interaction.options.getString("user") ?? config.listenbrainzAccount; const usesonglink = interaction.options.getBoolean("usesonglink") ?? true diff --git a/src/commands/shitpost.ts b/src/commands/shitpost.ts index b9829c6..ad785b6 100644 --- a/src/commands/shitpost.ts +++ b/src/commands/shitpost.ts @@ -6,48 +6,84 @@ import { SlashCommandBuilder } from "discord.js"; import {config, type Config} from "../config.ts"; -import {ListObjectsV2Command, type S3Client} from "@aws-sdk/client-s3"; import {inspect} from "node:util"; +import fs from "node:fs/promises"; +import path from "node:path"; +import {fileURLToPath} from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); export const BUCKETNAME = "shitposts" as const; +export const DOWNLOAD_FOLDER_PATH = path.join(__dirname, '..', '..', 'shitposts'); +async function getFilesInFolder(folderPath: string): Promise<{ name: string, value: string }[]> { + try { + const files = await fs.readdir(folderPath); + const fileList: { name: string, value: string }[] = []; + + for (const file of files) { + const filePath = path.join(folderPath, file); + const stats = await fs.stat(filePath); + + if (stats.isFile()) { + fileList.push({ + name: file, + value: file + }); + } + } + return fileList; + } catch (error) { + console.error(`Error reading directory ${folderPath}:`, error); + return []; + } +} export default class ShitPostCommand extends Command { - async run(interaction: ChatInputCommandInteraction, config: Config, s3: S3Client) { + async run(interaction: ChatInputCommandInteraction, config: Config) { await interaction.deferReply(); - const shitpost = interaction.options.getString("shitpost")!; + const fileName = interaction.options.getString('shitpost', true); - const shitpostUrl = "https://sp.amy.rip/" + encodeURIComponent(shitpost); + const filePath = path.join(DOWNLOAD_FOLDER_PATH, fileName); try { - const response = await fetch(shitpostUrl); + await fs.access(filePath); + const attachment = new AttachmentBuilder(filePath, { name: fileName }); + await interaction.editReply({ + files: [attachment] + }); - if (!response.ok) { - await interaction.followUp({content: "S3 bucket shat itself??????"}); - return; + } catch (error: any) { + if (error.code === 'ENOENT') { + console.error(`file not found ${filePath}`, error); + await interaction.editReply({ + content: `\`${fileName}\`. wasnt found, aka something shat itself`, + }); + } else { + console.error(`Error sending file ${fileName}:`, error); + await interaction.editReply({ + content: `buh, shitpost (\`${fileName}\`) wasnt posted.`, + }); } - - const buffer = await response.arrayBuffer(); - const attachment = new AttachmentBuilder(Buffer.from(buffer), {name: shitpost}); - - await interaction.followUp({files: [attachment]}); - - } catch (error) { - await interaction.followUp({content: "fileproccessing shat itself"}); } } - async autoComplete(interaction: AutocompleteInteraction, config: Config, option: AutocompleteFocusedOption, s3: S3Client): Promise { - await interaction.respond((await s3.send(new ListObjectsV2Command({Bucket: BUCKETNAME}))).Contents! - .filter((i): i is { Key: string } => !!i.Key) - .map(key => ({name: key.Key, value: key.Key}))) + async autoComplete(interaction: AutocompleteInteraction, config: Config, option: AutocompleteFocusedOption): Promise { + const files = await getFilesInFolder(DOWNLOAD_FOLDER_PATH); + const focusedValue = option.value.toLowerCase(); + const filteredFiles = files.filter(choice => choice.name.toLowerCase().includes(focusedValue)); + + await interaction.respond( + filteredFiles.slice(0, 25) + ); } slashCommand = new SlashCommandBuilder() .setName("shitpost") - .setDescription("shitpost with S3!!!!!").setIntegrationTypes([ + .setDescription("shitpost with the posix file system!!!!!!").setIntegrationTypes([ ApplicationIntegrationType.UserInstall ]).addStringOption(option => { return option.setName("shitpost").setRequired(true).setDescription("the shitposts name") diff --git a/src/config.ts b/src/config.ts index 85b8b8e..2745b51 100644 --- a/src/config.ts +++ b/src/config.ts @@ -10,8 +10,7 @@ const configT = z.object({ R2AccessKeyId: z.string(), R2SecretAccessKey: z.string(), }); -export type RawConfig = z.infer; -export type Config = RawConfig & {s3: S3Client} -export const config: RawConfig = configT.parse(rawconfig); +export type Config = z.infer; +export const config: Config = configT.parse(rawconfig); diff --git a/src/index.ts b/src/index.ts index 5729db4..2539594 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,15 +21,7 @@ const client = new Client({ const allCommands: ICommand[] = [] -const S3 = new S3Client({ - region: "auto", - endpoint: `https://${config.R2AccountID}.r2.cloudflarestorage.com`, - credentials: { - accessKeyId: config.R2AccessKeyId, - secretAccessKey: config.R2SecretAccessKey, - }, -}); -const enrichedConfig = {...config, s3:S3} satisfies Config; + const commandDir = path.join(__dirname, "commands"); for (const file of fs.readdirSync(commandDir)) { if (!file.endsWith('.ts')) continue @@ -81,7 +73,7 @@ client.on(Events.InteractionCreate, async (interaction) => { if (command.targetType != (interaction.isUserContextMenuCommand() ? ApplicationCommandType.User : ApplicationCommandType.Message)) console.error("Out of date discord definition of this context command") try { - await command.run(interaction, interaction.isUserContextMenuCommand() ? interaction.targetUser : interaction.targetMessage, enrichedConfig) + await command.run(interaction, interaction.isUserContextMenuCommand() ? interaction.targetUser : interaction.targetMessage, config) } catch (e) { console.error("error during context command execution: " + commandName, e) interaction.reply("something sharted itself") @@ -99,7 +91,7 @@ client.on(Events.InteractionCreate, async (interaction) => { } try { - await command.run(interaction, enrichedConfig, S3); + await command.run(interaction, config); } catch (e) { console.error("error during command execution: " + commandName, e) interaction.reply("something sharted itself") @@ -118,7 +110,7 @@ client.on(Events.InteractionCreate, async (interaction) => { } try { - await command.autoComplete(interaction, enrichedConfig, interaction.options.getFocused(true), S3); + await command.autoComplete(interaction, config, interaction.options.getFocused(true)); } catch (e) { console.error("error during command execution: " + commandName, e) }