@@ -7,23 +7,36 @@ import {
77 ButtonBuilder ,
88 ButtonStyle ,
99 ComponentType ,
10+ AttachmentBuilder ,
1011} from "discord.js" ;
1112import { FamilyManager } from "../utils/familyManager.js" ;
13+ import { FamilyTreeRenderer } from "../utils/familyTreeRenderer.js" ;
1214
1315export const data = new SlashCommandBuilder ( )
1416 . setName ( "family" )
1517 . setDescription ( "Manage your family relationships" )
1618 . addSubcommand ( ( subcommand ) =>
1719 subcommand
1820 . setName ( "tree" )
19- . setDescription ( "View your family tree or another user's " )
21+ . setDescription ( "View a visual family tree" )
2022 . addUserOption ( ( option ) =>
2123 option
2224 . setName ( "user" )
2325 . setDescription ( "The user whose family tree to view" )
2426 . setRequired ( false ) ,
2527 ) ,
2628 )
29+ . addSubcommand ( ( subcommand ) =>
30+ subcommand
31+ . setName ( "profile" )
32+ . setDescription ( "View family relationships as a text list" )
33+ . addUserOption ( ( option ) =>
34+ option
35+ . setName ( "user" )
36+ . setDescription ( "The user whose family profile to view" )
37+ . setRequired ( false ) ,
38+ ) ,
39+ )
2740 . addSubcommand ( ( subcommand ) =>
2841 subcommand
2942 . setName ( "marry" )
@@ -110,23 +123,87 @@ export async function execute(
110123 return ;
111124 }
112125
113- const spouse = await FamilyManager . getSpouse ( targetUser . id ) ;
126+ await interaction . deferReply ( ) ;
127+
128+ const spouseIds = await FamilyManager . getSpouses ( targetUser . id ) ;
129+ const spouses = await Promise . all (
130+ spouseIds . map ( ( id ) => interaction . client . users . fetch ( id ) . catch ( ( ) => null ) ) ,
131+ ) . then ( users => users . filter ( ( u ) : u is NonNullable < typeof u > => u !== null ) ) ;
132+
133+ const childrenIds = await FamilyManager . getChildren ( targetUser . id ) ;
134+ const children = await Promise . all (
135+ childrenIds . map ( ( id ) => interaction . client . users . fetch ( id ) . catch ( ( ) => null ) ) ,
136+ ) . then ( users => users . filter ( ( u ) : u is NonNullable < typeof u > => u !== null ) ) ;
137+
138+ const parentIds = await FamilyManager . getParents ( targetUser . id ) ;
139+ const parents = await Promise . all (
140+ parentIds . map ( ( id ) => interaction . client . users . fetch ( id ) . catch ( ( ) => null ) ) ,
141+ ) . then ( users => users . filter ( ( u ) : u is NonNullable < typeof u > => u !== null ) ) ;
142+
143+ const siblingIds = await FamilyManager . getSiblings ( targetUser . id ) ;
144+ const siblings = await Promise . all (
145+ siblingIds . map ( ( id ) => interaction . client . users . fetch ( id ) . catch ( ( ) => null ) ) ,
146+ ) . then ( users => users . filter ( ( u ) : u is NonNullable < typeof u > => u !== null ) ) ;
147+
148+ const treeBuffer = await FamilyTreeRenderer . generateTree ( targetUser , {
149+ spouses,
150+ parents,
151+ children,
152+ siblings,
153+ } ) ;
154+
155+ const attachment = new AttachmentBuilder ( treeBuffer , {
156+ name : "family-tree.png" ,
157+ } ) ;
158+
159+ const embed = new EmbedBuilder ( )
160+ . setColor ( "#cdcdcd" )
161+ . setTitle ( "🌳 FAMILY TREE" )
162+ . setDescription ( `**${ targetUser . tag } **'s big old family tree!` )
163+ . setImage ( "attachment://family-tree.png" )
164+ . setTimestamp ( ) ;
165+
166+ await interaction . editReply ( {
167+ embeds : [ embed ] ,
168+ files : [ attachment ] ,
169+ } ) ;
170+ break ;
171+ }
172+
173+ case "profile" : {
174+ const targetUser = interaction . options . getUser ( "user" ) || interaction . user ;
175+ const relationships = await FamilyManager . getUserRelationships ( targetUser . id ) ;
176+
177+ if ( relationships . length === 0 ) {
178+ await interaction . reply ( {
179+ content : `**${ targetUser . tag } has no family relationships yet!**` ,
180+ flags : MessageFlags . Ephemeral ,
181+ } ) ;
182+ return ;
183+ }
184+
185+ const spouses = await FamilyManager . getSpouses ( targetUser . id ) ;
114186 const children = await FamilyManager . getChildren ( targetUser . id ) ;
115187 const parents = await FamilyManager . getParents ( targetUser . id ) ;
116188 const siblings = await FamilyManager . getSiblings ( targetUser . id ) ;
117189
118190 const embed = new EmbedBuilder ( )
119191 . setColor ( "#FFD700" )
120- . setTitle ( "🌳 FAMILY TREE " )
192+ . setTitle ( "👤 FAMILY PROFILE " )
121193 . setDescription ( `Family relationships for **${ targetUser . tag } **` )
122194 . setThumbnail ( targetUser . displayAvatarURL ( ) )
123195 . setTimestamp ( ) ;
124196
125- if ( spouse ) {
126- const spouseUser = await interaction . client . users . fetch ( spouse ) ;
197+ if ( spouses . length > 0 ) {
198+ const spouseTags = await Promise . all (
199+ spouses . map ( async ( id ) => {
200+ const user = await interaction . client . users . fetch ( id ) ;
201+ return user . tag ;
202+ } ) ,
203+ ) ;
127204 embed . addFields ( {
128- name : "💍 Spouse" ,
129- value : spouseUser . tag ,
205+ name : spouses . length === 1 ? "💍 Spouse" : "💍 Spouses ",
206+ value : spouseTags . join ( "\n" ) ,
130207 inline : true ,
131208 } ) ;
132209 }
@@ -188,25 +265,30 @@ export async function execute(
188265 return ;
189266 }
190267
191- // existing relationships check
192268 const hasExistingRelationship = await FamilyManager . hasRelationship (
193269 interaction . user . id ,
194270 targetUser . id ,
195271 ) ;
196- const existingSpouse = await FamilyManager . getSpouse ( interaction . user . id ) ;
197- const targetSpouse = await FamilyManager . getSpouse ( targetUser . id ) ;
272+ const existingSpouses = await FamilyManager . getSpouses ( interaction . user . id ) ;
273+ const targetSpouses = await FamilyManager . getSpouses ( targetUser . id ) ;
198274
199- if ( hasExistingRelationship || existingSpouse || targetSpouse ) {
275+ if ( hasExistingRelationship || existingSpouses . length > 0 || targetSpouses . length > 0 ) {
200276 const warnings : string [ ] = [ ] ;
201-
202- if ( existingSpouse ) {
203- const spouseUser = await interaction . client . users . fetch ( existingSpouse ) ;
204- warnings . push ( `⚠️ **You are already married to ${ spouseUser . tag } !**` ) ;
277+
278+ if ( existingSpouses . length > 0 ) {
279+ const spouseUsers = await Promise . all (
280+ existingSpouses . map ( id => interaction . client . users . fetch ( id ) )
281+ ) ;
282+ const spouseNames = spouseUsers . map ( u => u . tag ) . join ( ", " ) ;
283+ warnings . push ( `⚠️ **You are already married to ${ spouseNames } !**` ) ;
205284 }
206-
207- if ( targetSpouse ) {
208- const targetSpouseUser = await interaction . client . users . fetch ( targetSpouse ) ;
209- warnings . push ( `⚠️ **${ targetUser . tag } is already married to ${ targetSpouseUser . tag } !**` ) ;
285+
286+ if ( targetSpouses . length > 0 ) {
287+ const targetSpouseUsers = await Promise . all (
288+ targetSpouses . map ( id => interaction . client . users . fetch ( id ) )
289+ ) ;
290+ const targetSpouseNames = targetSpouseUsers . map ( u => u . tag ) . join ( ", " ) ;
291+ warnings . push ( `⚠️ **${ targetUser . tag } is already married to ${ targetSpouseNames } !**` ) ;
210292 }
211293
212294 if ( hasExistingRelationship ) {
@@ -461,8 +543,8 @@ export async function execute(
461543 case "divorce" : {
462544 const targetUser = interaction . options . getUser ( "user" , true ) ;
463545
464- const spouse = await FamilyManager . getSpouse ( interaction . user . id ) ;
465- if ( ! spouse || spouse !== targetUser . id ) {
546+ const spouses = await FamilyManager . getSpouses ( interaction . user . id ) ;
547+ if ( ! spouses . includes ( targetUser . id ) ) {
466548 await interaction . reply ( {
467549 content : `**${ targetUser . tag } IS NOT YOUR SPOUSE!**` ,
468550 flags : MessageFlags . Ephemeral ,
0 commit comments