@open-discord-bots/framework 0.3.3 → 0.3.5
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.
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/main.d.ts +4 -0
- package/dist/api/main.js +3 -1
- package/dist/api/modules/client.d.ts +13 -12
- package/dist/api/modules/client.js +61 -56
- package/dist/api/modules/console.js +3 -1
- package/dist/api/modules/database.d.ts +1 -1
- package/dist/api/modules/fuse.d.ts +4 -2
- package/dist/api/modules/fuse.js +2 -1
- package/dist/api/modules/permission.d.ts +1 -1
- package/dist/api/modules/permission.js +1 -1
- package/dist/api/modules/post.js +3 -3
- package/dist/api/modules/responder.d.ts +3 -3
- package/dist/api/modules/responder.js +27 -27
- package/dist/api/modules/startscreen.js +2 -2
- package/dist/api/modules/state.d.ts +126 -0
- package/dist/api/modules/state.js +222 -0
- package/dist/startup/errorHandling.js +2 -0
- package/package.json +1 -1
- package/src/api/index.ts +1 -0
- package/src/api/main.ts +6 -1
- package/src/api/modules/client.ts +60 -56
- package/src/api/modules/console.ts +3 -1
- package/src/api/modules/database.ts +1 -1
- package/src/api/modules/fuse.ts +8 -5
- package/src/api/modules/permission.ts +1 -1
- package/src/api/modules/post.ts +3 -3
- package/src/api/modules/responder.ts +31 -31
- package/src/api/modules/startscreen.ts +2 -2
- package/src/api/modules/state.ts +294 -0
- package/src/startup/errorHandling.ts +3 -0
|
@@ -428,24 +428,24 @@ export class ODCommandResponderInstance extends ODBaseResponderInstance {
|
|
|
428
428
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
429
429
|
const sent = await this.interaction.editReply(finalMessage);
|
|
430
430
|
this.ignoreResponderTimeout = true;
|
|
431
|
-
return { success: true, message: sent };
|
|
431
|
+
return { success: true, message: sent, ephemeral: build.ephemeral };
|
|
432
432
|
}
|
|
433
433
|
else {
|
|
434
434
|
const sent = await this.interaction.reply(finalMessage);
|
|
435
435
|
this.ignoreResponderTimeout = true;
|
|
436
|
-
return { success: true, message: await sent.fetch() };
|
|
436
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
439
|
else if (this.type == "message" && this.interaction instanceof discord.Message && this.interaction.channel.type != discord.ChannelType.GroupDM) {
|
|
440
440
|
const sent = await this.interaction.channel.send(finalMessage);
|
|
441
441
|
this.ignoreResponderTimeout = true;
|
|
442
|
-
return { success: true, message: sent };
|
|
442
|
+
return { success: true, message: sent, ephemeral: false };
|
|
443
443
|
}
|
|
444
444
|
else
|
|
445
|
-
return { success: false
|
|
445
|
+
return { success: false };
|
|
446
446
|
}
|
|
447
447
|
catch {
|
|
448
|
-
return { success: false
|
|
448
|
+
return { success: false };
|
|
449
449
|
}
|
|
450
450
|
}
|
|
451
451
|
/**Defer this command. */
|
|
@@ -600,16 +600,16 @@ export class ODButtonResponderInstance extends ODBaseResponderInstance {
|
|
|
600
600
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
601
601
|
const sent = await this.interaction.editReply(finalMessage);
|
|
602
602
|
this.ignoreResponderTimeout = true;
|
|
603
|
-
return { success: true, message: sent };
|
|
603
|
+
return { success: true, message: sent, ephemeral: build.ephemeral };
|
|
604
604
|
}
|
|
605
605
|
else {
|
|
606
606
|
const sent = await this.interaction.reply(finalMessage);
|
|
607
607
|
this.ignoreResponderTimeout = true;
|
|
608
|
-
return { success: true, message: await sent.fetch() };
|
|
608
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
611
|
catch {
|
|
612
|
-
return { success: false
|
|
612
|
+
return { success: false };
|
|
613
613
|
}
|
|
614
614
|
}
|
|
615
615
|
/**Update the message of this button. */
|
|
@@ -619,16 +619,16 @@ export class ODButtonResponderInstance extends ODBaseResponderInstance {
|
|
|
619
619
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
620
620
|
const sent = await this.interaction.editReply(finalMessage);
|
|
621
621
|
this.ignoreResponderTimeout = true;
|
|
622
|
-
return { success: true, message: await sent.fetch() };
|
|
622
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
623
623
|
}
|
|
624
624
|
else {
|
|
625
625
|
const sent = await this.interaction.update(finalMessage);
|
|
626
626
|
this.ignoreResponderTimeout = true;
|
|
627
|
-
return { success: true, message: await sent.fetch() };
|
|
627
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
628
628
|
}
|
|
629
629
|
}
|
|
630
630
|
catch {
|
|
631
|
-
return { success: false
|
|
631
|
+
return { success: false };
|
|
632
632
|
}
|
|
633
633
|
}
|
|
634
634
|
/**Defer this button. */
|
|
@@ -908,16 +908,16 @@ export class ODDropdownResponderInstance extends ODBaseResponderInstance {
|
|
|
908
908
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
909
909
|
const sent = await this.interaction.editReply(finalMessage);
|
|
910
910
|
this.ignoreResponderTimeout = true;
|
|
911
|
-
return { success: true, message: sent };
|
|
911
|
+
return { success: true, message: sent, ephemeral: build.ephemeral };
|
|
912
912
|
}
|
|
913
913
|
else {
|
|
914
914
|
const sent = await this.interaction.reply(finalMessage);
|
|
915
915
|
this.ignoreResponderTimeout = true;
|
|
916
|
-
return { success: true, message: await sent.fetch() };
|
|
916
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
917
917
|
}
|
|
918
918
|
}
|
|
919
919
|
catch {
|
|
920
|
-
return { success: false
|
|
920
|
+
return { success: false };
|
|
921
921
|
}
|
|
922
922
|
}
|
|
923
923
|
/**Update the message of this dropdown. */
|
|
@@ -927,16 +927,16 @@ export class ODDropdownResponderInstance extends ODBaseResponderInstance {
|
|
|
927
927
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
928
928
|
const sent = await this.interaction.editReply(finalMessage);
|
|
929
929
|
this.ignoreResponderTimeout = true;
|
|
930
|
-
return { success: true, message: await sent.fetch() };
|
|
930
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
931
931
|
}
|
|
932
932
|
else {
|
|
933
933
|
const sent = await this.interaction.update(finalMessage);
|
|
934
934
|
this.ignoreResponderTimeout = true;
|
|
935
|
-
return { success: true, message: await sent.fetch() };
|
|
935
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
936
936
|
}
|
|
937
937
|
}
|
|
938
938
|
catch {
|
|
939
|
-
return { success: false
|
|
939
|
+
return { success: false };
|
|
940
940
|
}
|
|
941
941
|
}
|
|
942
942
|
/**Defer this dropdown. */
|
|
@@ -1132,10 +1132,10 @@ export class ODModalResponderInstance extends ODBaseResponderInstance {
|
|
|
1132
1132
|
const finalMessage = this.getMessageFromBuildResult(build, "interaction");
|
|
1133
1133
|
const sent = await this.interaction.followUp(finalMessage);
|
|
1134
1134
|
this.ignoreResponderTimeout = true;
|
|
1135
|
-
return { success: true, message: sent };
|
|
1135
|
+
return { success: true, message: sent, ephemeral: build.ephemeral };
|
|
1136
1136
|
}
|
|
1137
1137
|
catch {
|
|
1138
|
-
return { success: false
|
|
1138
|
+
return { success: false };
|
|
1139
1139
|
}
|
|
1140
1140
|
}
|
|
1141
1141
|
/**Update the message of this modal. */
|
|
@@ -1145,16 +1145,16 @@ export class ODModalResponderInstance extends ODBaseResponderInstance {
|
|
|
1145
1145
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
1146
1146
|
const sent = await this.interaction.editReply(finalMessage);
|
|
1147
1147
|
this.ignoreResponderTimeout = true;
|
|
1148
|
-
return { success: true, message: await sent.fetch() };
|
|
1148
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
1149
1149
|
}
|
|
1150
1150
|
else {
|
|
1151
1151
|
const sent = await this.interaction.reply(finalMessage);
|
|
1152
1152
|
this.ignoreResponderTimeout = true;
|
|
1153
|
-
return { success: true, message: await sent.fetch() };
|
|
1153
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
1154
1154
|
}
|
|
1155
1155
|
}
|
|
1156
1156
|
catch {
|
|
1157
|
-
return { success: false
|
|
1157
|
+
return { success: false };
|
|
1158
1158
|
}
|
|
1159
1159
|
}
|
|
1160
1160
|
/**Defer this modal. */
|
|
@@ -1296,16 +1296,16 @@ export class ODContextMenuResponderInstance extends ODBaseResponderInstance {
|
|
|
1296
1296
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
1297
1297
|
const sent = await this.interaction.editReply(finalMessage);
|
|
1298
1298
|
this.ignoreResponderTimeout = true;
|
|
1299
|
-
return { success: true, message: sent };
|
|
1299
|
+
return { success: true, message: sent, ephemeral: build.ephemeral };
|
|
1300
1300
|
}
|
|
1301
1301
|
else {
|
|
1302
1302
|
const sent = await this.interaction.reply(finalMessage);
|
|
1303
1303
|
this.ignoreResponderTimeout = true;
|
|
1304
|
-
return { success: true, message: await sent.fetch() };
|
|
1304
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
1305
1305
|
}
|
|
1306
1306
|
}
|
|
1307
1307
|
catch {
|
|
1308
|
-
return { success: false
|
|
1308
|
+
return { success: false };
|
|
1309
1309
|
}
|
|
1310
1310
|
}
|
|
1311
1311
|
/**Update the message of this context menu. */
|
|
@@ -1315,13 +1315,13 @@ export class ODContextMenuResponderInstance extends ODBaseResponderInstance {
|
|
|
1315
1315
|
if (this.interaction.replied || this.interaction.deferred) {
|
|
1316
1316
|
const sent = await this.interaction.editReply(finalMessage);
|
|
1317
1317
|
this.ignoreResponderTimeout = true;
|
|
1318
|
-
return { success: true, message: await sent.fetch() };
|
|
1318
|
+
return { success: true, message: await sent.fetch(), ephemeral: build.ephemeral };
|
|
1319
1319
|
}
|
|
1320
1320
|
else
|
|
1321
1321
|
throw new ODSystemError("Unable to update context menu interaction!");
|
|
1322
1322
|
}
|
|
1323
1323
|
catch {
|
|
1324
|
-
return { success: false
|
|
1324
|
+
return { success: false };
|
|
1325
1325
|
}
|
|
1326
1326
|
}
|
|
1327
1327
|
/**Defer this context menu. */
|
|
@@ -182,7 +182,7 @@ export class ODStartScreenCategoryComponent extends ODStartScreenComponent {
|
|
|
182
182
|
super(id, priority, async (location) => {
|
|
183
183
|
const contents = await render(location);
|
|
184
184
|
if (contents != "" || this.renderIfEmpty) {
|
|
185
|
-
return ansis.bold.underline("\n" + name.toUpperCase() + (contents != "" ? ":\n" : ":")) + contents;
|
|
185
|
+
return ansis.bold.underline("\n" + this.name.toUpperCase() + (contents != "" ? ":\n" : ":")) + contents;
|
|
186
186
|
}
|
|
187
187
|
else
|
|
188
188
|
return "";
|
|
@@ -265,7 +265,7 @@ export class ODStartScreenLiveStatusCategoryComponent extends ODStartScreenCateg
|
|
|
265
265
|
/**A reference to the Open Discord livestatus manager. */
|
|
266
266
|
livestatus;
|
|
267
267
|
constructor(id, priority, livestatus) {
|
|
268
|
-
super(id, priority, "
|
|
268
|
+
super(id, priority, "news & updates", async () => {
|
|
269
269
|
const messages = await this.livestatus.getAllMessages();
|
|
270
270
|
return this.livestatus.renderer.render(messages);
|
|
271
271
|
}, false);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { ODManager, ODValidId, ODManagerData, ODNoGeneric } from "./base.js";
|
|
2
|
+
import { ODClientManager } from "./client.js";
|
|
3
|
+
import { ODDebugger } from "./console.js";
|
|
4
|
+
import { ODDatabase, ODDatabaseIdConstraint } from "./database.js";
|
|
5
|
+
import * as discord from "discord.js";
|
|
6
|
+
/**## ODStateKey `type`
|
|
7
|
+
* The key template for message states.
|
|
8
|
+
*/
|
|
9
|
+
export interface ODStateKey {
|
|
10
|
+
/**A valid discord server/guild ID or instance. */
|
|
11
|
+
guild?: discord.Guild | string | null;
|
|
12
|
+
/**A valid discord channel ID or instance. */
|
|
13
|
+
channel: discord.Channel | string;
|
|
14
|
+
/**A valid discord message ID or instance. */
|
|
15
|
+
message: discord.Message | string;
|
|
16
|
+
/**A valid discord user ID or instance. */
|
|
17
|
+
user?: discord.User | discord.GuildMember | string | null;
|
|
18
|
+
}
|
|
19
|
+
/**## ODStateData `type`
|
|
20
|
+
* The raw data template for message states used for storing in the database.
|
|
21
|
+
*/
|
|
22
|
+
export interface ODStateData<StateData extends any> {
|
|
23
|
+
/**The linked Guild ID of this message state. */
|
|
24
|
+
guildId: string | null;
|
|
25
|
+
/**The linked Channel ID of this message state. */
|
|
26
|
+
channelId: string;
|
|
27
|
+
/**The linked Message ID of this message state. */
|
|
28
|
+
messageId: string;
|
|
29
|
+
/**The linked User ID of this message state. */
|
|
30
|
+
userId: string | null;
|
|
31
|
+
/**The creation date of this state (UNIX TIMESTAMP). */
|
|
32
|
+
createdDate: number;
|
|
33
|
+
/**The modified date of this state (UNIX TIMESTAMP). */
|
|
34
|
+
modifiedDate: number;
|
|
35
|
+
/**Optional date after which this state should be regarded as expired (UNIX TIMESTAMP). */
|
|
36
|
+
deleteAfterDate: number | null;
|
|
37
|
+
/**The state data. */
|
|
38
|
+
data: StateData;
|
|
39
|
+
}
|
|
40
|
+
/**## ODStateSettings `type`
|
|
41
|
+
* Configurable settings for an ODState.
|
|
42
|
+
*/
|
|
43
|
+
export interface ODStateSettings {
|
|
44
|
+
/**(Default: `false`) Completely disable autodelete of message states. (unless manually activated using other settings) */
|
|
45
|
+
disableAutodelete: boolean;
|
|
46
|
+
/**(Default: `true`) Delete state on channel deletion. */
|
|
47
|
+
autodeleteOnChannelDelete: boolean;
|
|
48
|
+
/**(Default: `true`) Delete state on message deletion. */
|
|
49
|
+
autodeleteOnMessageDelete: boolean;
|
|
50
|
+
/**(Default: `true`) Delete state when the user leaves the guild/server. */
|
|
51
|
+
autodeleteOnUserLeave: boolean;
|
|
52
|
+
/**(Default: `false`) Delete state on bot restart. */
|
|
53
|
+
autodeleteOnRestart: boolean;
|
|
54
|
+
/**(Default: `true`) Delete state after 1 hour of an ephemeral message being sent. */
|
|
55
|
+
autodeleteOnEphemeral: boolean;
|
|
56
|
+
/**(Default: `null`) Delete state when unmodified/inactive for more than.... */
|
|
57
|
+
autodeleteAfterTimeout: null | {
|
|
58
|
+
time: number;
|
|
59
|
+
unit: "seconds" | "minutes" | "hours" | "days";
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**## ODState `class`
|
|
63
|
+
* An Open Discord state is a system for storing additional chunks of metadata for Discord messages.
|
|
64
|
+
* A system for tracking messages or linking metadata, states or progress to Discord messages (ID-based).
|
|
65
|
+
*
|
|
66
|
+
* Features automatic garbage collection to clear expired states.
|
|
67
|
+
*/
|
|
68
|
+
export declare class ODState<StateData extends any> extends ODManagerData {
|
|
69
|
+
/**Alias to Open Discord message states database. */
|
|
70
|
+
protected database: ODDatabase<ODDatabaseIdConstraint>;
|
|
71
|
+
/**Alias to Open Discord client manager. */
|
|
72
|
+
protected client: ODClientManager;
|
|
73
|
+
/**Alias to Open Discord debugger. */
|
|
74
|
+
protected debug: ODDebugger | null;
|
|
75
|
+
/**The settings of this state. */
|
|
76
|
+
settings: ODStateSettings;
|
|
77
|
+
constructor(id: ODValidId, client: ODClientManager, database: ODDatabase<ODDatabaseIdConstraint>, settings: Partial<ODStateSettings>);
|
|
78
|
+
/**Use the Open Discord debugger in this manager for logs. */
|
|
79
|
+
useDebug(debug?: ODDebugger | null): void;
|
|
80
|
+
/**Init the state and start autodeleting. The client must already be logged-in for this to work. */
|
|
81
|
+
init(): Promise<void>;
|
|
82
|
+
/**Purge all expired message states that reached a timeout or ephemeral. */
|
|
83
|
+
protected purgeExpiredStates(): Promise<void>;
|
|
84
|
+
/**Transform the object-based message state key contents to a string. */
|
|
85
|
+
protected transformKey(key: ODStateKey): string;
|
|
86
|
+
/**Transform the message state data contents for storage in the database. */
|
|
87
|
+
protected transformData(key: ODStateKey, data: StateData, isEphemeral: boolean, keepCreatedDate?: number): ODStateData<StateData>;
|
|
88
|
+
/**Calculate milliseconds from a time + unit. */
|
|
89
|
+
protected timeoutToUnixTime(): number | null;
|
|
90
|
+
/**Set a message state using guild, channel & message id as key. Returns `true` when overwritten. */
|
|
91
|
+
setMsgState(key: ODStateKey, data: StateData, isEphemeral: boolean): Promise<boolean>;
|
|
92
|
+
/**Get a message state using guild, channel & message id as key. */
|
|
93
|
+
getMsgState(key: ODStateKey): Promise<ODStateData<StateData> | null>;
|
|
94
|
+
/**Delete a message state using guild, channel & message id as key. Returns `true` when deleted. */
|
|
95
|
+
deleteMsgState(key: ODStateKey): Promise<boolean>;
|
|
96
|
+
/**List all message states of this `ODState`. */
|
|
97
|
+
listMsgStates(): Promise<{
|
|
98
|
+
key: string;
|
|
99
|
+
value: ODStateData<StateData>;
|
|
100
|
+
}[]>;
|
|
101
|
+
/**Delete all message states from this ODState. */
|
|
102
|
+
clearAllMsgStates(): Promise<void>;
|
|
103
|
+
/**Delete a message state using the raw key. Returns `true` when deleted. */
|
|
104
|
+
protected deleteMsgStateWithRawKey(rawKey: string): Promise<boolean>;
|
|
105
|
+
}
|
|
106
|
+
/**## ODStateManagerIdConstraint `type`
|
|
107
|
+
* The constraint/layout for id mappings/interfaces of the `ODStateManager` class.
|
|
108
|
+
*/
|
|
109
|
+
export type ODStateManagerIdConstraint = Record<string, ODState<any>>;
|
|
110
|
+
/**## ODStateManager `class`
|
|
111
|
+
* The Open Discord state manager is a system for tracking messages or linking metadata, states or progress to Discord messages (ID-based).
|
|
112
|
+
*
|
|
113
|
+
* Features automatic garbage collection to clear expired states.
|
|
114
|
+
*/
|
|
115
|
+
export declare class ODStateManager<IdList extends ODStateManagerIdConstraint = ODStateManagerIdConstraint> extends ODManager<ODState<any>> {
|
|
116
|
+
constructor(debug: ODDebugger);
|
|
117
|
+
/**Init all states. */
|
|
118
|
+
init(): Promise<void>;
|
|
119
|
+
add(data: ODState<any>, overwrite?: boolean): boolean;
|
|
120
|
+
get<StateId extends keyof ODNoGeneric<IdList>>(id: StateId): IdList[StateId];
|
|
121
|
+
get(id: ODValidId): ODState<any> | null;
|
|
122
|
+
remove<StateId extends keyof ODNoGeneric<IdList>>(id: StateId): IdList[StateId];
|
|
123
|
+
remove(id: ODValidId): ODState<any> | null;
|
|
124
|
+
exists(id: keyof ODNoGeneric<IdList>): boolean;
|
|
125
|
+
exists(id: ODValidId): boolean;
|
|
126
|
+
}
|
|
@@ -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.
|
|
4
|
+
"version": "0.3.5",
|
|
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.
|
|
157
|
+
this.versions.add(ODVersion.fromString("opendiscord:api","v0.3.5"))
|
|
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
|