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

336
LINES

< > BotCompany Repo | #1021781 // Discord Bot [Gazelle-based, v10, with self-talk & looking at history, OLD]

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

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

1  
!7
2  
3  
set flag DynModule.
4  
5  
cmodule DiscordBot > DynPrintLogAndEnabled {
6  
  transient JDA bot;
7  
  transient new Set<Long> msgsReactedTo;
8  
  transient double deleteDelay = 60.0;
9  
  transient bool doDelete;
10  
  transient int maxMonologue = 5;
11  
  transient Color imageEmbedMysteriousLineColor = colorFromHex("36393f");
12  
13  
  transient new InheritableThreadLocal<MessageChannel> currentChannel;
14  
  transient new InheritableThreadLocal<Message> respondingTo;
15  
  
16  
  transient new L<Long> lastPosted;
17  
  transient new GazelleContextCache contextCache;
18  
  
19  
  transient Set<S> allowedEvals = lithashset("gazelle_startListMakerForUser()");
20  
  
21  
  transient Set<S> acceptablePurposes = litciset("");
22  
23  
  start {
24  
    logModuleOutput();
25  
    dm_useLocalMechListCopies();
26  
    ownResource(contextCache.listenToMessages());
27  
    // TODO: log JDA output
28  
    bot = discordBot(new ListenerAdapter {
29  
      public void onMessageReceived(MessageReceivedEvent e) pcall {
30  
        temp enter();
31  
        ret if !enabled || !licensed();
32  
        
33  
        bool bot = e.getAuthor().isBot();
34  
        long msgID = e.getMessage().getIdLong();
35  
        long userID = e.getAuthor().getIdLong();
36  
        S userName = e.getMember().getNickname(); // the changeable nick name - null sometimes?
37  
        if (userName == null) userName = e.getMember().getEffectiveName();
38  
        if (userName == null) userName = e.getAuthor().getName();
39  
        
40  
        final Message msg = e.getMessage();
41  
        
42  
        print("Channel type: " + e.getChannelType());
43  
        bool isPrivate = e.getChannelType() == ChannelType.PRIVATE;
44  
        S content = trim(msg.getContentRaw());
45  
        print("Msg from " + userName + ": " + content);
46  
        if (empty(content)) ret;
47  
        fS originalContent = content;
48  
49  
        O user = userConcept(e.getAuthor());
50  
        
51  
        S crud = dm_gazelle_linesCRUD();
52  
        O channel = dm_call(crud, 'uniqChannel, msg.getChannel().getIdLong());
53  
        dm_call(crud, 'cset, channel, litobjectarray(name := msg.getChannel().getName()));
54  
        
55  
        O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(+msgID));
56  
        dm_call(crud, 'cset, lineConcept, litobjectarray(
57  
          text := content,
58  
          +bot, +isPrivate,
59  
          author := user, +channel));
60  
        vmBus_send('newDiscordLine, lineConcept);
61  
        
62  
        final MessageChannel ch = e.getChannel();
63  
        long channelID = ch.getIdLong();
64  
        L<GazelleLine> linesInChannel = dm_discord_linesInChannel(channelID);
65  
        
66  
        if (bot) {
67  
          int monologueLength = cast dm_call(dm_gazelle_linesCRUD(), 'monologueLength, channelID);
68  
          if (monologueLength >= maxMonologue) ret;
69  
          //Long last = get(lastPosted, l(lastPosted)-3);
70  
          //if (last != null && now() < last+10000) ret;
71  
        }
72  
        
73  
        new Matches m;
74  
        
75  
        temp tempSetTL(currentChannel, ch);
76  
        temp tempSetTL(respondingTo, msg);
77  
        
78  
        try {
79  
          if (swicOneOf(content, "!eval ", "!fresh ", "!real-eval", "!safe-eval ") || eqic(content, "!fresh")) {
80  
            O safetyCheck = null;
81  
            bool authed = dm_discord_userCanEval(userID);
82  
            if (swic_trim(content, "!safe-eval ", m)) {
83  
              content = "!eval " + m.rest();
84  
              authed = false;
85  
            }
86  
            
87  
            if (regexpMatches("![a-z\\-]+\\s+again", content)) {
88  
              GazelleLine last = dm_discord_nextToLastEvalByUser(userID);
89  
              if (last == null) ret with postInChannel(ch, "No last eval found");
90  
              content = replaceSuffix("again", afterSpace(last.text), content);
91  
            }
92  
            
93  
            if (!authed) {
94  
              //ret with postInChannel(ch, "Sorry, you're not authed to eval");
95  
              safetyCheck = func(S code) -> S {
96  
                S safety = joinWithComma(getCodeFragmentSafety(code));
97  
                if (eq(safety, 'safe)) ret "";
98  
                S s = "Sorry. Safety level is " + safety;
99  
                Set<S> unknown = codeAnalysis_getUnknownIdentifiers(code);
100  
                if (nempty(unknown)) s += ". Unknown identifiers: " + joinWithComma(unknown);
101  
                ret s;
102  
              };
103  
            }
104  
            ret with dm_bot_execEvalCmd(voidfunc(S s) {
105  
              if (s != null)
106  
                postInChannel(ch, shorten(ifEmpty(s, [[""]]), 1000))
107  
            }, content, +safetyCheck);
108  
          }
109  
          
110  
          if (eqic(content, "!rule")) {
111  
            LS lines = linesInChannelBy(channelID, userID);
112  
            if (contains(nextToLast(lines), "=>"))
113  
              content = "!rule " + nextToLast(lines);
114  
            else {
115  
              if (l(lines) < 3) fail("Too few lines");
116  
              content = "!rule " + nextToNextToLast(lines) + " => " + nextToLast(lines);
117  
            }
118  
          }
119  
          
120  
          if (swicOneOf(content, m, "!rule ", "!rule\n")) {
121  
            S s = trim(m.rest());
122  
            print("Processing rule: " + s);
123  
            S opt = leadingSquareBracketStuff(s);
124  
            s = dropActuallyLeadingSquareBracketStuff(s);
125  
            if (startsWith(s, "=>"))
126  
              s = assertNotNull("No last line in channel", nextToLast(linesInChannelBy(channelID, userID))) + "\n" + s;
127  
            LS comments = ll("made by user", "discord", "tokenize out with javaTok");
128  
            if (cic(pairB(tok_splitAtDoubleArrow_pair(s)), userName)) {
129  
              s = optCurly(userName) + " says: " + s;
130  
              comments.add("with user name");
131  
            }
132  
            
133  
            gazelle_parsePublicRuleOptions(opt, comments);
134  
            s = replace(s, " + ", "\n+ ");
135  
            s = jreplace1(s, "=>", "\n=>");
136  
            s = gazelle_processSquareBracketAnnotations(s, comments);
137  
            temp tempSetTL(dm_gazelle_addRuleWithComment_renderWithoutComments, true);
138  
            dm_gazelle_addRuleWithComment(s, lines_rtrim(comments));
139  
            ret with postInChannel(ch, dm_gazelle_addRuleWithComment_msg!);
140  
          }
141  
          
142  
          // Check for modules
143  
          
144  
          LS modules = cast dm_call(dm_gazelle_modulesManager(), 'modulesForUser, "discord user " + userID);
145  
          for (S moduleID : unnull(modules)) pcall {
146  
            S answer = cast dm_callOpt(moduleID, 'answer, content);
147  
            if (nempty(answer))
148  
              postInChannel(ch, answer);
149  
          }
150  
151  
          // Go into normal reasoning
152  
          
153  
          bool skipBad = true;
154  
          if (swic_trim(content, "!withBad ", m)) {
155  
            skipBad = false;
156  
            content = m.rest();
157  
          }
158  
          
159  
          LS preContext = takeLast(2, dropLast(collect text(linesInChannel)));
160  
          print("preContext=" + preContext);
161  
          
162  
          GazelleEvalContext ctx = contextCache!;
163  
          ctx.engine.rules = withoutInstancesOf(RuleEngine2.SimplifyWithRule.class, ctx.engine.rules);
164  
          L<GazelleTree> l = dm_gazelle_reasonAboutChatInput_v2(userName, content, +skipBad, +preContext, +ctx,
165  
            badComments := mechCISet("Knock-out rule comments"),
166  
            +acceptablePurposes,
167  
            respondingToHuman := !bot,
168  
            humanOnlyDebug := true,
169  
            +userID);
170  
          
171  
          int idx = 0;
172  
          for (final GazelleTree t : takeFirst(10, l)) {
173  
            final int _idx = idx++;
174  
            
175  
            if (eqic(t.lineType, "temporary fact")) {
176  
              if (dm_gazelle_hasTempFact(t.line)) continue;
177  
              print("Saving temp fact: " + t.line); 
178  
              dm_gazelle_addTempFact(t.line, "discord msg " + msgID);
179  
            }
180  
            
181  
            if (eqic(t.lineType, "eval")) {
182  
              S code = t.rule().out;
183  
              if (!allowedEvals.contains(code))
184  
                print("Eval not allowed: " + code);
185  
              else {
186  
                S out = strOrNull(dm_javaEvalOrInterpret(code));
187  
                if (nempty(out))
188  
                  postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
189  
                    Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,
190  
                      outMsgID := msg2.getIdLong(),
191  
                      outText := out,
192  
                      inMsgID := msg.getIdLong(),
193  
                      inUserID := userID,
194  
                      inChannelID := channelID,
195  
                      inText := originalContent,
196  
                      tree := t,
197  
                      treeIndex := _idx);
198  
                    dm_gazelle_saveReasoningForLine(reasoning);
199  
                  });
200  
              }
201  
              continue;
202  
            }
203  
              
204  
            fS out = tok_dropCurlyBrackets(t.line);
205  
            print(">> " + t);
206  
            print("POSTING: " + out);
207  
            postInChannelWithDelete(ch, out, voidfunc(Message msg2) {
208  
              Gazelle_ReasoningForLine reasoning = nu(Gazelle_ReasoningForLine,
209  
                outMsgID := msg2.getIdLong(),
210  
                outText := out,
211  
                inMsgID := msg.getIdLong(),
212  
                inUserID := userID,
213  
                inChannelID := channelID,
214  
                inText := originalContent,
215  
                tree := t,
216  
                treeIndex := _idx);
217  
              dm_gazelle_saveReasoningForLine(reasoning);
218  
            });
219  
          }
220  
        } catch print error {
221  
          postInChannel(ch, exceptionToStringShort(error));
222  
        }
223  
      }
224  
      
225  
      public void onMessageReactionAdd(MessageReactionAddEvent e) pcall {
226  
        temp enter();
227  
        ret if !enabled || !licensed();
228  
        MessageReaction r = e.getReaction();
229  
        bool bot = e.getUser().isBot();
230  
        long msgID = r.getMessageIdLong();
231  
        add(msgsReactedTo, msgID);
232  
        print("User " + e.getUser() + (bot ? " (bot)" : "") + " reacted to message " + msgID + " with " + r.getReactionEmote());
233  
        if (bot) ret;
234  
235  
        S crud = dm_gazelle_linesCRUD();
236  
        O lineConcept = dm_call(crud, 'uniqConcept, litObjectArrayAsObject(+msgID));
237  
        L reactions = cast get(lineConcept, 'reactions);
238  
        print("lineConcept=" + lineConcept);
239  
        print("reactions=" + reactions);
240  
        O reaction = dm_call(crud, 'nuReaction, litObjectArrayAsObject(
241  
          user := userConcept(e.getUser()),
242  
          emoji := r.getReactionEmote().getName(),
243  
          created := now()));
244  
        print("reaction=" + reaction);
245  
          
246  
        dm_call(crud, 'cset, lineConcept, litobjectarray(
247  
          reactions := listPlus(reactions, reaction)));
248  
          
249  
        dm_discord_gatherFeedbackFromLine(dm_discord_importLine(lineConcept));
250  
      }
251  
    });
252  
    
253  
    dm_registerAs('discordBot);
254  
    print("Started bot");
255  
  }
256  
  
257  
  void cleanMeUp {
258  
    if (bot != null) pcall {
259  
      print("Shutting down bot");
260  
      bot.shutdown();
261  
      print("Bot shut down");
262  
    }
263  
    bot = null;
264  
  }
265  
  
266  
  O userConcept(User user) {
267  
    S crud = dm_gazelle_linesCRUD();
268  
    O userConcept = dm_call(crud, 'uniqUser, user.getIdLong());
269  
    dm_call(crud, 'cset, userConcept, litobjectarray(name := user.getName()));
270  
    ret userConcept;
271  
  }
272  
  
273  
  // API
274  
  
275  
  void postInChannel(long channelID, S msg) {
276  
    postInChannel(bot.getTextChannelById(channelID), msg);
277  
  }
278  
  
279  
  void postInChannel(MessageChannel channel, S msg) {
280  
    channel.sendMessage(msg).queue();
281  
    if (l(lastPosted) > 5) popFirst(lastPosted);
282  
    lastPosted.add(now());
283  
  }
284  
  
285  
  void postInChannel(S channel, S msg) {
286  
    long id = dm_discord_channelID(channel);
287  
    if (id == 0) fail("Channel not found: " + channel);
288  
    postInChannel(id, msg);
289  
  }
290  
  
291  
  void postInChannelWithDelete(MessageChannel channel, S msg, VF1<Message> onPost) {
292  
    channel.sendMessage(msg).queue(msg2 -> {
293  
      pcallF(onPost, msg2);
294  
      final long msgId = msg2.getIdLong();
295  
      print("I sent msg: " + msgId);
296  
      if (doDelete) doLater(deleteDelay, r {
297  
        ret if contains(msgsReactedTo, msgId);
298  
        print("Deleting msg " + msgId);
299  
        msg2.delete().queue();
300  
      });
301  
    }, error -> _handleException(error));
302  
  }
303  
  
304  
  void postText(S text) {
305  
    postInChannel(currentChannel!, text);
306  
  }
307  
  
308  
  void postImage(S url) {
309  
    postImage(currentChannel!, url);
310  
  }
311  
  
312  
  void postImage(S url, S title) {
313  
    postImage(currentChannel!, url, title);
314  
  }
315  
  
316  
  void postImage(MessageChannel channel, S url) {
317  
    postImage(channel, url, "");
318  
  }
319  
  
320  
  void postImage(MessageChannel channel, S url, S description) {
321  
    channel.sendMessage(
322  
      new EmbedBuilder()
323  
        .setImage(absoluteURL(url))
324  
        //.setTitle(unnull(title))
325  
        .setDescription(unnull(description))
326  
        .setColor(imageEmbedMysteriousLineColor)
327  
        .build()).queue();
328  
  }
329  
  
330  
  LS linesInChannelBy(long channelID, long userID) {
331  
    ret collect text(dm_discord_linesInChannelBy(channelID, userID));
332  
  }
333  
  
334  
  MessageChannel currentChannel() { ret currentChannel!; }
335  
  Message respondingTo() { ret respondingTo!; }
336  
}

Author comment

Began life as a copy of #1021750

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1021781
Snippet name: Discord Bot [Gazelle-based, v10, with self-talk & looking at history, OLD]
Eternal ID of this version: #1021781/72
Text MD5: 8f056c5a615e255998f0b5790e1fa22c
Transpilation MD5: c43af09a8c997a5875db12a01bbd0908
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-03-10 12:27:32
Source code size: 13073 bytes / 336 lines
Pitched / IR pitched: No / No
Views / Downloads: 444 / 2068
Version history: 71 change(s)
Referenced in: [show references]