@cherryblossom/discord-dynamic-messages v1.0.0
@cherryblossom/discord-dynamic-messages
The same as the original but using Discord.js v12.
npm i @cherryblossom/discord-dynamic-messagesPurpose
This library helps with creating messages that dynamically change their contents based on how people react on it. In other words, the message content acts as a screen, and the reactions act as input buttons.
import {Client} from 'discord.js'
import {DynamicMessage, OnReaction} from 'discord-dynamic-messages'
import type {Message} from 'discord.js'
export class CounterMessage extends DynamicMessage {
private counter
constructor(args: {initialCounterValue: number}) {
super()
this.counter = args.initialCounterValue
}
@OnReaction(':thumbsup:')
public increment(user, channel, reaction) {
this.counter += 1
}
@OnReaction(':thumbsdown:')
public decrement(user, channel, reaction) {
this.counter -= 1
}
public render() {
return `Counter: ${this.counter}`
}
}
const client = new Client()
client.on('message', ({channel}: Message) => new CounterMessage({initialCounterValue: 0}).sendTo(channel))
client.login(discordToken)
Install
This library depends on typescript decorators, and will therefore not work properly unless used in a correctly configured typescript project.
- Install library:
npm i @cherryblossom/discord-dynamic-messages. - Enable
experimentalDecoratorsandemitDecoratorMetadataintsconfig.json.
If you are using VSCode you might need to set javascript.implicitProjectConfig.experimentalDecorators to true in the workspace settings.
Documentation
DynamicMessage
abstract class DynamicMessage {
constructor(config: DynamicMessageConfig = {volatile: true})
}The base class of the library. Every dynamic message must extend this class.
config: Optional configuration for the message.
interface DynamicMessageConfigTame {
volatile: false
onError: (error: Error) => void
}
interface DynamicMessageConfigVolatile {
volatile: true
}
type DynamicMessageConfig = DynamicMessageConfigTame | DynamicMessageConfigVolatilevolatile: If true, errors will be thrown. If false, errors will be passed to onError.
- Default:
true
onError: An error handler for the errors if volatile is false.
message
message: Discord.Message | nullThe Discord message that this is attached to.
render
abstract render(): string | Discord.MessageEmbedDetermines the contents of the message.
Returns: The content of the message.
reRender
reRender(): voidUsed to manually trigger a re-render of the message content.
class Foo extends DynamicMessage {
public doStuff() {
// do some stuff
this.reRender()
}
public render() {
return 'stuff'
}
}addReactions
addReactions(emojiNames: string[]): voidManually adds reactions to a message.
emojiNames: An array of the emojis to react with.
Returns: The last reaction or void if there were no reactions.
class Foo extends DynamicMessage {
public addOneTwoThree() {
this.addReactions([':one:', ':two:', ':three:'])
}
public render() {
return 'stuff'
}
}sendTo
sendTo(channel: Discord.TextChannel | Discord.DMChannel): Promise<this>Sends the dynamic message to the given channel.
channel: The channel to send the message to.
Returns: This dynamic message.
class Foo extends DynamicMessage {
public render() {
return 'stuff'
}
}
client.on('message', (message: Discord.Message) => new Foo().sendTo(message.channel))replyTo
replyTo(message: Discord.Message): Promise<this>Sends the dynamic message as a reply to the given message.
message: The message to reply to.
Returns: This dynamic message.
class Foo extends DynamicMessage {
public render() {
return 'foo'
}
}
client.on('message', (message: Discord.Message) => new Foo().replyTo(message))attachTo
attachTo(message: Discord.Message, user?: Discord.User): thisAttaches an existing message to this instance, then call render on the instance and overwrite the content of the existing message.
message: The message to be attached.user: The user to reply to.
Returns: This dynamic message.
class Foo extends DynamicMessage {
render() {
return 'foo'
}
}
client.on('message', async (message: Discord.Message) => {
const reply = await message.reply('tmp')
// Attach in the same way as DynamicMessage#sendTo
new Foo().attachTo(reply)
// Attach in the same way as DynamicMessage#replyTo
new Foo().attachTo(reply, message.author)
})BaseReactionConfig
interface BaseReactionConfig {
triggerRender?: boolean
ignoreBots?: boolean
ignoreHumans?: boolean
}The configuration used for @OnReactionRemoved, @OnAnyReaction, and @OnAnyReactionRemoved.
| Property | Description | Default |
|---|---|---|
triggerRender | Whether the bot will call the render method of the dynamic message after the reaction callback have executed. | true |
ignoreBots | Whether reactions from bots should be ignored and not trigger the callback. | true |
ignoreHumans | Whether reactions from humans (non-bots) should be ignored and not trigger the callback. | false |
@OnInit
type OnInit = Decorator<InitHandler>A decorator that tells the dynamic message what functions to call when a Discord message is attached to the dynamic message. Note that if the dynamic message is reused (when attached to another message after the first one) the init function will fire again.
class Foo extends DynamicMessage {
@OnInit
public initialize() {
console.log(this.message?.content)
// => stuff
}
public render() {
return 'foo'
}
}@OnReaction
type OnReaction = (emoji: string, config?: OnReactionConfig) => Decorator<ReactionHandler>A decorator that tells the dynamic message what functions to call in response to what emoji when a reaction is made on the message.
emoji: The emoji to listen for.config: Optional configuration.
interface OnReactionConfig extends BaseReactionConfig {
hidden?: boolean
removeWhenDone?: boolean
doRetroactiveCallback?: boolean
}| Property | Description | Default |
|---|---|---|
hidden | Whether the bot shouldn't react with the given emoji to show the users what emoji the message is prepared to react to. | false |
removeWhenDone | Whether the bot will remove user reactions after the callback have executed. | true |
doRetroactiveCallback | Whether reactions made while the bot was offline or not setup trigger the callback. | true |
class Foo extends DynamicMessage {
@OnReaction(':thumbsup:')
public react(
user: Discord.User,
channel: Discord.TextChannel | Discord.DMChannel,
reaction: Discord.MessageReaction
) {
console.log('+1')
}
public render() {
return 'foo'
}
}@OnReactionRemoved
type OnReactionRemoved = (emoji: string, config?: BaseReactionConfig) => Decorator<ReactionHandler>A decorator that tells the dynamic message what functions to call in response to what emoji when a reaction removed from the message.
emoji: The emoji to listen for.config: Optional configuration.
class Foo extends DynamicMessage {
private toggle: boolean = false
@OnReaction(':thumbsup:', {removeWhenDone: false})
public on() {
this.toggle = true
}
@OnReactionRemoved(':thumbsup:')
public off() {
this.toggle = false
}
public render() {
return '```diff\n' + (this.toggle ? '+ on' : '- off') + '\n```'
}
}@OnAnyReaction
type OnAnyReaction = (config?: BaseReactionConfig) => Decorator<ReactionHandler>A decorator that tells the dynamic message what functions to call when any reaction is made on the message.
config: Optional configuration.
class Foo extends DynamicMessage {
private accumulator: string = ''
@OnAnyReaction()
public accumulate(
user: Discord.User,
channel: Discord.TextChannel | Discord.DMChannel,
reaction: Discord.MessageReaction
) {
this.accumulator += reaction.emoji.name
}
public render() {
return `Accumulator: ${this.addAccumulator}`
}
}@OnAnyReactionRemoved
type OnAnyReactionRemoved = (config?: BaseReactionConfig) => Decorator<ReactionHandler>A decorator that tells the dynamic message what functions to call when any reaction is removed from the message.
config: Optional configuration.
export class Foo extends DynamicMessage {
private accumulator: string = ''
@OnAnyReactionRemoved()
public accumulate(
user: Discord.User,
channel: Discord.TextChannel | Discord.DMChannel,
reaction: Discord.MessageReaction
) {
this.accumulator += reaction.emoji.name
}
public render() {
return `Accumulator: ${this.addAccumulator}`
}
}InitHandler
type InitHandler = () => voidA handler for @OnInit.
ReactionHandler
type ReactionHandler = (
user: Discord.User,
channel: Discord.Channel | Discord.DMChannel,
reaction: Discord.MessageReaction
) => voidA handler for @OnReaction, @OnReactionRemoved, @OnAnyReaction, and @OnAnyReactionRemoved.
user: The user who reacted.channel: The channel of the message.reaction: The reaction.
Decorators
Decorators are documented here using Decorator<T>, where T is the function type that the decorator can be applied to. All decorators here can only be applied to methods of a class inheriting DynamicMessage.
Emojis
Emojis are resolved using node-emoji, so you can use things like ':one:' instead of having to use Unicode emojis.
Demos
See the demo folder.
Development
- (Optional) Install pnpm:
npm i -g pnpmIf you don’t use pnpm, replace pnpm with npm.
2. Create a file called .env with this:
TOKEN=yourDiscordBotTokenHere- Install dependencies:
pnpm i. - Start demo:
pnpm run demo.
6 years ago