Add missing commands and functionality to the bot
Restores and registers previously missing commands such as /verify, /profile, /leaderboard, and others, alongside their associated functionality and the role management utilities. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e72fc1b7-94bd-4d6c-801f-cbac2fae245c Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 9f9fe241-9650-4ed0-9631-2a4d2267f526 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/e72fc1b7-94bd-4d6c-801f-cbac2fae245c/MSxeu36 Replit-Helium-Checkpoint-Created: true
This commit is contained in:
parent
42c2ba799f
commit
ba613d2a7c
12 changed files with 1274 additions and 1 deletions
55
aethex-bot/commands/help.js
Normal file
55
aethex-bot/commands/help.js
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("help")
|
||||
.setDescription("View all AeThex bot commands and features"),
|
||||
|
||||
async execute(interaction) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x7289da)
|
||||
.setTitle("🤖 AeThex Bot Commands")
|
||||
.setDescription("Here are all the commands you can use with the AeThex Discord bot.")
|
||||
.addFields(
|
||||
{
|
||||
name: "🔗 Account Linking",
|
||||
value: [
|
||||
"`/verify` - Link your Discord account to AeThex",
|
||||
"`/unlink` - Disconnect your Discord from AeThex",
|
||||
"`/profile` - View your linked AeThex profile",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
name: "⚔️ Realm Management",
|
||||
value: [
|
||||
"`/set-realm` - Choose your primary realm (Labs, GameForge, Corp, Foundation, Dev-Link)",
|
||||
"`/verify-role` - Check your assigned Discord roles",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
name: "📊 Community",
|
||||
value: [
|
||||
"`/stats` - View your AeThex statistics and activity",
|
||||
"`/leaderboard` - See the top contributors",
|
||||
"`/post` - Create a post in the AeThex community feed",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
name: "ℹ️ Information",
|
||||
value: "`/help` - Show this help message",
|
||||
},
|
||||
)
|
||||
.addFields({
|
||||
name: "🔗 Quick Links",
|
||||
value: [
|
||||
"[AeThex Platform](https://aethex.dev)",
|
||||
"[Creator Directory](https://aethex.dev/creators)",
|
||||
"[Community Feed](https://aethex.dev/community/feed)",
|
||||
].join(" | "),
|
||||
})
|
||||
.setFooter({ text: "AeThex | Build. Create. Connect." })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.reply({ embeds: [embed], ephemeral: true });
|
||||
},
|
||||
};
|
||||
155
aethex-bot/commands/leaderboard.js
Normal file
155
aethex-bot/commands/leaderboard.js
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("leaderboard")
|
||||
.setDescription("View the top AeThex contributors")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("category")
|
||||
.setDescription("Leaderboard category")
|
||||
.setRequired(false)
|
||||
.addChoices(
|
||||
{ name: "🔥 Most Active (Posts)", value: "posts" },
|
||||
{ name: "❤️ Most Liked", value: "likes" },
|
||||
{ name: "🎨 Top Creators", value: "creators" }
|
||||
)
|
||||
),
|
||||
|
||||
async execute(interaction, supabase) {
|
||||
await interaction.deferReply();
|
||||
|
||||
try {
|
||||
const category = interaction.options.getString("category") || "posts";
|
||||
|
||||
let leaderboardData = [];
|
||||
let title = "";
|
||||
let emoji = "";
|
||||
|
||||
if (category === "posts") {
|
||||
title = "Most Active Posters";
|
||||
emoji = "🔥";
|
||||
|
||||
const { data: posts } = await supabase
|
||||
.from("community_posts")
|
||||
.select("user_id")
|
||||
.not("user_id", "is", null);
|
||||
|
||||
const postCounts = {};
|
||||
posts?.forEach((post) => {
|
||||
postCounts[post.user_id] = (postCounts[post.user_id] || 0) + 1;
|
||||
});
|
||||
|
||||
const sortedUsers = Object.entries(postCounts)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.slice(0, 10);
|
||||
|
||||
for (const [userId, count] of sortedUsers) {
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("username, full_name, avatar_url")
|
||||
.eq("id", userId)
|
||||
.single();
|
||||
|
||||
if (profile) {
|
||||
leaderboardData.push({
|
||||
name: profile.full_name || profile.username || "Anonymous",
|
||||
value: `${count} posts`,
|
||||
username: profile.username,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (category === "likes") {
|
||||
title = "Most Liked Users";
|
||||
emoji = "❤️";
|
||||
|
||||
const { data: posts } = await supabase
|
||||
.from("community_posts")
|
||||
.select("user_id, likes_count")
|
||||
.not("user_id", "is", null)
|
||||
.order("likes_count", { ascending: false });
|
||||
|
||||
const likeCounts = {};
|
||||
posts?.forEach((post) => {
|
||||
likeCounts[post.user_id] =
|
||||
(likeCounts[post.user_id] || 0) + (post.likes_count || 0);
|
||||
});
|
||||
|
||||
const sortedUsers = Object.entries(likeCounts)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.slice(0, 10);
|
||||
|
||||
for (const [userId, count] of sortedUsers) {
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("username, full_name, avatar_url")
|
||||
.eq("id", userId)
|
||||
.single();
|
||||
|
||||
if (profile) {
|
||||
leaderboardData.push({
|
||||
name: profile.full_name || profile.username || "Anonymous",
|
||||
value: `${count} likes received`,
|
||||
username: profile.username,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (category === "creators") {
|
||||
title = "Top Creators";
|
||||
emoji = "🎨";
|
||||
|
||||
const { data: creators } = await supabase
|
||||
.from("aethex_creators")
|
||||
.select("user_id, total_projects, verified, featured")
|
||||
.order("total_projects", { ascending: false })
|
||||
.limit(10);
|
||||
|
||||
for (const creator of creators || []) {
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("username, full_name, avatar_url")
|
||||
.eq("id", creator.user_id)
|
||||
.single();
|
||||
|
||||
if (profile) {
|
||||
const badges = [];
|
||||
if (creator.verified) badges.push("✅");
|
||||
if (creator.featured) badges.push("⭐");
|
||||
|
||||
leaderboardData.push({
|
||||
name: profile.full_name || profile.username || "Anonymous",
|
||||
value: `${creator.total_projects || 0} projects ${badges.join(" ")}`,
|
||||
username: profile.username,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x7289da)
|
||||
.setTitle(`${emoji} ${title}`)
|
||||
.setDescription(
|
||||
leaderboardData.length > 0
|
||||
? leaderboardData
|
||||
.map(
|
||||
(user, index) =>
|
||||
`**${index + 1}.** ${user.name} - ${user.value}`
|
||||
)
|
||||
.join("\n")
|
||||
: "No data available yet. Be the first to contribute!"
|
||||
)
|
||||
.setFooter({ text: "AeThex Leaderboard | Updated in real-time" })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (error) {
|
||||
console.error("Leaderboard command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to fetch leaderboard. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
144
aethex-bot/commands/post.js
Normal file
144
aethex-bot/commands/post.js
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
const {
|
||||
SlashCommandBuilder,
|
||||
EmbedBuilder,
|
||||
ModalBuilder,
|
||||
TextInputBuilder,
|
||||
TextInputStyle,
|
||||
ActionRowBuilder,
|
||||
} = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("post")
|
||||
.setDescription("Create a post in the AeThex community feed")
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("content")
|
||||
.setDescription("Your post content")
|
||||
.setRequired(true)
|
||||
.setMaxLength(500)
|
||||
)
|
||||
.addStringOption((option) =>
|
||||
option
|
||||
.setName("category")
|
||||
.setDescription("Post category")
|
||||
.setRequired(false)
|
||||
.addChoices(
|
||||
{ name: "💬 General", value: "general" },
|
||||
{ name: "🚀 Project Update", value: "project_update" },
|
||||
{ name: "❓ Question", value: "question" },
|
||||
{ name: "💡 Idea", value: "idea" },
|
||||
{ name: "🎉 Announcement", value: "announcement" }
|
||||
)
|
||||
)
|
||||
.addAttachmentOption((option) =>
|
||||
option
|
||||
.setName("image")
|
||||
.setDescription("Attach an image to your post")
|
||||
.setRequired(false)
|
||||
),
|
||||
|
||||
async execute(interaction, supabase, client) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
const { data: link } = await supabase
|
||||
.from("discord_links")
|
||||
.select("user_id, primary_arm")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.single();
|
||||
|
||||
if (!link) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("❌ Not Linked")
|
||||
.setDescription(
|
||||
"You must link your Discord account to AeThex first.\nUse `/verify` to get started."
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("username, full_name, avatar_url")
|
||||
.eq("id", link.user_id)
|
||||
.single();
|
||||
|
||||
const content = interaction.options.getString("content");
|
||||
const category = interaction.options.getString("category") || "general";
|
||||
const attachment = interaction.options.getAttachment("image");
|
||||
|
||||
let imageUrl = null;
|
||||
if (attachment && attachment.contentType?.startsWith("image/")) {
|
||||
imageUrl = attachment.url;
|
||||
}
|
||||
|
||||
const categoryLabels = {
|
||||
general: "General",
|
||||
project_update: "Project Update",
|
||||
question: "Question",
|
||||
idea: "Idea",
|
||||
announcement: "Announcement",
|
||||
};
|
||||
|
||||
const { data: post, error } = await supabase
|
||||
.from("community_posts")
|
||||
.insert({
|
||||
user_id: link.user_id,
|
||||
content: content,
|
||||
category: category,
|
||||
arm_affiliation: link.primary_arm || "general",
|
||||
image_url: imageUrl,
|
||||
source: "discord",
|
||||
discord_message_id: interaction.id,
|
||||
discord_author_id: interaction.user.id,
|
||||
discord_author_name: interaction.user.username,
|
||||
discord_author_avatar: interaction.user.displayAvatarURL(),
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
const successEmbed = new EmbedBuilder()
|
||||
.setColor(0x00ff00)
|
||||
.setTitle("✅ Post Created!")
|
||||
.setDescription(content.length > 100 ? content.slice(0, 100) + "..." : content)
|
||||
.addFields(
|
||||
{
|
||||
name: "📁 Category",
|
||||
value: categoryLabels[category],
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "⚔️ Realm",
|
||||
value: link.primary_arm || "general",
|
||||
inline: true,
|
||||
}
|
||||
);
|
||||
|
||||
if (imageUrl) {
|
||||
successEmbed.setImage(imageUrl);
|
||||
}
|
||||
|
||||
successEmbed
|
||||
.addFields({
|
||||
name: "🔗 View Post",
|
||||
value: `[Open in AeThex](https://aethex.dev/community/feed)`,
|
||||
})
|
||||
.setFooter({ text: "Your post is now live on AeThex!" })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [successEmbed] });
|
||||
} catch (error) {
|
||||
console.error("Post command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to create post. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
93
aethex-bot/commands/profile.js
Normal file
93
aethex-bot/commands/profile.js
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("profile")
|
||||
.setDescription("View your AeThex profile in Discord"),
|
||||
|
||||
async execute(interaction, supabase) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
const { data: link } = await supabase
|
||||
.from("discord_links")
|
||||
.select("user_id, primary_arm")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.single();
|
||||
|
||||
if (!link) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("❌ Not Linked")
|
||||
.setDescription(
|
||||
"You must link your Discord account to AeThex first.\nUse `/verify` to get started.",
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("*")
|
||||
.eq("id", link.user_id)
|
||||
.single();
|
||||
|
||||
if (!profile) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("❌ Profile Not Found")
|
||||
.setDescription("Your AeThex profile could not be found.");
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
const armEmojis = {
|
||||
labs: "🧪",
|
||||
gameforge: "🎮",
|
||||
corp: "💼",
|
||||
foundation: "🤝",
|
||||
devlink: "💻",
|
||||
};
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x7289da)
|
||||
.setTitle(`${profile.full_name || "AeThex User"}'s Profile`)
|
||||
.setThumbnail(
|
||||
profile.avatar_url || "https://aethex.dev/placeholder.svg",
|
||||
)
|
||||
.addFields(
|
||||
{
|
||||
name: "👤 Username",
|
||||
value: profile.username || "N/A",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: `${armEmojis[link.primary_arm] || "⚔️"} Primary Realm`,
|
||||
value: link.primary_arm || "Not set",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "📊 Role",
|
||||
value: profile.user_type || "community_member",
|
||||
inline: true,
|
||||
},
|
||||
{ name: "📝 Bio", value: profile.bio || "No bio set", inline: false },
|
||||
)
|
||||
.addFields({
|
||||
name: "🔗 Links",
|
||||
value: `[Visit Full Profile](https://aethex.dev/creators/${profile.username})`,
|
||||
})
|
||||
.setFooter({ text: "AeThex | Your Web3 Creator Hub" });
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (error) {
|
||||
console.error("Profile command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to fetch profile. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
72
aethex-bot/commands/refresh-roles.js
Normal file
72
aethex-bot/commands/refresh-roles.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
||||
const { assignRoleByArm, getUserArm } = require("../utils/roleManager");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("refresh-roles")
|
||||
.setDescription(
|
||||
"Refresh your Discord roles based on your current AeThex settings",
|
||||
),
|
||||
|
||||
async execute(interaction, supabase, client) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
// Check if user is linked
|
||||
const { data: link } = await supabase
|
||||
.from("discord_links")
|
||||
.select("primary_arm")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.maybeSingle();
|
||||
|
||||
if (!link) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("❌ Not Linked")
|
||||
.setDescription(
|
||||
"You must link your Discord account to AeThex first.\nUse `/verify` to get started.",
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
if (!link.primary_arm) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xffaa00)
|
||||
.setTitle("⚠️ No Realm Set")
|
||||
.setDescription(
|
||||
"You haven't set your primary realm yet.\nUse `/set-realm` to choose one.",
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
// Assign role based on current primary arm
|
||||
const roleAssigned = await assignRoleByArm(
|
||||
interaction.guild,
|
||||
interaction.user.id,
|
||||
link.primary_arm,
|
||||
supabase,
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(roleAssigned ? 0x00ff00 : 0xffaa00)
|
||||
.setTitle("✅ Roles Refreshed")
|
||||
.setDescription(
|
||||
roleAssigned
|
||||
? `Your Discord roles have been synced with your AeThex account.\n\nPrimary Realm: **${link.primary_arm}**`
|
||||
: `Your roles could not be automatically assigned.\n\nPrimary Realm: **${link.primary_arm}**\n\n⚠️ Please contact an admin to set up the role mapping for this server.`,
|
||||
);
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (error) {
|
||||
console.error("Refresh-roles command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to refresh roles. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
139
aethex-bot/commands/set-realm.js
Normal file
139
aethex-bot/commands/set-realm.js
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
const {
|
||||
SlashCommandBuilder,
|
||||
EmbedBuilder,
|
||||
StringSelectMenuBuilder,
|
||||
ActionRowBuilder,
|
||||
} = require("discord.js");
|
||||
const { assignRoleByArm } = require("../utils/roleManager");
|
||||
|
||||
const REALMS = [
|
||||
{ value: "labs", label: "🧪 Labs", description: "Research & Development" },
|
||||
{
|
||||
value: "gameforge",
|
||||
label: "🎮 GameForge",
|
||||
description: "Game Development",
|
||||
},
|
||||
{ value: "corp", label: "💼 Corp", description: "Enterprise Solutions" },
|
||||
{
|
||||
value: "foundation",
|
||||
label: "🤝 Foundation",
|
||||
description: "Community & Education",
|
||||
},
|
||||
{
|
||||
value: "devlink",
|
||||
label: "💻 Dev-Link",
|
||||
description: "Professional Networking",
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("set-realm")
|
||||
.setDescription("Set your primary AeThex realm/arm"),
|
||||
|
||||
async execute(interaction, supabase, client) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
const { data: link } = await supabase
|
||||
.from("discord_links")
|
||||
.select("user_id, primary_arm")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.single();
|
||||
|
||||
if (!link) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("❌ Not Linked")
|
||||
.setDescription(
|
||||
"You must link your Discord account to AeThex first.\nUse `/verify` to get started.",
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
const select = new StringSelectMenuBuilder()
|
||||
.setCustomId("select_realm")
|
||||
.setPlaceholder("Choose your primary realm")
|
||||
.addOptions(
|
||||
REALMS.map((realm) => ({
|
||||
label: realm.label,
|
||||
description: realm.description,
|
||||
value: realm.value,
|
||||
default: realm.value === link.primary_arm,
|
||||
})),
|
||||
);
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(select);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x7289da)
|
||||
.setTitle("⚔️ Choose Your Realm")
|
||||
.setDescription(
|
||||
"Select your primary AeThex realm. This determines your main Discord role.",
|
||||
)
|
||||
.addFields({
|
||||
name: "Current Realm",
|
||||
value: link.primary_arm || "Not set",
|
||||
});
|
||||
|
||||
await interaction.editReply({ embeds: [embed], components: [row] });
|
||||
|
||||
const filter = (i) =>
|
||||
i.user.id === interaction.user.id && i.customId === "select_realm";
|
||||
const collector = interaction.channel.createMessageComponentCollector({
|
||||
filter,
|
||||
time: 60000,
|
||||
});
|
||||
|
||||
collector.on("collect", async (i) => {
|
||||
const selectedRealm = i.values[0];
|
||||
|
||||
await supabase
|
||||
.from("discord_links")
|
||||
.update({ primary_arm: selectedRealm })
|
||||
.eq("discord_id", interaction.user.id);
|
||||
|
||||
const realm = REALMS.find((r) => r.value === selectedRealm);
|
||||
|
||||
// Assign Discord role based on selected realm
|
||||
const roleAssigned = await assignRoleByArm(
|
||||
interaction.guild,
|
||||
interaction.user.id,
|
||||
selectedRealm,
|
||||
supabase,
|
||||
);
|
||||
|
||||
const roleStatus = roleAssigned
|
||||
? "✅ Discord role assigned!"
|
||||
: "⚠️ No role mapping found for this realm in this server.";
|
||||
|
||||
const confirmEmbed = new EmbedBuilder()
|
||||
.setColor(0x00ff00)
|
||||
.setTitle("✅ Realm Set")
|
||||
.setDescription(
|
||||
`Your primary realm is now **${realm.label}**\n\n${roleStatus}`,
|
||||
);
|
||||
|
||||
await i.update({ embeds: [confirmEmbed], components: [] });
|
||||
});
|
||||
|
||||
collector.on("end", (collected) => {
|
||||
if (collected.size === 0) {
|
||||
interaction.editReply({
|
||||
content: "Realm selection timed out.",
|
||||
components: [],
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Set-realm command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to update realm. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
140
aethex-bot/commands/stats.js
Normal file
140
aethex-bot/commands/stats.js
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("stats")
|
||||
.setDescription("View your AeThex statistics and activity"),
|
||||
|
||||
async execute(interaction, supabase) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
const { data: link } = await supabase
|
||||
.from("discord_links")
|
||||
.select("user_id, primary_arm, created_at")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.single();
|
||||
|
||||
if (!link) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("❌ Not Linked")
|
||||
.setDescription(
|
||||
"You must link your Discord account to AeThex first.\nUse `/verify` to get started."
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("*")
|
||||
.eq("id", link.user_id)
|
||||
.single();
|
||||
|
||||
const { count: postCount } = await supabase
|
||||
.from("community_posts")
|
||||
.select("*", { count: "exact", head: true })
|
||||
.eq("user_id", link.user_id);
|
||||
|
||||
const { count: likeCount } = await supabase
|
||||
.from("community_likes")
|
||||
.select("*", { count: "exact", head: true })
|
||||
.eq("user_id", link.user_id);
|
||||
|
||||
const { count: commentCount } = await supabase
|
||||
.from("community_comments")
|
||||
.select("*", { count: "exact", head: true })
|
||||
.eq("user_id", link.user_id);
|
||||
|
||||
const { data: creatorProfile } = await supabase
|
||||
.from("aethex_creators")
|
||||
.select("verified, featured, total_projects")
|
||||
.eq("user_id", link.user_id)
|
||||
.single();
|
||||
|
||||
const armEmojis = {
|
||||
labs: "🧪",
|
||||
gameforge: "🎮",
|
||||
corp: "💼",
|
||||
foundation: "🤝",
|
||||
devlink: "💻",
|
||||
};
|
||||
|
||||
const linkedDate = new Date(link.created_at);
|
||||
const daysSinceLinked = Math.floor(
|
||||
(Date.now() - linkedDate.getTime()) / (1000 * 60 * 60 * 24)
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x7289da)
|
||||
.setTitle(`📊 ${profile?.full_name || interaction.user.username}'s Stats`)
|
||||
.setThumbnail(profile?.avatar_url || interaction.user.displayAvatarURL())
|
||||
.addFields(
|
||||
{
|
||||
name: `${armEmojis[link.primary_arm] || "⚔️"} Primary Realm`,
|
||||
value: link.primary_arm || "Not set",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "👤 Account Type",
|
||||
value: profile?.user_type || "community_member",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "📅 Days Linked",
|
||||
value: `${daysSinceLinked} days`,
|
||||
inline: true,
|
||||
}
|
||||
)
|
||||
.addFields(
|
||||
{
|
||||
name: "📝 Posts",
|
||||
value: `${postCount || 0}`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "❤️ Likes Given",
|
||||
value: `${likeCount || 0}`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "💬 Comments",
|
||||
value: `${commentCount || 0}`,
|
||||
inline: true,
|
||||
}
|
||||
);
|
||||
|
||||
if (creatorProfile) {
|
||||
embed.addFields({
|
||||
name: "🎨 Creator Status",
|
||||
value: [
|
||||
creatorProfile.verified ? "✅ Verified Creator" : "⏳ Pending Verification",
|
||||
creatorProfile.featured ? "⭐ Featured" : "",
|
||||
`📁 ${creatorProfile.total_projects || 0} Projects`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n"),
|
||||
});
|
||||
}
|
||||
|
||||
embed
|
||||
.addFields({
|
||||
name: "🔗 Full Profile",
|
||||
value: `[View on AeThex](https://aethex.dev/creators/${profile?.username || link.user_id})`,
|
||||
})
|
||||
.setFooter({ text: "AeThex | Your Creative Hub" })
|
||||
.setTimestamp();
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (error) {
|
||||
console.error("Stats command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to fetch stats. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
75
aethex-bot/commands/unlink.js
Normal file
75
aethex-bot/commands/unlink.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("unlink")
|
||||
.setDescription("Unlink your Discord account from AeThex"),
|
||||
|
||||
async execute(interaction, supabase) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
const { data: link } = await supabase
|
||||
.from("discord_links")
|
||||
.select("*")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.single();
|
||||
|
||||
if (!link) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("ℹ️ Not Linked")
|
||||
.setDescription("Your Discord account is not linked to AeThex.");
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
// Delete the link
|
||||
await supabase
|
||||
.from("discord_links")
|
||||
.delete()
|
||||
.eq("discord_id", interaction.user.id);
|
||||
|
||||
// Remove Discord roles from user
|
||||
const guild = interaction.guild;
|
||||
const member = await guild.members.fetch(interaction.user.id);
|
||||
|
||||
// Find and remove all AeThex-related roles
|
||||
const rolesToRemove = member.roles.cache.filter(
|
||||
(role) =>
|
||||
role.name.includes("Labs") ||
|
||||
role.name.includes("GameForge") ||
|
||||
role.name.includes("Corp") ||
|
||||
role.name.includes("Foundation") ||
|
||||
role.name.includes("Dev-Link") ||
|
||||
role.name.includes("Premium") ||
|
||||
role.name.includes("Creator"),
|
||||
);
|
||||
|
||||
for (const [, role] of rolesToRemove) {
|
||||
try {
|
||||
await member.roles.remove(role);
|
||||
} catch (e) {
|
||||
console.warn(`Could not remove role ${role.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x00ff00)
|
||||
.setTitle("✅ Account Unlinked")
|
||||
.setDescription(
|
||||
"Your Discord account has been unlinked from AeThex.\nAll associated roles have been removed.",
|
||||
);
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (error) {
|
||||
console.error("Unlink command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to unlink account. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
97
aethex-bot/commands/verify-role.js
Normal file
97
aethex-bot/commands/verify-role.js
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
const { SlashCommandBuilder, EmbedBuilder } = require("discord.js");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("verify-role")
|
||||
.setDescription("Check your AeThex-assigned Discord roles"),
|
||||
|
||||
async execute(interaction, supabase) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
const { data: link } = await supabase
|
||||
.from("discord_links")
|
||||
.select("user_id, primary_arm")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.single();
|
||||
|
||||
if (!link) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff6b6b)
|
||||
.setTitle("❌ Not Linked")
|
||||
.setDescription(
|
||||
"You must link your Discord account to AeThex first.\nUse `/verify` to get started.",
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("user_type")
|
||||
.eq("id", link.user_id)
|
||||
.single();
|
||||
|
||||
const { data: mappings } = await supabase
|
||||
.from("discord_role_mappings")
|
||||
.select("discord_role")
|
||||
.eq("arm", link.primary_arm)
|
||||
.eq("user_type", profile?.user_type || "community_member");
|
||||
|
||||
const member = await interaction.guild.members.fetch(interaction.user.id);
|
||||
const aethexRoles = member.roles.cache.filter(
|
||||
(role) =>
|
||||
role.name.includes("Labs") ||
|
||||
role.name.includes("GameForge") ||
|
||||
role.name.includes("Corp") ||
|
||||
role.name.includes("Foundation") ||
|
||||
role.name.includes("Dev-Link") ||
|
||||
role.name.includes("Premium") ||
|
||||
role.name.includes("Creator"),
|
||||
);
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x7289da)
|
||||
.setTitle("🔐 Your AeThex Roles")
|
||||
.addFields(
|
||||
{
|
||||
name: "⚔️ Primary Realm",
|
||||
value: link.primary_arm || "Not set",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "👤 User Type",
|
||||
value: profile?.user_type || "community_member",
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: "🎭 Discord Roles",
|
||||
value:
|
||||
aethexRoles.size > 0
|
||||
? aethexRoles.map((r) => r.name).join(", ")
|
||||
: "None assigned yet",
|
||||
},
|
||||
{
|
||||
name: "📋 Expected Roles",
|
||||
value:
|
||||
mappings?.length > 0
|
||||
? mappings.map((m) => m.discord_role).join(", ")
|
||||
: "No mappings found",
|
||||
},
|
||||
)
|
||||
.setFooter({
|
||||
text: "Roles are assigned automatically based on your AeThex profile",
|
||||
});
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
} catch (error) {
|
||||
console.error("Verify-role command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription("Failed to verify roles. Please try again.");
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
85
aethex-bot/commands/verify.js
Normal file
85
aethex-bot/commands/verify.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
const {
|
||||
SlashCommandBuilder,
|
||||
EmbedBuilder,
|
||||
ActionRowBuilder,
|
||||
ButtonBuilder,
|
||||
ButtonStyle,
|
||||
} = require("discord.js");
|
||||
const { syncRolesAcrossGuilds } = require("../utils/roleManager");
|
||||
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("verify")
|
||||
.setDescription("Link your Discord account to your AeThex account"),
|
||||
|
||||
async execute(interaction, supabase, client) {
|
||||
await interaction.deferReply({ ephemeral: true });
|
||||
|
||||
try {
|
||||
const { data: existingLink } = await supabase
|
||||
.from("discord_links")
|
||||
.select("*")
|
||||
.eq("discord_id", interaction.user.id)
|
||||
.single();
|
||||
|
||||
if (existingLink) {
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x00ff00)
|
||||
.setTitle("✅ Already Linked")
|
||||
.setDescription(
|
||||
`Your Discord account is already linked to AeThex (User ID: ${existingLink.user_id})`,
|
||||
);
|
||||
|
||||
return await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
|
||||
// Generate verification code
|
||||
const verificationCode = Math.random()
|
||||
.toString(36)
|
||||
.substring(2, 8)
|
||||
.toUpperCase();
|
||||
const expiresAt = new Date(Date.now() + 15 * 60 * 1000); // 15 minutes
|
||||
|
||||
// Store verification code with Discord username
|
||||
await supabase.from("discord_verifications").insert({
|
||||
discord_id: interaction.user.id,
|
||||
verification_code: verificationCode,
|
||||
username: interaction.user.username,
|
||||
expires_at: expiresAt.toISOString(),
|
||||
});
|
||||
|
||||
const verifyUrl = `https://aethex.dev/discord-verify?code=${verificationCode}`;
|
||||
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0x7289da)
|
||||
.setTitle("🔗 Link Your AeThex Account")
|
||||
.setDescription(
|
||||
"Click the button below to link your Discord account to AeThex.",
|
||||
)
|
||||
.addFields(
|
||||
{ name: "⏱️ Expires In", value: "15 minutes" },
|
||||
{ name: "📝 Verification Code", value: `\`${verificationCode}\`` },
|
||||
)
|
||||
.setFooter({ text: "Your security code will expire in 15 minutes" });
|
||||
|
||||
const row = new ActionRowBuilder().addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel("Link Account")
|
||||
.setStyle(ButtonStyle.Link)
|
||||
.setURL(verifyUrl),
|
||||
);
|
||||
|
||||
await interaction.editReply({ embeds: [embed], components: [row] });
|
||||
} catch (error) {
|
||||
console.error("Verify command error:", error);
|
||||
const embed = new EmbedBuilder()
|
||||
.setColor(0xff0000)
|
||||
.setTitle("❌ Error")
|
||||
.setDescription(
|
||||
"Failed to generate verification code. Please try again.",
|
||||
);
|
||||
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -2,6 +2,87 @@ const { REST, Routes } = require('discord.js');
|
|||
require('dotenv').config();
|
||||
|
||||
const commands = [
|
||||
{
|
||||
name: 'verify',
|
||||
description: 'Link your Discord account to your AeThex account',
|
||||
},
|
||||
{
|
||||
name: 'unlink',
|
||||
description: 'Unlink your Discord account from AeThex',
|
||||
},
|
||||
{
|
||||
name: 'profile',
|
||||
description: 'View your AeThex profile in Discord',
|
||||
},
|
||||
{
|
||||
name: 'help',
|
||||
description: 'View all AeThex bot commands and features',
|
||||
},
|
||||
{
|
||||
name: 'leaderboard',
|
||||
description: 'View the top AeThex contributors',
|
||||
options: [
|
||||
{
|
||||
name: 'category',
|
||||
type: 3,
|
||||
description: 'Leaderboard category',
|
||||
required: false,
|
||||
choices: [
|
||||
{ name: 'Most Active (Posts)', value: 'posts' },
|
||||
{ name: 'Most Liked', value: 'likes' },
|
||||
{ name: 'Top Creators', value: 'creators' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'stats',
|
||||
description: 'View your AeThex statistics and activity',
|
||||
},
|
||||
{
|
||||
name: 'set-realm',
|
||||
description: 'Set your primary AeThex realm/arm',
|
||||
},
|
||||
{
|
||||
name: 'verify-role',
|
||||
description: 'Check your AeThex-assigned Discord roles',
|
||||
},
|
||||
{
|
||||
name: 'refresh-roles',
|
||||
description: 'Refresh your Discord roles based on your current AeThex settings',
|
||||
},
|
||||
{
|
||||
name: 'post',
|
||||
description: 'Create a post in the AeThex community feed',
|
||||
options: [
|
||||
{
|
||||
name: 'content',
|
||||
type: 3,
|
||||
description: 'Your post content',
|
||||
required: true,
|
||||
max_length: 500,
|
||||
},
|
||||
{
|
||||
name: 'category',
|
||||
type: 3,
|
||||
description: 'Post category',
|
||||
required: false,
|
||||
choices: [
|
||||
{ name: 'General', value: 'general' },
|
||||
{ name: 'Project Update', value: 'project_update' },
|
||||
{ name: 'Question', value: 'question' },
|
||||
{ name: 'Idea', value: 'idea' },
|
||||
{ name: 'Announcement', value: 'announcement' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
type: 11,
|
||||
description: 'Attach an image to your post',
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'ticket',
|
||||
description: 'Ticket management system',
|
||||
|
|
@ -146,7 +227,7 @@ const rest = new REST({ version: '10' }).setToken(token);
|
|||
{ body: allCommands }
|
||||
);
|
||||
|
||||
console.log(`Successfully registered ${data.length} commands`);
|
||||
console.log(`Successfully registered ${data.length} commands:`);
|
||||
data.forEach(cmd => console.log(` - /${cmd.name}`));
|
||||
} catch (error) {
|
||||
console.error('Error registering commands:', error);
|
||||
|
|
|
|||
137
aethex-bot/utils/roleManager.js
Normal file
137
aethex-bot/utils/roleManager.js
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
const { EmbedBuilder } = require("discord.js");
|
||||
|
||||
/**
|
||||
* Assign Discord role based on user's arm and type
|
||||
* @param {Guild} guild - Discord guild
|
||||
* @param {string} discordId - Discord user ID
|
||||
* @param {string} arm - User's primary arm (labs, gameforge, corp, foundation, devlink)
|
||||
* @param {object} supabase - Supabase client
|
||||
* @returns {Promise<boolean>} - Success status
|
||||
*/
|
||||
async function assignRoleByArm(guild, discordId, arm, supabase) {
|
||||
try {
|
||||
// Fetch guild member
|
||||
const member = await guild.members.fetch(discordId);
|
||||
if (!member) {
|
||||
console.warn(`Member not found: ${discordId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get role mapping from Supabase
|
||||
const { data: mapping, error: mapError } = await supabase
|
||||
.from("discord_role_mappings")
|
||||
.select("discord_role")
|
||||
.eq("arm", arm)
|
||||
.eq("server_id", guild.id)
|
||||
.maybeSingle();
|
||||
|
||||
if (mapError) {
|
||||
console.error("Error fetching role mapping:", mapError);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mapping) {
|
||||
console.warn(
|
||||
`No role mapping found for arm: ${arm} in server: ${guild.id}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find role by name or ID
|
||||
let roleToAssign = guild.roles.cache.find(
|
||||
(r) => r.id === mapping.discord_role || r.name === mapping.discord_role,
|
||||
);
|
||||
|
||||
if (!roleToAssign) {
|
||||
console.warn(`Role not found: ${mapping.discord_role}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove old arm roles
|
||||
const armRoles = member.roles.cache.filter((role) =>
|
||||
["Labs", "GameForge", "Corp", "Foundation", "Dev-Link"].some((arm) =>
|
||||
role.name.includes(arm),
|
||||
),
|
||||
);
|
||||
|
||||
for (const [, role] of armRoles) {
|
||||
try {
|
||||
if (role.id !== roleToAssign.id) {
|
||||
await member.roles.remove(role);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Could not remove role ${role.name}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign new role
|
||||
if (!member.roles.cache.has(roleToAssign.id)) {
|
||||
await member.roles.add(roleToAssign);
|
||||
console.log(
|
||||
`✅ Assigned role ${roleToAssign.name} to ${member.user.tag}`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error assigning role:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user's primary arm from Supabase
|
||||
* @param {string} discordId - Discord user ID
|
||||
* @param {object} supabase - Supabase client
|
||||
* @returns {Promise<string>} - Primary arm (labs, gameforge, corp, foundation, devlink)
|
||||
*/
|
||||
async function getUserArm(discordId, supabase) {
|
||||
try {
|
||||
const { data: link, error } = await supabase
|
||||
.from("discord_links")
|
||||
.select("primary_arm")
|
||||
.eq("discord_id", discordId)
|
||||
.maybeSingle();
|
||||
|
||||
if (error) {
|
||||
console.error("Error fetching user arm:", error);
|
||||
return null;
|
||||
}
|
||||
|
||||
return link?.primary_arm || null;
|
||||
} catch (error) {
|
||||
console.error("Error getting user arm:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync roles for a user across all guilds
|
||||
* @param {Client} client - Discord client
|
||||
* @param {string} discordId - Discord user ID
|
||||
* @param {string} arm - Primary arm
|
||||
* @param {object} supabase - Supabase client
|
||||
*/
|
||||
async function syncRolesAcrossGuilds(client, discordId, arm, supabase) {
|
||||
try {
|
||||
for (const [, guild] of client.guilds.cache) {
|
||||
try {
|
||||
const member = await guild.members.fetch(discordId);
|
||||
if (member) {
|
||||
await assignRoleByArm(guild, discordId, arm, supabase);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Could not sync roles in guild ${guild.id}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error syncing roles across guilds:", error);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
assignRoleByArm,
|
||||
getUserArm,
|
||||
syncRolesAcrossGuilds,
|
||||
};
|
||||
Loading…
Reference in a new issue