Skip to content

Commit ce4eb2d

Browse files
committed
feat: family tree, profile, and spouses fix
1 parent 692f3f7 commit ce4eb2d

File tree

2 files changed

+120
-35
lines changed

2 files changed

+120
-35
lines changed

src/commands/family.ts

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,36 @@ import {
77
ButtonBuilder,
88
ButtonStyle,
99
ComponentType,
10+
AttachmentBuilder,
1011
} from "discord.js";
1112
import { FamilyManager } from "../utils/familyManager.js";
13+
import { FamilyTreeRenderer } from "../utils/familyTreeRenderer.js";
1214

1315
export 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,

src/utils/familyManager.ts

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,21 @@ export class FamilyManager {
7676
);
7777
}
7878

79-
public static async getSpouse(userId: string): Promise<string | null> {
79+
public static async getSpouses(userId: string): Promise<string[]> {
8080
const data = await this.loadData();
81-
const marriage = data.relationships.find(
82-
(rel) =>
83-
rel.relationshipType === "spouse" &&
84-
(rel.userId === userId || rel.relatedUserId === userId),
85-
);
81+
const spouses = new Set<string>();
82+
83+
for (const rel of data.relationships) {
84+
if (rel.relationshipType === "spouse") {
85+
if (rel.userId === userId) {
86+
spouses.add(rel.relatedUserId);
87+
} else if (rel.relatedUserId === userId) {
88+
spouses.add(rel.userId);
89+
}
90+
}
91+
}
8692

87-
if (!marriage) return null;
88-
return marriage.userId === userId
89-
? marriage.relatedUserId
90-
: marriage.userId;
93+
return Array.from(spouses);
9194
}
9295

9396
public static async getChildren(userId: string): Promise<string[]> {
@@ -110,19 +113,19 @@ export class FamilyManager {
110113

111114
public static async getSiblings(userId: string): Promise<string[]> {
112115
const data = await this.loadData();
113-
const siblings: string[] = [];
116+
const siblings = new Set<string>();
114117

115118
for (const rel of data.relationships) {
116119
if (rel.relationshipType === "sibling") {
117120
if (rel.userId === userId) {
118-
siblings.push(rel.relatedUserId);
121+
siblings.add(rel.relatedUserId);
119122
} else if (rel.relatedUserId === userId) {
120-
siblings.push(rel.userId);
123+
siblings.add(rel.userId);
121124
}
122125
}
123126
}
124127

125-
return siblings;
128+
return Array.from(siblings);
126129
}
127130

128131
public static async addRelationship(

0 commit comments

Comments
 (0)