1 | abstract sclass DynDiscordHopper > DynPrintLogAndEnabled { |
2 | !include #1034049 // Sharded Discord with JDA 4.0 |
3 | |
4 | bool discordEnabled() { ret enabled; } |
5 | bool printToModule() { true; } |
6 | |
7 | bool discordHopperMetaCmdsEnabled; |
8 | long preferredChannelID; |
9 | |
10 | transient double minutesToKeepRecordedAnswers = 60.0; |
11 | L<RecordedAnswer> recordedAnswers = syncList(); |
12 | |
13 | sclass RecordedAnswer { |
14 | long timestamp; |
15 | long questionMsgID, answerMsgID, channelID; |
16 | S questionText, answerText; |
17 | |
18 | toString { ret stdToString(this); } |
19 | } |
20 | |
21 | switchable transient bool verboseEvents; |
22 | transient new L<Runnable> onDiscordStarted; |
23 | transient L<IVF1<Event>> onDiscordEvent = syncList(); |
24 | |
25 | ListenerAdapter genericListener() { |
26 | ret new ListenerAdapter { |
27 | public void onGenericEvent(Event e) enter { |
28 | if (verboseEvents) print("Discord event: " + e); |
29 | pcallFAll(onDiscordEvent, e); |
30 | } |
31 | }; |
32 | } |
33 | |
34 | void onDiscordEvent(IVF1<Event> e) { |
35 | onDiscordEvent.add(e); |
36 | } |
37 | |
38 | enhanceFrame { |
39 | internalFramePopupMenuItem(f, "Start keep-alive module", rThread startKeepAliveModule); |
40 | minFrameSize(f, 300, 300); |
41 | } |
42 | |
43 | // Note: Breaking change - moved start to thread |
44 | start-thread { |
45 | init(); |
46 | startMe(); |
47 | } |
48 | |
49 | void startMe { |
50 | dm_vmBus_onMessage_q incomingDiscordMessage(voidfunc(Map map) { |
51 | if (!enabled) ret; |
52 | O module = map.get('module); |
53 | if (!dm_isMe(module)) ret; |
54 | S s = getString content(map); |
55 | |
56 | long channelID = toLong(map.get('channelID)); |
57 | if (channelID != 0 && preferredChannelID == 0) |
58 | setField(preferredChannelID := channelID); |
59 | |
60 | cleanRecordedAnswers(); |
61 | |
62 | S answer = answer(s, map); |
63 | sendReply(map, answer); |
64 | }); |
65 | if (!enabled) ret; |
66 | print("Starting Discord"); |
67 | startDiscord(); |
68 | print("Started Discord"); |
69 | discord.addEventListener(genericListener()); |
70 | pcallFAll(onDiscordStarted); |
71 | } |
72 | |
73 | void sendReply(Map map, S answer) { |
74 | if (empty(answer)) ret; |
75 | cleanRecordedAnswers(); |
76 | new RecordedAnswer a; |
77 | if (minutesToKeepRecordedAnswers > 0) { |
78 | a.timestamp = now(); |
79 | a.questionMsgID = getLong msgID(map); |
80 | a.channelID = getLong channelID(map); |
81 | a.questionText = getString content(map); |
82 | a.answerText = answer; |
83 | print("Recording answer : " + a); |
84 | recordedAnswers.add(a); |
85 | change(); |
86 | } |
87 | |
88 | dm_rcall reply(map.get('module), map, answer, (IVF1<Message>) msg -> { |
89 | a.answerMsgID = rcall_long getIdLong(msg); |
90 | change(); |
91 | }); |
92 | } |
93 | |
94 | S answer(S s, Map map) { |
95 | if (discordHopperMetaCmdsEnabled && eq(s, "!bot count")) |
96 | ret lstr(dm_activeDiscordTokens()); |
97 | |
98 | if (eq(s, "!ip address")) |
99 | ret myPublicIP(); |
100 | |
101 | LS tokens = extractPossibleDiscordTokens(s); |
102 | for unnull (S token : tokens) { |
103 | if (getLong guildID(map) != 0) |
104 | ret "Send tokens only in private messages!!"; |
105 | |
106 | S answer = "That's a Discord token!"; |
107 | if (contains(concatLists((LLS) vmBus_queryAll activeDiscordTokens()), token)) |
108 | answer += " And I'm there already."; |
109 | else { |
110 | answer += " Jumping there!!"; |
111 | dm_showNewModuleWithParams(dm_moduleLibID(), discordToken := token); |
112 | } |
113 | reply(map, answer); |
114 | } |
115 | |
116 | null; |
117 | } |
118 | |
119 | S atSelf() { ret discordAt(discordBotID); } |
120 | |
121 | // sometimes mentions come in like this? |
122 | S atExclamSelf() { ret discordAtExclam(discordBotID); } |
123 | |
124 | void onDiscordStarted(Runnable r) { onDiscordStarted.add(r); } |
125 | |
126 | // OVERRIDE ME |
127 | void init { |
128 | dm_watchField discordToken(r { if (nempty(discordToken) && enabled) dm_reloadModule() }); |
129 | dm_reloadOnFieldChange enabled(); |
130 | } |
131 | |
132 | void cleanRecordedAnswers { |
133 | RecordedAnswer a; |
134 | bool change; |
135 | synchronized(recordedAnswers) { |
136 | while ((a = first(recordedAnswers)) != null |
137 | && elapsedMinutes_timestamp(a.timestamp) > minutesToKeepRecordedAnswers) { |
138 | print("Removing old answer: " + a); |
139 | recordedAnswers.remove(a); |
140 | } |
141 | set change; |
142 | } |
143 | if (change) change(); |
144 | } |
145 | |
146 | afterVisualize { |
147 | if (empty(discordToken)) |
148 | containerAddFirst(buttons, jThreadedButton("Give me a token!", r { |
149 | temp enter(); dm_stringFieldDialog discordToken(); |
150 | })); |
151 | |
152 | addToContainer(buttons, jPopDownButton_noText( |
153 | "Export all my data...", rThread { dm_exportStructureToTextFileDialog(module()) }, |
154 | "Import all my data...", rThread { dm_importStructureFromTextFileDialog(module()) } |
155 | )); |
156 | } |
157 | |
158 | bool warnOnDelete() { true; } |
159 | |
160 | // API |
161 | |
162 | S migrateToType(S moduleLibID) { |
163 | S moduleID = assertNotNull(dm_showNewModuleWithParams(moduleLibID, +discordToken)); |
164 | print("Migrated to new module " + moduleID + ", disabling me"); |
165 | setEnabled(false); |
166 | dm_reload(); // actually disable Discord |
167 | ret moduleID; |
168 | } |
169 | } |
Began life as a copy of #1034050
download show line numbers debug dex old transpilations
Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj
No comments. add comment
Snippet ID: | #1034057 |
Snippet name: | DynShardedDiscordHopper renamed |
Eternal ID of this version: | #1034057/1 |
Text MD5: | 888cfdc239f0ded59625b0b414223897 |
Author: | stefan |
Category: | javax / discord / a.i. |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2022-01-19 17:21:22 |
Source code size: | 4994 bytes / 169 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 107 / 139 |
Referenced in: | [show references] |