Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

380
LINES

< > BotCompany Repo | #1034049 // Sharded Discord Include [JDA 4.x]

JavaX fragment (include)

1  
import net.dv8tion.jda.api.events.guild.member.*;
2  
import net.dv8tion.jda.api.Permission;
3  
import net.dv8tion.jda.api.requests.restaction.MessageAction;
4  
import net.dv8tion.jda.api.requests.RestAction;
5  
import java.util.function.Consumer;
6  
7  
switchable S discordToken;
8  
9  
S discordBotName;
10  
long discordBotID;
11  
12  
bool reactToBots = true;
13  
transient bool sendToPostedLinesCRUD = true;
14  
transient bool escapeAtEveryone;
15  
transient bool membersIntent; // is members intent needed (reacting to user join/leave)
16  
17  
transient ShardManager discord;
18  
transient Color discordImageEmbedMysteriousLineColor = colorFromHex("36393f");
19  
transient L<IVF2<MessageChannel, S>> onPostedInChannel = syncList();
20  
//transient bool debugPosting;
21  
22  
// when a discord action failed
23  
transient Consumer<Throwable> onQueueError = error -> {
24  
  temp enter();
25  
  printStackTrace(error);
26  
};
27  
28  
void startDiscord {
29  
  if (!discordEnabled()) ret with print("Not enabled");
30  
  
31  
  vm_cleanPrints(); // avoid Swing Unicode problem
32  
  logModuleOutput(); // good idea for chat bots
33  
34  
  // TODO: log JDA output going to System.out/err
35  
  discord = shardedDiscordBot40(new ListenerAdapter {
36  
    @Override
37  
    public void onMessageUpdate(MessageUpdateEvent e) pcall {
38  
      temp enter();
39  
      ret if !discordEnabled() || !licensed();
40  
      
41  
      Message msg = e.getMessage();
42  
      long msgID = msg.getIdLong();
43  
      User user = e.getAuthor();
44  
      long userID = user == null ? 0 : user.getIdLong();
45  
      S content = e.getMessage().getContentRaw();
46  
      MessageChannel channel = e.getChannel();
47  
      long channelID = channel.getIdLong();
48  
      S rendered = msgID + ": " + content;
49  
      //print("Message edited: " + rendered);
50  
      
51  
      vmBus_send editedDiscordMessage(
52  
        litmapparams(event := e, module := dm_me(), +msgID, +msg, +content,
53  
          +channelID, +channel, +user, +userID));
54  
          
55  
      O lineConcept = dm_discord_lineForMsgID_unimported(msgID);
56  
      if (lineConcept == null) ret; // logging module not enabled
57  
      call(lineConcept, '_setField, editedText := content);
58  
    }
59  
    
60  
    @Override
61  
    public void onMessageReceived(MessageReceivedEvent e) pcall {
62  
      temp enter();
63  
      ret if !discordEnabled() || !licensed();
64  
      
65  
      // send out raw event
66  
      vmBus_send discord_onMessageReceived(module(), e);
67  
      
68  
      User user = e.getAuthor();
69  
      if (!reactToUser(user)) ret with print("Not reacting to user " + user);
70  
      bool bot = user.isBot();
71  
72  
      long msgID = e.getMessage().getIdLong();
73  
      long userID = user.getIdLong();
74  
      Guild guild = e.isFromType(ChannelType.TEXT) ? e.getGuild() : null;
75  
      long guildID = toLong(call(guild, 'getIdLong);
76  
      Member member = e.getMember();
77  
      S userName = member == null ? null : member.getNickname(); // the changeable nick name - null sometimes?
78  
      if (userName == null && member != null) userName = member.getEffectiveName();
79  
      if (userName == null) userName = e.getAuthor().getName();
80  
      
81  
      final Message msg = e.getMessage();
82  
      
83  
      MessageChannel channel = e.getChannel();
84  
      long channelID = channel.getIdLong();
85  
      //print("Channel type: " + e.getChannelType());
86  
      bool isPrivate = e.getChannelType() == ChannelType.PRIVATE;
87  
      S content = trim(msg.getContentRaw());
88  
      //print("Msg from " + userName + ": " + content);
89  
90  
      vmBus_send incomingDiscordMessage(
91  
        litmapparams(fromBot := bot, module := dm_me(), +msgID, +userID, +userName, event := e, +guildID, +guild, +member,
92  
          +msg,
93  
          +content, +isPrivate, +channelID, +channel));
94  
    }
95  
    
96  
    @Override
97  
    public void onMessageReactionAdd(MessageReactionAddEvent e) pcall {
98  
      temp enter();
99  
      ret if !discordEnabled() || !licensed();
100  
      MessageReaction r = e.getReaction();
101  
      
102  
      User user = e.getUser();
103  
      if (!reactToUser(user)) ret;
104  
      bool bot = user.isBot();
105  
      
106  
      long msgID = r.getMessageIdLong();
107  
      S emoji = r.getReactionEmote().getName();
108  
      
109  
      vmBus_send('incomingDiscordReaction, litmapparams(
110  
        reaction := r,
111  
        fromBot := bot, +user, userID := user.getIdLong(),
112  
        module := dm_me(), +msgID, +emoji
113  
      ));
114  
    }
115  
    
116  
    @Override
117  
    public void onMessageReactionRemove(MessageReactionRemoveEvent e) pcall {
118  
      temp enter();
119  
      ret if !discordEnabled() || !licensed();
120  
      MessageReaction r = e.getReaction();
121  
      
122  
      User user = e.getUser();
123  
      if (!reactToUser(user)) ret;
124  
      bool bot = user.isBot();
125  
      
126  
      long msgID = r.getMessageIdLong();
127  
      S emoji = r.getReactionEmote().getName();
128  
      
129  
      vmBus_send('discordReactionRemoved, litmapparams(
130  
        reaction := r,
131  
        fromBot := bot, +user, userID := user.getIdLong(),
132  
        module := dm_me(), +msgID, +emoji
133  
      ));
134  
    }
135  
    
136  
    @Override
137  
    public void onMessageReactionRemoveAll(MessageReactionRemoveAllEvent e) pcall {
138  
      temp enter();
139  
      print("TODO: onMessageReactionRemoveAll");
140  
    }
141  
    
142  
    @Override
143  
    public void onGuildMemberJoin(GuildMemberJoinEvent event) pcall {
144  
      temp enter();
145  
      print("Got guild join");
146  
      ret if !discordEnabled() || !licensed();
147  
      
148  
      print("Join >> getting user ID");
149  
      long userID = rcall_long getIdLong(rcall getUser(event.getMember()));
150  
      print("Join >> sending");
151  
      vmBus_send('discordGuildJoin, litmapparams(
152  
        module := dm_me(), +event, +userID, guildID := discord_guildIDFromEvent_gen(event)
153  
      ));
154  
    }
155  
    
156  
    @Override
157  
    public void onGuildMemberLeave(GuildMemberLeaveEvent event) pcall {
158  
      temp enter();
159  
      print("Got guild leave");
160  
      ret if !discordEnabled() || !licensed();
161  
162  
      long userID = rcall_long getIdLong(rcall getUser(event.getMember()));
163  
      vmBus_send('discordGuildLeave, litmapparams(
164  
        module := dm_me(), +event, +userID
165  
      ));
166  
    } // end of JDA listener
167  
  }, token := discordToken, +membersIntent);
168  
  
169  
  dm_registerAs('liveDiscordModule);
170  
  dm_vmBus_answerToMessage activeDiscordTokens(func -> LS { ll(discordToken) });
171  
  
172  
  int safety = 0;
173  
  int nShards = nShards();
174  
  while (licensed() && ++safety < 60) {
175  
    int connected = nShardsConnected();
176  
    print(connected + " of " + n2(nShards, "shard") + " connected");
177  
    if (connected >= nShards) break;
178  
    sleepSeconds(1);
179  
  }
180  
  
181  
  User selfUser = getSelfUser();
182  
  if (selfUser == null) fail("No shards connected after 20 seconds");
183  
184  
  setField(discordBotName := selfUser.getName());
185  
  setField(discordBotID := selfUser.getIdLong());
186  
  print("Bot name: " + discordBotName);
187  
}
188  
189  
int nShards() {
190  
  ret l(discord.getShards());
191  
}
192  
193  
public int nShardsConnected() {
194  
  int n = 0;
195  
  for (JDA shard : discord.getShards()) {
196  
    if (shard.getStatus().equals(JDA.Status.CONNECTED))
197  
      n++;
198  
  }
199  
  ret n;
200  
}
201  
202  
public SelfUser getSelfUser() {
203  
  for (JDA shard : discord.getShards()) {
204  
    if (shard.getStatus().equals(JDA.Status.CONNECTED))
205  
      ret shard.getSelfUser();
206  
  }
207  
  null;
208  
}
209  
210  
void cleanMeUp {
211  
  if (discord != null) pcall {
212  
    print("Shutting down discord");
213  
    discord.shutdown();
214  
    print("Bot shut down");
215  
  }
216  
  discord = null;
217  
}
218  
219  
O userConcept(User user) {
220  
  S crud = dm_gazelle_linesCRUD();
221  
  O userConcept = dm_call(crud, 'uniqUser, user.getIdLong());
222  
  dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName()));
223  
  ret userConcept;
224  
}
225  
226  
// API
227  
228  
MessageChannel getChannel(long channelID) {
229  
  ret discord.getTextChannelById(channelID);
230  
}
231  
232  
233  
void postInChannel(long channelID, S msg) {
234  
  postInChannel(channelID, msg, null);
235  
}
236  
237  
void postInChannel(long channelID, S msg, IVF1<Message> onPost) {
238  
  if (channelID == 0) ret;
239  
  postInChannel(getChannel(channelID), msg, onPost);
240  
}
241  
242  
// returns null for your convenience
243  
S uploadFileInChannel(long channelID, File file, S fileName, S msg, IVF1<Message> onPost) {
244  
  if (channelID == 0) null;
245  
  ret uploadFileInChannel(getChannel(channelID), file, fileName, msg, onPost);
246  
}
247  
248  
void postInChannel(MessageChannel channel, S msg, IVF1<Message> onPost) {
249  
  S originalMsg = msg;
250  
  msg = ecapeAndShortenForDiscord(msg);
251  
  if (empty(msg)) ret;
252  
  
253  
  S postID = !sendToPostedLinesCRUD ? null : (S) dm_pcall(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg);
254  
255  
  print("Posting in channel " + channel + ": " + msg);
256  
  pcallFAll(onPostedInChannel, channel, msg);
257  
  MessageAction a = channel.sendMessage(msg);
258  
  if (msg != originalMsg)
259  
    a.addFile(toUtf8(originalMsg), genericTextFileName());
260  
  a.queue(m -> msgPosted(m, onPost, postID), onQueueError);
261  
}
262  
263  
S genericTextFileName() {
264  
  ret "full-output-" + ymd_minus_hms() + ".txt";
265  
}
266  
267  
void msgPosted(Message m, IVF1<Message> onPost, S postID) enter {
268  
  if (nempty(postID))
269  
    dm_call(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + m.getId());
270  
  long msgId = m.getIdLong();
271  
  print("I sent msg: " + msgId);
272  
  pcallF(onPost, m);
273  
}
274  
275  
void postInChannel(S channel, S msg) {
276  
  long id = dm_discord_channelID(channel);
277  
  if (id == 0) fail("Channel not found: " + channel);
278  
  postInChannel(id, msg);
279  
}
280  
281  
void postInChannel(MessageChannel channel, S msg) {
282  
  postInChannel(channel, msg, null);
283  
}
284  
285  
void postImage(Map msgMap, S url) {
286  
  postImage(msgMap, url, "");
287  
}
288  
289  
void postImage(Map msgMap, S url, S description) {
290  
  postImage((MessageChannel) get channel(msgMap), url, description);
291  
}
292  
293  
void postImage(MessageChannel channel, S url, S description) {
294  
  print("Posting image: " + url);
295  
  channel.sendMessage(
296  
    new EmbedBuilder()
297  
      .setImage(absoluteURL(url))
298  
      //.setTitle(unnull(title))
299  
      .setDescription(unnull(description))
300  
      .setColor(discordImageEmbedMysteriousLineColor)
301  
      .build()).queue(null, onQueueError); // TODO: posted lines
302  
}
303  
304  
void editMessage(long channelID, long msgID, S text) {
305  
  getChannel(channelID).editMessageById(str(msgID), text).queue(null, onQueueError);
306  
}
307  
308  
void sendPM(long userID, S _text) {
309  
  S text = ecapeAndShortenForDiscord(_text);
310  
  if (empty(text)) ret;
311  
  print("Sending PM to " + userID + ": " + text);
312  
  discord.getUserById(userID).openPrivateChannel().queue(channel -> {
313  
    channel.sendMessage(text).queue(null, onQueueError);
314  
  }, onQueueError);
315  
  print("PM queued");
316  
}
317  
318  
void reply(Map msgMap, S text) {
319  
  reply(msgMap, text, null);
320  
}
321  
322  
void reply(Map msgMap, S text, O onPost) {
323  
  if (empty(text = trim(text))) ret;
324  
  postInChannel((MessageChannel) msgMap.get('channel), text, toIVF1(onPost));
325  
}
326  
327  
void iAmTyping(Map msgMap) pcall {
328  
  ((MessageChannel) msgMap.get('channel)).sendTyping().queue(null, onQueueError);
329  
}
330  
331  
S startKeepAliveModule() enter {
332  
  ret dm_discord_startKeepAliveModule(module());
333  
}
334  
335  
bool reactToUser(User user) {
336  
  if (user == null) false;
337  
  
338  
  bool bot = user.isBot();
339  
  
340  
  // don't react to other bots
341  
  if (bot && !reactToBots) false;
342  
  
343  
  // don't read msgs from myself
344  
  if (user.getIdLong() == discordBotID) false;
345  
  
346  
  true;
347  
}
348  
349  
S ecapeAndShortenForDiscord(S s) {
350  
  if (escapeAtEveryone) s = escapeAtEveryone(s);
351  
  ret shortenForDiscord(s);
352  
}
353  
354  
// returns null for your convenience
355  
S uploadFileInChannel(MessageChannel channel, File file, S fileName, S msg, IVF1<Message> onPost) {
356  
  msg = ecapeAndShortenForDiscord(msg);
357  
  MessageAction a = empty(msg)
358  
    ? channel.sendFile(file, fileName)
359  
    : channel.sendMessage(msg).addFile(file, fileName);
360  
  a.queue(m -> msgPosted(m, onPost, null), onQueueError);
361  
  null;
362  
}
363  
364  
// returns null... you know the drill
365  
S uploadFileInChannel(MessageChannel channel, byte[] data, S fileName, S msg, IVF1<Message> onPost) {
366  
  msg = ecapeAndShortenForDiscord(msg);
367  
  MessageAction a = empty(msg)
368  
    ? channel.sendFile(data, fileName)
369  
    : channel.sendMessage(msg).addFile(data, fileName);
370  
  a.queue(m -> msgPosted(m, onPost, null), onQueueError);
371  
  null;
372  
}
373  
374  
S uploadFileInChannel(long channelID, byte[] data, S fileName, S msg, IVF1<Message> onPost) {
375  
  ret uploadFileInChannel(getChannel(channelID), data, fileName, msg, onPost);
376  
}
377  
378  
void <A> queue(RestAction<A> action) {
379  
  action.queue(null, onQueueError);
380  
}

Author comment

Began life as a copy of #1026292

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1034049
Snippet name: Sharded Discord Include [JDA 4.x]
Eternal ID of this version: #1034049/11
Text MD5: b97256dbd4740fe472cfb03f3efdc6df
Author: stefan
Category: javax / discord
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-01-19 18:58:58
Source code size: 12273 bytes / 380 lines
Pitched / IR pitched: No / No
Views / Downloads: 64 / 121
Version history: 10 change(s)
Referenced in: [show references]