@open-discord-bots/framework 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,222 @@
1
+ ///////////////////////////////////////
2
+ //STATE MODULE
3
+ ///////////////////////////////////////
4
+ import { ODManager, ODSystemError, ODManagerData } from "./base.js";
5
+ /**## ODState `class`
6
+ * An Open Discord state is a system for storing additional chunks of metadata for Discord messages.
7
+ * A system for tracking messages or linking metadata, states or progress to Discord messages (ID-based).
8
+ *
9
+ * Features automatic garbage collection to clear expired states.
10
+ */
11
+ export class ODState extends ODManagerData {
12
+ /**Alias to Open Discord message states database. */
13
+ database;
14
+ /**Alias to Open Discord client manager. */
15
+ client;
16
+ /**Alias to Open Discord debugger. */
17
+ debug = null;
18
+ /**The settings of this state. */
19
+ settings;
20
+ constructor(id, client, database, settings) {
21
+ super(id);
22
+ this.client = client;
23
+ this.database = database;
24
+ this.settings = {
25
+ disableAutodelete: false,
26
+ autodeleteOnChannelDelete: true,
27
+ autodeleteOnMessageDelete: true,
28
+ autodeleteOnUserLeave: true,
29
+ autodeleteOnRestart: false,
30
+ autodeleteOnEphemeral: true,
31
+ autodeleteAfterTimeout: null,
32
+ ...settings
33
+ };
34
+ }
35
+ /**Use the Open Discord debugger in this manager for logs. */
36
+ useDebug(debug) {
37
+ this.debug = debug ?? null;
38
+ }
39
+ /**Init the state and start autodeleting. The client must already be logged-in for this to work. */
40
+ async init() {
41
+ if (!this.client.loggedIn)
42
+ throw new ODSystemError("ODState('" + this.id.value + "').init() => The client must be logged in for states to initialize.");
43
+ //autodelete on restart
44
+ if (!this.settings.disableAutodelete && this.settings.autodeleteOnRestart)
45
+ this.clearAllMsgStates();
46
+ //autodelete on channelDelete
47
+ this.client.client.on("channelDelete", async (deletedChannel) => {
48
+ if (this.settings.disableAutodelete || !this.settings.autodeleteOnChannelDelete)
49
+ return;
50
+ for (const { key, value } of await this.listMsgStates()) {
51
+ if (value.channelId === deletedChannel.id)
52
+ await this.deleteMsgStateWithRawKey(key);
53
+ }
54
+ });
55
+ //autodelete on messageDelete
56
+ this.client.client.on("messageDelete", async (deletedMsg) => {
57
+ if (this.settings.disableAutodelete || !this.settings.autodeleteOnMessageDelete)
58
+ return;
59
+ for (const { key, value } of await this.listMsgStates()) {
60
+ if (value.messageId === deletedMsg.id)
61
+ await this.deleteMsgStateWithRawKey(key);
62
+ }
63
+ });
64
+ //autodelete on userLeave
65
+ this.client.client.on("guildMemberRemove", async (member) => {
66
+ if (this.settings.disableAutodelete || !this.settings.autodeleteOnUserLeave)
67
+ return;
68
+ if (member.guild.id !== this.client.mainServer?.id)
69
+ return;
70
+ for (const { key, value } of await this.listMsgStates()) {
71
+ if (value.userId === member.id)
72
+ await this.deleteMsgStateWithRawKey(key);
73
+ }
74
+ });
75
+ //autodelete when expired (ephemeral/timeout) every 30 seconds
76
+ this.purgeExpiredStates();
77
+ setInterval(() => {
78
+ this.purgeExpiredStates();
79
+ }, 30 * 1000);
80
+ //create a list of all channels, messages & users that still exist (and are part of states)
81
+ const existingChannelIds = [];
82
+ const existingMessageIds = [];
83
+ const existingUserIds = [];
84
+ for (const { key, value } of await this.listMsgStates()) {
85
+ if (!existingChannelIds.includes(value.channelId)) {
86
+ //check if channel still exists
87
+ const channel = await this.client.fetchChannel(value.channelId);
88
+ if (channel)
89
+ existingChannelIds.push(channel.id);
90
+ }
91
+ if (!existingMessageIds.includes(value.messageId)) {
92
+ //check if message still exists
93
+ const message = await this.client.fetchChannelMessage(value.channelId, value.messageId);
94
+ if (message)
95
+ existingMessageIds.push(message.id);
96
+ }
97
+ if (value.userId && this.client.mainServer && !existingUserIds.includes(value.userId)) {
98
+ //check if user still exists
99
+ const user = await this.client.fetchGuildMember(this.client.mainServer.id, value.userId);
100
+ if (user)
101
+ existingUserIds.push(user.id);
102
+ }
103
+ }
104
+ //delete all states where a channel, message or user is missing (when the bot was offline)
105
+ for (const { key, value } of await this.listMsgStates()) {
106
+ if (value.channelId && !existingChannelIds.includes(value.channelId))
107
+ await this.deleteMsgStateWithRawKey(key);
108
+ if (value.messageId && !existingMessageIds.includes(value.messageId))
109
+ await this.deleteMsgStateWithRawKey(key);
110
+ if (value.userId && !existingUserIds.includes(value.userId))
111
+ await this.deleteMsgStateWithRawKey(key);
112
+ }
113
+ }
114
+ /**Purge all expired message states that reached a timeout or ephemeral. */
115
+ async purgeExpiredStates() {
116
+ for (const { key, value } of await this.listMsgStates()) {
117
+ if (value.deleteAfterDate && value.deleteAfterDate < Date.now())
118
+ await this.deleteMsgStateWithRawKey(key);
119
+ }
120
+ }
121
+ /**Transform the object-based message state key contents to a string. */
122
+ transformKey(key) {
123
+ const newGuild = (!key.guild) ? "NULL" : (typeof key.guild === "string" ? key.guild : key.guild.id);
124
+ const newChannel = (!key.channel) ? "NULL" : (typeof key.channel === "string" ? key.channel : key.channel.id);
125
+ const newMessage = (!key.message) ? "NULL" : (typeof key.message === "string" ? key.message : key.message.id);
126
+ const newUser = (!key.user) ? "NULL" : (typeof key.user === "string" ? key.user : key.user.id);
127
+ return `G:${newGuild},C:${newChannel},M:${newMessage},U:${newUser}`;
128
+ }
129
+ /**Transform the message state data contents for storage in the database. */
130
+ transformData(key, data, isEphemeral, keepCreatedDate) {
131
+ const guildId = (!key.guild) ? null : (typeof key.guild === "string" ? key.guild : key.guild.id);
132
+ const channelId = (typeof key.channel === "string" ? key.channel : key.channel.id);
133
+ const messageId = (typeof key.message === "string" ? key.message : key.message.id);
134
+ const userId = (!key.user) ? null : (typeof key.user === "string" ? key.user : key.user.id);
135
+ const createdDate = keepCreatedDate ?? Date.now();
136
+ const modifiedDate = Date.now();
137
+ const unmodifiedTimeoutDate = this.timeoutToUnixTime();
138
+ const ephemeralTimeoutDate = (isEphemeral) ? Date.now() + (3600 * 1000) : null; //delete ephemeral state after 1 hour
139
+ const deleteAfterDate = (unmodifiedTimeoutDate) ? unmodifiedTimeoutDate : ephemeralTimeoutDate;
140
+ return { guildId, channelId, messageId, userId, createdDate, modifiedDate, deleteAfterDate, data };
141
+ }
142
+ /**Calculate milliseconds from a time + unit. */
143
+ timeoutToUnixTime() {
144
+ const timeout = this.settings.autodeleteAfterTimeout;
145
+ if (!timeout)
146
+ return null;
147
+ if (timeout.unit == "seconds")
148
+ return Date.now() + (timeout.time * 1000);
149
+ else if (timeout.unit == "minutes")
150
+ return Date.now() + (timeout.time * 1000 * 60);
151
+ else if (timeout.unit == "hours")
152
+ return Date.now() + (timeout.time * 1000 * 3600);
153
+ else if (timeout.unit == "days")
154
+ return Date.now() + (timeout.time * 1000 * 3600 * 24);
155
+ else
156
+ return null;
157
+ }
158
+ /**Set a message state using guild, channel & message id as key. Returns `true` when overwritten. */
159
+ async setMsgState(key, data, isEphemeral) {
160
+ const rawKey = this.transformKey(key);
161
+ const existingData = await this.getMsgState(key);
162
+ const contents = this.transformData(key, data, isEphemeral, existingData?.createdDate);
163
+ return await this.database.set(this.id.value, rawKey, contents);
164
+ }
165
+ /**Get a message state using guild, channel & message id as key. */
166
+ async getMsgState(key) {
167
+ const rawKey = this.transformKey(key);
168
+ const rawData = await this.database.get(this.id.value, rawKey);
169
+ if (typeof rawData !== "object")
170
+ return null;
171
+ else
172
+ return rawData;
173
+ }
174
+ /**Delete a message state using guild, channel & message id as key. Returns `true` when deleted. */
175
+ async deleteMsgState(key) {
176
+ const rawKey = this.transformKey(key);
177
+ return await this.database.delete(this.id.value, rawKey);
178
+ }
179
+ /**List all message states of this `ODState`. */
180
+ async listMsgStates() {
181
+ return ((await this.database.getCategory(this.id.value)) ?? []);
182
+ }
183
+ /**Delete all message states from this ODState. */
184
+ async clearAllMsgStates() {
185
+ for (const state of await this.database.getAll()) {
186
+ await this.database.delete(state.category, state.key);
187
+ }
188
+ }
189
+ /**Delete a message state using the raw key. Returns `true` when deleted. */
190
+ async deleteMsgStateWithRawKey(rawKey) {
191
+ return await this.database.delete(this.id.value, rawKey);
192
+ }
193
+ }
194
+ /**## ODStateManager `class`
195
+ * The Open Discord state manager is a system for tracking messages or linking metadata, states or progress to Discord messages (ID-based).
196
+ *
197
+ * Features automatic garbage collection to clear expired states.
198
+ */
199
+ export class ODStateManager extends ODManager {
200
+ constructor(debug) {
201
+ super(debug, "state");
202
+ }
203
+ /**Init all states. */
204
+ async init() {
205
+ for (const state of this.getAll()) {
206
+ await state.init();
207
+ }
208
+ }
209
+ add(data, overwrite) {
210
+ data.useDebug(this.debug);
211
+ return super.add(data, overwrite);
212
+ }
213
+ get(id) {
214
+ return super.get(id);
215
+ }
216
+ remove(id) {
217
+ return super.remove(id);
218
+ }
219
+ exists(id) {
220
+ return super.exists(id);
221
+ }
222
+ }
@@ -1,5 +1,7 @@
1
1
  import * as api from "../api/index.js";
2
2
  export function loadErrorHandling(opendiscord, project) {
3
+ //increase error stack trace
4
+ Error.stackTraceLimit = 50;
3
5
  process.on("uncaughtException", async (error, origin) => {
4
6
  try {
5
7
  const beforeEvent = opendiscord.events.get("onErrorHandling");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@open-discord-bots/framework",
3
3
  "author": "DJj123dj",
4
- "version": "0.3.2",
4
+ "version": "0.3.4",
5
5
  "description": "The core framework of the popular open-source discord bots: Open Ticket & Open Moderation.",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
package/src/api/index.ts CHANGED
@@ -25,6 +25,7 @@ export * from "./modules/progressbar.js"
25
25
  export * from "./modules/responder.js"
26
26
  export * from "./modules/session.js"
27
27
  export * from "./modules/startscreen.js"
28
+ export * from "./modules/state.js"
28
29
  export * from "./modules/statistic.js"
29
30
  export * from "./modules/verifybar.js"
30
31
  export * from "./modules/worker.js"
package/src/api/main.ts CHANGED
@@ -24,6 +24,7 @@ import { ODClientManager } from "./modules/client.js"
24
24
  import { ODSharedFuseManager } from "./modules/fuse.js"
25
25
  import { ODStartScreenManager } from "./modules/startscreen.js"
26
26
  import { ODComponentManager } from "./modules/component.js"
27
+ import { ODStateManager } from "./modules/state.js"
27
28
 
28
29
  /**## ODMainManagers `interface`
29
30
  * The global properties for the main class of the bot.
@@ -89,6 +90,8 @@ export interface ODMainManagers {
89
90
  code: ODCodeManager
90
91
  /**A collection of static Discord post channels. It allows the bot to find back log, transcript or configured channels based on a linked ID. */
91
92
  posts: ODPostManager
93
+ /**A system for tracking messages or linking metadata, states or progress to Discord messages (ID-based). Features automatic garbage collection. */
94
+ states: ODStateManager
92
95
 
93
96
  /**A wrapper around the `discord.Client` class. It handles client login, activity and registering text/slash commands. */
94
97
  client: ODClientManager
@@ -140,6 +143,7 @@ export abstract class ODMain implements ODMainManagers {
140
143
  readonly statistics: ODStatisticManager
141
144
  readonly code: ODCodeManager
142
145
  readonly posts: ODPostManager
146
+ readonly states: ODStateManager
143
147
 
144
148
  readonly client: ODClientManager
145
149
  readonly sharedFuses: ODSharedFuseManager
@@ -150,7 +154,7 @@ export abstract class ODMain implements ODMainManagers {
150
154
  constructor(managers:ODMainManagers,project:ODProjectType){
151
155
  this.project = project
152
156
  this.versions = managers.versions
153
- this.versions.add(ODVersion.fromString("opendiscord:api","v0.3.2"))
157
+ this.versions.add(ODVersion.fromString("opendiscord:api","v0.3.4"))
154
158
  this.versions.add(ODVersion.fromString("opendiscord:livestatus","v2.0.0"))
155
159
 
156
160
  this.debugfile = managers.debugfile
@@ -179,6 +183,7 @@ export abstract class ODMain implements ODMainManagers {
179
183
  this.statistics = managers.statistics
180
184
  this.code = managers.code
181
185
  this.posts = managers.posts
186
+ this.states = managers.states
182
187
 
183
188
  this.sharedFuses = managers.sharedFuses
184
189
  this.env = managers.env
@@ -123,7 +123,7 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
123
123
  }
124
124
  /**Get all servers the bot is part of. */
125
125
  async getGuilds(): Promise<discord.Guild[]> {
126
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
126
+ if (!this.initiated) throw new ODSystemError("ODClientManager() => Unable to use this method. Client isn't initiated yet.")
127
127
  if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
128
128
 
129
129
  return this.client.guilds.cache.map((guild) => guild)
@@ -146,15 +146,15 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
146
146
  /**Log-in with a discord auth token. Rejects returns `false` using 'softErrors' on failure. */
147
147
  login(softErrors?:boolean): Promise<boolean> {
148
148
  return new Promise(async (resolve,reject) => {
149
- if (!this.initiated) reject("Client isn't initiated yet!")
150
- if (!this.token) reject("Client doesn't have a token!")
149
+ if (!this.initiated) reject("ODClientManager.login() => Unable to use this method. Client isn't initiated yet.")
150
+ if (!this.token) reject("ODClientManager.login() => Unable to login, client does not have a token.")
151
151
 
152
152
  try {
153
153
  this.client.once("clientReady",async () => {
154
154
  this.ready = true
155
155
 
156
156
  //set slashCommandManager & contextMenuManager to client applicationCommandManager
157
- if (!this.client.application) throw new ODSystemError("Couldn't get client application for slashCommand & contextMenu managers!")
157
+ if (!this.client.application) throw new ODSystemError("ODClientManager.login() => Unable to fetch client application for slashCommand & contextMenu managers.")
158
158
  this.slashCommands.commandManager = this.client.application.commands
159
159
  this.contextMenus.commandManager = this.client.application.commands
160
160
  this.autocompletes.commandManager = this.client.application.commands
@@ -177,10 +177,10 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
177
177
  }
178
178
  })
179
179
  }
180
- /**A simplified shortcut to get a `discord.User` :) */
180
+ /**A simplified shortcut to get a `discord.User`. */
181
181
  async fetchUser(id:string): Promise<discord.User|null> {
182
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
183
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
182
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchUser() => Unable to use this method. Client isn't initiated yet.")
183
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchUser() => Unable to use this method. Client isn't ready and logged in yet.")
184
184
 
185
185
  try{
186
186
  return await this.client.users.fetch(id)
@@ -188,10 +188,10 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
188
188
  return null
189
189
  }
190
190
  }
191
- /**A simplified shortcut to get a `discord.Guild` :) */
191
+ /**A simplified shortcut to get a `discord.Guild`. */
192
192
  async fetchGuild(id:string): Promise<discord.Guild|null> {
193
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
194
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
193
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchGuild() => Unable to use this method. Client isn't initiated yet.")
194
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchGuild() => Unable to use this method. Client isn't ready and logged in yet.")
195
195
 
196
196
  try{
197
197
  return await this.client.guilds.fetch(id)
@@ -199,10 +199,10 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
199
199
  return null
200
200
  }
201
201
  }
202
- /**A simplified shortcut to get a `discord.Channel` :) */
202
+ /**A simplified shortcut to get a `discord.Channel`. */
203
203
  async fetchChannel(id:string): Promise<discord.Channel|null> {
204
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
205
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
204
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchChannel() => Unable to use this method. Client isn't initiated yet.")
205
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchChannel() => Unable to use this method. Client isn't ready and logged in yet.")
206
206
 
207
207
  try{
208
208
  return await this.client.channels.fetch(id)
@@ -210,10 +210,23 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
210
210
  return null
211
211
  }
212
212
  }
213
- /**A simplified shortcut to get a `discord.GuildBasedChannel` :) */
213
+ /**A simplified shortcut to get a `discord.TextChannel` (guild or DM). */
214
+ async fetchTextChannel(id:string): Promise<discord.TextChannel|discord.DMChannel|discord.PartialDMChannel|null> {
215
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchTextChannel() => Unable to use this method. Client isn't initiated yet.")
216
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchTextChannel() => Unable to use this method. Client isn't ready and logged in yet.")
217
+
218
+ try{
219
+ const channel = await this.client.channels.fetch(id)
220
+ if (!channel || (channel.type != discord.ChannelType.GuildText && channel.type != discord.ChannelType.DM)) return null
221
+ return channel
222
+ }catch{
223
+ return null
224
+ }
225
+ }
226
+ /**A simplified shortcut to get a `discord.GuildBasedChannel`. */
214
227
  async fetchGuildChannel(guildId:string|discord.Guild, id:string): Promise<discord.GuildBasedChannel|null> {
215
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
216
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
228
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchGuildChannel() => Unable to use this method. Client isn't initiated yet.")
229
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchGuildChannel() => Unable to use this method. Client isn't ready and logged in yet.")
217
230
 
218
231
  try{
219
232
  const guild = (guildId instanceof discord.Guild) ? guildId : await this.fetchGuild(guildId)
@@ -224,10 +237,10 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
224
237
  return null
225
238
  }
226
239
  }
227
- /**A simplified shortcut to get a `discord.TextChannel` :) */
240
+ /**A simplified shortcut to get a `discord.TextChannel`. */
228
241
  async fetchGuildTextChannel(guildId:string|discord.Guild, id:string): Promise<discord.TextChannel|null> {
229
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
230
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
242
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchGuildTextChannel() => Unable to use this method. Client isn't initiated yet.")
243
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchGuildTextChannel() => Unable to use this method. Client isn't ready and logged in yet.")
231
244
 
232
245
  try{
233
246
  const guild = (guildId instanceof discord.Guild) ? guildId : await this.fetchGuild(guildId)
@@ -239,10 +252,10 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
239
252
  return null
240
253
  }
241
254
  }
242
- /**A simplified shortcut to get a `discord.CategoryChannel` :) */
255
+ /**A simplified shortcut to get a `discord.CategoryChannel`. */
243
256
  async fetchGuildCategoryChannel(guildId:string|discord.Guild, id:string): Promise<discord.CategoryChannel|null> {
244
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
245
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
257
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchGuildCategoryChannel() => Unable to use this method. Client isn't initiated yet.")
258
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchGuildCategoryChannel() => Unable to use this method. Client isn't ready and logged in yet.")
246
259
 
247
260
  try{
248
261
  const guild = (guildId instanceof discord.Guild) ? guildId : await this.fetchGuild(guildId)
@@ -254,12 +267,11 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
254
267
  return null
255
268
  }
256
269
  }
257
- /**A simplified shortcut to get a `discord.GuildMember` :) */
270
+ /**A simplified shortcut to get a `discord.GuildMember`. */
258
271
  async fetchGuildMember(guildId:string|discord.Guild, id:string): Promise<discord.GuildMember|null> {
259
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
260
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
261
- if (typeof id != "string") throw new ODSystemError("TEMP ERROR => ODClientManager.fetchGuildMember() => id param isn't string")
262
-
272
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchGuildMember() => Unable to use this method. Client isn't initiated yet.")
273
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchGuildMember() => Unable to use this method. Client isn't ready and logged in yet.")
274
+
263
275
  try{
264
276
  const guild = (guildId instanceof discord.Guild) ? guildId : await this.fetchGuild(guildId)
265
277
  if (!guild) return null
@@ -268,12 +280,11 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
268
280
  return null
269
281
  }
270
282
  }
271
- /**A simplified shortcut to get a `discord.Role` :) */
283
+ /**A simplified shortcut to get a `discord.Role`. */
272
284
  async fetchGuildRole(guildId:string|discord.Guild, id:string): Promise<discord.Role|null> {
273
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
274
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
275
- if (typeof id != "string") throw new ODSystemError("TEMP ERROR => ODClientManager.fetchGuildRole() => id param isn't string")
276
-
285
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchGuildRole() => Unable to use this method. Client isn't initiated yet.")
286
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchGuildRole() => Unable to use this method. Client isn't ready and logged in yet.")
287
+
277
288
  try{
278
289
  const guild = (guildId instanceof discord.Guild) ? guildId : await this.fetchGuild(guildId)
279
290
  if (!guild) return null
@@ -282,30 +293,23 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
282
293
  return null
283
294
  }
284
295
  }
285
- /**A simplified shortcut to get a `discord.Message` :) */
286
- async fetchGuildChannelMessage(guildId:string|discord.Guild, channelId:string|discord.TextChannel, id:string): Promise<discord.Message<true>|null>
287
- async fetchGuildChannelMessage(channelId:discord.TextChannel, id:string): Promise<discord.Message<true>|null>
288
- async fetchGuildChannelMessage(guildId:string|discord.Guild|discord.TextChannel, channelId:string|discord.TextChannel|string, id?:string): Promise<discord.Message<true>|null> {
289
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
290
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
296
+ /**A simplified shortcut to get a `discord.Message`. */
297
+ async fetchChannelMessage(channelId:string|discord.TextChannel|discord.DMChannel, id:string): Promise<discord.Message<boolean>|null> {
298
+ if (!this.initiated) throw new ODSystemError("ODClientManager.fetchChannelMessage() => Unable to use this method. Client isn't initiated yet.")
299
+ if (!this.ready) throw new ODSystemError("ODClientManager.fetchChannelMessage() => Unable to use this method. Client isn't ready and logged in yet.")
291
300
 
292
301
  try{
293
- if (guildId instanceof discord.TextChannel && typeof channelId == "string"){
294
- const channel = guildId
295
- return await channel.messages.fetch(channelId)
296
- }else if (!(guildId instanceof discord.TextChannel) && id){
297
- const channel = (channelId instanceof discord.TextChannel) ? channelId : await this.fetchGuildTextChannel(guildId,channelId)
298
- if (!channel) return null
299
- return await channel.messages.fetch(id)
300
- }else return null
302
+ const channel = (channelId instanceof discord.TextChannel || channelId instanceof discord.DMChannel) ? channelId : await this.fetchTextChannel(channelId)
303
+ if (!channel) return null
304
+ return await channel.messages.fetch(id)
301
305
  }catch{
302
306
  return null
303
307
  }
304
308
  }
305
- /**A simplified shortcut to send a DM to a user :) */
309
+ /**A simplified shortcut to send a DM to a user. */
306
310
  async sendUserDm(user:string|discord.User, build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<false>> {
307
- if (!this.initiated) throw new ODSystemError("Client isn't initiated yet!")
308
- if (!this.ready) throw new ODSystemError("Client isn't ready yet!")
311
+ if (!this.initiated) throw new ODSystemError("ODClientManager.sendUserDm() => Unable to use this method. Client isn't initiated yet.")
312
+ if (!this.ready) throw new ODSystemError("ODClientManager.sendUserDm() => Unable to use this method. Client isn't ready and logged in yet.")
309
313
 
310
314
  try{
311
315
  const msgFlags: number[] = []
@@ -325,26 +329,26 @@ export class ODClientManager<SlashIdList extends ODSlashCommandManagerIdConstrai
325
329
  const finalMessage = Object.assign(msgData,{flags:msgFlags})
326
330
 
327
331
  if (user instanceof discord.User){
328
- if (user.bot) return {success:false,message:null}
332
+ if (user.bot) return {success:false}
329
333
  const channel = await user.createDM()
330
334
  const msg = await channel.send(finalMessage)
331
335
  return {success:true,message:msg}
332
336
  }else{
333
337
  const newUser = await this.fetchUser(user)
334
338
  if (!newUser) throw new Error()
335
- if (newUser.bot) return {success:false,message:null}
339
+ if (newUser.bot) return {success:false}
336
340
  const channel = await newUser.createDM()
337
341
  const msg = await channel.send(finalMessage)
338
342
  return {success:true,message:msg}
339
343
  }
340
344
  }catch{
341
345
  try{
342
- this.debug.console.log("Failed to send DM to user! ","warning",[
346
+ this.debug.console.log("ODClientManager.sendUserDm() => Failed to send DM. User may have DMs disabled for non-friends. ","warning",[
343
347
  {key:"id",value:(user instanceof discord.User ? user.id : user)},
344
348
  {key:"message-build",value:build.id.value}
345
349
  ])
346
350
  }catch{}
347
- return {success:false,message:null}
351
+ return {success:false}
348
352
  }
349
353
  }
350
354
  }
@@ -417,7 +421,7 @@ export class ODClientActivityManager {
417
421
 
418
422
  /**Update the client status */
419
423
  private updateClientActivity(type:ODClientActivityType,text:string){
420
- if (!this.client.client.user) throw new ODSystemError("Couldn't set client status: client.user == undefined")
424
+ if (!this.client.client.user) throw new ODSystemError("ODClientActivityManager.updateClientActivity() => Couldn't set client status: client.user is 'undefined'.")
421
425
  if (type == false){
422
426
  this.client.client.user.setActivity()
423
427
  return
@@ -217,7 +217,9 @@ export class ODError {
217
217
  }
218
218
  /**Create a more-detailed, non-colored version of this error to store it in the `debug.txt` file! */
219
219
  toDebugString(){
220
- return "[UNKNOWN OD ERROR]: "+this.error.message+" | origin: "+this.origin+"\n"+this.error.stack
220
+ const date = new Date()
221
+ const dstring = `${date.getDate()}/${date.getMonth()+1}/${date.getFullYear()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`
222
+ return "["+dstring+" UNKNOWN OPENDISCORD ERROR]: "+this.error.message+" | Error Origin: "+this.origin+" | Stacktrace:\n"+this.error.stack
221
223
  }
222
224
  }
223
225
 
@@ -89,7 +89,7 @@ export abstract class ODDatabase<IdList extends ODDatabaseIdConstraint = ODDatab
89
89
  abstract get(category:string, key:string): ODOptionalPromise<ODValidJsonType|undefined>
90
90
  abstract get(category:string, key:string): ODOptionalPromise<ODValidJsonType|undefined>
91
91
 
92
- /**Delete a specific category & key in the database */
92
+ /**Delete a specific category & key in the database. Returns `true` when deleted. */
93
93
  abstract delete<CategoryId extends keyof ODNoGeneric<IdList>>(category:CategoryId, key:string): ODOptionalPromise<boolean>
94
94
  abstract delete(category:string, key:string): ODOptionalPromise<boolean>
95
95
  abstract delete(category:string, key:string): ODOptionalPromise<boolean>
@@ -142,9 +142,6 @@ export interface ODSharedFuseList {
142
142
  /**Load the default Open Discord client activity initialization (& status refresh). */
143
143
  clientActivityInitiating:boolean,
144
144
 
145
- /**Load the default Open Discord priority levels. */
146
- priorityLoading:boolean,
147
-
148
145
  /**Load the default Open Discord slash commands. */
149
146
  slashCommandLoading:boolean,
150
147
  /**Load the default Open Discord slash command registerer (register slash cmds in discord). */
@@ -164,6 +161,11 @@ export interface ODSharedFuseList {
164
161
  /**Load the default Open Discord text commands. */
165
162
  textCommandLoading:boolean,
166
163
 
164
+ /**Load the default Open Discord message states. */
165
+ stateLoading:boolean,
166
+ /**Initiate the default Open Discord message states. */
167
+ stateInitiating:boolean,
168
+
167
169
  /**Load the default Open Discord button builders. */
168
170
  buttonBuildersLoading:boolean,
169
171
  /**Load the default Open Discord dropdown builders. */
@@ -177,6 +179,13 @@ export interface ODSharedFuseList {
177
179
  /**Load the default Open Discord modal builders. */
178
180
  modalBuildersLoading:boolean,
179
181
 
182
+ /**Load the default Open Discord shared components. */
183
+ sharedComponentsLoading:boolean,
184
+ /**Load the default Open Discord message components. */
185
+ messageComponentsLoading:boolean,
186
+ /**Load the default Open Discord modal components. */
187
+ modalComponentsLoading:boolean,
188
+
180
189
  /**Load the default Open Discord command responders. */
181
190
  commandRespondersLoading:boolean,
182
191
  /**Load the default Open Discord button responders. */
@@ -291,8 +300,6 @@ export class ODSharedFuseManager extends ODFuseManager<ODSharedFuseList> {
291
300
  clientActivityLoading:true,
292
301
  clientActivityInitiating:true,
293
302
 
294
- priorityLoading:true,
295
-
296
303
  slashCommandLoading:true,
297
304
  slashCommandRegistering:true,
298
305
  forceSlashCommandRegistration:false,
@@ -303,6 +310,9 @@ export class ODSharedFuseManager extends ODFuseManager<ODSharedFuseList> {
303
310
  allowContextMenuRemoval:true,
304
311
  textCommandLoading:true,
305
312
 
313
+ stateLoading:true,
314
+ stateInitiating:true,
315
+
306
316
  buttonBuildersLoading:true,
307
317
  dropdownBuildersLoading:true,
308
318
  fileBuildersLoading:true,
@@ -310,6 +320,10 @@ export class ODSharedFuseManager extends ODFuseManager<ODSharedFuseList> {
310
320
  messageBuildersLoading:true,
311
321
  modalBuildersLoading:true,
312
322
 
323
+ sharedComponentsLoading:true,
324
+ messageComponentsLoading:true,
325
+ modalComponentsLoading:true,
326
+
313
327
  commandRespondersLoading:true,
314
328
  buttonRespondersLoading:true,
315
329
  dropdownRespondersLoading:true,
@@ -107,13 +107,13 @@ export class ODPost<ChannelType extends discord.GuildBasedChannel> extends ODMan
107
107
  }
108
108
  /**Send a message to this channel using the Open Discord builder system */
109
109
  async send(build:ODMessageBuildResult|ODMessageComponentBuildResult): Promise<ODResponderSendResult<true>> {
110
- if (!this.channel || !this.channel.isTextBased()) return {success:false,message:null}
110
+ if (!this.channel || !this.channel.isTextBased()) return {success:false}
111
111
  try{
112
112
  const finalMessage = this.getMessageFromBuildResult(build,"message")
113
113
  const sent = await this.channel.send(finalMessage)
114
114
  return {success:true,message:sent}
115
115
  }catch{
116
- return {success:false,message:null}
116
+ return {success:false}
117
117
  }
118
118
  }
119
119
  /**Get the final `messageCreateOptions` from a returned build result from builders/components. */