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

602
LINES

< > BotCompany Repo | #1022488 // Discord Gazelle [v13, LIVE]

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

Uses 11217K of libraries. Click here for Pure Java version (30435L/185K).

1  
!7
2  
3  
set flag JDA40.
4  
set flag DynModule.
5  
6  
import java.util.function.Consumer;
7  
import net.dv8tion.jda.api.requests.restaction.*;
8  
9  
// Note: Bot is always started & logs lines,
10  
// but doesn't respond when enabled is false.
11  
12  
cm DiscordBot > DynPrintLogAndEnabled {
13  
  transient JDA bot;
14  
  transient new Set<Long> msgsReactedTo;
15  
  transient double deleteDelay = 60.0;
16  
  transient bool doDelete;
17  
  transient int maxMonologue = 5;
18  
  transient bool selfTalk = false;
19  
  transient Color imageEmbedMysteriousLineColor = colorFromHex("36393f");
20  
21  
  transient new InheritableThreadLocal<MessageChannel> currentChannel;
22  
  transient new InheritableThreadLocal<Message> respondingTo;
23  
  
24  
  transient new L<Long> lastPosted;
25  
  transient GazelleContextCache_v2 contextCache;
26  
  
27  
  transient Set<S> acceptablePurposes = litciset("");
28  
  
29  
  switchable S gazoogleDefaultCountry = "us";
30  
  
31  
  switchable S powerWordURL = "https://gazelle.rocks/beaHTML/274191";
32  
  
33  
  transient LS voice9speakers = tok_splitAtComma("p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p236, p237, p238, p239, p240, p241, p243, p244, p245, p246, p247, p248, p249, p250, p251, p252, p253, p254, p255, p256, p257, p258, p259, p260, p261, p262, p263, p264, p265, p266, p267, p268, p269, p270, p271, p272, p273, p274, p275, p276, p277, p278, p279, p280, p281, p282, p283, p284, p285, p286, p287, p288, p292, p293, p294, p295, p297, p298, p299, p300, p301, p302, p303, p304, p305, p306, p307, p308, p310, p311, p312, p313, p314, p316, p317, p318, p323, p326, p329, p330, p333, p334, p335, p336, p339, p340, p341, p343, p345, p347, p351, p360, p361, p362, p363, p364, p374, p376");
34  
  
35  
  transient Consumer<Throwable> onQueueError = error -> {
36  
    temp enter();
37  
    printStackTrace(error);
38  
  };
39  
  
40  
  // map user command message id to bot message id
41  
  // (for editing)
42  
  Map<Long> cmdToResponse = syncMap();
43  
44  
  start-thread {
45  
    vm_cleanPrints();
46  
    logModuleOutput();
47  
    dm_useLocalMechListCopies();
48  
    
49  
    print("Making cache");
50  
    time "Made cache" {
51  
      contextCache = new GazelleContextCache_v2(me());
52  
      ownResource(contextCache.listenToMessages());
53  
      contextCache.fill();
54  
    }
55  
    
56  
    // TODO: log JDA output
57  
    bot = discordBot40(new ListenerAdapter {
58  
      public void onMessageUpdate(MessageUpdateEvent e) pcall {
59  
        temp enter();
60  
        ret if !enabled || !licensed();
61  
        
62  
        long msgID = e.getMessage().getIdLong();
63  
        S content = e.getMessage().getContentRaw();
64  
        print(content := quote(content));
65  
        O lineConcept = dm_discord_lineForMsgID_unimported(msgID);
66  
        S rendered = msgID + ": " + content;
67  
        if (lineConcept == null)
68  
          ret with print("Weird: Message author, but not found: " + rendered);
69  
        call(lineConcept, '_setField, editedText := content);
70  
        print("Message edited: " + rendered);
71  
        
72  
        handleMessage(e, e.getAuthor(), e.getMember(), e.getMessage(), false);
73  
      }
74  
      
75  
      public void onMessageReceived(MessageReceivedEvent e) pcall {
76  
        temp enter();
77  
        ret if !licensed();
78  
        
79  
        handleMessage(e, e.getAuthor(), e.getMember(), e.getMessage(), true);
80  
      }
81  
      
82  
      void handleMessage(GenericMessageEvent e, User author, Member member, Message message, bool newMsg) {
83  
        bool bot = author.isBot();
84  
        long msgID = message.getIdLong();
85  
        long userID = author.getIdLong();
86  
        S userName = member == null ? null : member.getNickname(); // the changeable nick name - null sometimes?
87  
        if (userName == null && member != null) userName = member.getEffectiveName();
88  
        if (userName == null) userName = author.getName();
89  
        
90  
        final Message msg = message;
91  
        
92  
        print("Channel type: " + e.getChannelType());
93  
        bool isPrivate = e.getChannelType() == ChannelType.PRIVATE;
94  
        S content = trim(backtickUnquote(trim(msg.getContentRaw())));
95  
        print("Msg from " + userName + ": " + content);
96  
        if (empty(content)) ret;
97  
        fS originalContent = content;
98  
99  
        O user = userConcept(author);
100  
        
101  
        if (newMsg) {
102  
          S crud = dm_gazelle_linesCRUD();
103  
          O channel = dm_call(crud, 'uniqChannel, msg.getChannel().getIdLong());
104  
          dm_call(crud, 'cset, channel, litobjectarray(name := msg.getChannel().getName()));
105  
          
106  
          O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(+msgID));
107  
          dm_call(crud, 'cset, lineConcept, litobjectarray(
108  
            text := content,
109  
            +bot, +isPrivate,
110  
            author := user, +channel));
111  
          vmBus_send('newDiscordLine, lineConcept);
112  
        }
113  
        
114  
        ret if !enabled;
115  
        
116  
        final MessageChannel ch = e.getChannel();
117  
        long channelID = ch.getIdLong();
118  
        L<GazelleLine> linesInChannel = dm_discord_linesInChannel(channelID);
119  
        
120  
        // monologue prevention
121  
        
122  
        if (bot) {
123  
          int monologueLength = cast dm_call(dm_gazelle_linesCRUD(), 'monologueLength, channelID);
124  
          if (monologueLength >= maxMonologue) ret;
125  
          //Long last = get(lastPosted, l(lastPosted)-3);
126  
          //if (last != null && now() < last+10000) ret;
127  
        }
128  
        
129  
        temp tempSetTL(currentChannel, ch);
130  
        temp tempSetTL(respondingTo, msg);
131  
        
132  
        // module meta ("gazelle stop" etc.)
133  
        
134  
        S userForModulesManager = "discord user " + userID;
135  
        
136  
        if (!bot) {
137  
          for (T3S t : dm_gazelle_allRulesWithPurpose("moduleMeta")) {
138  
            PairS p = splitAtDoubleArrow_pair(t.a);
139  
            if (eqic(p.b, "stop module") && nempty(p.a) && matchX2(p.a, content)) {
140  
              print("Stopping modules");
141  
              postText((S) dm_call(dm_gazelle_modulesManager(), 'deleteAllModulesForUser, userForModulesManager, silentIfEmpty := true));
142  
            }
143  
          }
144  
        }
145  
146  
        new Matches m;
147  
        
148  
        try {
149  
          content = fixNewLines(content);
150  
          content = replacePrefix("!eval\n", "!eval ", content);
151  
          
152  
          content = regexpReplace_direct(content, "^=" + regexpLookahead("[\\.\\w0-9\\s\\]\\{]"), "!eval ");
153  
          
154  
          // g me
155  
          if (eqic(content, "g me"))
156  
            ret with postInChannel(ch, newMsg, msgID, author.getId());
157  
          
158  
          // G command - power word search
159  
          
160  
          if (regexpFindIC("^g\\b", content)) {
161  
            S answer = loadPageWithParams(powerWordURL, text := content, user := userName);
162  
            if (nempty(answer))
163  
              ret with postInChannel(ch, newMsg, msgID, answer);
164  
          }
165  
          
166  
          // GAZOOGLE command - TODO: find out when it's just a sentence starting with "gazoogle"
167  
          
168  
          S googleQuery = regexpFirstGroupIC("^gazoogle\\b[,:.!]*\\s*(.*)", content);
169  
          if (nempty(googleQuery)) {
170  
            /*if (newMsg)*/ sendTyping();
171  
            MapSO urlParams = litmap(gl := gazoogleDefaultCountry);
172  
            ret with postInChannel(ch, newMsg, msgID, or2_rev("Gazoogle doesn't know :(", lines(map(dm_webSearch_all(googleQuery, urlParams),
173  
              result -> result! + " <" + result.url() + ">"))));
174  
          }
175  
          
176  
          // GAZTUBE/GAZELLETUBE command (YouTube search)
177  
          
178  
          S ytQuery = regexpFirstGroupIC("^gaz\\w*tube\\b[,:.!]*\\s*(.*)", content);
179  
          if (nempty(ytQuery)) {
180  
            /*if (newMsg)*/ sendTyping();
181  
            ret with postInChannel(ch, newMsg, msgID, 
182  
              or2_rev("GazelleTube is showing a black screen :(",
183  
                mapSingle(dm_youTubeSearch(ytQuery),
184  
                  result -> result.url())));
185  
          }
186  
          
187  
          // GPIC/GAZPIC command (Pixabay search)
188  
          
189  
          S picQuery = regexpFirstGroupIC("(?:gpic|gazpic)\\b[,:.!]*\\s*(.*)", content);
190  
          if (nempty(picQuery)) {
191  
            sendTyping();
192  
            S imgURL = cast mapGet(first(pixabaySearch(picQuery)), "webformatURL");
193  
            ret with postInChannel(ch, newMsg, msgID, 
194  
              or2(imgURL, "No image found on Pixabay"));
195  
          }
196  
197  
          // gsay / gazsay / gazellesay / !say
198  
          S toSay = regexpFirstGroupIC("(?:!say|gsay|gazsay|gazellesay)\\b[,:.!]*\\s*(.*)", content);
199  
          if (nempty(toSay)) {
200  
            sendTyping();
201  
            S fileName = "speech.mp3";
202  
            
203  
            toSay = shorten(trim(toSay), 500);
204  
            if (eqic(toSay, "voices"))
205  
              ret with postInChannel(ch, newMsg, msgID, "Voice list: https://mouth.gazelle.rocks/beaHTML/22");
206  
              
207  
            LS groups = regexpFirstGroupsIC("voice\\s*(\\d+)\\b[,:.!]*\\s*(.*)", toSay);
208  
            S voiceNr = null, speaker = null;
209  
            if (groups != null) {
210  
              voiceNr = first(groups);
211  
              toSay = second(groups);
212  
              
213  
              groups = regexpFirstGroupsIC("speaker\\s*(\\S+)\\b[,:.!]*\\s*(.*)", toSay);
214  
              if (groups != null) {
215  
                speaker = first(groups);
216  
                if (eqic(speaker, "random")) {
217  
                  speaker = random(voice9speakers);
218  
                  fileName = "voice-9-" + speaker + ".mp3";
219  
                }
220  
                toSay = second(groups);
221  
              }
222  
            }
223  
            
224  
            File f = programFile(fileName);
225  
            postBinaryPageToFile(f, "https://mouth.gazelle.rocks/beaHTML/16",
226  
              page := 0, +voiceNr, +speaker, text := toSay);
227  
            ret with uploadFileInChannel(f);
228  
          }
229  
          
230  
          S evalHelp = regexpFirstGroupIC("^\\!eval[\\s:\\-]*help\\s+(.*)", content);
231  
          if (evalHelp != null)
232  
            ret with postInChannel(ch, newMsg, msgID,
233  
              htmlDemo(javaxSourceToHTML(evalHelp)));
234  
235  
          if (swicOneOf(content, "!eval ", "!fresh ", "!real-eval ", "!safe-eval ") || eqic(content, "!fresh")) {
236  
            O safetyCheck = null;
237  
            bool authed = dm_discord_userCanEval(userID);
238  
            if (swic_trim(content, "!safe-eval ", m)) {
239  
              content = "!eval " + m.rest();
240  
              authed = false;
241  
            }
242  
            
243  
            if (regexpMatches("![a-z\\-]+\\s+again", content)) {
244  
              GazelleLine last = dm_discord_nextToLastEvalByUser(userID);
245  
              if (last == null) ret with postInChannel(ch, newMsg, msgID, "No last eval found");
246  
              content = replaceSuffix("again", afterSpace(last.text), content);
247  
            }
248  
            
249  
            if (!authed)
250  
              safetyCheck = botEval_strictSafetyCheck();
251  
            
252  
            /*if (newMsg)*/ sendTyping();
253  
            
254  
            var post = voidfunc(S s) {
255  
              if (s != null)
256  
                postInChannel(ch, newMsg, msgID, shorten(ifEmpty(s, [[""]]), 1000))
257  
            };
258  
            
259  
            if (startsWith(content, "!eval ", m))
260  
              ret with dm_bot_execFreshRealEval(post, m.rest(), +safetyCheck);
261  
            else
262  
              ret with dm_bot_execEvalCmd(post, content, +safetyCheck, alwaysFresh := true);
263  
          }
264  
          
265  
          if (eqic(content, "!rule")) {
266  
            LS lines = linesInChannelBy(channelID, userID);
267  
            if (contains(nextToLast(lines), "=>"))
268  
              content = "!rule " + nextToLast(lines);
269  
            else {
270  
              if (l(lines) < 3) fail("Too few lines");
271  
              content = "!rule " + nextToNextToLast(lines) + " => " + nextToLast(lines);
272  
            }
273  
          }
274  
          
275  
          if (swicOneOf(content, m, "!rule ", "!rule\n")) {
276  
            S s = trim(m.rest());
277  
            print("Processing rule: " + s);
278  
            S opt = leadingSquareBracketStuff(s);
279  
            s = dropActuallyLeadingSquareBracketStuff(s);
280  
            if (startsWith(s, "=>"))
281  
              s = assertNotNull("No last line in channel", nextToLast(linesInChannelBy(channelID, userID))) + "\n" + s;
282  
            LS comments = ll("made by user", "discord", "tokenize out with javaTok");
283  
            if (cic(pairB(tok_splitAtDoubleArrow_pair(s)), userName)) {
284  
              s = optCurly(userName) + " says: " + s;
285  
              comments.add("with user name");
286  
            }
287  
            
288  
            gazelle_parsePublicRuleOptions(opt, comments);
289  
            s = replace(s, " + ", "\n+ ");
290  
            s = jreplace1(s, "=>", "\n=>");
291  
            s = gazelle_processSquareBracketAnnotations(s, comments);
292  
            temp tempSetTL(dm_gazelle_addRuleWithComment_renderWithoutComments, true);
293  
            dm_gazelle_addRuleWithComment(s, lines_rtrim(comments));
294  
            ret with postText(dm_gazelle_addRuleWithComment_msg!);
295  
          }
296  
          
297  
          if (swic_trim(content, "!phrase ", m)) {
298  
            gazelle_createRuleForPhrase(m.rest());
299  
            ret with postText(dm_gazelle_addRuleWithComment_msg!);
300  
          }
301  
          
302  
          // Go into normal reasoning
303  
          
304  
          if (bot && !selfTalk) ret;
305  
          
306  
          S purpose = null;
307  
          bool debug = false, skipBad = true;
308  
          Set<S> restrictToRules = null;
309  
            
310  
          bool change;
311  
          do {
312  
            change = false;
313  
            if (swic_trim(content, "!withBad ", m)) {
314  
              skipBad = false;
315  
              content = m.rest(); set change;
316  
            }
317  
            
318  
            if (swic_trim(content, "!preprocess ", m)) {
319  
              purpose = 'preprocess;
320  
              content = m.rest(); set change;
321  
            }
322  
          
323  
            if (swic_trim(content, "!debug ", m)) {
324  
              set debug;
325  
              content = m.rest(); set change;
326  
            }
327  
            
328  
            if (swic(content, "!using[", m)) {
329  
              int i = indexOf(m.rest(), ']');
330  
              restrictToRules = litset(substring(m.rest(), 0, i));
331  
              content = trimSubstring(m.rest(), i+1);
332  
            }
333  
          } while (change);
334  
          
335  
          print("debug=" + debug + ", content=" + content);
336  
          
337  
          LS preContext = takeLast(2, dropLast(collect text(linesInChannel)));
338  
          print("preContext=" + preContext);
339  
          
340  
          GazelleContextCache_v2 contextCache = DiscordBot.this.contextCache;
341  
          if (restrictToRules != null) {
342  
            contextCache = gazelle_cloneContextCache(contextCache);
343  
            Set<S> _restrictToRules = restrictToRules;
344  
            print("restrictToRules=" + restrictToRules);
345  
            contextCache.cachedCtx.engine.dropRulesWhere(r -> !contains(_restrictToRules, r.globalID));
346  
            print("Using rules: " + collect globalID(contextCache.cachedCtx.engine.rules));
347  
          }
348  
          
349  
          O[] params = litmapparams(+userName, +skipBad, +preContext,
350  
            badComments := mechCISet("Knock-out rule comments"),
351  
            acceptablePurposes := nempty(purpose)
352  
              ? litciset(purpose)
353  
              : acceptablePurposes,
354  
            respondingToHuman := !bot,
355  
            +debug,
356  
            +userID,
357  
            contextMaker := contextCache,
358  
            debugPreprocessing := true,
359  
            sendToModules := func(S input) -> L<GazelleTree> {
360  
              gazelle_wrapLinesAsTree(gazelle_answersFromModules(userForModulesManager, input))
361  
      });
362  
            
363  
          if (eq(purpose, 'preprocess))
364  
            ret with postText(lines_rtrim(takeFirst(10, gazelle_preprocess(content, params))));
365  
366  
          L<GazelleTree> l;
367  
          if (nempty(purpose))
368  
            l = gazelle_postprocess(dm_gazelle_reasonAboutChatInput_v2(userName, content, params));
369  
          else
370  
            l = gazelle_reason_repeat(content, params);
371  
372  
          int idx = 0;
373  
          for (final GazelleTree t : l) {
374  
            final int _idx = idx++;
375  
            
376  
            if (eqic(t.lineType, "temporary fact")) {
377  
              if (dm_gazelle_hasTempFact(t.line)) continue;
378  
              print("Saving temp fact: " + t.line); 
379  
              dm_gazelle_addTempFact(t.line, "discord msg " + msgID);
380  
            }
381  
            
382  
            if (eqic(t.lineType, "eval")) {
383  
              S code = t.rule().out;
384  
              if (!mechSet("Gazelle | Allowed Evals").contains(code))
385  
                print("Eval not allowed: " + code);
386  
              else {
387  
                /*if (newMsg)*/ sendTyping();
388  
                S out = strOrNull(dm_javaEvalOrInterpret(code));
389  
                if (nempty(out))
390  
                  postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
391  
                    Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,
392  
                      outMsgID := msg2.getIdLong(),
393  
                      outText := out,
394  
                      inMsgID := msg.getIdLong(),
395  
                      inUserID := userID,
396  
                      inChannelID := channelID,
397  
                      inText := originalContent,
398  
                      tree := t,
399  
                      treeIndex := _idx);
400  
                    dm_gazelle_saveReasoningForLine(reasoning);
401  
                  });
402  
              }
403  
              continue;
404  
            }
405  
            
406  
            // Now that we actually post, store recently used mapping
407  
            gazelle_storeRecentlyUsedMappings(ll(t),
408  
              context := "discord channel " + channelID);
409  
              
410  
            fS out = tok_dropCurlyBrackets(t.line);
411  
            print(">> " + t);
412  
            print("POSTING: " + out);
413  
            
414  
            postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
415  
              Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,
416  
                outMsgID := msg2.getIdLong(),
417  
                outText := out,
418  
                inMsgID := msg.getIdLong(),
419  
                inUserID := userID,
420  
                inChannelID := channelID,
421  
                inText := originalContent,
422  
                tree := t,
423  
                treeIndex := _idx);
424  
              dm_gazelle_saveReasoningForLine(reasoning);
425  
            });
426  
          }
427  
        } catch print error {
428  
          postInChannel(ch, newMsg, msgID, exceptionToStringShort(error));
429  
        }
430  
      }
431  
      
432  
      public void onMessageReactionAdd(MessageReactionAddEvent e) pcall {
433  
        temp enter();
434  
        ret if !enabled || !licensed();
435  
        MessageReaction r = e.getReaction();
436  
        bool bot = e.getUser().isBot();
437  
        long msgID = r.getMessageIdLong();
438  
        add(msgsReactedTo, msgID);
439  
        print("User " + e.getUser() + (bot ? " (bot)" : "") + " reacted to message " + msgID + " with " + r.getReactionEmote());
440  
        if (bot) ret;
441  
442  
        S crud = dm_gazelle_linesCRUD();
443  
        O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(+msgID));
444  
        L reactions = cast get(lineConcept, 'reactions);
445  
        print("lineConcept=" + lineConcept);
446  
        print("reactions=" + reactions);
447  
        O reaction = dm_call(crud, 'nuReaction, litObjectArrayAsObject(
448  
          user := userConcept(e.getUser()),
449  
          emoji := r.getReactionEmote().getName(),
450  
          created := now()));
451  
        print("reaction=" + reaction);
452  
          
453  
        dm_call(crud, 'cset, lineConcept, litobjectarray(
454  
          reactions := listPlus(reactions, reaction)));
455  
          
456  
        dm_discord_gatherFeedbackFromLine(dm_discord_importLine(lineConcept));
457  
      }
458  
    });
459  
    
460  
    dm_registerAs('discordBot);
461  
    print("Started bot");
462  
  }
463  
  
464  
  void cleanMeUp {
465  
    if (bot != null) pcall {
466  
      print("Shutting down bot");
467  
      bot.shutdown();
468  
      print("Bot shut down");
469  
    }
470  
    bot = null;
471  
  }
472  
  
473  
  O userConcept(User user) {
474  
    S crud = dm_gazelle_linesCRUD();
475  
    O userConcept = dm_call(crud, 'uniqUser, user.getIdLong());
476  
    dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName()));
477  
    ret userConcept;
478  
  }
479  
  
480  
  // API
481  
  
482  
  MessageChannel getChannel(long channelID) {
483  
    ret bot.getTextChannelById(channelID);
484  
  }
485  
  
486  
  /*void postInChannel(long channelID, S msg) {
487  
    postInChannel(getChannel(channelID), msg);
488  
  }*/
489  
  
490  
  void postInChannel(MessageChannel channel, S msg) {
491  
    postInChannel(channel, true, 0, msg);
492  
  }
493  
  
494  
  void postInChannel(MessageChannel channel, bool newMsg, long userMsgID, S msg) {
495  
    if (!newMsg) {
496  
      Long responseID = cmdToResponse.get(userMsgID);
497  
      if (responseID != null) {
498  
        editMessage(channel.getIdLong(), responseID, msg);
499  
        ret;
500  
      }
501  
    }
502  
    
503  
    S postID = cast dm_call(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg);
504  
    channel.sendMessage(msg).queue(m -> {
505  
      dm_call(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + m.getId());
506  
      cmdToResponse.put(userMsgID, m.getIdLong());
507  
      change();
508  
    });
509  
    if (l(lastPosted) > 5) popFirst(lastPosted);
510  
    lastPosted.add(now());
511  
  }
512  
  
513  
  /*void postInChannel(S channel, S msg) {
514  
    long id = dm_discord_channelID(channel);
515  
    if (id == 0) fail("Channel not found: " + channel);
516  
    postInChannel(id, msg);
517  
  }*/
518  
  
519  
  void postInChannelWithDelete(MessageChannel channel, S msg, VF1<Message> onPost) {
520  
    S postID = cast dm_call(gazelle_postedLinesCRUD(), 'postingLine, channel.getId(), msg);
521  
    channel.sendMessage(msg).queue(msg2 -> {
522  
      dm_pcall(gazelle_postedLinesCRUD(), 'donePosting, postID, "discord msg " + msg2.getId());
523  
      pcallF(onPost, msg2);
524  
      final long msgId = msg2.getIdLong();
525  
      print("I sent msg: " + msgId);
526  
      if (doDelete) doLater(deleteDelay, r {
527  
        ret if contains(msgsReactedTo, msgId);
528  
        print("Deleting msg " + msgId);
529  
        msg2.delete().queue();
530  
      });
531  
    }, error -> _handleException(error));
532  
  }
533  
  
534  
  void postText(S text) {
535  
    postInChannel(currentChannel!, text);
536  
  }
537  
  
538  
  void postImage(S url) {
539  
    postImage(currentChannel!, url);
540  
  }
541  
  
542  
  void postImage(S url, S title) {
543  
    postImage(currentChannel!, url, title);
544  
  }
545  
  
546  
  void postImage(MessageChannel channel, S url) {
547  
    postImage(channel, url, "");
548  
  }
549  
  
550  
  void postImage(MessageChannel channel, S url, S description) {
551  
    channel.sendMessage(
552  
      new EmbedBuilder()
553  
        .setImage(absoluteURL(url))
554  
        //.setTitle(unnull(title))
555  
        .setDescription(unnull(description))
556  
        .setColor(imageEmbedMysteriousLineColor)
557  
        .build()).queue(); // TODO: posted lines
558  
  }
559  
  
560  
  LS linesInChannelBy(long channelID, long userID) {
561  
    ret collect text(dm_discord_linesInChannelBy(channelID, userID));
562  
  }
563  
  
564  
  MessageChannel currentChannel() { ret currentChannel!; }
565  
  Message respondingTo() { ret respondingTo!; }
566  
  
567  
  void sendTyping() {
568  
    currentChannel().sendTyping().queue();
569  
  }
570  
  
571  
  // for testing, outdated
572  
  L<GazelleTree> reasonAbout(S line, O... _) {
573  
    ret gazelle_reasonAbout(line, paramsPlus(_, ctx := contextCache!));
574  
  }
575  
  
576  
  void rebuildCache {
577  
    contextCache.fill();
578  
  }
579  
  
580  
  void editMessage(long channelID, long msgID, S text) {
581  
    getChannel(channelID).editMessageById(str(msgID), text).queue();
582  
  }
583  
  
584  
  S respondingToUserID() {
585  
    ret respondingTo! == null ? null : "discord user " + respondingTo->getAuthor().getIdLong();
586  
  }
587  
  
588  
  void uploadFileInChannel(File file) {
589  
    uploadFileInChannel(file, "", null);
590  
  }
591  
  
592  
  void uploadFileInChannel(File file, S msg, IVF1<Message> onPost) {
593  
    if (!isFile(file)) fail("Not a file: " + file);
594  
    
595  
    var channel = currentChannel!;
596  
    S fileName = fileName(file);
597  
    MessageAction a = empty(msg)
598  
      ? channel.sendFile(file, fileName)
599  
      : channel.sendMessage(msg).addFile(file, fileName);
600  
    a.queue(onPost == null ?: m -> onPost.get(m), onQueueError);
601  
  }
602  
}

Author comment

Began life as a copy of #1022193

download  show line numbers  debug dex  old transpilations   

Travelled to 7 computer(s): bhatertpkbcr, cfunsshuasjs, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1022488
Snippet name: Discord Gazelle [v13, LIVE]
Eternal ID of this version: #1022488/86
Text MD5: a28f0c0a35fa58bcc4b2bec42eb22242
Transpilation MD5: 80ab60721d94bd0f686bba617bfdb592
Author: stefan
Category: javax / discord
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-10-18 08:31:12
Source code size: 23860 bytes / 602 lines
Pitched / IR pitched: No / No
Views / Downloads: 556 / 26162
Version history: 85 change(s)
Referenced in: [show references]