Files
ShaiBot/commands/miscellaneous/poke.js
T
2026-08-15 00:38:21 +02:00

365 lines
6.4 KiB
JavaScript

const {
SlashCommandBuilder,
MessageFlags
} = require("discord.js");
/*
* CONFIG
*/
const POKE_MAX_TIMES = 5;
const POKE_DELAY_MS = 750;
const POKE_COOLDOWN_MS = 30_000;
/*
* In-memory cooldowns.
*
* Key:
* Discord user ID of the person sending the poke.
*
* Value:
* Timestamp when they may poke again.
*/
const pokeCooldowns = new Map();
module.exports = {
data: new SlashCommandBuilder()
.setName("fun")
.setDescription("Fun commands.")
.addSubcommand(subcommand =>
subcommand
.setName("poke")
.setDescription(
"Poke another server member in DMs."
)
.addUserOption(option =>
option
.setName("member")
.setDescription(
"The member you want to poke."
)
.setRequired(true)
)
.addIntegerOption(option =>
option
.setName("times")
.setDescription(
`How many times to poke them. Max ${POKE_MAX_TIMES}.`
)
.setMinValue(1)
.setMaxValue(
POKE_MAX_TIMES
)
.setRequired(false)
)
),
async execute(interaction) {
switch (
interaction.options.getSubcommand()
) {
case "poke":
return poke(
interaction
);
}
}
};
/*
* POKE
*/
async function poke(interaction) {
/*
* Guild only.
*/
if (!interaction.guild) {
return interaction.reply({
content:
"This command can only be used inside a server.",
flags:
MessageFlags.Ephemeral
});
}
/*
* Options
*/
const user =
interaction.options.getUser(
"member",
true
);
const times =
interaction.options.getInteger(
"times"
)
?? 1;
/*
* Don't poke yourself.
*/
if (
user.id ===
interaction.user.id
) {
return interaction.reply({
content:
"You can't poke yourself! 👉",
flags:
MessageFlags.Ephemeral
});
}
/*
* Don't poke bots.
*/
if (user.bot) {
return interaction.reply({
content:
"You can't poke a bot!",
flags:
MessageFlags.Ephemeral
});
}
/*
* Make sure the target is actually
* a member of this guild.
*/
let member;
try {
member =
await interaction.guild
.members
.fetch(
user.id
);
} catch {
return interaction.reply({
content:
"I couldn't find that member in this server.",
flags:
MessageFlags.Ephemeral
});
}
/*
* COOLDOWN
*/
const now =
Date.now();
const cooldownUntil =
pokeCooldowns.get(
interaction.user.id
);
if (
cooldownUntil &&
cooldownUntil > now
) {
const remaining =
Math.ceil(
(
cooldownUntil -
now
)
/
1000
);
return interaction.reply({
content:
`You need to wait **${remaining}s** before poking someone again.`,
flags:
MessageFlags.Ephemeral
});
}
/*
* Defer before sending DMs.
*/
await interaction.deferReply({
flags:
MessageFlags.Ephemeral
});
/*
* Set cooldown before sending.
*
* This prevents repeated invocations
* while the DM loop is running.
*/
pokeCooldowns.set(
interaction.user.id,
now + POKE_COOLDOWN_MS
);
/*
* Sender name.
*
* Prefer their server display name.
*/
let senderName =
interaction.user.displayName;
try {
const senderMember =
await interaction.guild
.members
.fetch(
interaction.user.id
);
senderName =
senderMember.displayName;
} catch {
/*
* interaction.user.displayName
* remains the fallback.
*/
}
/*
* SEND POKES
*/
let sent =
0;
try {
for (
let i = 0;
i < times;
i++
) {
await member.send({
content:
`👉 **${senderName} poked you!**\n` +
`From **${interaction.guild.name}**`
});
sent++;
/*
* Don't sleep after the final DM.
*/
if (
i <
times - 1
) {
await sleep(
POKE_DELAY_MS
);
}
}
} catch (error) {
console.error(
`Failed to poke ${user.tag}:`,
error
);
/*
* If no messages made it through,
* this is most likely closed DMs.
*/
if (sent === 0) {
return interaction.editReply({
content:
`I couldn't poke ${user}. They may have DMs disabled.`
});
}
/*
* Some messages succeeded before
* Discord rejected a later one.
*/
return interaction.editReply({
content:
`I poked ${user} **${sent}/${times}** times before I couldn't send any more.`
});
}
/*
* SUCCESS
*/
return interaction.editReply({
content:
times === 1
? `👉 Poked ${user}!`
: `👉 Poked ${user} **${times} times!**`
});
}
/*
* SLEEP
*/
function sleep(ms) {
return new Promise(
resolve =>
setTimeout(
resolve,
ms
)
);
}