stoat basic functionality, most things just dont work with this api yet
All checks were successful
Build and Push Docker Image / build (push) Successful in 17s
All checks were successful
Build and Push Docker Image / build (push) Successful in 17s
This commit is contained in:
186
index.js
186
index.js
@@ -1,182 +1,62 @@
|
||||
import { createRequire } from 'module';
|
||||
import { Client } from "stoat.js";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
let config = {};
|
||||
try {
|
||||
config = require('./config.json');
|
||||
} catch {
|
||||
if (process.env.DISCORD && process.env.DISCORD != "TOKEN_HERE") {
|
||||
config.token = process.env.DISCORD;
|
||||
if (process.env.TOKEN && process.env.TOKEN != "TOKEN_HERE") {
|
||||
config.token = process.env.TOKEN;
|
||||
} else {
|
||||
console.error("FATAL: Discord token required. Either pass it as an environment variable \"DISCORD\", or fill out config.json.default.");
|
||||
console.error("FATAL: Stoat token required. Either pass it as an environment variable \"TOKEN\", or fill out config.json.default.");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
// the basic discord setup stuff yoinked from their guide
|
||||
const { Client, Events, GatewayIntentBits, Partials, ActivityType, MessageFlags, Collection } = require('discord.js');
|
||||
const client = new Client({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds,
|
||||
GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.DirectMessages,
|
||||
GatewayIntentBits.MessageContent,
|
||||
GatewayIntentBits.GuildMessageReactions
|
||||
],
|
||||
partials: [
|
||||
Partials.Channel,
|
||||
Partials.Message,
|
||||
Partials.Reaction,
|
||||
Partials.User
|
||||
]
|
||||
|
||||
const client = new Client();
|
||||
|
||||
client.on("error", (err) => {
|
||||
console.error("Client error:", err?.data?.type || err?.type || err);
|
||||
});
|
||||
|
||||
const avatarRotation = require('./lib/avatarRotation');
|
||||
|
||||
// build the button below the message that swaps the URL
|
||||
const { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');
|
||||
const swapRow = new ActionRowBuilder()
|
||||
.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId('swap_twitter')
|
||||
.setLabel('Swap URL')
|
||||
.setStyle(ButtonStyle.Secondary),
|
||||
);
|
||||
|
||||
// the main function that swaps the URL, takes
|
||||
function convertURL(url, regex, domain) {
|
||||
const match = url.match(regex);
|
||||
if (match) {
|
||||
console.log(`Converting ${url} to ${domain}`)
|
||||
console.log(`Converting ${url} to ${domain}`);
|
||||
return `https://${domain}/${match[1]}/status/${match[2]}`;
|
||||
}
|
||||
}
|
||||
|
||||
// called by the swap button, swaps girlcock links to fxtwitter, and back again if needed
|
||||
function swapify(url) {
|
||||
const girlcockRegex = /https?:\/\/girlcockx\.com\/(.*?)\/status\/(\d+)/;
|
||||
const fxtwitterRegex = /https?:\/\/fxtwitter\.com\/(.*?)\/status\/(\d+)/;
|
||||
const fixupxRegex = /https?:\/\/fixupx\.com\/(.*?)\/status\/(\d+)/;
|
||||
const twitterRegex = /https?:\/\/x\.com\/(.*?)\/status\/(\d+)/;
|
||||
if (url.match(girlcockRegex)) return convertURL(url, girlcockRegex, "fxtwitter.com");
|
||||
if (url.match(fxtwitterRegex)) return convertURL(url, fxtwitterRegex, "fixupx.com");
|
||||
if (url.match(fixupxRegex)) return convertURL(url, fixupxRegex, "x.com");
|
||||
if (url.match(twitterRegex)) return convertURL(url, twitterRegex, "girlcockx.com");
|
||||
return url; // give up, we'll just return the original URL
|
||||
}
|
||||
|
||||
// what actually listens for the button press and calls swapify
|
||||
client.on(Events.InteractionCreate, async interaction => {
|
||||
if (interaction.isButton()) {
|
||||
if (interaction.customId === 'swap_twitter') {
|
||||
try {
|
||||
await interaction.update(swapify(interaction.message.content));
|
||||
} catch (error) { // honestly the swapify function has its own error handling and it should be fine but whatever
|
||||
console.error(error);
|
||||
await interaction.reply({ content: "Something went wrong trying to swap the URL", ephemeral: true });
|
||||
}
|
||||
}
|
||||
return;
|
||||
client.on("ready", async () => {
|
||||
console.log(`Stoat: Connected as ${client.user?.username}`);
|
||||
if (config.status) {
|
||||
await client.user?.edit({ status: { text: config.status, presence: "Online" } });
|
||||
}
|
||||
});
|
||||
|
||||
client.once(Events.ClientReady, readyClient => {
|
||||
console.log(`Discord: Connected as ${readyClient.user.tag}`);
|
||||
client.user.setActivity(config.status, { type: ActivityType.Custom });
|
||||
avatarRotation.start(client, config);
|
||||
});
|
||||
client.login(config.token);
|
||||
client.on("messageCreate", async (message) => {
|
||||
// ignore our own messages
|
||||
if (message.authorId === client.user?.id) return;
|
||||
|
||||
client.on(Events.MessageCreate, message => {
|
||||
// if we smell a twitter link, girlcock it!
|
||||
const twitterRegex = /https?:\/\/x\.com\/(.*?)\/status\/(\d+)/;
|
||||
const regexProfile = message.content.match(twitterRegex);
|
||||
if (regexProfile) {
|
||||
const cocklink = convertURL(regexProfile[0], twitterRegex, "girlcockx.com")
|
||||
message.channel.send({ content: cocklink, flags: MessageFlags.SuppressNotifications, components: [swapRow] })
|
||||
message.suppressEmbeds().catch(err =>
|
||||
// this next bit just cuts down the error to the important part, which will usually end up being "no permissions"
|
||||
console.error("Removing original embed failed: " + err.stack?.split('\n')[0] || err.message || String(err).split('\n')[0])
|
||||
)
|
||||
}
|
||||
|
||||
// wouldnt it be funny to react to 1 in like 1000 messages with emoji from a list
|
||||
if (Math.random() < 0.001 && !message.author.bot) {
|
||||
const customEmojis = [
|
||||
'Shitten:1430413059574206555',
|
||||
'BLOWSUP:1430413011918651503',
|
||||
'grin_cat:1445254917991436449',
|
||||
'R_:1461939667657298081',
|
||||
'crumble:1461939666121920605',
|
||||
'jumble:1461939664306045008',
|
||||
'scrumble:1461939662930055278',
|
||||
'Chundle:1461939661541867713',
|
||||
'chimgen:1461939660212408351'
|
||||
];
|
||||
const randomEmoji = customEmojis[Math.floor(Math.random() * customEmojis.length)];
|
||||
message.react(randomEmoji);
|
||||
}
|
||||
});
|
||||
|
||||
// funny auto mpreg react
|
||||
const mpregs = [
|
||||
'mpreg01:1434029622206398556',
|
||||
'mpreg02:1434029708038639807',
|
||||
'mpreg03:1434029731321352192',
|
||||
'mpreg04:1434029755619086517',
|
||||
'mpreg05:1434029779514032228',
|
||||
'mpreg06:1434029803358523482',
|
||||
'mpreg07:1434029827681161266',
|
||||
'mpreg08:1434029848866717798',
|
||||
'mpreg09:1434029865593606215',
|
||||
'mpreg10:1434029885009166467',
|
||||
'mpreg11:1434029910158217327',
|
||||
'mpreg12:1434029928768077865',
|
||||
'mpreg13:1434029953346830417',
|
||||
'mpreg14:1434029984808304730',
|
||||
'mpreg15:1434030008124309585',
|
||||
'mpreg16:1434030025144795207',
|
||||
'mpreg17:1434030048586760303',
|
||||
'mpreg18:1434030067419451402',
|
||||
'mpreg19:1434030085794435092'
|
||||
];
|
||||
client.on(Events.MessageReactionAdd, (reaction, user) => {
|
||||
if (reaction.emoji.name === '🫃' && !user.bot) {
|
||||
reaction.message.react('🫃');
|
||||
for (const mpreg of mpregs) {
|
||||
reaction.message.react(mpreg).catch(err => console.error(err.stack?.split('\n')[0] || err.message || String(err).split('\n')[0]));
|
||||
const cocklink = convertURL(regexProfile[0], twitterRegex, "girlcockx.com");
|
||||
try {
|
||||
// send the converted link, crediting the sender (silent to avoid pinging)
|
||||
await message.channel?.sendMessage({
|
||||
content: `<@${message.authorId}> sent: ${cocklink}`,
|
||||
flags: 1, // silent / suppress notifications
|
||||
});
|
||||
// delete the original message to prevent embed
|
||||
await message.delete();
|
||||
} catch (err) {
|
||||
console.error("Link replacement failed: " + (err.stack?.split('\n')[0] || err.message || String(err).split('\n')[0]));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// command handling for ./commands
|
||||
const fs = require('fs');
|
||||
const path = require('node:path');
|
||||
client.commands = new Collection();
|
||||
const commandsPath = path.join(__dirname, 'commands');
|
||||
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
|
||||
for (const file of commandFiles) {
|
||||
const filePath = path.join(commandsPath, file);
|
||||
const command = require(filePath);
|
||||
if ('data' in command && 'execute' in command) {
|
||||
client.commands.set(command.data.name, command);
|
||||
console.debug(`Commands: Registered "${command.data.name}"`);
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
|
||||
client.on(Events.InteractionCreate, async interaction => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
const command = interaction.client.commands.get(interaction.commandName);
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
} try {
|
||||
console.debug(`Command: Executing ${interaction.commandName}`);
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (interaction.replied || interaction.deferred) {
|
||||
await interaction.followUp({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
|
||||
} else {
|
||||
await interaction.reply({ content: 'There was an error while executing this command!', flags: MessageFlags.Ephemeral });
|
||||
}
|
||||
}
|
||||
});
|
||||
client.loginBot(config.token);
|
||||
|
||||
Reference in New Issue
Block a user