1 | import net.dv8tion.jda.core.events.guild.member.*; |
2 | import net.dv8tion.jda.core.Permission; |
3 | import net.dv8tion.jda.core.requests.restaction.MessageAction; |
4 | import net.dv8tion.jda.core.requests.RestAction; |
5 | import java.util.function.Consumer; |
6 | |
7 | switchable S discordToken; |
8 | S discordBotName; |
9 | long discordBotID; |
10 | bool reactToBots = true; |
11 | transient bool sendToPostedLinesCRUD = true; |
12 | |
13 | transient JDA discord; |
14 | transient Color discordImageEmbedMysteriousLineColor = colorFromHex("36393f"); |
15 | transient L<IVF2<MessageChannel, S>> onPostedInChannel = syncList(); |
16 | //transient bool debugPosting; |
17 | |
18 | // when a discord action failed |
19 | transient Consumer<Throwable> onQueueError = error -> { |
20 | temp enter(); |
21 | printStackTrace(error); |
22 | }; |
23 | |
24 | void startDiscord { |
25 | if (!discordEnabled()) ret with print("Not enabled"); |
26 | |
27 | vm_cleanPrints(); // avoid Swing Unicode problem |
28 | logModuleOutput(); // good idea for chat bots |
29 | |
30 | // TODO: log JDA output going to System.out/err |
31 | discord = discordBot(new ListenerAdapter { |
32 | @Override |
33 | public void onMessageUpdate(MessageUpdateEvent e) pcall { |
34 | temp enter(); |
35 | ret if !discordEnabled() || !licensed(); |
36 | |
37 | Message msg = e.getMessage(); |
38 | long msgID = msg.getIdLong(); |
39 | User user = e.getAuthor(); |
40 | long userID = user == null ? 0 : user.getIdLong(); |
41 | S content = e.getMessage().getContentRaw(); |
42 | MessageChannel channel = e.getChannel(); |
43 | long channelID = channel.getIdLong(); |
44 | S rendered = msgID + ": " + content; |
45 | //print("Message edited: " + rendered); |
46 | |
47 | vmBus_send editedDiscordMessage( |
48 | litmapparams(event := e, module := dm_me(), +msgID, +msg, +content, |
49 | +channelID, +channel, +user, +userID)); |
50 | |
51 | O lineConcept = dm_discord_lineForMsgID_unimported(msgID); |
52 | if (lineConcept == null) ret; // logging module not enabled |
53 | call(lineConcept, '_setField, editedText := content); |
54 | } |
55 | |
56 | @Override |
57 | public void onMessageReceived(MessageReceivedEvent e) pcall { |
58 | temp enter(); |
59 | ret if !discordEnabled() || !licensed(); |
60 | |
61 | // send out raw event |
62 | vmBus_send discord_onMessageReceived(module(), e); |
63 | |
64 | User user = e.getAuthor(); |
65 | if (!reactToUser(user)) ret with print("Not reacting to user " + user); |
66 | bool bot = user.isBot(); |
67 | |
68 | long msgID = e.getMessage().getIdLong(); |
69 | long userID = user.getIdLong(); |
70 | Guild guild = e.getGuild(); |
71 | long guildID = toLong(call(guild, 'getIdLong); |
72 | Member member = e.getMember(); |
73 | S userName = member == null ? null : member.getNickname(); // the changeable nick name - null sometimes? |
74 | if (userName == null && member != null) userName = member.getEffectiveName(); |
75 | if (userName == null) userName = e.getAuthor().getName(); |
76 | |
77 | final Message msg = e.getMessage(); |
78 | |
79 | MessageChannel channel = e.getChannel(); |
80 | long channelID = channel.getIdLong(); |
81 | //print("Channel type: " + e.getChannelType()); |
82 | bool isPrivate = e.getChannelType() == ChannelType.PRIVATE; |
83 | S content = trim(msg.getContentRaw()); |
84 | print("Msg from " + userName + ": " + content); |
85 | |
86 | vmBus_send incomingDiscordMessage( |
87 | litmapparams(fromBot := bot, module := dm_me(), +msgID, +userID, +userName, event := e, +guildID, +guild, +member, |
88 | +msg, |
89 | +content, +isPrivate, +channelID, +channel)); |
90 | } |
91 | |
92 | @Override |
93 | public void onMessageReactionAdd(MessageReactionAddEvent e) pcall { |
94 | temp enter(); |
95 | ret if !discordEnabled() || !licensed(); |
96 | MessageReaction r = e.getReaction(); |
97 | |
98 | User user = e.getUser(); |
99 | if (!reactToUser(user)) ret; |
100 | bool bot = user.isBot(); |
101 | |
102 | long msgID = r.getMessageIdLong(); |
103 | S emoji = r.getReactionEmote().getName(); |
104 | |
105 | vmBus_send('incomingDiscordReaction, litmapparams( |
106 | reaction := r, |
107 | fromBot := bot, +user, userID := user.getIdLong(), |
108 | module := dm_me(), +msgID, +emoji |
109 | )); |
110 | } |
111 | |
112 | @Override |
113 | public void onMessageReactionRemove(MessageReactionRemoveEvent e) pcall { |
114 | temp enter(); |
115 | ret if !discordEnabled() || !licensed(); |
116 | MessageReaction r = e.getReaction(); |
117 | |
118 | User user = e.getUser(); |
119 | if (!reactToUser(user)) ret; |
120 | bool bot = user.isBot(); |
121 | |
122 | long msgID = r.getMessageIdLong(); |
123 | S emoji = r.getReactionEmote().getName(); |
124 | |
125 | vmBus_send('discordReactionRemoved, litmapparams( |
126 | reaction := r, |
127 | fromBot := bot, +user, userID := user.getIdLong(), |
128 | module := dm_me(), +msgID, +emoji |
129 | )); |
130 | } |
131 | |
132 | @Override |
133 | public void onMessageReactionRemoveAll(MessageReactionRemoveAllEvent e) pcall { |
134 | temp enter(); |
135 | print("TODO: onMessageReactionRemoveAll"); |
136 | } |
137 | |
138 | @Override |
139 | public void onGuildMemberJoin(GuildMemberJoinEvent event) pcall { |
140 | temp enter(); |
141 | print("Got guild join"); |
142 | ret if !discordEnabled() || !licensed(); |
143 | |
144 | print("Join >> getting user ID"); |
145 | long userID = rcall_long getIdLong(rcall getUser(event.getMember())); |
146 | print("Join >> sending"); |
147 | vmBus_send('discordGuildJoin, litmapparams( |
148 | module := dm_me(), +event, +userID, guildID := discord_guildIDFromEvent_gen(event) |
149 | )); |
150 | } |
151 | |
152 | @Override |
153 | public void onGuildMemberLeave(GuildMemberLeaveEvent event) pcall { |
154 | temp enter(); |
155 | print("Got guild leave"); |
156 | ret if !discordEnabled() || !licensed(); |
157 | |
158 | long userID = rcall_long getIdLong(rcall getUser(event.getMember())); |
159 | vmBus_send('discordGuildLeave, litmapparams( |
160 | module := dm_me(), +event, +userID |
161 | )); |
162 | } |
163 | }, token := discordToken); |
164 | |
165 | dm_registerAs('liveDiscordModule); |
166 | dm_vmBus_answerToMessage activeDiscordTokens(func -> LS { ll(discordToken) }); |
167 | pcall { |
168 | setField(discordBotName := jda_selfUserName(discord)); |
169 | setField(discordBotID := jda_selfUserID(discord)); |
170 | } |
171 | print("Bot name: " + discordBotName); |
172 | } |
173 | |
174 | void cleanMeUp { |
175 | if (discord != null) pcall { |
176 | print("Shutting down discord"); |
177 | discord.shutdown(); |
178 | print("Bot shut down"); |
179 | } |
180 | discord = null; |
181 | } |
182 | |
183 | O userConcept(User user) { |
184 | S crud = dm_gazelle_linesCRUD(); |
185 | O userConcept = dm_call(crud, 'uniqUser, user.getIdLong()); |
186 | dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName())); |
187 | ret userConcept; |
188 | } |
189 | |
190 | // API |
191 | |
192 | MessageChannel getChannel(long channelID) { |
193 | ret discord.getTextChannelById(channelID); |
194 | } |
195 | |
196 | |
197 | void postInChannel(long channelID, S msg) { |
198 | postInChannel(channelID, msg, null); |
199 | } |
200 | |
201 | void postInChannel(long channelID, S msg, IVF1<Message> onPost) { |
202 | if (channelID == 0) ret; |
203 | postInChannel(getChannel(channelID), msg, onPost); |
204 | } |
205 | |
206 | void uploadFileInChannel(long channelID, File file, S fileName, S msg, IVF1<Message> onPost) { |
207 | if (channelID == 0) ret; |
208 | uploadFileInChannel(getChannel(channelID), file, fileName, msg, onPost); |
209 | } |
210 | |
211 | void postInChannel(MessageChannel channel, S msg, IVF1<Message> onPost) { |
212 | S originalMsg = msg; |
213 | msg = shortenForDiscord(msg); |
214 | if (empty(msg)) ret; |
215 | |
216 | S postID = !sendToPostedLinesCRUD ? null : (S) dm_pcall(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg); |
217 | |
218 | print("Posting in channel " + channel + ": " + msg); |
219 | pcallFAll(onPostedInChannel, channel, msg); |
220 | MessageAction a = channel.sendMessage(msg); |
221 | if (msg != originalMsg) |
222 | a.addFile(toUtf8(originalMsg), genericTextFileName()); |
223 | a.queue(m -> msgPosted(m, onPost, postID), onQueueError); |
224 | } |
225 | |
226 | S genericTextFileName() { |
227 | ret ymd_minus_hms() + ".txt"; |
228 | } |
229 | |
230 | void msgPosted(Message m, IVF1<Message> onPost, S postID) enter { |
231 | if (nempty(postID)) |
232 | dm_call(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + m.getId()); |
233 | long msgId = m.getIdLong(); |
234 | print("I sent msg: " + msgId); |
235 | pcallF(onPost, m); |
236 | } |
237 | |
238 | void postInChannel(S channel, S msg) { |
239 | long id = dm_discord_channelID(channel); |
240 | if (id == 0) fail("Channel not found: " + channel); |
241 | postInChannel(id, msg); |
242 | } |
243 | |
244 | void postInChannel(MessageChannel channel, S msg) { |
245 | postInChannel(channel, msg, null); |
246 | } |
247 | |
248 | void postImage(Map msgMap, S url) { |
249 | postImage(msgMap, url, ""); |
250 | } |
251 | |
252 | void postImage(Map msgMap, S url, S description) { |
253 | postImage((MessageChannel) get channel(msgMap), url, description); |
254 | } |
255 | |
256 | void postImage(MessageChannel channel, S url, S description) { |
257 | print("Posting image: " + url); |
258 | channel.sendMessage( |
259 | new EmbedBuilder() |
260 | .setImage(absoluteURL(url)) |
261 | //.setTitle(unnull(title)) |
262 | .setDescription(unnull(description)) |
263 | .setColor(discordImageEmbedMysteriousLineColor) |
264 | .build()).queue(null, onQueueError); // TODO: posted lines |
265 | } |
266 | |
267 | void editMessage(long channelID, long msgID, S text) { |
268 | getChannel(channelID).editMessageById(str(msgID), text).queue(null, onQueueError); |
269 | } |
270 | |
271 | void sendPM(long userID, S _text) { |
272 | S text = shortenForDiscord(_text); |
273 | if (empty(text)) ret; |
274 | print("Sending PM to " + userID + ": " + text); |
275 | discord.getUserById(userID).openPrivateChannel().queue(channel -> { |
276 | channel.sendMessage(text).queue(null, onQueueError); |
277 | }, onQueueError); |
278 | print("PM queued"); |
279 | } |
280 | |
281 | void reply(Map msgMap, S text) { |
282 | reply(msgMap, text, null); |
283 | } |
284 | |
285 | void reply(Map msgMap, S text, O onPost) { |
286 | if (empty(text = trim(text))) ret; |
287 | postInChannel((MessageChannel) msgMap.get('channel), text, toIVF1(onPost)); |
288 | } |
289 | |
290 | void iAmTyping(Map msgMap) pcall { |
291 | ((MessageChannel) msgMap.get('channel)).sendTyping().queue(null, onQueueError); |
292 | } |
293 | |
294 | S startKeepAliveModule() enter { |
295 | ret dm_discord_startKeepAliveModule(module()); |
296 | } |
297 | |
298 | bool reactToUser(User user) { |
299 | bool bot = user.isBot(); |
300 | |
301 | // don't react to other bots |
302 | if (bot && !reactToBots) false; |
303 | |
304 | // don't read msgs from myself |
305 | if (user.getIdLong() == discordBotID) false; |
306 | |
307 | true; |
308 | } |
309 | |
310 | void uploadFileInChannel(MessageChannel channel, File file, S fileName, S msg, IVF1<Message> onPost) { |
311 | msg = shortenForDiscord(msg); |
312 | MessageAction a = empty(msg) |
313 | ? channel.sendFile(file, fileName) |
314 | : channel.sendMessage(msg).addFile(file, fileName); |
315 | a.queue(m -> msgPosted(m, onPost, null), onQueueError); |
316 | } |
317 | |
318 | void uploadFileInChannel(MessageChannel channel, byte[] data, S fileName, S msg, IVF1<Message> onPost) { |
319 | msg = shortenForDiscord(msg); |
320 | MessageAction a = empty(msg) |
321 | ? channel.sendFile(data, fileName) |
322 | : channel.sendMessage(msg).addFile(data, fileName); |
323 | a.queue(m -> msgPosted(m, onPost, null), onQueueError); |
324 | } |
325 | |
326 | void uploadFileInChannel(long channelID, byte[] data, S fileName, S msg, IVF1<Message> onPost) { |
327 | uploadFileInChannel(getChannel(channelID), data, fileName, msg, onPost); |
328 | } |
329 | |
330 | void <A> queue(RestAction<A> action) { |
331 | action.queue(null, onQueueError); |
332 | } |
Began life as a copy of #1022869
download show line numbers debug dex old transpilations
Travelled to 8 computer(s): bhatertpkbcr, cfunsshuasjs, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1023434 |
Snippet name: | use #1026292 instead - Discord Include [just Discord reading & posting] |
Eternal ID of this version: | #1023434/92 |
Text MD5: | cf148c2a035ad8754a89945261316ad6 |
Author: | stefan |
Category: | javax / discord |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-12-15 20:17:56 |
Source code size: | 10955 bytes / 332 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 511 / 1957 |
Version history: | 91 change(s) |
Referenced in: | [show references] |