
本文详解如何使用 discord.js v18 正确获取某用户在指定 guild(服务器)中的加入时间,通过 guildmember.joinedat 和 joinedtimestamp 属性实现,并提供可直接集成的代码示例与关键注意事项。
本文详解如何使用 discord.js v18 正确获取某用户在指定 guild(服务器)中的加入时间,通过 guildmember.joinedat 和 joinedtimestamp 属性实现,并提供可直接集成的代码示例与关键注意事项。
在 Discord.js v18 中,用户账户创建时间(user.createdAt)与用户加入某个服务器的时间(即“入服时间”)是两个完全独立的字段。前者可通过 User 实例直接访问,而后者必须通过 GuildMember 实例获取——因为加入时间属于服务器上下文(guild-scoped)数据,User 对象本身不包含该信息。
要获取目标用户在当前交互所在服务器中的加入时间,需执行以下关键步骤:
- 确保已启用必要 Gateway Intent:GuildMembers intent 是读取 joinedAt 的前提(注意:即使使用 .fetch(),若未启用该 intent,部分成员可能无法被获取,尤其对非在线/不可见成员);
- 从交互中获取 Guild 实例:interaction.guild 提供当前命令触发的服务器对象;
- 获取 GuildMember 实例:调用 guild.members.fetch(userId) 获取成员数据;
-
提取时间字段:
- guildMember.joinedAt → 返回 JavaScript Date 对象,适合格式化显示;
- guildMember.joinedTimestamp → 返回毫秒级数值时间戳,便于计算或存档。
以下是整合进你原有命令的优化后完整代码段(已修复登录逻辑、intent 缺失、错误颜色处理及健壮性问题):
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
const { createFooter, userAvatar, creationDateTimestamp } = require('../../utility');
const { token } = require('../../config.json');
module.exports = {
data: new SlashCommandBuilder()
.setName('user')
.setDescription('Provides information about the user.')
.addUserOption(option =>
option
.setName('user')
.setDescription('Select a user to view')),
async execute(interaction) {
// ✅ 确保 interaction 在 guild 上下文中(非 DM)
if (!interaction.guild) {
return await interaction.reply({ content: 'This command can only be used in a server.', ephemeral: true });
}
const targetUser = interaction.options.getUser('user') ?? interaction.user;
try {
// ✅ 获取 GuildMember(需 GatewayIntentBits.GuildMembers)
const guildMember = await interaction.guild.members.fetch(targetUser.id);
// ✅ 获取加入时间(Date 对象)
const joinDate = guildMember.joinedAt;
const joinTimestamp = guildMember.joinedTimestamp;
// ✅ 构建嵌入消息
const userEmbed = new EmbedBuilder()
.setColor(guildMember.displayColor || 0x5865F2)
.setTitle(guildMember.displayName)
.setDescription(`@${targetUser.username}`)
.setThumbnail(userAvatar(targetUser))
.addFields(
{ name: 'Account created', value: `${creationDateTimestamp(targetUser)}`, inline: true },
{ name: 'Joined this server', value: joinDate ? `<t:${Math.floor(joinTimestamp / 1000)}:F>` : 'Unknown', inline: true },
{ name: 'Join timestamp (ms)', value: `\`${joinTimestamp}\``, inline: false }
)
.setTimestamp()
.setFooter(createFooter(interaction.user));
await interaction.reply({ embeds: [userEmbed] });
} catch (error) {
console.error('Failed to fetch guild member:', error);
await interaction.reply({
content: `❌ Could not retrieve join date for ${targetUser.tag}. The user may have left the server or permissions are insufficient.`,
ephemeral: true
});
}
}
};⚠️ 重要注意事项:
-
Intent 配置:务必在 Client 初始化时添加 GatewayIntentBits.GuildMembers,否则 members.fetch() 可能失败或返回不完整数据:
const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers // ← 必须显式启用 ] }); - 权限与可见性:Bot 需拥有 View Audit Log 权限(非必需但推荐),且目标用户不能是已退出/被踢出服务器的成员(此时 fetch() 将抛出 DiscordAPIError[50034]);
- 时间格式化建议:Discord 支持原生时间戳格式化(如 <t:1717023456:F>),比手动 toLocaleString() 更一致、本地化友好;
- 避免重复登录:你的原始代码中 client.login(token) 在每次命令执行时调用——这是严重错误,应仅在应用启动时调用一次(通常在 index.js 中)。
掌握 GuildMember.joinedAt 的正确用法,不仅能完善用户信息卡片,也是实现欢迎系统、会员等级、活跃度统计等高级功能的基础。始终优先通过 GuildMember 而非 User 访问服务器维度属性,这是 Discord.js v14+ 架构设计的核心原则之一。










