Add Discord integration to display messages in the community feed
Integrates Discord messages into the community feed by adding a Discord source type, displaying channel names and author tags, and creating a Discord icon for the UI. The bot now handles messages from main chat channels and syncs them to the feed, creating guest profiles for unlinked Discord users. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 0a135de7-860c-4a4d-9cbe-7644c5fca3f0 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/ebxARkc Replit-Helium-Checkpoint-Created: true
This commit is contained in:
commit
e867fe998b
6 changed files with 199 additions and 242 deletions
4
.replit
4
.replit
|
|
@ -60,6 +60,10 @@ externalPort = 3000
|
|||
localPort = 40437
|
||||
externalPort = 3001
|
||||
|
||||
[[ports]]
|
||||
localPort = 43237
|
||||
externalPort = 3002
|
||||
|
||||
[deployment]
|
||||
deploymentTarget = "autoscale"
|
||||
run = ["node", "dist/server/production.mjs"]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { aethexSocialService } from "@/lib/aethex-social-service";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { normalizeErrorMessage } from "@/lib/error-utils";
|
||||
|
|
@ -110,6 +111,9 @@ interface FeedItem {
|
|||
likes: number;
|
||||
comments: number;
|
||||
arm?: ArmType;
|
||||
source?: "discord" | "web" | null;
|
||||
discordChannelName?: string | null;
|
||||
discordAuthorTag?: string | null;
|
||||
}
|
||||
|
||||
function parseContent(content: string) {
|
||||
|
|
@ -125,9 +129,12 @@ function parseContent(content: string) {
|
|||
? "video"
|
||||
: "image"
|
||||
: "none"),
|
||||
source: obj.source || null,
|
||||
discordChannelName: obj.discord_channel_name || obj.discord_channel || null,
|
||||
discordAuthorTag: obj.discord_author_tag || null,
|
||||
};
|
||||
} catch {
|
||||
return { text: content, mediaUrl: null, mediaType: "none" };
|
||||
return { text: content, mediaUrl: null, mediaType: "none", source: null };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +144,7 @@ interface ArmFeedProps {
|
|||
|
||||
export default function ArmFeed({ arm }: ArmFeedProps) {
|
||||
const { user, loading } = useAuth();
|
||||
const { toast } = useAuth().toast || { toast: () => {} };
|
||||
const { toast } = useToast();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [following, setFollowing] = useState<string[]>([]);
|
||||
|
|
@ -160,6 +167,9 @@ export default function ArmFeed({ arm }: ArmFeedProps) {
|
|||
likes: p.likes_count ?? 0,
|
||||
comments: p.comments_count ?? 0,
|
||||
arm: p.arm_affiliation || "labs",
|
||||
source: meta.source,
|
||||
discordChannelName: meta.discordChannelName,
|
||||
discordAuthorTag: meta.discordAuthorTag,
|
||||
};
|
||||
}),
|
||||
[],
|
||||
|
|
|
|||
|
|
@ -15,9 +15,15 @@ import { cn } from "@/lib/utils";
|
|||
import { useAuth } from "@/contexts/AuthContext";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { communityService } from "@/lib/supabase-service";
|
||||
import { Heart, MessageCircle, Share2, Volume2, VolumeX } from "lucide-react";
|
||||
import { Heart, MessageCircle, Share2, Volume2, VolumeX, MessageSquare } from "lucide-react";
|
||||
import type { FeedItem } from "@/pages/Feed";
|
||||
|
||||
const DiscordIcon = () => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" className="h-4 w-4">
|
||||
<path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028 14.09 14.09 0 0 0 1.226-1.994.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ARM_COLORS: Record<
|
||||
string,
|
||||
{ bg: string; border: string; badge: string; text: string }
|
||||
|
|
@ -179,6 +185,14 @@ export function FeedItemCard({
|
|||
>
|
||||
{armLabel}
|
||||
</Badge>
|
||||
{item.source === "discord" && (
|
||||
<Badge
|
||||
className="bg-[#5865F2]/20 text-[#5865F2] border-[#5865F2]/30 text-xs font-medium flex items-center gap-1"
|
||||
>
|
||||
<DiscordIcon />
|
||||
{item.discordChannelName ? `#${item.discordChannelName}` : "Discord"}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -77,6 +77,9 @@ export interface FeedItem {
|
|||
likes: number;
|
||||
comments: number;
|
||||
arm?: ArmType;
|
||||
source?: "discord" | "web" | null;
|
||||
discordChannelName?: string | null;
|
||||
discordAuthorTag?: string | null;
|
||||
}
|
||||
|
||||
interface TrendingTopic {
|
||||
|
|
@ -96,6 +99,9 @@ function parseContent(content: string): {
|
|||
text?: string;
|
||||
mediaUrl?: string | null;
|
||||
mediaType: "video" | "image" | "none";
|
||||
source?: "discord" | "web" | null;
|
||||
discordChannelName?: string | null;
|
||||
discordAuthorTag?: string | null;
|
||||
} {
|
||||
try {
|
||||
const obj = JSON.parse(content || "{}");
|
||||
|
|
@ -109,9 +115,12 @@ function parseContent(content: string): {
|
|||
? "video"
|
||||
: "image"
|
||||
: "none"),
|
||||
source: obj.source || null,
|
||||
discordChannelName: obj.discord_channel_name || obj.discord_channel || null,
|
||||
discordAuthorTag: obj.discord_author_tag || null,
|
||||
};
|
||||
} catch {
|
||||
return { text: content, mediaUrl: null, mediaType: "none" };
|
||||
return { text: content, mediaUrl: null, mediaType: "none", source: null };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +156,9 @@ export default function Feed() {
|
|||
likes: p.likes_count ?? 0,
|
||||
comments: p.comments_count ?? 0,
|
||||
arm: p.arm_affiliation || "labs",
|
||||
source: meta.source,
|
||||
discordChannelName: meta.discordChannelName,
|
||||
discordAuthorTag: meta.discordAuthorTag,
|
||||
};
|
||||
}),
|
||||
[],
|
||||
|
|
|
|||
|
|
@ -1,237 +0,0 @@
|
|||
const { createClient } = require("@supabase/supabase-js");
|
||||
|
||||
// Initialize Supabase
|
||||
const supabase = createClient(
|
||||
process.env.SUPABASE_URL,
|
||||
process.env.SUPABASE_SERVICE_ROLE,
|
||||
);
|
||||
|
||||
const API_BASE = process.env.VITE_API_BASE || "https://api.aethex.dev";
|
||||
|
||||
// Channel IDs for syncing
|
||||
const ANNOUNCEMENT_CHANNELS = process.env.DISCORD_ANNOUNCEMENT_CHANNELS
|
||||
? process.env.DISCORD_ANNOUNCEMENT_CHANNELS.split(",")
|
||||
: ["1435667453244866702"]; // Default to feed channel if env not set
|
||||
|
||||
// Arm affiliation mapping based on guild/channel name
|
||||
const getArmAffiliation = (message) => {
|
||||
const guildName = message.guild?.name?.toLowerCase() || "";
|
||||
const channelName = message.channel?.name?.toLowerCase() || "";
|
||||
|
||||
const searchString = `${guildName} ${channelName}`.toLowerCase();
|
||||
|
||||
if (searchString.includes("gameforge")) return "gameforge";
|
||||
if (searchString.includes("corp")) return "corp";
|
||||
if (searchString.includes("foundation")) return "foundation";
|
||||
if (searchString.includes("devlink") || searchString.includes("dev-link"))
|
||||
return "devlink";
|
||||
if (searchString.includes("nexus")) return "nexus";
|
||||
if (searchString.includes("staff")) return "staff";
|
||||
|
||||
return "labs"; // Default
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: "messageCreate",
|
||||
async execute(message, client, supabase) {
|
||||
try {
|
||||
// Ignore bot messages
|
||||
if (message.author.bot) return;
|
||||
|
||||
// Only process messages in announcement channels
|
||||
if (!ANNOUNCEMENT_CHANNELS.includes(message.channelId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip empty messages
|
||||
if (!message.content && message.attachments.size === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[Announcements Sync] Processing message from ${message.author.tag} in #${message.channel.name}`,
|
||||
);
|
||||
|
||||
// Get or create system admin user for announcements
|
||||
let adminUser = await supabase
|
||||
.from("user_profiles")
|
||||
.select("id")
|
||||
.eq("username", "aethex-announcements")
|
||||
.single();
|
||||
|
||||
let authorId = adminUser.data?.id;
|
||||
|
||||
if (!authorId) {
|
||||
// Create a system user if it doesn't exist
|
||||
const { data: newUser } = await supabase
|
||||
.from("user_profiles")
|
||||
.insert({
|
||||
username: "aethex-announcements",
|
||||
full_name: "AeThex Announcements",
|
||||
avatar_url: "https://aethex.dev/logo.png",
|
||||
})
|
||||
.select("id");
|
||||
|
||||
authorId = newUser?.[0]?.id;
|
||||
}
|
||||
|
||||
if (!authorId) {
|
||||
console.error("[Announcements Sync] Could not get author ID");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare message content
|
||||
let content = message.content || "Announcement from Discord";
|
||||
|
||||
// Handle embeds (convert to text)
|
||||
if (message.embeds.length > 0) {
|
||||
const embed = message.embeds[0];
|
||||
if (embed.title) content = embed.title + "\n\n" + content;
|
||||
if (embed.description) content += "\n\n" + embed.description;
|
||||
}
|
||||
|
||||
// Handle attachments (images, videos)
|
||||
let mediaUrl = null;
|
||||
let mediaType = "none";
|
||||
|
||||
if (message.attachments.size > 0) {
|
||||
const attachment = message.attachments.first();
|
||||
if (attachment) {
|
||||
mediaUrl = attachment.url;
|
||||
|
||||
const imageExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
|
||||
const videoExtensions = [".mp4", ".webm", ".mov", ".avi"];
|
||||
|
||||
const attachmentLower = attachment.name.toLowerCase();
|
||||
|
||||
if (imageExtensions.some((ext) => attachmentLower.endsWith(ext))) {
|
||||
mediaType = "image";
|
||||
} else if (
|
||||
videoExtensions.some((ext) => attachmentLower.endsWith(ext))
|
||||
) {
|
||||
mediaType = "video";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine arm affiliation
|
||||
const armAffiliation = getArmAffiliation(message);
|
||||
|
||||
// Prepare post content JSON
|
||||
const postContent = JSON.stringify({
|
||||
text: content,
|
||||
mediaUrl: mediaUrl,
|
||||
mediaType: mediaType,
|
||||
source: "discord",
|
||||
discord_message_id: message.id,
|
||||
discord_author: message.author.tag,
|
||||
});
|
||||
|
||||
// Create post in AeThex
|
||||
const { data: createdPost, error: insertError } = await supabase
|
||||
.from("community_posts")
|
||||
.insert({
|
||||
title: content.substring(0, 100) || "Discord Announcement",
|
||||
content: postContent,
|
||||
arm_affiliation: armAffiliation,
|
||||
author_id: authorId,
|
||||
tags: ["discord", "announcement"],
|
||||
category: "announcement",
|
||||
is_published: true,
|
||||
likes_count: 0,
|
||||
comments_count: 0,
|
||||
})
|
||||
.select(
|
||||
`id, title, content, arm_affiliation, author_id, created_at, likes_count, comments_count,
|
||||
user_profiles!community_posts_author_id_fkey (id, username, full_name, avatar_url)`,
|
||||
);
|
||||
|
||||
if (insertError) {
|
||||
console.error(
|
||||
"[Announcements Sync] Failed to create post:",
|
||||
insertError,
|
||||
);
|
||||
try {
|
||||
await message.react("❌");
|
||||
} catch (reactionError) {
|
||||
console.warn(
|
||||
"[Announcements Sync] Could not add reaction:",
|
||||
reactionError,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Sync to Discord feed webhook if configured
|
||||
if (process.env.DISCORD_FEED_WEBHOOK_URL && createdPost?.[0]) {
|
||||
try {
|
||||
const post = createdPost[0];
|
||||
const armColors = {
|
||||
labs: 0xfbbf24,
|
||||
gameforge: 0x22c55e,
|
||||
corp: 0x3b82f6,
|
||||
foundation: 0xef4444,
|
||||
devlink: 0x06b6d4,
|
||||
nexus: 0xa855f7,
|
||||
staff: 0x6366f1,
|
||||
};
|
||||
|
||||
const embed = {
|
||||
title: post.title,
|
||||
description: content.substring(0, 1024),
|
||||
color: armColors[armAffiliation] || 0x8b5cf6,
|
||||
author: {
|
||||
name: `${message.author.username} (${armAffiliation.toUpperCase()})`,
|
||||
icon_url: message.author.displayAvatarURL(),
|
||||
},
|
||||
footer: {
|
||||
text: "Synced from Discord",
|
||||
},
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
|
||||
await fetch(process.env.DISCORD_FEED_WEBHOOK_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: "AeThex Community Feed",
|
||||
embeds: [embed],
|
||||
}),
|
||||
});
|
||||
} catch (webhookError) {
|
||||
console.warn(
|
||||
"[Announcements Sync] Failed to sync to webhook:",
|
||||
webhookError,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[Announcements Sync] ✅ Posted announcement from Discord to AeThex (${armAffiliation})`,
|
||||
);
|
||||
|
||||
// React with success emoji
|
||||
try {
|
||||
await message.react("✅");
|
||||
} catch (reactionError) {
|
||||
console.warn(
|
||||
"[Announcements Sync] Could not add success reaction:",
|
||||
reactionError,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[Announcements Sync] Unexpected error:", error);
|
||||
|
||||
try {
|
||||
await message.react("⚠️");
|
||||
} catch (reactionError) {
|
||||
console.warn(
|
||||
"[Announcements Sync] Could not add warning reaction:",
|
||||
reactionError,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -15,6 +15,11 @@ const ANNOUNCEMENT_CHANNELS = process.env.DISCORD_ANNOUNCEMENT_CHANNELS
|
|||
? process.env.DISCORD_ANNOUNCEMENT_CHANNELS.split(",").map((id) => id.trim())
|
||||
: [];
|
||||
|
||||
// Main chat channels - sync ALL messages from these channels to the feed
|
||||
const MAIN_CHAT_CHANNELS = process.env.DISCORD_MAIN_CHAT_CHANNELS
|
||||
? process.env.DISCORD_MAIN_CHAT_CHANNELS.split(",").map((id) => id.trim())
|
||||
: [];
|
||||
|
||||
// Helper: Get arm affiliation from message context
|
||||
function getArmAffiliation(message) {
|
||||
const guildName = message.guild?.name?.toLowerCase() || "";
|
||||
|
|
@ -32,6 +37,147 @@ function getArmAffiliation(message) {
|
|||
return "labs";
|
||||
}
|
||||
|
||||
// Handle main chat messages - sync ALL messages to feed
|
||||
async function handleMainChatSync(message) {
|
||||
try {
|
||||
console.log(
|
||||
`[Main Chat] Processing from ${message.author.tag} in #${message.channel.name}`,
|
||||
);
|
||||
|
||||
// Check if user has linked account
|
||||
const { data: linkedAccount } = await supabase
|
||||
.from("discord_links")
|
||||
.select("user_id")
|
||||
.eq("discord_id", message.author.id)
|
||||
.single();
|
||||
|
||||
let authorId = linkedAccount?.user_id;
|
||||
let authorInfo = null;
|
||||
|
||||
if (authorId) {
|
||||
// Get linked user's profile
|
||||
const { data: profile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("id, username, full_name, avatar_url")
|
||||
.eq("id", authorId)
|
||||
.single();
|
||||
authorInfo = profile;
|
||||
}
|
||||
|
||||
// If no linked account, use or create a Discord guest profile
|
||||
if (!authorId) {
|
||||
// Check if we have a discord guest profile for this user
|
||||
const discordUsername = `discord-${message.author.id}`;
|
||||
let { data: guestProfile } = await supabase
|
||||
.from("user_profiles")
|
||||
.select("id, username, full_name, avatar_url")
|
||||
.eq("username", discordUsername)
|
||||
.single();
|
||||
|
||||
if (!guestProfile) {
|
||||
// Create guest profile
|
||||
const { data: newProfile, error: createError } = await supabase
|
||||
.from("user_profiles")
|
||||
.insert({
|
||||
username: discordUsername,
|
||||
full_name: message.author.displayName || message.author.username,
|
||||
avatar_url: message.author.displayAvatarURL({ size: 256 }),
|
||||
})
|
||||
.select("id, username, full_name, avatar_url")
|
||||
.single();
|
||||
|
||||
if (createError) {
|
||||
console.error("[Main Chat] Could not create guest profile:", createError);
|
||||
return;
|
||||
}
|
||||
guestProfile = newProfile;
|
||||
}
|
||||
|
||||
authorId = guestProfile?.id;
|
||||
authorInfo = guestProfile;
|
||||
}
|
||||
|
||||
if (!authorId) {
|
||||
console.error("[Main Chat] Could not get author ID");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare content
|
||||
let content = message.content || "Shared a message on Discord";
|
||||
let mediaUrl = null;
|
||||
let mediaType = "none";
|
||||
|
||||
if (message.attachments.size > 0) {
|
||||
const attachment = message.attachments.first();
|
||||
if (attachment) {
|
||||
mediaUrl = attachment.url;
|
||||
const attachmentLower = attachment.name.toLowerCase();
|
||||
|
||||
if (
|
||||
[".jpg", ".jpeg", ".png", ".gif", ".webp"].some((ext) =>
|
||||
attachmentLower.endsWith(ext),
|
||||
)
|
||||
) {
|
||||
mediaType = "image";
|
||||
} else if (
|
||||
[".mp4", ".webm", ".mov", ".avi"].some((ext) =>
|
||||
attachmentLower.endsWith(ext),
|
||||
)
|
||||
) {
|
||||
mediaType = "video";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine arm affiliation
|
||||
const armAffiliation = getArmAffiliation(message);
|
||||
|
||||
// Prepare post content with Discord metadata
|
||||
const postContent = JSON.stringify({
|
||||
text: content,
|
||||
mediaUrl: mediaUrl,
|
||||
mediaType: mediaType,
|
||||
source: "discord",
|
||||
discord_message_id: message.id,
|
||||
discord_channel_id: message.channelId,
|
||||
discord_channel_name: message.channel.name,
|
||||
discord_guild_id: message.guildId,
|
||||
discord_guild_name: message.guild?.name,
|
||||
discord_author_id: message.author.id,
|
||||
discord_author_tag: message.author.tag,
|
||||
discord_author_avatar: message.author.displayAvatarURL({ size: 256 }),
|
||||
is_linked_user: !!linkedAccount,
|
||||
});
|
||||
|
||||
// Create post
|
||||
const { data: createdPost, error: insertError } = await supabase
|
||||
.from("community_posts")
|
||||
.insert({
|
||||
title: content.substring(0, 100) || "Discord Message",
|
||||
content: postContent,
|
||||
arm_affiliation: armAffiliation,
|
||||
author_id: authorId,
|
||||
tags: ["discord", "main-chat"],
|
||||
category: "discord",
|
||||
is_published: true,
|
||||
likes_count: 0,
|
||||
comments_count: 0,
|
||||
})
|
||||
.select("id");
|
||||
|
||||
if (insertError) {
|
||||
console.error("[Main Chat] Post creation failed:", insertError);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[Main Chat] ✅ Synced message from ${message.author.tag} to AeThex feed`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("[Main Chat] Error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle announcements from designated channels
|
||||
async function handleAnnouncementSync(message) {
|
||||
try {
|
||||
|
|
@ -169,6 +315,14 @@ module.exports = {
|
|||
return handleAnnouncementSync(message);
|
||||
}
|
||||
|
||||
// Check if this is a main chat channel - sync ALL messages
|
||||
if (
|
||||
MAIN_CHAT_CHANNELS.length > 0 &&
|
||||
MAIN_CHAT_CHANNELS.includes(message.channelId)
|
||||
) {
|
||||
return handleMainChatSync(message);
|
||||
}
|
||||
|
||||
// Check if this is in the feed channel (for user-generated posts)
|
||||
if (FEED_CHANNEL_ID && message.channelId !== FEED_CHANNEL_ID) {
|
||||
return;
|
||||
|
|
@ -183,7 +337,7 @@ module.exports = {
|
|||
const { data: linkedAccount, error } = await supabase
|
||||
.from("discord_links")
|
||||
.select("user_id")
|
||||
.eq("discord_user_id", message.author.id)
|
||||
.eq("discord_id", message.author.id)
|
||||
.single();
|
||||
|
||||
if (error || !linkedAccount) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue