Files
cockinator/index.js
Cesium 95777bb389 accept discord token as env
we only really need that to function anyway, other things like jellyfin will
just not do anything and we have default value for avatar reset.
2026-03-11 13:25:15 -04:00

181 lines
7.3 KiB
JavaScript

let config = {};
try {
config = require('./config.json');
} catch {
if (process.env.DISCORD && process.env.DISCORD != "TOKEN_HERE") {
config.token = process.env.DISCORD;
} else {
console.error("FATAL: Discord token required. Either pass it as an environment variable \"DISCORD\", 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
]
});
// 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}`)
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+)/;
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, "girlcockx.com");
// if we got this far, somethings not right but we'll try twitter before giving up
const twitterRegex = /https?:\/\/x\.com\/(.*?)\/status\/(\d+)/;
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.once(Events.ClientReady, readyClient => {
console.log(`Discord: Connected as ${readyClient.user.tag}`);
client.user.setActivity(config.status, { type: ActivityType.Custom });
});
client.login(config.token);
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]));
}
}
});
// 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 });
}
}
});