-
Notifications
You must be signed in to change notification settings - Fork 13
/
CompactModeHelper.ts
125 lines (106 loc) · 3.95 KB
/
CompactModeHelper.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { Message, GuildMember, User } from 'discord.js';
import { Client } from '../client/Client';
interface SingleUseButton { expires: number, consumed: boolean, emoji: string, action: Function }
/**
* Helper singleton for attaching single-use, expiring reaction buttons to
* Messages, to be used by the Client when compact mode is active
*/
export class CompactModeHelper
{
private static _instance: CompactModeHelper;
private readonly _client: Client;
private readonly _buttons: { [identifier: string]: SingleUseButton };
private constructor(client: Client)
{
if (CompactModeHelper._instance)
throw new Error('Cannot create multiple instances of CompactModeHelper');
this._client = client;
this._buttons = {};
// Handle button clicks
this._client.on('messageReactionAdd', (reaction, user) =>
{
const emojiIdentifier: string = reaction.emoji.id || reaction.emoji.name;
const buttonIdentifier: string = `${reaction.message.id}:${emojiIdentifier}`;
if (!(buttonIdentifier in this._buttons)) return;
if (user.id !== reaction.message.author.id) return;
const button: SingleUseButton = this._buttons[buttonIdentifier];
if (button.consumed || button.expires < Date.now()) return;
button.consumed = true;
button.action();
});
// Clean up expired/consumed buttons
this._client.setInterval(() =>
{
for (const identifier of Object.keys(this._buttons))
{
const button: SingleUseButton = this._buttons[identifier];
if (button.consumed || button.expires < Date.now())
delete this._buttons[identifier];
}
}, 30e3);
}
/**
* Create the CompactModeHelper singleton instance
* >**Note:** This is called automatically by the Client.
* You do not need to create your own instance
* @param {Client} client YAMDBF Client instance
* @returns {void}
*/
public static createInstance(client: Client): void
{
CompactModeHelper._instance = new CompactModeHelper(client);
}
/**
* Register a single-use reaction button on a Message that will
* execute the given action when clicked by the Message author.
*
* Buttons remain clickable for the given lifespan (30 seconds by
* default), or until consumed via click by the Message author
*
* >If the Client doesn't have permissions to add reactions the
* given action function will be invoked immediately
* @param {Message} message Message to register a button for
* @param {string} emoji A unicode emoji, custom emoji ID, or a button
* key from {@link Client#buttons}
* @param {Function} action Function to execute when the reaction button is clicked
* @param {number} [lifespan=30000] Lifespan of the button in MS
* @returns {Promise<void>}
*/
public static async registerButton(
message: Message,
emoji: string,
action: Function,
lifespan: number = 30e3
): Promise<void>
{
if (!CompactModeHelper._instance)
throw new Error('CompactModeHelper instance has not been created');
if (typeof emoji !== 'string')
throw new TypeError('Emoji must be a unicode emoji, custom emoji id, or client button key');
if (CompactModeHelper._instance._client.buttons[emoji])
// eslint-disable-next-line no-param-reassign
emoji = CompactModeHelper._instance._client.buttons[emoji];
let clientMember!: GuildMember;
let invokeImmediately: boolean = false;
if (message.channel.type === 'text')
{
try
{
const clientUser: User = CompactModeHelper._instance._client.user!;
clientMember = message.guild!.members.cache.get(clientUser.id)
|| await message.guild!.members.fetch(clientUser);
}
catch { invokeImmediately = true; }
}
if (clientMember && !clientMember.permissionsIn(message.channel).has('ADD_REACTIONS'))
invokeImmediately = true;
if (!invokeImmediately)
{
await message.react(emoji);
// eslint-disable-next-line require-atomic-updates
CompactModeHelper._instance._buttons[`${message.id}:${emoji}`] =
{ expires: Date.now() + lifespan, consumed: false, emoji, action };
}
else action();
}
}