|
import { |
|
Client, |
|
GatewayDispatchEvents, |
|
GatewayIntentBits, |
|
} from "@discordjs/core"; |
|
import { REST } from "@discordjs/rest"; |
|
import { WebSocketManager } from "@discordjs/ws"; |
|
import { DiscordSnowflake } from "@sapphire/snowflake"; |
|
import z from "zod"; |
|
import { readFileSync } from "fs"; |
|
|
|
const configSchema = z.object({ |
|
token: z.string(), |
|
roles: z.record(z.string(), z.number()), |
|
reason: z.string(), |
|
}); |
|
|
|
let config; |
|
try { |
|
const rawConfig = readFileSync("./config.json", "utf8"); |
|
config = configSchema.parse(JSON.parse(rawConfig)); |
|
} catch (error) { |
|
console.log("Error loading config:", error); |
|
process.exit(1); |
|
} |
|
|
|
const rest = new REST({ version: "10" }).setToken(config.token); |
|
const gateway = new WebSocketManager({ |
|
token: config.token, |
|
intents: GatewayIntentBits.Guilds | GatewayIntentBits.GuildMembers, |
|
rest, |
|
}); |
|
const client = new Client({ rest, gateway }); |
|
|
|
client.on( |
|
GatewayDispatchEvents.GuildMemberAdd, |
|
async ({ data: member, api }) => { |
|
if (member.user.bot) return; |
|
|
|
const createdTimestamp = DiscordSnowflake.timestampFrom(member.user.id); |
|
const daysSinceCreation = Math.floor( |
|
(Date.now() - createdTimestamp) / (1000 * 60 * 60 * 24) |
|
); |
|
const roles = []; |
|
|
|
for (const [role, days] of Object.entries(config.roles)) { |
|
if (daysSinceCreation >= days) { |
|
roles.push(role); |
|
} |
|
} |
|
|
|
if (roles.length === 0) return; |
|
|
|
// Use the idempotent PUT route to avoid overriding roles |
|
for (const role of roles) { |
|
await api.guilds.addRoleToMember(member.guild_id, member.user.id, role, { |
|
reason: config.reason, |
|
}); |
|
} |
|
} |
|
); |
|
|
|
client.once(GatewayDispatchEvents.Ready, () => console.log("Ready!")); |
|
|
|
gateway.connect(); |