import net.dv8tion.jda.api.events.guild.member.*; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.requests.restaction.MessageAction; import net.dv8tion.jda.api.requests.RestAction; import java.util.function.Consumer; switchable S discordToken; S discordBotName; long discordBotID; bool reactToBots = true; transient bool sendToPostedLinesCRUD = true; transient bool escapeAtEveryone; transient bool membersIntent; // is members intent needed (reacting to user join/leave) transient ShardManager discord; transient Color discordImageEmbedMysteriousLineColor = colorFromHex("36393f"); transient L<IVF2<MessageChannel, S>> onPostedInChannel = syncList(); //transient bool debugPosting; // when a discord action failed transient Consumer<Throwable> onQueueError = error -> { temp enter(); printStackTrace(error); }; void startDiscord { if (!discordEnabled()) ret with print("Not enabled"); vm_cleanPrints(); // avoid Swing Unicode problem logModuleOutput(); // good idea for chat bots // TODO: log JDA output going to System.out/err discord = shardedDiscordBot40(new ListenerAdapter { @Override public void onMessageUpdate(MessageUpdateEvent e) pcall { temp enter(); ret if !discordEnabled() || !licensed(); Message msg = e.getMessage(); long msgID = msg.getIdLong(); User user = e.getAuthor(); long userID = user == null ? 0 : user.getIdLong(); S content = e.getMessage().getContentRaw(); MessageChannel channel = e.getChannel(); long channelID = channel.getIdLong(); S rendered = msgID + ": " + content; //print("Message edited: " + rendered); vmBus_send editedDiscordMessage( litmapparams(event := e, module := dm_me(), +msgID, +msg, +content, +channelID, +channel, +user, +userID)); O lineConcept = dm_discord_lineForMsgID_unimported(msgID); if (lineConcept == null) ret; // logging module not enabled call(lineConcept, '_setField, editedText := content); } @Override public void onMessageReceived(MessageReceivedEvent e) pcall { temp enter(); ret if !discordEnabled() || !licensed(); // send out raw event vmBus_send discord_onMessageReceived(module(), e); User user = e.getAuthor(); if (!reactToUser(user)) ret with print("Not reacting to user " + user); bool bot = user.isBot(); long msgID = e.getMessage().getIdLong(); long userID = user.getIdLong(); Guild guild = e.isFromType(ChannelType.TEXT) ? e.getGuild() : null; long guildID = toLong(call(guild, 'getIdLong); Member member = e.getMember(); S userName = member == null ? null : member.getNickname(); // the changeable nick name - null sometimes? if (userName == null && member != null) userName = member.getEffectiveName(); if (userName == null) userName = e.getAuthor().getName(); final Message msg = e.getMessage(); MessageChannel channel = e.getChannel(); long channelID = channel.getIdLong(); //print("Channel type: " + e.getChannelType()); bool isPrivate = e.getChannelType() == ChannelType.PRIVATE; S content = trim(msg.getContentRaw()); //print("Msg from " + userName + ": " + content); vmBus_send incomingDiscordMessage( litmapparams(fromBot := bot, module := dm_me(), +msgID, +userID, +userName, event := e, +guildID, +guild, +member, +msg, +content, +isPrivate, +channelID, +channel)); } @Override public void onMessageReactionAdd(MessageReactionAddEvent e) pcall { temp enter(); ret if !discordEnabled() || !licensed(); MessageReaction r = e.getReaction(); User user = e.getUser(); if (!reactToUser(user)) ret; bool bot = user.isBot(); long msgID = r.getMessageIdLong(); S emoji = r.getReactionEmote().getName(); vmBus_send('incomingDiscordReaction, litmapparams( reaction := r, fromBot := bot, +user, userID := user.getIdLong(), module := dm_me(), +msgID, +emoji )); } @Override public void onMessageReactionRemove(MessageReactionRemoveEvent e) pcall { temp enter(); ret if !discordEnabled() || !licensed(); MessageReaction r = e.getReaction(); User user = e.getUser(); if (!reactToUser(user)) ret; bool bot = user.isBot(); long msgID = r.getMessageIdLong(); S emoji = r.getReactionEmote().getName(); vmBus_send('discordReactionRemoved, litmapparams( reaction := r, fromBot := bot, +user, userID := user.getIdLong(), module := dm_me(), +msgID, +emoji )); } @Override public void onMessageReactionRemoveAll(MessageReactionRemoveAllEvent e) pcall { temp enter(); print("TODO: onMessageReactionRemoveAll"); } @Override public void onGuildMemberJoin(GuildMemberJoinEvent event) pcall { temp enter(); print("Got guild join"); ret if !discordEnabled() || !licensed(); print("Join >> getting user ID"); long userID = rcall_long getIdLong(rcall getUser(event.getMember())); print("Join >> sending"); vmBus_send('discordGuildJoin, litmapparams( module := dm_me(), +event, +userID, guildID := discord_guildIDFromEvent_gen(event) )); } @Override public void onGuildMemberLeave(GuildMemberLeaveEvent event) pcall { temp enter(); print("Got guild leave"); ret if !discordEnabled() || !licensed(); long userID = rcall_long getIdLong(rcall getUser(event.getMember())); vmBus_send('discordGuildLeave, litmapparams( module := dm_me(), +event, +userID )); } // end of JDA listener }, token := discordToken, +membersIntent); dm_registerAs('liveDiscordModule); dm_vmBus_answerToMessage activeDiscordTokens(func -> LS { ll(discordToken) }); int safety = 0; int nShards = nShards(); while (licensed() && ++safety < 60) { int connected = nShardsConnected(); print(connected + " of " + n2(nShards, "shard") + " connected"); if (connected >= nShards) break; sleepSeconds(1); } User selfUser = getSelfUser(); if (selfUser == null) fail("No shards connected after 20 seconds"); setField(discordBotName := selfUser.getName()); setField(discordBotID := selfUser.getIdLong()); print("Bot name: " + discordBotName); } int nShards() { ret l(discord.getShards()); } public int nShardsConnected() { int n = 0; for (JDA shard : discord.getShards()) { if (shard.getStatus().equals(JDA.Status.CONNECTED)) n++; } ret n; } public SelfUser getSelfUser() { for (JDA shard : discord.getShards()) { if (shard.getStatus().equals(JDA.Status.CONNECTED)) ret shard.getSelfUser(); } null; } void cleanMeUp { if (discord != null) pcall { print("Shutting down discord"); discord.shutdown(); print("Bot shut down"); } discord = null; } O userConcept(User user) { S crud = dm_gazelle_linesCRUD(); O userConcept = dm_call(crud, 'uniqUser, user.getIdLong()); dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName())); ret userConcept; } // API MessageChannel getChannel(long channelID) { ret discord.getTextChannelById(channelID); } void postInChannel(long channelID, S msg) { postInChannel(channelID, msg, null); } void postInChannel(long channelID, S msg, IVF1<Message> onPost) { if (channelID == 0) ret; postInChannel(getChannel(channelID), msg, onPost); } // returns null for your convenience S uploadFileInChannel(long channelID, File file, S fileName, S msg, IVF1<Message> onPost) { if (channelID == 0) null; ret uploadFileInChannel(getChannel(channelID), file, fileName, msg, onPost); } void postInChannel(MessageChannel channel, S msg, IVF1<Message> onPost) { S originalMsg = msg; msg = ecapeAndShortenForDiscord(msg); if (empty(msg)) ret; S postID = !sendToPostedLinesCRUD ? null : (S) dm_pcall(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg); print("Posting in channel " + channel + ": " + msg); pcallFAll(onPostedInChannel, channel, msg); MessageAction a = channel.sendMessage(msg); if (msg != originalMsg) a.addFile(toUtf8(originalMsg), genericTextFileName()); a.queue(m -> msgPosted(m, onPost, postID), onQueueError); } S genericTextFileName() { ret "full-output-" + ymd_minus_hms() + ".txt"; } void msgPosted(Message m, IVF1<Message> onPost, S postID) enter { if (nempty(postID)) dm_call(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + m.getId()); long msgId = m.getIdLong(); print("I sent msg: " + msgId); pcallF(onPost, m); } void postInChannel(S channel, S msg) { long id = dm_discord_channelID(channel); if (id == 0) fail("Channel not found: " + channel); postInChannel(id, msg); } void postInChannel(MessageChannel channel, S msg) { postInChannel(channel, msg, null); } void postImage(Map msgMap, S url) { postImage(msgMap, url, ""); } void postImage(Map msgMap, S url, S description) { postImage((MessageChannel) get channel(msgMap), url, description); } void postImage(MessageChannel channel, S url, S description) { print("Posting image: " + url); channel.sendMessage( new EmbedBuilder() .setImage(absoluteURL(url)) //.setTitle(unnull(title)) .setDescription(unnull(description)) .setColor(discordImageEmbedMysteriousLineColor) .build()).queue(null, onQueueError); // TODO: posted lines } void editMessage(long channelID, long msgID, S text) { getChannel(channelID).editMessageById(str(msgID), text).queue(null, onQueueError); } void sendPM(long userID, S _text) { S text = ecapeAndShortenForDiscord(_text); if (empty(text)) ret; print("Sending PM to " + userID + ": " + text); discord.getUserById(userID).openPrivateChannel().queue(channel -> { channel.sendMessage(text).queue(null, onQueueError); }, onQueueError); print("PM queued"); } void reply(Map msgMap, S text) { reply(msgMap, text, null); } void reply(Map msgMap, S text, O onPost) { if (empty(text = trim(text))) ret; postInChannel((MessageChannel) msgMap.get('channel), text, toIVF1(onPost)); } void iAmTyping(Map msgMap) pcall { ((MessageChannel) msgMap.get('channel)).sendTyping().queue(null, onQueueError); } S startKeepAliveModule() enter { ret dm_discord_startKeepAliveModule(module()); } bool reactToUser(User user) { if (user == null) false; bool bot = user.isBot(); // don't react to other bots if (bot && !reactToBots) false; // don't read msgs from myself if (user.getIdLong() == discordBotID) false; true; } S ecapeAndShortenForDiscord(S s) { if (escapeAtEveryone) s = escapeAtEveryone(s); ret shortenForDiscord(s); } // returns null for your convenience S uploadFileInChannel(MessageChannel channel, File file, S fileName, S msg, IVF1<Message> onPost) { msg = ecapeAndShortenForDiscord(msg); MessageAction a = empty(msg) ? channel.sendFile(file, fileName) : channel.sendMessage(msg).addFile(file, fileName); a.queue(m -> msgPosted(m, onPost, null), onQueueError); null; } // returns null... you know the drill S uploadFileInChannel(MessageChannel channel, byte[] data, S fileName, S msg, IVF1<Message> onPost) { msg = ecapeAndShortenForDiscord(msg); MessageAction a = empty(msg) ? channel.sendFile(data, fileName) : channel.sendMessage(msg).addFile(data, fileName); a.queue(m -> msgPosted(m, onPost, null), onQueueError); null; } S uploadFileInChannel(long channelID, byte[] data, S fileName, S msg, IVF1<Message> onPost) { ret uploadFileInChannel(getChannel(channelID), data, fileName, msg, onPost); } void <A> queue(RestAction<A> action) { action.queue(null, onQueueError); }
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: | 112 / 177 |
Version history: | 10 change(s) |
Referenced in: | #1034050 - DynShardedDiscordHopper [dev.] #1034057 - DynShardedDiscordHopper renamed |