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

633
LINES

< > BotCompany Repo | #1034047 // MKLab FM Bot with JDA 4.2 [backup before sharding]

JavaX source code (Dynamic Module) [tags: use-pretranspiled] - run with: Stefan's OS

Uses 39550K of libraries. Compilation Failed (21486L/117K).

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

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: #1034047
Snippet name: MKLab FM Bot with JDA 4.2 [backup before sharding]
Eternal ID of this version: #1034047/1
Text MD5: 5ddf0b76d5a53a1b4c8a37dd0c2681b2
Transpilation MD5: c3d4e948c87959e91dae57893dff3c81
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 14:31:40
Source code size: 22702 bytes / 633 lines
Pitched / IR pitched: No / No
Views / Downloads: 62 / 90
Referenced in: [show references]