Compare commits
10 commits
253d6ac0c1
...
29cfc2ab10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29cfc2ab10 | ||
|
|
79740a8639 | ||
|
|
994e4f72b2 | ||
|
|
ef03b23fa9 | ||
|
|
4eb781732b | ||
|
|
7d035b1479 | ||
|
|
6ff915efca | ||
|
|
13457c3fbe | ||
|
|
5f4e28f7bc | ||
|
|
b5dd8c498a |
15 changed files with 1595 additions and 79 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -10,5 +10,5 @@ yarn-error.log*
|
||||||
/.pnp
|
/.pnp
|
||||||
src/**/*.js
|
src/**/*.js
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
/shitposts
|
||||||
.vscode/*
|
.vscode/*
|
||||||
7
.idea/prettier.xml
generated
Normal file
7
.idea/prettier.xml
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PrettierConfiguration">
|
||||||
|
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||||
|
<option name="myRunOnSave" value="true" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
|
|
@ -9,11 +9,12 @@
|
||||||
"check": "tsc"
|
"check": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.808.0",
|
||||||
"@types/node": "^22.14.0",
|
"@types/node": "^22.14.0",
|
||||||
"acorn": "^8.14.1",
|
"acorn": "^8.14.1",
|
||||||
"astring": "^1.9.0",
|
"astring": "^1.9.0",
|
||||||
"canvas": "^3.1.0",
|
"canvas": "^3.1.0",
|
||||||
"discord.js": "^14.17.2",
|
"discord.js": "^14.19.3",
|
||||||
"sharp": "git+ssh://git@github.com/lovell/sharp.git",
|
"sharp": "git+ssh://git@github.com/lovell/sharp.git",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
|
|
|
||||||
1277
pnpm-lock.yaml
generated
1277
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,6 @@
|
||||||
import { ApplicationCommandType, type AutocompleteFocusedOption, AutocompleteInteraction, ChatInputCommandInteraction, ContextMenuCommandBuilder, ContextMenuCommandInteraction, Message, SharedSlashCommand, User } from "discord.js";
|
import { ApplicationCommandType, type AutocompleteFocusedOption, AutocompleteInteraction, ChatInputCommandInteraction, ContextMenuCommandBuilder, ContextMenuCommandInteraction, Message, SharedSlashCommand, User } from "discord.js";
|
||||||
import { type Config } from "./config.ts";
|
import { type Config } from "./config.ts";
|
||||||
|
import type {S3Client} from "@aws-sdk/client-s3";
|
||||||
|
|
||||||
export abstract class ICommand { }
|
export abstract class ICommand { }
|
||||||
|
|
||||||
|
|
@ -9,7 +10,7 @@ export abstract class ContextCommand<T extends User | Message> extends ICommand
|
||||||
T extends Message ? ApplicationCommandType.Message :
|
T extends Message ? ApplicationCommandType.Message :
|
||||||
never;
|
never;
|
||||||
abstract contextDefinition: ContextMenuCommandBuilder
|
abstract contextDefinition: ContextMenuCommandBuilder
|
||||||
abstract run(interaction: ContextMenuCommandInteraction, target: T extends User ? User : T extends Message ? Message : never): Promise<void>
|
abstract run(interaction: ContextMenuCommandInteraction, target: T extends User ? User : T extends Message ? Message : never, config: Config): Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class Command extends ICommand {
|
export abstract class Command extends ICommand {
|
||||||
|
|
|
||||||
60
src/commands/addtoshitpost.ts
Normal file
60
src/commands/addtoshitpost.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
import {
|
||||||
|
ApplicationCommandType, type Attachment,
|
||||||
|
ContextMenuCommandBuilder,
|
||||||
|
ContextMenuCommandInteraction,
|
||||||
|
Message
|
||||||
|
} from "discord.js";
|
||||||
|
import { ContextCommand } from "../command.ts";
|
||||||
|
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<Message> {
|
||||||
|
targetType: ApplicationCommandType.Message = ApplicationCommandType.Message;
|
||||||
|
contextDefinition: ContextMenuCommandBuilder =
|
||||||
|
new ContextMenuCommandBuilder()
|
||||||
|
.setName('AddToShitposts')
|
||||||
|
.setType(ApplicationCommandType.Message)
|
||||||
|
async run(interaction: ContextMenuCommandInteraction, target: Message, config:Config): Promise<void> {
|
||||||
|
await interaction.deferReply();
|
||||||
|
await interaction.followUp({content: "uploading..."});
|
||||||
|
|
||||||
|
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.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);
|
||||||
|
|
||||||
|
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!"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,7 +14,7 @@ export default class FediemojiCommand extends Command {
|
||||||
return typedEmojis
|
return typedEmojis
|
||||||
}
|
}
|
||||||
|
|
||||||
async run(interaction: ChatInputCommandInteraction, config: Config) {
|
async run(interaction: ChatInputCommandInteraction, config: Config, ) {
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
const emojiname = interaction.options.getString("emoji");
|
const emojiname = interaction.options.getString("emoji");
|
||||||
const shit = await interaction.client.application.emojis.fetch();
|
const shit = await interaction.client.application.emojis.fetch();
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,19 @@ export default class LastListenedCommand extends Command {
|
||||||
async run(interaction: ChatInputCommandInteraction, config: Config) {
|
async run(interaction: ChatInputCommandInteraction, config: Config) {
|
||||||
await interaction.deferReply()
|
await interaction.deferReply()
|
||||||
const user = interaction.options.getString("user") ?? config.listenbrainzAccount;
|
const user = interaction.options.getString("user") ?? config.listenbrainzAccount;
|
||||||
|
const historyAmount = interaction.options.getInteger("count") ?? 3;
|
||||||
const meow = await fetch(`https://api.listenbrainz.org/1/user/${user}/listens`).then((res) => res.json());
|
const meow = await fetch(`https://api.listenbrainz.org/1/user/${user}/listens`).then((res) => res.json());
|
||||||
const zodded = listenBrainzListensShape.parse(meow)
|
const zodded = listenBrainzListensShape.parse(meow)
|
||||||
const object = zodded.payload.listens.slice(0, 3);
|
const object = zodded.payload.listens.slice(0, historyAmount);
|
||||||
|
|
||||||
|
const songs = object.slice(0, historyAmount).map((i) => {
|
||||||
|
const shit = i.track_metadata;
|
||||||
|
const name = shit.track_name;
|
||||||
|
return `- ${name} by ${shit.artist_name}`;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
await interaction.followUp({
|
await interaction.followUp({
|
||||||
content: `the last 3 songs of ${user} was:\n\n- ${object[0].track_metadata.release_name} by ${object[0].track_metadata.artist_name}\n- ${object[1].track_metadata.release_name} by ${object[1].track_metadata.artist_name}\n- ${object[2].track_metadata.release_name} by ${object[2].track_metadata.artist_name}`
|
content: `The last ${historyAmount} songs of ${user} were:\n\n${songs}`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,6 +52,9 @@ export default class LastListenedCommand extends Command {
|
||||||
.setDescription("get that last listened music of a person").setIntegrationTypes([
|
.setDescription("get that last listened music of a person").setIntegrationTypes([
|
||||||
ApplicationIntegrationType.UserInstall
|
ApplicationIntegrationType.UserInstall
|
||||||
])
|
])
|
||||||
|
.addIntegerOption(option => {
|
||||||
|
return option.setName("count").setDescription("amount of history you want").setRequired(false)
|
||||||
|
})
|
||||||
.addStringOption(option => {
|
.addStringOption(option => {
|
||||||
return option.setName("user").setDescription("listenbrainz username").setRequired(false)
|
return option.setName("user").setDescription("listenbrainz username").setRequired(false)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
58
src/commands/loss.ts
Normal file
58
src/commands/loss.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import {Command} from "../command.ts";
|
||||||
|
import {
|
||||||
|
ActionRowBuilder,
|
||||||
|
ApplicationIntegrationType, ButtonBuilder, ButtonStyle,
|
||||||
|
ChatInputCommandInteraction, ContainerBuilder,
|
||||||
|
InteractionContextType, type MessageActionRowComponentBuilder, MessageFlags,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from "discord.js";
|
||||||
|
import { type Config } from "../config.ts";
|
||||||
|
|
||||||
|
export default class LossCommand extends Command {
|
||||||
|
async run(interaction: ChatInputCommandInteraction, config: Config) {
|
||||||
|
const components = [
|
||||||
|
new ContainerBuilder()
|
||||||
|
.addActionRowComponents(
|
||||||
|
new ActionRowBuilder<MessageActionRowComponentBuilder>()
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
.setLabel("|")
|
||||||
|
.setCustomId("a"),
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
.setLabel("|i")
|
||||||
|
.setCustomId("b"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.addActionRowComponents(
|
||||||
|
new ActionRowBuilder<MessageActionRowComponentBuilder>()
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
.setLabel("||")
|
||||||
|
.setCustomId("c"),
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setStyle(ButtonStyle.Secondary)
|
||||||
|
.setLabel("|_")
|
||||||
|
.setCustomId("d"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
await interaction.reply({
|
||||||
|
components: components,
|
||||||
|
flags: [MessageFlags.IsComponentsV2],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
slashCommand = new SlashCommandBuilder()
|
||||||
|
.setName("loss")
|
||||||
|
.setDescription("why").setIntegrationTypes([
|
||||||
|
ApplicationIntegrationType.UserInstall
|
||||||
|
])
|
||||||
|
.setContexts([
|
||||||
|
InteractionContextType.BotDM,
|
||||||
|
InteractionContextType.Guild,
|
||||||
|
InteractionContextType.PrivateChannel
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
@ -4,14 +4,16 @@ import {
|
||||||
ApplicationIntegrationType,
|
ApplicationIntegrationType,
|
||||||
ButtonBuilder,
|
ButtonBuilder,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
ChatInputCommandInteraction,
|
ChatInputCommandInteraction, ContainerBuilder,
|
||||||
EmbedBuilder,
|
EmbedBuilder,
|
||||||
InteractionContextType,
|
MessageFlags,
|
||||||
SlashCommandBuilder
|
InteractionContextType, type MessageActionRowComponentBuilder, MessageFlagsBitField,
|
||||||
|
SlashCommandBuilder, TextDisplayBuilder, SectionBuilder, ThumbnailBuilder
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
|
|
||||||
import {getSongOnPreferredProvider, kyzaify} from "../helper.ts"
|
import {getSongOnPreferredProvider, kyzaify} from "../helper.ts"
|
||||||
import {type Config} from "../config.ts";
|
import {type Config} from "../config.ts";
|
||||||
|
import type {S3Client} from "@aws-sdk/client-s3";
|
||||||
|
|
||||||
function keepV(url: string): string {
|
function keepV(url: string): string {
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
|
|
@ -27,12 +29,11 @@ function keepV(url: string): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PingCommand extends Command {
|
export default class PingCommand extends Command {
|
||||||
async run(interaction: ChatInputCommandInteraction, config: Config) {
|
async run(interaction: ChatInputCommandInteraction, config: Config): Promise<void> {
|
||||||
await interaction.deferReply()
|
await interaction.deferReply()
|
||||||
const user = interaction.options.getString("user") ?? config.listenbrainzAccount;
|
const user = interaction.options.getString("user") ?? config.listenbrainzAccount;
|
||||||
const usesonglink = interaction.options.getBoolean("usesonglink") ?? true
|
const usesonglink = interaction.options.getBoolean("usesonglink") ?? true
|
||||||
const useitunes = interaction.options.getBoolean("useitunes") ?? true
|
const useitunes = interaction.options.getBoolean("useitunes") ?? false
|
||||||
|
|
||||||
const meow = await fetch(`https://api.listenbrainz.org/1/user/${user}/playing-now`).then((res) => res.json());
|
const meow = await fetch(`https://api.listenbrainz.org/1/user/${user}/playing-now`).then((res) => res.json());
|
||||||
if (!meow) {
|
if (!meow) {
|
||||||
await interaction.followUp("something shat itself!");
|
await interaction.followUp("something shat itself!");
|
||||||
|
|
@ -51,19 +52,22 @@ export default class PingCommand extends Command {
|
||||||
}
|
}
|
||||||
const songlink = await fetch(`https://api.song.link/v1-alpha.1/links?url=${link}`).then(a => a.json())
|
const songlink = await fetch(`https://api.song.link/v1-alpha.1/links?url=${link}`).then(a => a.json())
|
||||||
const preferredApi = getSongOnPreferredProvider(songlink, link)
|
const preferredApi = getSongOnPreferredProvider(songlink, link)
|
||||||
|
|
||||||
if (preferredApi && usesonglink) {
|
if (preferredApi && usesonglink) {
|
||||||
const embed = new EmbedBuilder()
|
const components = [
|
||||||
.setAuthor({
|
new ContainerBuilder()
|
||||||
name: preferredApi.artist,
|
.addSectionComponents(
|
||||||
})
|
new SectionBuilder()
|
||||||
.setTitle(preferredApi.title)
|
.setThumbnailAccessory(
|
||||||
.setThumbnail(preferredApi.thumbnailUrl)
|
new ThumbnailBuilder()
|
||||||
.setFooter({
|
.setURL(preferredApi.thumbnailUrl)
|
||||||
text: "amy jr",
|
)
|
||||||
});
|
.addTextDisplayComponents(
|
||||||
|
new TextDisplayBuilder().setContent(`# ${preferredApi.artist} - ${preferredApi.title}`),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
];
|
||||||
const meow = Object.keys(songlink.linksByPlatform)
|
const meow = Object.keys(songlink.linksByPlatform)
|
||||||
let message = ""
|
|
||||||
|
|
||||||
const nya: ActionRowBuilder<ButtonBuilder>[] = [];
|
const nya: ActionRowBuilder<ButtonBuilder>[] = [];
|
||||||
let currentRow = new ActionRowBuilder<ButtonBuilder>();
|
let currentRow = new ActionRowBuilder<ButtonBuilder>();
|
||||||
|
|
@ -83,11 +87,11 @@ export default class PingCommand extends Command {
|
||||||
if (currentRow.components.length > 0) {
|
if (currentRow.components.length > 0) {
|
||||||
nya.push(currentRow);
|
nya.push(currentRow);
|
||||||
}
|
}
|
||||||
|
components[0].addActionRowComponents(nya)
|
||||||
await interaction.followUp({
|
await interaction.followUp({
|
||||||
components: nya,
|
components: components,
|
||||||
embeds: [embed]
|
flags: [MessageFlags.IsComponentsV2],
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
const embedfallback = new EmbedBuilder()
|
const embedfallback = new EmbedBuilder()
|
||||||
.setAuthor({
|
.setAuthor({
|
||||||
|
|
@ -97,7 +101,7 @@ export default class PingCommand extends Command {
|
||||||
.setFooter({
|
.setFooter({
|
||||||
text: "song.link proxying was turned off or failed - amy jr",
|
text: "song.link proxying was turned off or failed - amy jr",
|
||||||
});
|
});
|
||||||
|
|
||||||
await interaction.followUp({embeds:[embedfallback]})
|
await interaction.followUp({embeds:[embedfallback]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
41
src/commands/randnum.ts
Normal file
41
src/commands/randnum.ts
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
import {Command} from "../command.ts";
|
||||||
|
import {
|
||||||
|
ApplicationIntegrationType,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
InteractionContextType,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from "discord.js";
|
||||||
|
import { type Config } from "../config.ts";
|
||||||
|
|
||||||
|
export default class PingCommand extends Command {
|
||||||
|
async run(interaction: ChatInputCommandInteraction, config: Config) {
|
||||||
|
const upperbound = interaction.options.getInteger("upperbound")!;
|
||||||
|
const comment = interaction.options.getString("comment");
|
||||||
|
|
||||||
|
if (comment === null){
|
||||||
|
await interaction.reply({
|
||||||
|
content: "random number is: " + `${Math.floor(Math.random() * upperbound)}`,
|
||||||
|
});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await interaction.reply({
|
||||||
|
content: `chances of ${comment} out of ${upperbound} is ${Math.floor(Math.random() * upperbound)}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
slashCommand = new SlashCommandBuilder()
|
||||||
|
.setName("randnum")
|
||||||
|
.setDescription("random number").setIntegrationTypes([
|
||||||
|
ApplicationIntegrationType.UserInstall
|
||||||
|
]).addIntegerOption(option => {
|
||||||
|
return option.setName("upperbound").setRequired(true).setDescription("idk nea told me")
|
||||||
|
}).addStringOption(option => {
|
||||||
|
return option.setName("comment").setRequired(false).setDescription("comment")
|
||||||
|
})
|
||||||
|
.setContexts([
|
||||||
|
InteractionContextType.BotDM,
|
||||||
|
InteractionContextType.Guild,
|
||||||
|
InteractionContextType.PrivateChannel
|
||||||
|
]);
|
||||||
|
}
|
||||||
51
src/commands/renameshitpost.ts
Normal file
51
src/commands/renameshitpost.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
import {Command} from "../command.ts";
|
||||||
|
import {
|
||||||
|
ApplicationIntegrationType, type AutocompleteFocusedOption, AutocompleteInteraction,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
InteractionContextType,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from "discord.js";
|
||||||
|
import { config, type Config } from "../config.ts";
|
||||||
|
import {DOWNLOAD_FOLDER_PATH, getFilesInFolder} from "./shitpost.ts";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
|
||||||
|
export default class RenameshitpostCommand extends Command {
|
||||||
|
|
||||||
|
async run(interaction: ChatInputCommandInteraction, config: Config, ) {
|
||||||
|
await interaction.deferReply();
|
||||||
|
const originalname = interaction.options.getString("originalname")!;
|
||||||
|
const newname = interaction.options.getString("newname")!;
|
||||||
|
|
||||||
|
fs.renameSync(path.join(DOWNLOAD_FOLDER_PATH, originalname), path.join(DOWNLOAD_FOLDER_PATH, newname));
|
||||||
|
await interaction.followUp("uhhh this shit shouldve worked")
|
||||||
|
}
|
||||||
|
|
||||||
|
async autoComplete(interaction: AutocompleteInteraction, config: Config, option: AutocompleteFocusedOption): Promise<void> {
|
||||||
|
if (option.name === 'originalname') {
|
||||||
|
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("renameshitpost")
|
||||||
|
.setDescription("rename the shitpost").setIntegrationTypes([
|
||||||
|
ApplicationIntegrationType.UserInstall
|
||||||
|
]).addStringOption(option => {
|
||||||
|
return option.setName("originalname").setRequired(true).setDescription("the original shitpost name")
|
||||||
|
.setAutocomplete(true)
|
||||||
|
}).addStringOption(option => {
|
||||||
|
return option.setName("newname").setRequired(true).setDescription("the new shitpost name")
|
||||||
|
})
|
||||||
|
.setContexts([
|
||||||
|
InteractionContextType.BotDM,
|
||||||
|
InteractionContextType.Guild,
|
||||||
|
InteractionContextType.PrivateChannel
|
||||||
|
]);
|
||||||
|
}
|
||||||
97
src/commands/shitpost.ts
Normal file
97
src/commands/shitpost.ts
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
import {Command} from "../command.ts";
|
||||||
|
import {
|
||||||
|
ApplicationIntegrationType, AttachmentBuilder, type AutocompleteFocusedOption, AutocompleteInteraction,
|
||||||
|
ChatInputCommandInteraction,
|
||||||
|
InteractionContextType,
|
||||||
|
SlashCommandBuilder
|
||||||
|
} from "discord.js";
|
||||||
|
import {config, type Config} from "../config.ts";
|
||||||
|
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');
|
||||||
|
export 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) {
|
||||||
|
await interaction.deferReply();
|
||||||
|
const fileName = interaction.options.getString('shitpost', true);
|
||||||
|
|
||||||
|
const filePath = path.join(DOWNLOAD_FOLDER_PATH, fileName);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(filePath);
|
||||||
|
const attachment = new AttachmentBuilder(filePath, { name: fileName });
|
||||||
|
await interaction.editReply({
|
||||||
|
files: [attachment]
|
||||||
|
});
|
||||||
|
|
||||||
|
} 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.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async autoComplete(interaction: AutocompleteInteraction, config: Config, option: AutocompleteFocusedOption): Promise<void> {
|
||||||
|
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 the posix file system!!!!!!").setIntegrationTypes([
|
||||||
|
ApplicationIntegrationType.UserInstall
|
||||||
|
]).addStringOption(option => {
|
||||||
|
return option.setName("shitpost").setRequired(true).setDescription("the shitposts name")
|
||||||
|
.setAutocomplete(true)
|
||||||
|
})
|
||||||
|
.setContexts([
|
||||||
|
InteractionContextType.BotDM,
|
||||||
|
InteractionContextType.Guild,
|
||||||
|
InteractionContextType.PrivateChannel
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
import rawconfig from "../config.json" with {type: "json"};
|
import rawconfig from "../config.json" with {type: "json"};
|
||||||
import {z} from 'zod';
|
import {z} from 'zod';
|
||||||
|
import type {S3Client} from "@aws-sdk/client-s3";
|
||||||
const configT = z.object({
|
const configT = z.object({
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
listenbrainzAccount: z.string(),
|
listenbrainzAccount: z.string(),
|
||||||
gitapi: z.string(),
|
gitapi: z.string(),
|
||||||
sharkeyInstance:z.string(),
|
sharkeyInstance:z.string(),
|
||||||
// applicationid: z.string(),
|
R2AccountID: z.string(),
|
||||||
|
R2AccessKeyId: z.string(),
|
||||||
|
R2SecretAccessKey: z.string(),
|
||||||
});
|
});
|
||||||
export type Config = z.infer<typeof configT>;
|
export type Config = z.infer<typeof configT>;
|
||||||
export const config: Config = configT.parse(rawconfig);
|
export const config: Config = configT.parse(rawconfig);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ import path from "node:path";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import { Command, ContextCommand, ICommand } from "./command.ts";
|
import { Command, ContextCommand, ICommand } from "./command.ts";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { config } from "./config.ts";
|
import {type Config, config} from "./config.ts";
|
||||||
|
import {ListObjectsV2Command, S3Client} from "@aws-sdk/client-s3";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
@ -20,6 +21,7 @@ const client = new Client({
|
||||||
|
|
||||||
const allCommands: ICommand[] = []
|
const allCommands: ICommand[] = []
|
||||||
|
|
||||||
|
|
||||||
const commandDir = path.join(__dirname, "commands");
|
const commandDir = path.join(__dirname, "commands");
|
||||||
for (const file of fs.readdirSync(commandDir)) {
|
for (const file of fs.readdirSync(commandDir)) {
|
||||||
if (!file.endsWith('.ts')) continue
|
if (!file.endsWith('.ts')) continue
|
||||||
|
|
@ -71,7 +73,7 @@ client.on(Events.InteractionCreate, async (interaction) => {
|
||||||
if (command.targetType != (interaction.isUserContextMenuCommand() ? ApplicationCommandType.User : ApplicationCommandType.Message))
|
if (command.targetType != (interaction.isUserContextMenuCommand() ? ApplicationCommandType.User : ApplicationCommandType.Message))
|
||||||
console.error("Out of date discord definition of this context command")
|
console.error("Out of date discord definition of this context command")
|
||||||
try {
|
try {
|
||||||
await command.run(interaction, interaction.isUserContextMenuCommand() ? interaction.targetUser : interaction.targetMessage)
|
await command.run(interaction, interaction.isUserContextMenuCommand() ? interaction.targetUser : interaction.targetMessage, config)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("error during context command execution: " + commandName, e)
|
console.error("error during context command execution: " + commandName, e)
|
||||||
interaction.reply("something sharted itself")
|
interaction.reply("something sharted itself")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue