@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-messages
Purpose
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
experimentalDecorators
andemitDecoratorMetadata
intsconfig.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 | DynamicMessageConfigVolatile
volatile
: 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 | null
The Discord message that this is attached to.
render
abstract render(): string | Discord.MessageEmbed
Determines the contents of the message.
Returns: The content of the message.
reRender
reRender(): void
Used 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[]): void
Manually 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): this
Attaches 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 = () => void
A handler for @OnInit
.
ReactionHandler
type ReactionHandler = (
user: Discord.User,
channel: Discord.Channel | Discord.DMChannel,
reaction: Discord.MessageReaction
) => void
A 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 pnpm
If 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
.
4 years ago