From 8c2dee5d9eaed6b76dda3d9149df9df69925b30a Mon Sep 17 00:00:00 2001 From: sirpiglr <49359077-sirpiglr@users.noreply.replit.com> Date: Wed, 10 Dec 2025 02:36:59 +0000 Subject: [PATCH] Add pricing page and update navigation links Replaced `.single()` with `.maybeSingle()` in multiple command files to handle cases where no record is found, and added a new /pricing route and navigation links to the UI. Replit-Commit-Author: Agent Replit-Commit-Session-Id: aed2e46d-25bb-4b73-81a1-bb9e8437c261 Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: e91d020a-35a6-4add-9945-887dd3ecae9f Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/aed2e46d-25bb-4b73-81a1-bb9e8437c261/tdDjujk Replit-Helium-Checkpoint-Created: true --- .replit | 4 + aethex-bot/commands/badges.js | 4 +- aethex-bot/commands/daily.js | 4 +- aethex-bot/commands/foundation.js | 4 +- aethex-bot/commands/leaderboard.js | 6 +- aethex-bot/commands/post.js | 6 +- aethex-bot/commands/prestige.js | 6 +- aethex-bot/commands/profile.js | 4 +- aethex-bot/commands/rank.js | 4 +- aethex-bot/commands/set-realm.js | 2 +- aethex-bot/commands/stats.js | 6 +- aethex-bot/commands/studio.js | 4 +- aethex-bot/commands/unlink.js | 2 +- aethex-bot/commands/userinfo.js | 4 +- aethex-bot/commands/verify-role.js | 4 +- aethex-bot/commands/verify.js | 2 +- aethex-bot/public/federation.html | 2 +- aethex-bot/public/index.html | 3 +- aethex-bot/public/pricing.html | 660 +++++++++++++++++++++++++++++ aethex-bot/server/webServer.js | 35 ++ 20 files changed, 733 insertions(+), 33 deletions(-) create mode 100644 aethex-bot/public/pricing.html diff --git a/.replit b/.replit index 9585c0f..a798d30 100644 --- a/.replit +++ b/.replit @@ -21,6 +21,10 @@ externalPort = 80 localPort = 8080 externalPort = 8080 +[[ports]] +localPort = 34785 +externalPort = 3000 + [workflows] runButton = "Project" diff --git a/aethex-bot/commands/badges.js b/aethex-bot/commands/badges.js index 38986ae..1592007 100644 --- a/aethex-bot/commands/badges.js +++ b/aethex-bot/commands/badges.js @@ -57,7 +57,7 @@ module.exports = { .from('discord_links') .select('user_id') .eq('discord_id', target.id) - .single(); + .maybeSingle(); if (!link) { return interaction.editReply({ @@ -73,7 +73,7 @@ module.exports = { .from('user_profiles') .select('username, avatar_url, badges, xp, daily_streak') .eq('id', link.user_id) - .single(); + .maybeSingle(); let earnedBadges = []; diff --git a/aethex-bot/commands/daily.js b/aethex-bot/commands/daily.js index e31928f..e8f1ceb 100644 --- a/aethex-bot/commands/daily.js +++ b/aethex-bot/commands/daily.js @@ -78,7 +78,7 @@ async function handleFederatedDaily(interaction, supabase, client) { .from('discord_links') .select('user_id') .eq('discord_id', interaction.user.id) - .single(); + .maybeSingle(); if (!link) { return interaction.editReply({ @@ -94,7 +94,7 @@ async function handleFederatedDaily(interaction, supabase, client) { .from('user_profiles') .select('xp, daily_streak, last_daily, prestige_level, total_xp_earned') .eq('id', link.user_id) - .single(); + .maybeSingle(); const now = new Date(); const lastDaily = profile?.last_daily ? new Date(profile.last_daily) : null; diff --git a/aethex-bot/commands/foundation.js b/aethex-bot/commands/foundation.js index b99851a..e9a6694 100644 --- a/aethex-bot/commands/foundation.js +++ b/aethex-bot/commands/foundation.js @@ -22,14 +22,14 @@ module.exports = { .from('discord_links') .select('user_id') .eq('discord_id', target.id) - .single(); + .maybeSingle(); if (link) { const { data: contribution } = await supabase .from('foundation_contributions') .select('total_donated, volunteer_hours, badges') .eq('user_id', link.user_id) - .single(); + .maybeSingle(); contributionData = contribution; } diff --git a/aethex-bot/commands/leaderboard.js b/aethex-bot/commands/leaderboard.js index d0e20a9..d959bc2 100644 --- a/aethex-bot/commands/leaderboard.js +++ b/aethex-bot/commands/leaderboard.js @@ -319,7 +319,7 @@ async function handleFederatedLeaderboard(interaction, supabase, category) { .from("user_profiles") .select("username, full_name, avatar_url") .eq("id", userId) - .single(); + .maybeSingle(); if (profile) { leaderboardData.push({ @@ -355,7 +355,7 @@ async function handleFederatedLeaderboard(interaction, supabase, category) { .from("user_profiles") .select("username, full_name, avatar_url") .eq("id", userId) - .single(); + .maybeSingle(); if (profile) { leaderboardData.push({ @@ -381,7 +381,7 @@ async function handleFederatedLeaderboard(interaction, supabase, category) { .from("user_profiles") .select("username, full_name, avatar_url") .eq("id", creator.user_id) - .single(); + .maybeSingle(); if (profile) { const badges = []; diff --git a/aethex-bot/commands/post.js b/aethex-bot/commands/post.js index 08a1e7f..3febaf8 100644 --- a/aethex-bot/commands/post.js +++ b/aethex-bot/commands/post.js @@ -49,7 +49,7 @@ module.exports = { .from("discord_links") .select("user_id, primary_arm") .eq("discord_id", interaction.user.id) - .single(); + .maybeSingle(); if (!link) { const embed = new EmbedBuilder() @@ -66,7 +66,7 @@ module.exports = { .from("user_profiles") .select("username, full_name, avatar_url") .eq("id", link.user_id) - .single(); + .maybeSingle(); const content = interaction.options.getString("content"); const category = interaction.options.getString("category") || "general"; @@ -100,7 +100,7 @@ module.exports = { discord_author_avatar: interaction.user.displayAvatarURL(), }) .select() - .single(); + .maybeSingle(); if (error) throw error; diff --git a/aethex-bot/commands/prestige.js b/aethex-bot/commands/prestige.js index 05e2166..4edd210 100644 --- a/aethex-bot/commands/prestige.js +++ b/aethex-bot/commands/prestige.js @@ -98,7 +98,7 @@ async function viewPrestige(interaction, supabase, mode) { .from('discord_links') .select('user_id') .eq('discord_id', target.id) - .single(); + .maybeSingle(); if (!link) { return interaction.editReply({ @@ -114,7 +114,7 @@ async function viewPrestige(interaction, supabase, mode) { .from('user_profiles') .select('username, xp, prestige_level, total_xp_earned') .eq('id', link.user_id) - .single(); + .maybeSingle(); const prestige = profile?.prestige_level || 0; const totalXpEarned = profile?.total_xp_earned || profile?.xp || 0; @@ -295,7 +295,7 @@ async function prestigeUp(interaction, supabase, client, mode) { .from('user_profiles') .select('username, xp, prestige_level, total_xp_earned') .eq('id', link.user_id) - .single(); + .maybeSingle(); const currentXp = profile?.xp || 0; const level = Math.floor(Math.sqrt(currentXp / 100)); diff --git a/aethex-bot/commands/profile.js b/aethex-bot/commands/profile.js index 4111527..4d451f0 100644 --- a/aethex-bot/commands/profile.js +++ b/aethex-bot/commands/profile.js @@ -107,7 +107,7 @@ async function handleFederatedProfile(interaction, supabase, targetUser) { .from("discord_links") .select("user_id, primary_arm") .eq("discord_id", targetUser.id) - .single(); + .maybeSingle(); if (!link) { const embed = new EmbedBuilder() @@ -127,7 +127,7 @@ async function handleFederatedProfile(interaction, supabase, targetUser) { .from("user_profiles") .select("*") .eq("id", link.user_id) - .single(); + .maybeSingle(); if (!profile) { const embed = new EmbedBuilder() diff --git a/aethex-bot/commands/rank.js b/aethex-bot/commands/rank.js index 1f62e7a..ba7dab1 100644 --- a/aethex-bot/commands/rank.js +++ b/aethex-bot/commands/rank.js @@ -91,7 +91,7 @@ async function handleFederatedRank(interaction, supabase, target) { .from('discord_links') .select('user_id, primary_arm') .eq('discord_id', target.id) - .single(); + .maybeSingle(); if (!link) { return interaction.editReply({ @@ -107,7 +107,7 @@ async function handleFederatedRank(interaction, supabase, target) { .from('user_profiles') .select('username, avatar_url, xp, bio, prestige_level, total_xp_earned') .eq('id', link.user_id) - .single(); + .maybeSingle(); const xp = profile?.xp || 0; const prestige = profile?.prestige_level || 0; diff --git a/aethex-bot/commands/set-realm.js b/aethex-bot/commands/set-realm.js index 79f83c9..e583074 100644 --- a/aethex-bot/commands/set-realm.js +++ b/aethex-bot/commands/set-realm.js @@ -54,7 +54,7 @@ module.exports = { .from("discord_links") .select("user_id, primary_arm") .eq("discord_id", interaction.user.id) - .single(); + .maybeSingle(); if (!link) { const embed = new EmbedBuilder() diff --git a/aethex-bot/commands/stats.js b/aethex-bot/commands/stats.js index c764777..0d5bff0 100644 --- a/aethex-bot/commands/stats.js +++ b/aethex-bot/commands/stats.js @@ -16,7 +16,7 @@ module.exports = { .from("discord_links") .select("user_id, primary_arm, linked_at") .eq("discord_id", interaction.user.id) - .single(); + .maybeSingle(); if (linkError) { console.error("Stats link query error:", linkError); @@ -37,7 +37,7 @@ module.exports = { .from("user_profiles") .select("*") .eq("id", link.user_id) - .single(); + .maybeSingle(); const { count: postCount } = await supabase .from("community_posts") @@ -58,7 +58,7 @@ module.exports = { .from("aethex_creators") .select("verified, featured, total_projects") .eq("user_id", link.user_id) - .single(); + .maybeSingle(); const armEmojis = { labs: "🧪", diff --git a/aethex-bot/commands/studio.js b/aethex-bot/commands/studio.js index e046a21..a84c7c8 100644 --- a/aethex-bot/commands/studio.js +++ b/aethex-bot/commands/studio.js @@ -23,7 +23,7 @@ module.exports = { .from('discord_links') .select('user_id') .eq('discord_id', target.id) - .single(); + .maybeSingle(); if (!link) { return interaction.editReply({ @@ -39,7 +39,7 @@ module.exports = { .from('user_profiles') .select('username, avatar_url, bio') .eq('id', link.user_id) - .single(); + .maybeSingle(); const { data: projects, count: projectCount } = await supabase .from('studio_projects') diff --git a/aethex-bot/commands/unlink.js b/aethex-bot/commands/unlink.js index f179310..58d4ebf 100644 --- a/aethex-bot/commands/unlink.js +++ b/aethex-bot/commands/unlink.js @@ -16,7 +16,7 @@ module.exports = { .from("discord_links") .select("*") .eq("discord_id", interaction.user.id) - .single(); + .maybeSingle(); if (!link) { const embed = new EmbedBuilder() diff --git a/aethex-bot/commands/userinfo.js b/aethex-bot/commands/userinfo.js index d3234f7..56a31e8 100644 --- a/aethex-bot/commands/userinfo.js +++ b/aethex-bot/commands/userinfo.js @@ -51,14 +51,14 @@ module.exports = { .from('discord_links') .select('user_id, primary_arm, linked_at') .eq('discord_id', target.id) - .single(); + .maybeSingle(); if (link) { const { data: profile } = await supabase .from('user_profiles') .select('username, xp') .eq('id', link.user_id) - .single(); + .maybeSingle(); embed.addFields( { name: 'AeThex Linked', value: 'Yes', inline: true }, diff --git a/aethex-bot/commands/verify-role.js b/aethex-bot/commands/verify-role.js index f71d587..3b539fb 100644 --- a/aethex-bot/commands/verify-role.js +++ b/aethex-bot/commands/verify-role.js @@ -16,7 +16,7 @@ module.exports = { .from("discord_links") .select("user_id, primary_arm") .eq("discord_id", interaction.user.id) - .single(); + .maybeSingle(); if (!link) { const embed = new EmbedBuilder() @@ -33,7 +33,7 @@ module.exports = { .from("user_profiles") .select("user_type") .eq("id", link.user_id) - .single(); + .maybeSingle(); const { data: mappings } = await supabase .from("discord_role_mappings") diff --git a/aethex-bot/commands/verify.js b/aethex-bot/commands/verify.js index 225869a..dc10ad5 100644 --- a/aethex-bot/commands/verify.js +++ b/aethex-bot/commands/verify.js @@ -23,7 +23,7 @@ module.exports = { .from("discord_links") .select("*") .eq("discord_id", interaction.user.id) - .single(); + .maybeSingle(); if (existingLink) { const embed = new EmbedBuilder() diff --git a/aethex-bot/public/federation.html b/aethex-bot/public/federation.html index f67d376..3a4a891 100644 --- a/aethex-bot/public/federation.html +++ b/aethex-bot/public/federation.html @@ -429,7 +429,7 @@ Features Commands Federation - Dashboard + Pricing Add to Server diff --git a/aethex-bot/public/index.html b/aethex-bot/public/index.html index d8b6365..a9e43f2 100644 --- a/aethex-bot/public/index.html +++ b/aethex-bot/public/index.html @@ -471,7 +471,8 @@ Home Features Commands - Discord + Federation + Pricing
diff --git a/aethex-bot/public/pricing.html b/aethex-bot/public/pricing.html new file mode 100644 index 0000000..99a69e2 --- /dev/null +++ b/aethex-bot/public/pricing.html @@ -0,0 +1,660 @@ + + + + + + Pricing - AeThex | Warden + + + +
+
+ +
+ +
+ +
+
+
+

Simple, Transparent Pricing

+

Warden is free forever for all core features. Premium options available for servers that need extra protection or visibility.

+
+ + Free Forever - No credit card required +
+
+
+ +
+
+
+
+

Free

+
+ $0 + forever +
+

Everything you need for a great community

+
    +
  • Unified XP & Leveling system
  • +
  • Prestige system (up to P10)
  • +
  • Achievements & Quests
  • +
  • Shop & Economy features
  • +
  • Welcome/Goodbye messages
  • +
  • Role panels & Giveaways
  • +
  • Sentinel anti-nuke protection
  • +
  • Auto-moderation tools
  • +
  • Federation: Critical threat auto-bans
  • +
  • Server directory listing
  • +
  • Cross-server reputation
  • +
+ Add Warden Free +
+ + + +
+

Featured Slot

+
+ $200 + per week +
+

Maximum visibility across the federation

+
    +
  • Featured in all member servers
  • +
  • Cross-server promotion
  • +
  • Boosted directory ranking
  • +
  • Featured badge on server card
  • +
  • Recruitment announcements
  • +
  • Analytics dashboard
  • +
  • Custom invite tracking
  • +
  • Priority listing renewal
  • +
+ Get Featured +
+
+
+
+ +
+
+

Feature Comparison

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeatureFreePremium
XP & Leveling System
Prestige System
Achievements & Quests
Economy & Shop
Sentinel Anti-Nuke
Auto-Moderation
Critical Threat Protection
Low/Medium Threat Protection
Priority Alerts
Premium Badge
Priority Support
+
+
+ +
+
+

Frequently Asked Questions

+
+
+

Is Warden really free forever?

+

Yes! All core features including XP, leveling, moderation, and critical threat protection are completely free with no time limits or hidden costs.

+
+
+

What's the difference in protection levels?

+

Free tier auto-bans critical threats (the most dangerous). Premium adds auto-kick for low/medium severity threats for paranoid admins who want extra security.

+
+
+

Can I cancel Premium anytime?

+

Absolutely. Cancel anytime and you'll retain Premium until the end of your billing period. You'll then revert to Free tier automatically.

+
+
+

What is a Featured Slot?

+

Featured Slots promote your server across all federation member servers. Great for growing communities looking to attract new members.

+
+
+

Do I need Premium to use the Federation?

+

No! Federation membership is free. Premium just adds extra protection layers. All servers can join and benefit from the shared ban list.

+
+
+

How do payments work?

+

We use Stripe for secure payments. Premium is billed monthly, Featured Slots are billed weekly. All major credit cards accepted.

+
+
+
+
+ +
+
+
+

Ready to Get Started?

+

Add Warden to your server now. Free forever, upgrade when you need it.

+ Add Warden Free +
+
+
+
+ + + + diff --git a/aethex-bot/server/webServer.js b/aethex-bot/server/webServer.js index 6f90c7b..86fba7e 100644 --- a/aethex-bot/server/webServer.js +++ b/aethex-bot/server/webServer.js @@ -1434,10 +1434,45 @@ function createWebServer(discordClient, supabase, options = {}) { }); }); + app.get('/api/bot-stats', async (req, res) => { + try { + const guilds = discordClient.guilds?.cache?.size || 0; + let users = 0; + + if (discordClient.guilds?.cache) { + discordClient.guilds.cache.forEach(guild => { + users += guild.memberCount || 0; + }); + } + + let linkedProfiles = 0; + if (supabase) { + const { count } = await supabase + .from('discord_links') + .select('*', { count: 'exact', head: true }); + linkedProfiles = count || 0; + } + + res.json({ + guilds, + users, + linkedProfiles, + commands: 66, + uptime: process.uptime() + }); + } catch (error) { + res.json({ guilds: 0, users: 0, linkedProfiles: 0, commands: 66 }); + } + }); + app.get('/federation', (req, res) => { res.sendFile(path.join(__dirname, '../public/federation.html')); }); + app.get('/pricing', (req, res) => { + res.sendFile(path.join(__dirname, '../public/pricing.html')); + }); + app.get('/dashboard', (req, res) => { res.sendFile(path.join(__dirname, '../public/dashboard.html')); });