diff --git a/src/app.js b/src/app.js index c0625b6b5..30c264200 100644 --- a/src/app.js +++ b/src/app.js @@ -1,4 +1,4 @@ -import 'dotenv/config'; +import 'dotenv/config'; import { Client, Collection, GatewayIntentBits } from 'discord.js'; import { REST } from '@discordjs/rest'; import express from 'express'; @@ -88,6 +88,9 @@ class TitanBot extends Client { await this.registerCommands(); startupLog('Slash commands registration complete'); + // Set bot presence after login + this.setInitialPresence(); + const databaseMode = dbStatus.isDegraded ? 'Optional in-memory mode (data resets after restart)' : 'Connected (persistent data enabled)'; @@ -103,6 +106,25 @@ class TitanBot extends Client { } } + setInitialPresence() { + try { + this.user.setPresence({ + activities: [ + { + name: 'Minecraft: Void Edition', + type: 'PLAYING', + state: 'Always Online' + } + ], + status: 'online' + }).catch(error => { + logger.error('Failed to set initial presence:', error); + }); + } catch (error) { + logger.error('Error setting initial presence:', error); + } + } + startWebServer() { const app = express(); const configuredPort = Number(this.config.api?.port || process.env.PORT || 3000); @@ -231,6 +253,29 @@ class TitanBot extends Client { cron.schedule('0 6 * * *', () => checkBirthdays(this)); cron.schedule('* * * * *', () => checkGiveaways(this)); cron.schedule('*/15 * * * *', () => this.updateAllCounters()); + + // Update presence every 30 seconds with rotating statuses + this.presenceIndex = 0; + const presences = [ + { name: 'Minecraft: Void Edition', type: 'PLAYING', state: 'Always Online' }, + { name: 'Over Your Server', type: 'WATCHING', state: 'Ready to help!' }, + { name: 'Your Commands', type: 'LISTENING', state: 'Void Edition' } + ]; + + setInterval(() => { + try { + const presence = presences[this.presenceIndex % presences.length]; + this.user.setPresence({ + activities: [presence], + status: 'online' + }).catch(error => { + logger.error('Failed to update presence:', error); + }); + this.presenceIndex++; + } catch (error) { + logger.error('Error updating presence:', error); + } + }, 30000); } async updateAllCounters() { @@ -382,5 +427,3 @@ try { export default TitanBot; - - diff --git a/src/commands/Economy/slut.js b/src/commands/Economy/slut.js deleted file mode 100644 index 3d4791cf9..000000000 --- a/src/commands/Economy/slut.js +++ /dev/null @@ -1,193 +0,0 @@ -import { SlashCommandBuilder } from 'discord.js'; -import { createEmbed } from '../../utils/embeds.js'; -import { getEconomyData, setEconomyData } from '../../utils/economy.js'; -import { withErrorHandling, createError, ErrorTypes } from '../../utils/errorHandler.js'; -import { logger } from '../../utils/logger.js'; -import { InteractionHelper } from '../../utils/interactionHelper.js'; - -const SLUT_COOLDOWN = 45 * 60 * 1000; - -const SLUT_ACTIVITIES = [ - { name: "Cam Stream", min: 120, max: 450, risk: 0.2 }, - { name: "Private Dance Session", min: 220, max: 700, risk: 0.25 }, - { name: "After-Hours Club Host", min: 320, max: 900, risk: 0.3 }, - { name: "VIP Companion Booking", min: 550, max: 1400, risk: 0.35 }, - { name: "Exclusive Livestream", min: 850, max: 2200, risk: 0.4 }, -]; - -const POSITIVE_OUTCOMES = [ - "Your stream blew up and tips poured in.", - "A VIP booking paid far above average.", - "Your after-hours shift was packed and profitable.", - "Premium requests came through and your payout jumped.", -]; - -const FINE_OUTCOMES = [ - "Venue security issued a compliance fine.", - "A moderation strike triggered a platform fee.", - "You were flagged and had to pay a penalty.", -]; - -const ROBBED_OUTCOMES = [ - "A fake buyer chargeback wiped part of your earnings.", - "A scam booking cleaned out a chunk of your cash.", - "You got baited by a fraud account and lost money.", -]; - -const LOSS_OUTCOMES = [ - "The set flopped and you had to cover operating costs.", - "You burned budget on prep and made no return.", - "The shift went sideways and left you in the red.", -]; - -function randomInt(min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - -function randomChoice(items) { - return items[Math.floor(Math.random() * items.length)]; -} - -function resolveOutcome(activity, wallet) { - const successChance = Math.max(0.35, 0.55 - activity.risk * 0.2); - const fineChance = 0.22; - const robbedChance = 0.2; - const roll = Math.random(); - - if (roll < successChance) { - const amount = randomInt(activity.min, activity.max); - return { - type: 'payout', - delta: amount, - message: randomChoice(POSITIVE_OUTCOMES), - title: `💰 ${activity.name} - Payout` - }; - } - - const remainingAfterSuccess = roll - successChance; - - if (remainingAfterSuccess < fineChance) { - const maxFine = Math.min(wallet, Math.max(150, Math.floor(activity.max * 0.4))); - const minFine = Math.min(maxFine, Math.max(50, Math.floor(activity.min * 0.2))); - const amount = maxFine > 0 ? randomInt(minFine, maxFine) : 0; - return { - type: 'fine', - delta: -amount, - message: randomChoice(FINE_OUTCOMES), - title: `🚨 ${activity.name} - Fined` - }; - } - - if (remainingAfterSuccess < fineChance + robbedChance) { - const maxRobbed = Math.min(wallet, Math.max(200, Math.floor(wallet * 0.35))); - const minRobbed = Math.min(maxRobbed, Math.max(75, Math.floor(wallet * 0.1))); - const amount = maxRobbed > 0 ? randomInt(minRobbed, maxRobbed) : 0; - return { - type: 'robbed', - delta: -amount, - message: randomChoice(ROBBED_OUTCOMES), - title: `🕵️ ${activity.name} - Robbed` - }; - } - - const maxLoss = Math.min(wallet, Math.max(100, Math.floor(activity.max * 0.3))); - const minLoss = Math.min(maxLoss, Math.max(40, Math.floor(activity.min * 0.15))); - const amount = maxLoss > 0 ? randomInt(minLoss, maxLoss) : 0; - return { - type: 'loss', - delta: -amount, - message: randomChoice(LOSS_OUTCOMES), - title: `❌ ${activity.name} - Loss` - }; -} - -export default { - data: new SlashCommandBuilder() - .setName('slut') - .setDescription('Take a risky provocative job for random payout or loss'), - - execute: withErrorHandling(async (interaction, config, client) => { - const deferred = await InteractionHelper.safeDefer(interaction); - if (!deferred) return; - - const userId = interaction.user.id; - const guildId = interaction.guildId; - const now = Date.now(); - - logger.debug(`[ECONOMY] Slut command started for ${userId}`, { userId, guildId }); - - const userData = await getEconomyData(client, guildId, userId); - - if (!userData) { - throw createError( - "Failed to load economy data for slut command", - ErrorTypes.DATABASE, - "Failed to load your economy data. Please try again later.", - { userId, guildId } - ); - } - - const lastSlut = userData.lastSlut || 0; - - if (now - lastSlut < SLUT_COOLDOWN) { - const remainingTime = lastSlut + SLUT_COOLDOWN - now; - throw createError( - "Slut cooldown active", - ErrorTypes.RATE_LIMIT, - `You need to wait before you can work again! Try again in **${Math.ceil(remainingTime / 60000)}** minutes.`, - { timeRemaining: remainingTime, cooldownType: 'slut' } - ); - } - - const activity = randomChoice(SLUT_ACTIVITIES); - - const outcome = resolveOutcome(activity, userData.wallet || 0); - - userData.lastSlut = now; - userData.totalSluts = (userData.totalSluts || 0) + 1; - userData.totalSlutEarnings = (userData.totalSlutEarnings || 0) + Math.max(0, outcome.delta); - userData.totalSlutLosses = (userData.totalSlutLosses || 0) + Math.max(0, -outcome.delta); - - if (outcome.type !== 'payout') { - userData.failedSluts = (userData.failedSluts || 0) + 1; - } - - userData.wallet = Math.max(0, (userData.wallet || 0) + outcome.delta); - - await setEconomyData(client, guildId, userId, userData); - - logger.info(`[ECONOMY_TRANSACTION] Slut activity resolved`, { - userId, - guildId, - activity: activity.name, - outcomeType: outcome.type, - amountDelta: outcome.delta, - newWallet: userData.wallet, - timestamp: new Date().toISOString() - }); - - const amountLabel = `${outcome.delta >= 0 ? '+' : '-'}$${Math.abs(outcome.delta).toLocaleString()}`; - const summaryLines = [ - `${outcome.message}`, - `💸 **Net Result:** ${amountLabel}`, - `💳 **Current Balance:** $${userData.wallet.toLocaleString()}`, - `📊 **Total Sessions:** ${userData.totalSluts}`, - `💵 **Total Earned:** $${(userData.totalSlutEarnings || 0).toLocaleString()}`, - `🧾 **Total Lost:** $${(userData.totalSlutLosses || 0).toLocaleString()}` - ]; - - const embed = createEmbed({ - title: outcome.title, - description: summaryLines.join('\n'), - color: outcome.delta >= 0 ? 'success' : 'error', - timestamp: true - }); - - await InteractionHelper.safeEditReply(interaction, { embeds: [embed] }); - }, { command: 'slut' }) -}; - - - - - diff --git a/src/config/application.js b/src/config/application.js index d4d741bfc..43dfd2b45 100644 --- a/src/config/application.js +++ b/src/config/application.js @@ -11,7 +11,6 @@ const __dirname = path.dirname(__filename); - const appConfig = { paths: { root: path.join(__dirname, "../.."), diff --git a/src/config/bot.js b/src/config/bot.js index 36e588cd4..c2150f1a3 100644 --- a/src/config/bot.js +++ b/src/config/bot.js @@ -24,8 +24,8 @@ export const botConfig = { // 5 = Competing activities: [ { - // Text users will see (example: "Playing /help | Titan Bot"). - name: "Made with ❤️", + // Text users will see (example: "Playing Minecraft: Void Edition"). + name: "Minecraft: Void Edition", // Activity type number (0 = Playing). type: 0, }, @@ -57,8 +57,10 @@ export const botConfig = { // Default questions shown when someone fills out an application. defaultQuestions: [ { question: "What is your name?", required: true }, - { question: "How old are you?", required: true }, + { question: "How old are you?", required: false }, { question: "Why do you want to join?", required: true }, + { question: "How Long have you been in the Server", required: true }, + { question: "What role do you want?", required: true }, ], // Embed colors by application status. @@ -136,7 +138,7 @@ export const botConfig = { }, footer: { // Default footer text used in bot embeds. - text: "Titan Bot", + text: "VoidXZ", // Footer icon URL (null = no icon). icon: null, }, @@ -544,6 +546,3 @@ export function getRandomColor() { export default botConfig; - - - diff --git a/src/config/presence.js b/src/config/presence.js new file mode 100644 index 000000000..6adbd0adc --- /dev/null +++ b/src/config/presence.js @@ -0,0 +1,16 @@ +static void UpdatePresence() +{ + DiscordRichPresence discordPresence; + memset(&discordPresence, 0, sizeof(discordPresence)); + discordPresence.state = "Playing Minecraft: Void Edition"; + discordPresence.details = "Survival"; + discordPresence.startTimestamp = 1507665886; + discordPresence.endTimestamp = 1507665886; + discordPresence.largeImageText = "Numbani"; + discordPresence.smallImageText = "Rogue - Level 100"; + discordPresence.partyId = "ae488379-351d-4a4f-ad32-2b9b01c91657"; + discordPresence.partySize = 3; + discordPresence.partyMax = 5; + discordPresence.joinSecret = "MTI4NzM0OjFpMmhuZToxMjMxMjM= "; + Discord_UpdatePresence(&discordPresence); +} diff --git a/src/events/ready.js b/src/events/ready.js index 8f3db5096..8679cde31 100644 --- a/src/events/ready.js +++ b/src/events/ready.js @@ -9,7 +9,10 @@ export default { async execute(client) { try { - client.user.setPresence(config.bot.presence); + // Set initial presence + client.user.setPresence(config.bot.presence).catch(error => { + logger.error("Failed to set initial presence:", error); + }); startupLog(`Ready! Logged in as ${client.user.tag}`); startupLog(`Serving ${client.guilds.cache.size} guild(s)`); @@ -19,10 +22,21 @@ export default { startupLog( `Reaction role reconciliation: scanned ${reconciliationSummary.scannedMessages}, removed ${reconciliationSummary.removedMessages}, errors ${reconciliationSummary.errors}` ); + + // Update presence every 30 seconds to keep it active + setInterval(() => { + try { + client.user.setPresence(config.bot.presence).catch(error => { + logger.error("Failed to update presence:", error); + }); + } catch (error) { + logger.error("Error updating presence:", error); + } + }, 30000); + } catch (error) { logger.error("Error in ready event:", error); } }, }; -