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

640
LINES

< > BotCompany Repo | #1034058 // MKLab FM Bot with JDA 4.2 [using renamed sharded classes]

JavaX source code (Dynamic Module) - run with: Stefan's OS

1  
!7
2  
3  
/*static MapSO _renameClasses = litmap(
4  
  "DynDiscordHopper" := "DynShardedDiscordHopper",
5  
  "DynServerAwareDiscordBot" := "DynShardedServerAwareDiscordBot",
6  
  "DynTalkBot2" := "DynShardedTalkBot2");*/
7  
8  
set flag JDA40.
9  
10  
!include once #1034054 // DynTalkBot2 with sharding
11  
!include once #1034056 // DynServerAwareDiscordBot
12  
!include once #1034057 // DynDiscordHopper
13  
14  
// TODO: on restart, check if we actually are in vc with voiceChannelID
15  
// (if permissions have changed)
16  
17  
// Changes in this version:
18  
// uploadImagesAsFiles
19  
// !!!listeners fixed
20  
// check if embed fields are longer than 1k
21  
22  
// Note: dm owners only takes ONE LINE
23  
// TODO: send dms only once when owner has two guilds
24  
25  
!include once #1030357 // Discord Audio
26  
27  
cmodule MKLabFM extends DynTalkBot2<.ByServer> {
28  
  switchable S radioURL = "http://streamlive2.hearthis.at:8080/26450.ogg";
29  
  switchable S currentSongURL = "https://widgets.autopo.st/widgets/public/MKLabFM/nowplaying.php";
30  
  switchable S coverArtURL = "http://95.154.196.33:26687/playingart?sid=1&rand=<RNDINT>";
31  
  switchable bool enableShadowStop = true;
32  
  switchable bool putSongInStatus = true;
33  
  transient double bufferSeconds = 30.0;
34  
  transient int grabInterval = 15; // try to grab frames quicker to see if it fixes the jumps
35  
  transient double considerDownInterval = 60.0;
36  
  
37  
  // restart stream when no frames received for this long
38  
  transient double autoRestartInterval = 20.0;
39  
  
40  
  switchable S madeBy = trim([[
41  
MKLab: <https://tinyurl.com/musikcolabradiofm>
42  
Support channel: <https://discord.gg/u6E4muY>
43  
Bot by: <https://BotCompany.de>
44  
You like this bot? Donate: <https://paypal.me/BotCompany>
45  
]]);
46  
47  
  switchable S voteLink = "\n\nPlease help us grow by voting here: <https://discordbots.org/bot/625377645982384128>";
48  
  
49  
  switchable S npMsg = "Now playing: <track> - Listen at }{ https://tinyurl.com/mklab-live-fm }{ #edm #music #radio #ibiza #dance #chillout #psy #live #miami #memes #detroit #techno #dj #synthwave #housemusic #deephouse #onair";
50  
  
51  
  switchable bool uploadImagesAsFiles = true;
52  
  
53  
  transient SimpleCircularBuffer<byte[]> buffer;
54  
  transient AudioPlayer player;
55  
  switchable transient bool simulateStreamDown, restartNow;
56  
  transient long lastFrameReceived, lastActualFrameReceived; // sys time
57  
  transient long moduleStarted; // sys time
58  
  transient NotTooOften postRestarting = onlyEveryHour();
59  
  transient S currentSong;
60  
61  
  // stats
62  
  transient new AtomicLong framesReceived;
63  
  transient new AtomicLong canProvideCalls;
64  
  transient new AtomicLong provideCalls;
65  
  transient new AtomicLong dataSent;
66  
67  
  O _getReloadData() { ret currentSong; }
68  
  void _setReloadData(S data) { currentSong = data; }
69  
  
70  
  void init {
71  
    super.init();
72  
    myName = "!!!";
73  
    makeByServer = () -> new ByServer;
74  
    dropPunctuation = false;
75  
    preprocessAtSelfToMyName = true;
76  
    ngbCommands = false;
77  
    sendToPostedLinesCRUD = false;
78  
    
79  
    // First thing, start the stream, so we can restart the radios
80  
    initStream();
81  
    
82  
    onDiscordStarted(r {
83  
      temp enter();
84  
      print("Bot back up");
85  
      for (Long id, ByServer bs : cloneMap(dataByServer)) { pcall {
86  
        bs.restart(discord.getGuildById(id));
87  
      }}
88  
      
89  
      postInChannel(nextGenBotsTestRoomID(), "Rebooted " + computerID() + "/" + dm_moduleID());
90  
    });
91  
    
92  
    onDiscordEvent(e -> {
93  
      if (e cast GenericGuildVoiceEvent)
94  
        getByServer(e.getGuild()).handleVoiceUpdateEvent(e);
95  
    });
96  
    
97  
    doEveryAndNow(20.0, r grabSong);
98  
    doEvery(60.0, r updateVoiceChannelCount_allServers);
99  
  }
100  
  
101  
  void initStream {
102  
    buffer = new SimpleCircularBuffer(iround(bufferSeconds*50));
103  
    player = lavaPlayer_createPlayer();
104  
    lavaPlayer_printPlayerEvents(player);
105  
    print("Have player");
106  
107  
    doEvery(grabInterval, r {
108  
      temp enter();
109  
      if (!simulateStreamDown) {
110  
        AudioFrame frame = player.provide();
111  
        if (frame != null) {
112  
          buffer.add(frame.getData());
113  
          inc(framesReceived);
114  
          lastFrameReceived = lastActualFrameReceived = sysNow();
115  
        }
116  
      }
117  
    });
118  
    
119  
    doEvery(1.0, r {
120  
      temp enter();
121  
      if (discord == null) ret;
122  
      if (restartNow || elapsedSeconds(lastFrameReceived) >= autoRestartInterval) {
123  
        if (postRestarting.yo())
124  
          postInChannel(nextGenBotsTestRoomID(), "Restarting radio stream");
125  
        simulateStreamDown = restartNow = false;
126  
        lastFrameReceived = sysNow();
127  
        loadTrack();
128  
        lastFrameReceived = sysNow();
129  
      }
130  
    });
131  
  }
132  
133  
  class ByServer extends DynTalkBot2.ByServer {
134  
    bool shouldBePlaying;
135  
    long voiceChannelID, lastVoiceChannelID;
136  
    long songChannelID;
137  
    
138  
    transient S voiceChannelName;
139  
    transient VoiceChannel vc;
140  
    transient MyAudioSendHandler sendHandler;
141  
    transient Set<Long> usersInVoiceChannel = synchroSet();
142  
143  
    synchronized S processSimplifiedLine(S s, O... _) enter {
144  
      try {
145  
        ret processSimplifiedLine2(s, _);
146  
      } catch print e {
147  
        if (cic(str(e), "InsufficientPermission"))
148  
          ret "There were insufficient permissions to perform this action.";
149  
        ret "Sorry, an error occurred trying to perform this action.";
150  
      }
151  
    }
152  
    
153  
    synchronized S processSimplifiedLine2(S s, O... _) {
154  
      try answer super.processSimplifiedLine(s, _); // add authorized users etc.
155  
      new Matches m;
156  
      
157  
      print("myPrefix", quote(myPrefix()));
158  
      s = dropMyPrefixOrNull(s);
159  
      print("dropped", quote(s));
160  
      if (s == null) null;
161  
      optPar Message msg;
162  
      Guild guild = msg.getGuild();
163  
      Member member = msg == null ? null : msg.getMember();
164  
      
165  
      if (eqic(s, "help")) {
166  
        optPar MessageChannel channel;
167  
        
168  
        S syntax = trim([[
169  
`@me play | start` - start playing
170  
`@me stop | pause` - stop playing
171  
`@me leave` - leave voice channel
172  
`@me help` - show this help
173  
`@me np` - show currently playing song
174  
`@me join` - join any voice channel
175  
`@me set channel` - post now playing track names in current channel
176  
`@me stop posting` - stop posting track names
177  
[MORE]
178  
]].replace("\n[MORE]", renderChannelListIfNotOne(guild))
179  
  .replace("@me ", myPrefix()))
180  
          + voteLink;
181  
  
182  
        new EmbedBuilder eb;
183  
        eb.setColor(Color.yellow);
184  
        eb.setAuthor("MKLab FM Bot", null, null);
185  
        eb.setTitle("Purpose", null);
186  
        eb.setDescription("I play MKLab FM, the best Electronic Dance Music Radio, in a voice channel of your choice.");
187  
        discordEmbed_addField(eb, "Syntax", syntax);
188  
        
189  
        if (authed(_))
190  
          eb.addField("Admin commands", rtrim([[
191  
`@me guild count/guild names` - show stats
192  
`@me reboot` - reboot the whole bot (if music stream is completely broken for some reason)
193  
`@me dm owners: <message>` - DM all guild owners where radio is playing
194  
`@me stream url http...` - set stream URL. current: $STREAMURL
195  
`@me np url http...` - set current song URL. current: $NPURL
196  
`@me cover url http...` - set cover art URL. current: $COVERURL
197  
`@me cover url -` - disable cover art
198  
`@me np msg Playing <track>...` - set nowplaying message. current: $NPMSG
199  
`@me listeners` - full list of listeners
200  
]])
201  
  .replace("@me ", myPrefix())
202  
  .replace("$STREAMURL", backtickQuote(radioURL))
203  
  .replace("$NPURL", backtickQuote(currentSongURL))
204  
  .replace("$NPMSG", backtickQuote(npMsg))
205  
  .replace("$COVERURL", backtickQuote(coverArtURL)), false);
206  
        
207  
        eb.addField("Made by", madeBy, false);
208  
        
209  
        eb.setThumbnail(imageSnippetURL(#1102722));
210  
        channel.sendMessage(eb.build()).queue();
211  
        null;
212  
      }
213  
214  
      if (guild == null) null;
215  
      
216  
      if (authed(_)) {
217  
        /*if "restart radios"
218  
          ret "OK" with restartRadios();*/
219  
        if "reboot" {
220  
          doAfter(2.0, rEnter dm_reload);
221  
          ret "OK, rebooting bot";
222  
        }
223  
        if "simulate stream down" {
224  
          simulateStreamDown = true;
225  
          ret "OK";
226  
        }
227  
        if (swic_trim(s, "dm owners:", m))
228  
          ret dmOwners($1);
229  
        
230  
        if (swic_trim(s, "stream url ", m) && isWebURL(m.rest())) {
231  
          if (!MKLabFM.this.setField(radioURL := m.rest())) ret "No change";
232  
          restartNow = true;
233  
          ret "OK, restarting with radio URL " + radioURL;
234  
        }
235  
        
236  
        if (swic_trim(s, "np url ", m) && isWebURL(m.rest())) {
237  
          if (!MKLabFM.this.setField(currentSongURL := m.rest())) ret "No change";
238  
          ret "Current song URL changed to " + currentSongURL;
239  
        }
240  
        
241  
        if (swic_trim(s, "cover url ", m)) {
242  
          if (!MKLabFM.this.setField(coverArtURL := m.rest())) ret "No change";
243  
          ret "Cover art URL changed to " + coverArtURL;
244  
        }
245  
        
246  
        if (swic_trim(s, "np msg ", m)) {
247  
          if (!MKLabFM.this.setField(npMsg := m.rest())) ret "No change";
248  
          ret "Nowplaying message changed to " + npMsg;
249  
        }
250  
        
251  
        if "listeners"
252  
          ret or2(listeningUsers_full(), "No listeners");
253  
      }
254  
255  
      if (matchX2("play ...|start...|radio|radio on", s))
256  
        ret action_play(guild, member);
257  
        
258  
      if (matchX2("stop ...|pause ...|quiet|silence|shut up|... off", s) && !match("stop posting", s)) 
259  
        ret action_stop(guild);
260  
261  
      if (matchX2("leave|leave channel|leave voice channel", s))
262  
        ret action_leave(guild);
263  
264  
      if (matchX2("join|join channel|join voice channel", s))
265  
        ret action_join(guild, member);
266  
267  
      if (matchX2("... channel *", s, m) && isInteger($1))
268  
        ret action_selectChannel(guild, parseInt($1));
269  
        
270  
      if (matchX2("np|n p|... song ...|... playing ...", s))
271  
        ret "MKLab FM is currently playing: " + (nempty(currentSong) ? backtickQuote(currentSong) : "Hmm. Not sure, I think something went wrong.");
272  
      
273  
      if (matchX2("set channel|post here|set np", s)) {
274  
        long channelID = longPar channelID(_);
275  
        if (channelID == 0) ret "Please do this in a channel";
276  
        if (songChannelID != channelID) {
277  
          songChannelID = channelID;
278  
          change();
279  
        }
280  
        doAfter(2.0, r postSong);
281  
        ret "OK, I will post the currently playing tracks in this channel. Type `" + atSelfOrMyName_space() + "stop posting` to stop.";
282  
      }
283  
      
284  
      if (matchX2("stop posting|no channel", s)) {
285  
        songChannelID = 0;
286  
        change();
287  
        ret "OK";
288  
      }
289  
      
290  
      if "users in channel" 
291  
        ret "Users in voice channel: " + n2(usersInVoiceChannel);
292  
        
293  
      if "internal stats"
294  
        ret "Frames received: " + n2(framesReceived!)
295  
          + ", canProvide calls: " + n2(canProvideCalls!)
296  
          + ", provide calls: " + n2(provideCalls!)
297  
          + ", bytes sent: " + n2(dataSent!);
298  
299  
      null;
300  
    }
301  
    
302  
    S renderGuildCount() {
303  
      L<Guild> guilds = discord.getGuilds();
304  
      int shouldPlayCount = 0, shadowCount = 0;
305  
      for (Guild guild : guilds) {
306  
        ByServer bs = getByServer(guild.getIdLong(), true);
307  
        if (bs != null) {
308  
          //if (bs.playing) ++playCount;
309  
          if (bs.shouldBePlaying) {
310  
            ++shouldPlayCount;
311  
            if (bs.shadowStopped()) ++shadowCount;
312  
          }
313  
        }
314  
      }
315  
      ret super.renderGuildCount() + ". " + nRadios(shouldPlayCount-shadowCount) + " playing to " + n2(listeningUsers(), "listener") + ", " + shadowCount + " playing without listeners";
316  
    }
317  
318  
    void joinVoiceChannel(Guild guild) {
319  
      vc = guild.getVoiceChannelById(voiceChannelID);
320  
      guild.getAudioManager().openAudioConnection(vc);
321  
      voiceChannelName = vc.getName();
322  
      updateVoiceChannelCount();
323  
    }
324  
    
325  
    void updateVoiceChannelCount {
326  
      if (vc == null) ret with usersInVoiceChannel.clear();
327  
      L<Member> members = vc.getMembers();
328  
      int n = l(usersInVoiceChannel);
329  
      replaceCollection(usersInVoiceChannel,
330  
        mapNonNulls_pcall(members, m -> {
331  
          User user = m.getUser();
332  
          ret user.isBot() ? null : user.getIdLong();
333  
        }));
334  
      int n2 = l(usersInVoiceChannel);
335  
      if (n != n2) print(guildID + " voice channel count corrected: " + n + " -> " + n2);
336  
    }
337  
    
338  
    bool shadowStopped() {
339  
      ret enableShadowStop && empty(usersInVoiceChannel);
340  
    }
341  
    
342  
    class MyAudioSendHandler implements AudioSendHandler {
343  
      long counter = -1;
344  
      byte[] data;
345  
      
346  
      public bool canProvide() enter {
347  
        inc(canProvideCalls);
348  
        if (!shouldBePlaying) false;
349  
        if (shadowStopped()) {
350  
          counter = -1;
351  
          false;
352  
        }
353  
        
354  
        synchronized(buffer) {
355  
          if (!buffer.isFull()) false;
356  
          
357  
          // out of range? go back to middle
358  
          data = buffer.get(counter);
359  
          if (data == null) {
360  
            long oldCounter = counter;
361  
            counter = buffer.getBase()+buffer.size()/2;
362  
            if (oldCounter >= 0) printWithTime("RESETTING " + oldCounter + " => " + counter
363  
              + " (delta=" + (counter-oldCounter) + ")");
364  
            data = buffer.get(counter);
365  
          }
366  
          ++counter;
367  
          true;
368  
        }
369  
      }
370  
    
371  
      public java.nio.ByteBuffer provide20MsAudio() {
372  
        inc(provideCalls);
373  
        inc(dataSent, l(data));
374  
        java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(data);
375  
        ret buf;
376  
      }
377  
    
378  
      public bool isOpus() { true; }
379  
    }
380  
    
381  
    // when already in channel
382  
    S startPlaying(Guild guild) {
383  
      AudioManager am = guild.getAudioManager();
384  
      sendHandler = new MyAudioSendHandler;
385  
      am.setSendingHandler(sendHandler);
386  
387  
      ret "Playing radio in channel " + quote(voiceChannelName);
388  
    }
389  
390  
    // try to select the best channel
391  
    // -the one the user is in, or
392  
    // -the one that was used before, or
393  
    // -just any channel.
394  
    // returns error message or null
395  
    S selectAVoiceChannel(Guild guild, Member member) {
396  
      print("selectAVoiceChannel member=" + member);
397  
      if (member != null) {
398  
        GuildVoiceState vs = member.getVoiceState();
399  
        VoiceChannel vc = vs.getChannel();
400  
        print("selectAVoiceChannel vc=" + vc);
401  
        if (vc != null) {
402  
          voiceChannelID = lastVoiceChannelID = vc.getIdLong();
403  
          ret null with change();
404  
        }
405  
      }
406  
      
407  
      if (voiceChannelID != 0) null;
408  
      L<VoiceChannel> allChannels = guild.getVoiceChannels();
409  
      L<VoiceChannel> channels = filter(allChannels, c -> isJoinableVoiceChannel(guild, c));
410  
411  
      if (empty(channels))
412  
        if (empty(allChannels))
413  
          ret "Please create a voice channel.";
414  
        else
415  
          ret "Sorry, I am not allowed to enter any of your voice channels. Maybe my invite link was wrong?"; // TODO: show better link
416  
417  
      // Check if lastVoiceChannelID still exists
418  
      if (lastVoiceChannelID != 0 && !hasWhereMethodReturns getIdLong(channels, lastVoiceChannelID)) {
419  
        lastVoiceChannelID = 0; change();
420  
      }
421  
422  
      voiceChannelID = lastVoiceChannelID != 0 ? lastVoiceChannelID
423  
        : first(channels).getIdLong();
424  
      change();
425  
      null;
426  
    }
427  
428  
    S action_play(Guild guild, Member member) {
429  
      if (shouldBePlaying && sendHandler != null) ret "I am playing already!";
430  
      try answer selectAVoiceChannel(guild, member);
431  
      joinVoiceChannel(guild);
432  
      shouldBePlaying = true; change();
433  
      ret startPlaying(guild);
434  
    }
435  
436  
    S action_stop(Guild guild) {
437  
      if (shouldBePlaying) {
438  
        shouldBePlaying = false; change();
439  
        ret "OK, radio switched off";
440  
      }
441  
      ret "I am stopped, I think. No?";
442  
    }
443  
    
444  
    S action_leave(Guild guild) {
445  
      voiceChannelID = 0; change();
446  
      this.vc = null;
447  
      GuildVoiceState state = getSelfMember(guild).getVoiceState();
448  
      VoiceChannel vc = state.getChannel();
449  
      if (vc == null) ret "I don't think I am in any voice channel";
450  
      guild.getAudioManager().closeAudioConnection();
451  
      ret "OK, leaving channel " + quote(vc.getName());
452  
    }
453  
    
454  
    S action_join(Guild guild, Member member) {
455  
      GuildVoiceState state = getSelfMember(guild).getVoiceState();
456  
      VoiceChannel vc = state.getChannel();
457  
      try answer selectAVoiceChannel(guild, member);
458  
      joinVoiceChannel(guild);
459  
      ret "OK, joined voice channel " + quote(voiceChannelName);
460  
    }
461  
462  
    S action_selectChannel(Guild guild, int i) {
463  
      L<VoiceChannel> channels = guild.getVoiceChannels();
464  
      channels = filter(channels, c -> isJoinableVoiceChannel(guild, c));
465  
      VoiceChannel vc = _get(channels, i-1);
466  
      if (vc == null) ret "Sorry, I count only " + nChannels(channels);
467  
      voiceChannelID = lastVoiceChannelID = vc.getIdLong();
468  
      change();
469  
      joinVoiceChannel(guild);
470  
      ret "OK, joined voice channel " + quote(voiceChannelName);
471  
    }
472  
473  
    S renderChannelListIfNotOne(Guild guild) {
474  
      if (guild == null) ret "";
475  
      L<VoiceChannel> channels = guild.getVoiceChannels();
476  
      channels = filter(channels, c -> isJoinableVoiceChannel(guild, c));
477  
      if (empty(channels)) ret "\nNote: You don't have any voice channels.";
478  
      if (l(channels) == 1) ret "";
479  
      new LS out;
480  
      for i over channels:
481  
        out.add("`@me join channel " + (i+1) + "` - join channel " + quote(channels.get(i).getName()));
482  
      ret "\n" + lines_rtrim(out);
483  
    }
484  
    
485  
    bool isJoinableVoiceChannel(Guild guild, VoiceChannel vc) {
486  
      if (vc == null || guild == null) false;
487  
      try {
488  
        ret guild.getSelfMember().hasPermission(vc, Permission.VOICE_CONNECT);
489  
      } catch print e { ret false; }
490  
    }
491  
    
492  
    // normal restart (on bot load)
493  
    void restart(Guild guild) {
494  
      if (guild == null) ret; // no longer in guild
495  
      if (shouldBePlaying) {
496  
        print("RESTARTING RADIO in " + guild);
497  
        print(action_play(guild, null));
498  
      } else if (voiceChannelID != 0) {
499  
        print("Rejoining voice channel in " + guild);
500  
        print(action_join(guild, null));
501  
      }
502  
    }
503  
    
504  
    // restart with stop (after stream interruption) - no longer needed
505  
    /*void hardRestart(Guild guild) {
506  
      restart(guild);
507  
    }*/
508  
509  
    void postSong {     
510  
      if (songChannelID == 0) ret;
511  
      postInChannel(songChannelID, npMsg.replace("<track>", backtickQuote(currentSong)));
512  
      if (isWebURL(coverArtURL)) {
513  
        S imageURL = appendQueryToURL(coverArtURL, rand := randomInt());
514  
        if (uploadImagesAsFiles)
515  
          uploadFileInChannel(songChannelID, loadBinaryPage(imageURL), "cover-art.jpg", "", null);
516  
        else
517  
          postImage(getChannel(songChannelID), imageURL, "");
518  
      }
519  
    }
520  
    
521  
    void handleUserJoined(long userID, VoiceChannel joined) {
522  
      if (joined != null && joined.getIdLong() == voiceChannelID) {
523  
        usersInVoiceChannel.add(userID);
524  
        print(guildID + " adding user to voice channel: " + userID + " (n=" + l(usersInVoiceChannel));
525  
      }
526  
    }
527  
    
528  
    void handleVoiceUpdateEvent(GenericGuildVoiceEvent e) {
529  
      User user = ((GenericGuildVoiceEvent) e).getMember().getUser();
530  
      if (user.isBot()) ret;
531  
      long userID = user.getIdLong();
532  
      if (e cast GuildVoiceUpdateEvent) {
533  
        VoiceChannel left = e.getChannelLeft();
534  
        if (e.getChannelLeft().getIdLong() == voiceChannelID) {
535  
          usersInVoiceChannel.remove(userID);
536  
          print(guildID + " removed user from voice channel: " + userID + " (n=" + l(usersInVoiceChannel));
537  
        }
538  
      }
539  
      if (e cast GuildVoiceMoveEvent)
540  
        handleUserJoined(userID, e.getChannelJoined());
541  
      if (e cast GuildVoiceJoinEvent)
542  
        handleUserJoined(userID, e.getChannelJoined());
543  
    }
544  
  } // end of ByServer
545  
  
546  
  /*void restartRadios {
547  
    L<Guild> guilds = discord.getGuilds();
548  
    for (Guild guild : guilds) pcall {
549  
      ByServer bs = getByServer(guild);
550  
      if (bs != null)
551  
        bs.hardRestart(guild);
552  
    }
553  
  }*/
554  
  
555  
  S dmOwners(S msg) {
556  
    L<Guild> guilds = discord.getGuilds();
557  
    int n = 0;
558  
    for (Guild guild : guilds) pcall {
559  
      ByServer bs = getByServer(guild.getIdLong(), true);
560  
      if (bs != null && bs.shouldBePlaying) {
561  
        gazelle_dmGuildOwner(guild, msg);
562  
        ++n;
563  
      }
564  
    }
565  
    ret n2(n, "guild owner") + " contacted.";
566  
  }
567  
  
568  
  void loadTrack {
569  
    if (!isURL(radioURL)) ret;
570  
    lavaPlayer_loadItem(module(), lavaPlayer_playerManager(), player, radioURL.replace("<RNDINT>", str(randomInt())));
571  
  }
572  
  
573  
  S currentSong() {
574  
    temp tempSetTL(loadPage_sizeLimit, 128*1024L);
575  
    if (l(currentSongURL) <= 1) ret "";
576  
    if (cic(currentSongURL, "autopo.st/"))
577  
      ret trackTitleFromAutoPostWidget(loadPageWithTimeout(currentSongURL, 10.0));
578  
    if (cic(currentSongURL, "caster.fm/"))
579  
      ret (S) mapGet(jsonDecodeMap(loadPageWithHeaders(currentSongURL,
580  
        "X-Requested-With", "XMLHttpRequest")), "playing");
581  
    if (endsWith(currentSongURL, "status.xsl"))
582  
      ret trackTitleFromIceCastStatusXSL(loadPageWithTimeout(currentSongURL, 10.0));
583  
    ret loadPageWithTimeout(currentSongURL, 10.0);
584  
  }
585  
  
586  
  void grabSong enter {
587  
    if (discord == null) ret;
588  
    S song = currentSong();
589  
    bool post = nempty(currentSong) && nempty(song) && neq(currentSong, song);
590  
    currentSong = song;
591  
    if (post)
592  
      postSong_allServers();
593  
    if (putSongInStatus) {
594  
      S s = currentSong;
595  
      if (elapsedSeconds(lastActualFrameReceived) >= considerDownInterval && elapsedSeconds(moduleStarted) >= considerDownInterval)
596  
        s = "Stream currently down";
597  
      if (nempty(s))
598  
        gazelle_discord_playingGame(s);
599  
    }
600  
  }
601  
  
602  
  void postSong_allServers {
603  
    L<Guild> guilds = discord.getGuilds();
604  
    for (Guild guild : guilds) pcall {
605  
      ByServer bs = getByServer(guild);
606  
      if (bs != null)
607  
        bs.postSong();
608  
    }
609  
  }
610  
  
611  
  void updateVoiceChannelCount_allServers {
612  
    if (discord == null) ret;
613  
    for (ByServer bs : cloneValues(dataByServer))
614  
      bs.updateVoiceChannelCount();
615  
  }
616  
  
617  
  int listeningUsers() {
618  
    int n = 0;
619  
    for (Guild guild : discord.getGuilds()) {
620  
      ByServer bs = getByServer(guild.getIdLong(), true);
621  
      if (bs != null && bs.shouldBePlaying)
622  
        n += l(bs.usersInVoiceChannel);
623  
    }
624  
    ret n;
625  
  }
626  
  
627  
  S listeningUsers_full() {
628  
    new LS out;
629  
    for (Guild guild : discord.getGuilds()) {
630  
      ByServer bs = getByServer(guild.getIdLong(), true);
631  
      if (bs != null && bs.shouldBePlaying) {
632  
        L<Long> users = cloneList(bs.usersInVoiceChannel);
633  
        if (empty(users)) continue;
634  
        out.add(guild.getName() + ": playing to " + joinWithComma(map(users,
635  
          id -> or2(discord_memberIDToEffectiveName(guild, id), str(id)))));
636  
      }
637  
    }
638  
    ret lines(out);
639  
  }
640  
}

Author comment

Began life as a copy of #1030356

download  show line numbers  debug dex  old transpilations   

Travelled to 2 computer(s): bhatertpkbcr, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1034058
Snippet name: MKLab FM Bot with JDA 4.2 [using renamed sharded classes]
Eternal ID of this version: #1034058/1
Text MD5: 600c28bfec582627dddc1eca5ea430b0
Author: stefan
Category: javax
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-01-19 17:27:47
Source code size: 23008 bytes / 640 lines
Pitched / IR pitched: No / No
Views / Downloads: 53 / 61
Referenced in: [show references]