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

253
LINES

< > BotCompany Repo | #1021750 // Discord Bot [Gazelle-based, v9, with statements]

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

Uses 9211K of libraries. Click here for Pure Java version (18188L/108K).

1  
!7
2  
3  
set flag DynModule.
4  
5  
sclass ReasoningForLine {
6  
  long timestamp = now();
7  
  long outMsgID;
8  
  S outText;
9  
  long inMsgID, inUserID, inChannelID;
10  
  S inText;
11  
  GazelleTree tree;
12  
  int treeIndex; // line chosen from tree
13  
}
14  
15  
cmodule DiscordBot > DynPrintLogAndEnabled {
16  
  transient JDA bot;
17  
  transient new Set<Long> msgsReactedTo;
18  
  transient double deleteDelay = 60.0;
19  
  transient bool doDelete;
20  
  transient Color imageEmbedMysteriousLineColor = colorFromHex("36393f");
21  
  transient bool skipBad = true;
22  
  
23  
  transient new InheritableThreadLocal<MessageChannel> currentChannel;
24  
  transient new InheritableThreadLocal<Message> respondingTo;
25  
26  
  start {
27  
    logModuleOutput();
28  
    dm_useLocalMechListCopies();
29  
    // TODO: log JDA output
30  
    bot = discordBot(new ListenerAdapter {
31  
      public void onMessageReceived(MessageReceivedEvent e) pcall {
32  
        ret if !enabled || !licensed();
33  
        
34  
        bool bot = e.getAuthor().isBot();
35  
        
36  
        final Message msg = e.getMessage();
37  
        
38  
        print("Channel type: " + e.getChannelType());
39  
        bool isPrivate = e.getChannelType() == ChannelType.PRIVATE;
40  
        print("Msg from " + e.getAuthor().getName() + ": " + e.getMessage().getContentDisplay());
41  
        S content = trim(msg.getContentRaw());
42  
        if (empty(content)) ret;
43  
        fS originalContent = content;
44  
45  
        O user = userConcept(e.getAuthor());
46  
        long userID = e.getAuthor().getIdLong();
47  
        S userName = e.getAuthor().getName();
48  
        
49  
        S crud = dm_gazelle_linesCRUD();
50  
        O channel = dm_call(crud, 'uniqChannel, msg.getChannel().getIdLong());
51  
        dm_call(crud, 'cset, channel, litobjectarray(name := msg.getChannel().getName()));
52  
        
53  
        O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(msgID := e.getMessage().getIdLong()));
54  
        dm_call(crud, 'cset, lineConcept, litobjectarray(
55  
          text := content,
56  
          +bot, +isPrivate,
57  
          author := user, +channel));
58  
        vmBus_send('newDiscordLine, lineConcept);
59  
        
60  
        ret if bot;
61  
        
62  
        final MessageChannel ch = e.getChannel();
63  
        long channelID = ch.getIdLong();
64  
        new Matches m;
65  
        
66  
        try {
67  
        
68  
          if (swicOneOf(content, "!eval ", "!fresh ", "!real-eval", "!safe-eval ") || eqic(content, "!fresh")) {
69  
            O safetyCheck = null;
70  
            bool authed = dm_discord_userCanEval(userID);
71  
            if (swic(content, "!safe-eval ", m)) {
72  
              content = "!eval " + m.rest();
73  
              authed = false;
74  
            }
75  
            
76  
            if (!authed) {
77  
              //ret with postInChannel(ch, "Sorry, you're not authed to eval");
78  
              safetyCheck = func(S code) -> S {
79  
                S safety = joinWithComma(getCodeFragmentSafety(code));
80  
                if (eq(safety, 'safe)) ret "";
81  
                S s = "Sorry. Safety level is " + safety;
82  
                Set<S> unknown = codeAnalysis_getUnknownIdentifiers(code);
83  
                if (nempty(unknown)) s += ". Unknown identifiers: " + joinWithComma(unknown);
84  
                ret s;
85  
              };
86  
            }
87  
            temp tempSetTL(currentChannel, ch);
88  
            temp tempSetTL(respondingTo, msg);
89  
            ret with dm_bot_execEvalCmd(voidfunc(S s) {
90  
              if (s != null)
91  
                postInChannel(ch, shorten(s, 1000))
92  
            }, content, +safetyCheck);
93  
          }
94  
          
95  
          if (eqic(content, "!rule")) {
96  
            LS lines = linesInChannelBy(channelID, userID);
97  
            if (contains(nextToLast(lines), "=>"))
98  
              content = "!rule " + nextToLast(lines);
99  
            else {
100  
              if (l(lines) < 3) fail("Too few lines");
101  
              content = "!rule " + nextToNextToLast(lines) + " => " + nextToLast(lines);
102  
            }
103  
          }
104  
          
105  
          if (swic_trim(content, "!rule ", m)) {
106  
            S s = m.rest();
107  
            S opt = leadingSquareBracketStuff(s);
108  
            s = dropActuallyLeadingSquareBracketStuff(s);
109  
            if (startsWith(s, "=>"))
110  
              s = assertNotNull("No last line in channel", nextToLast(linesInChannelBy(channelID, userID))) + "\n" + s;
111  
            LS comments = ll("discord");
112  
            if (cic(pairB(tok_splitAtDoubleArrow_pair(s)), userName)) {
113  
              s = optCurly(userName) + " says: " + s;
114  
              comments.add("with user name");
115  
            }
116  
            if (match("with *", opt, m) && remoteMechListMirror_isPublic($1))
117  
              comments.add("use helper table mech list " + quote($1));
118  
            s = replace(s, " + ", "\n+ ");
119  
            s = gazelle_processSquareBracketAnnotations(s, comments);
120  
            dm_gazelle_addRuleWithComment(s, lines_rtrim(comments));
121  
            ret with postInChannel(ch, dm_gazelle_addRuleWithComment_msg!);
122  
          }
123  
          
124  
          L<GazelleTree> l = dm_gazelle_reasonAboutChatInput(userName, content, +skipBad);
125  
          
126  
          int idx = 0;
127  
          for (final GazelleTree t : takeFirst(10, l)) {
128  
            final int _idx = idx++;
129  
            fS out = tok_dropCurlyBrackets(t.line);
130  
            print(">> " + t);
131  
            postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
132  
              ReasoningForLine reasoning = nu(ReasoningForLine,
133  
                outMsgID := msg2.getIdLong(),
134  
                outText := out,
135  
                inMsgID := msg.getIdLong(),
136  
                inUserID := userID,
137  
                inChannelID := channelID,
138  
                inText := originalContent,
139  
                tree := t,
140  
                treeIndex := _idx);
141  
              saveGZStructToFileVerbose(javaxDataDir("Discord Reasonings/" + reasoning.outMsgID + ".gz"), reasoning);
142  
            });
143  
          }
144  
        } catch print error {
145  
          postInChannel(ch, exceptionToStringShort(error));
146  
        }
147  
      }
148  
      
149  
      public void onMessageReactionAdd(MessageReactionAddEvent e) pcall {
150  
        ret if !enabled || !licensed();
151  
        MessageReaction r = e.getReaction();
152  
        bool bot = e.getUser().isBot();
153  
        long msgID = r.getMessageIdLong();
154  
        add(msgsReactedTo, msgID);
155  
        print("User " + e.getUser() + (bot ? " (bot)" : "") + " reacted to message " + msgID + " with " + r.getReactionEmote());
156  
        if (bot) ret;
157  
158  
        S crud = dm_gazelle_linesCRUD();
159  
        O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(+msgID));
160  
        L reactions = cast get(lineConcept, 'reactions);
161  
        print("lineConcept=" + lineConcept);
162  
        print("reactions=" + reactions);
163  
        O reaction = dm_call(crud, 'nuReaction, litObjectArrayAsObject(
164  
          user := userConcept(e.getUser()),
165  
          emoji := r.getReactionEmote().getName(),
166  
          created := now()));
167  
        print("reaction=" + reaction);
168  
          
169  
        dm_call(crud, 'cset, lineConcept, litobjectarray(
170  
          reactions := listPlus(reactions, reaction)));
171  
          
172  
        dm_discord_gatherFeedbackFromLine(dm_discord_importLine(lineConcept));
173  
      }
174  
    });
175  
    
176  
    dm_registerAs('discordBot);
177  
    print("Started bot");
178  
  }
179  
  
180  
  void cleanMeUp {
181  
    if (bot != null) pcall {
182  
      print("Shutting down bot");
183  
      bot.shutdown();
184  
      print("Bot shut down");
185  
    }
186  
    bot = null;
187  
  }
188  
  
189  
  O userConcept(User user) {
190  
    S crud = dm_gazelle_linesCRUD();
191  
    O userConcept = dm_call(crud, 'uniqUser, user.getIdLong());
192  
    dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName()));
193  
    ret userConcept;
194  
  }
195  
  
196  
  // API
197  
  
198  
  void postInChannel(long channelID, S msg) {
199  
    postInChannel(bot.getTextChannelById(channelID), msg);
200  
  }
201  
  
202  
  void postInChannel(MessageChannel channel, S msg) {
203  
    channel.sendMessage(msg).queue();
204  
  }
205  
  
206  
  void postInChannel(S channel, S msg) {
207  
    long id = dm_discord_channelID(channel);
208  
    if (id == 0) fail("Channel not found: " + channel);
209  
    postInChannel(id, msg);
210  
  }
211  
  
212  
  void postInChannelWithDelete(MessageChannel channel, S msg, VF1<Message> onPost) {
213  
    channel.sendMessage(msg).queue(msg2 -> {
214  
      pcallF(onPost, msg2);
215  
      final long msgId = msg2.getIdLong();
216  
      print("I sent msg: " + msgId);
217  
      if (doDelete) doLater(deleteDelay, r {
218  
        ret if contains(msgsReactedTo, msgId);
219  
        print("Deleting msg " + msgId);
220  
        msg2.delete().queue();
221  
      });
222  
    });
223  
  }
224  
  
225  
  void postImage(S url) {
226  
    postImage(currentChannel!, url);
227  
  }
228  
  
229  
  void postImage(S url, S title) {
230  
    postImage(currentChannel!, url, title);
231  
  }
232  
  
233  
  void postImage(MessageChannel channel, S url) {
234  
    postImage(channel, url, "");
235  
  }
236  
  
237  
  void postImage(MessageChannel channel, S url, S description) {
238  
    channel.sendMessage(
239  
      new EmbedBuilder()
240  
        .setImage(absoluteURL(url))
241  
        //.setTitle(unnull(title))
242  
        .setDescription(unnull(description))
243  
        .setColor(imageEmbedMysteriousLineColor)
244  
        .build()).queue();
245  
  }
246  
  
247  
  LS linesInChannelBy(long channelID, long userID) {
248  
    ret collect text(dm_discord_linesInChannelBy(channelID, userID));
249  
  }
250  
  
251  
  MessageChannel currentChannel() { ret currentChannel!; }
252  
  Message respondingTo() { ret respondingTo!; }
253  
}

Author comment

Began life as a copy of #1021671

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: #1021750
Snippet name: Discord Bot [Gazelle-based, v9, with statements]
Eternal ID of this version: #1021750/9
Text MD5: 8163d64e5b46ed32d02dca6f5ef00e01
Transpilation MD5: 426e4693713cb487f3b0bf2f70453d4a
Author: stefan
Category: javax / discord
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-02-26 15:17:35
Source code size: 9327 bytes / 253 lines
Pitched / IR pitched: No / No
Views / Downloads: 284 / 479
Version history: 8 change(s)
Referenced in: [show references]