Reply with a message
The smallest useful script.
hearth.sendMessage('Hello!');Write a few lines of JavaScript and have Hearth run them when someone says the right thing.
Hearth Script is JavaScript. If you have written JavaScript before, you already know it — variables, if, loops, functions, strings, arrays, Math and JSON all behave as you expect.
A script runs when a custom command’s trigger matches a message. It cannot run on its own, it cannot run forever, and it can only affect your server through the hearth object described below.
Scripting is a Pro feature.
Scripts run inside a sandbox. This is not a list of things that are discouraged — none of them are possible, and each is covered by a test that tries it:
eval and no building code from strings at runtime.@everyone or @here. A script runs on messages, so one that could do this would turn a busy day into an outage.If a script breaks one of these rules, none of its actions happen — not the ones before the mistake either. A half-applied script is worse than one that plainly failed.
Discord IDs are always strings in Hearth Script, and must stay that way. They are larger than JavaScript can hold as an exact number, so treating one as a number rounds it — and a rounded ID is a different, real member.
To find an ID, turn on Developer Mode in Discord, then right-click a user, role or channel and choose “Copy ID”.
userNameuserIdguildNameguildIdmessagememberserverchannelmsghearth.sendMessage(content, channelId?)hearth.sendTemplate(name, channelId?)hearth.addRole(userId, roleId)hearth.removeRole(userId, roleId)hearth.adjustCurrency(userId, amount)hearth.log(content)hearth.set(key, value)hearth.setForUser(userId, key, value)Every script shares Hearth with every other server, so each run has a budget.
Start here. A script is JavaScript, and these are the shapes almost everything uses.
The smallest useful script.
hearth.sendMessage('Hello!');Variables are ordinary JavaScript values, so string concatenation works.
hearth.sendMessage('Hello ' + userName + '!');A real Discord mention is <@ then the ID then >. Hearth will not let it ping the whole server, but it does highlight for that member.
hearth.sendMessage('Welcome <@' + userId + '>!');Useful when the same script is shared between servers.
hearth.sendMessage('You are in ' + guildName + '.');The full triggering message is available, so one command can branch on what was said.
One command, several answers.
if (message.includes('rules')) {
hearth.sendMessage('Please read #rules.');
} else if (message.includes('mod')) {
hearth.sendMessage('A moderator will be with you shortly.');
} else {
hearth.sendMessage('Ask away and someone will help.');
}With a StartsWith trigger of "!echo", everything after it is the argument.
const argument = message.slice('!echo'.length).trim();
if (argument.length === 0) {
hearth.sendMessage('Give me something to echo.');
} else {
hearth.sendMessage(argument);
}Any JavaScript comparison works.
if (userName.toLowerCase().startsWith('a')) {
hearth.sendMessage('An A name! Nice.');
}A script that sends nothing is fine. Nothing is posted and nobody is told, which is what you want for a rare-response command.
if (Math.random() < 0.1) {
hearth.sendMessage('Rare response!');
}Read-only details about the member, the channel and the server. None of it can be changed from a script — it is there so a script can decide.
Match on the role name, or on its ID if names change.
if (member.roleNames.includes('Moderator')) {
hearth.sendMessage('Yes, boss.');
} else {
hearth.sendMessage('That command is for moderators.');
}A common anti-spam check. Dates are ISO strings, so JavaScript’s Date does the work.
const ageDays = (Date.now() - new Date(member.createdAt)) / 86400000;
if (ageDays < 7) {
hearth.sendMessage('This command needs an account older than a week.');
} else {
hearth.sendMessage('Welcome aboard!');
}Nickname is null when unset, so fall back to the username.
hearth.sendMessage('Hi ' + (member.nickname || member.name) + '!');How long they have been in this server, rather than on Discord.
const days = (Date.now() - new Date(member.joinedAt)) / 86400000;
if (days > 365) {
hearth.adjustCurrency(userId, 100);
hearth.sendMessage('A year here! Have 100 coins.');
}Member count and channel name are both available.
hearth.sendMessage(server.name + ' has ' + server.memberCount + ' members. You are in #' + channel.name + '.');Math and the rest of JavaScript’s standard library are available in full.
Math.random and Math.floor, exactly as anywhere else.
const roll = Math.floor(Math.random() * 6) + 1;
hearth.sendMessage(userName + ' rolled a ' + roll + '.');An array plus a random index is the whole trick.
const answers = ['Yes', 'No', 'Ask again later', 'Definitely'];
const pick = answers[Math.floor(Math.random() * answers.length)];
hearth.sendMessage(pick);A conditional expression keeps it to one line.
hearth.sendMessage(Math.random() < 0.5 ? 'Heads' : 'Tails');Parse the count and sides out of the message, then sum the rolls.
const match = /(\d+)d(\d+)/.exec(message);
const count = match ? Math.min(Number(match[1]), 20) : 1;
const sides = match ? Math.min(Number(match[2]), 100) : 6;
let total = 0;
for (let i = 0; i < count; i++) {
total += Math.floor(Math.random() * sides) + 1;
}
hearth.sendMessage(userName + ' rolled ' + total + '.');Hearth can only grant roles below its own. Move Hearth’s role up in Server Settings if a grant is refused.
IDs are always strings. A Discord ID is too large for JavaScript to hold exactly, and an unquoted one silently becomes a different, real member.
hearth.addRole(userId, '112233445566778899');
hearth.sendMessage(userName + ' has been given the role.');The mirror of addRole.
hearth.removeRole(userId, '112233445566778899');Remove the others first so only one is ever held.
const colours = {
red: '111111111111111111',
blue: '222222222222222222',
green: '333333333333333333',
};
const wanted = message.split(' ')[1];
if (colours[wanted]) {
for (const id of Object.values(colours)) {
hearth.removeRole(userId, id);
}
hearth.addRole(userId, colours[wanted]);
hearth.sendMessage(userName + ' is now ' + wanted + '.');
} else {
hearth.sendMessage('Pick one of: red, blue, green.');
}Balances never go below zero, so a negative adjustment cannot overdraw an account.
A positive amount adds.
hearth.adjustCurrency(userId, 10);
hearth.sendMessage(userName + ' earned 10 coins.');A negative amount removes, clamped at zero.
hearth.adjustCurrency(userId, -5);
hearth.sendMessage('That cost you 5 coins.');Combine randomness with currency for a daily-style command.
const payout = Math.floor(Math.random() * 50) + 10;
hearth.adjustCurrency(userId, payout);
hearth.sendMessage(userName + ' found ' + payout + ' coins.');Storage is small and simple. A script writes; the dashboard shows what is stored. There is no read from within a script yet.
One value against the whole server.
hearth.set('lastAsked', userName);The same, keyed to one person.
hearth.setForUser(userId, 'favouriteColour', 'blue');Trim it, because stored values are bounded.
const note = message.slice('!note'.length).trim().slice(0, 200);
hearth.setForUser(userId, 'note', note);
hearth.sendMessage('Noted.');Writes to the log channel configured for your server.
Goes to your log channel, not to the channel the command was used in.
hearth.log(userName + ' used the report command.');Reply to the member while telling moderators separately.
hearth.sendMessage('Thanks, a moderator will look into it.');
hearth.log('Report from ' + userName + ': ' + message.slice(0, 200));Discord markdown works, and a message can be sent somewhere other than here.
Pass a channel ID as the last argument.
hearth.sendMessage('A report was filed.', '998877665544332211');Keeps formatting out of the script.
hearth.sendTemplate('welcome-pack');Discord markdown is just text — bold, lists and code all work.
const lines = [
'**Server rules**',
'1. Be kind',
'2. Stay on topic',
'3. No spam',
];
hearth.sendMessage(lines.join('\n'));Once enough servers are writing scripts, the best of them will be collected here. Nothing is listed yet — and rather than fill this with invented entries, it stays empty until there is something real to show.