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

432
LINES

< > BotCompany Repo | #1025624 // Katherine

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

Uses 5614K of libraries. Click here for Pure Java version (25791L/192K).

1  
!7
2  
3  
module NLRulesBot extends DynTalkBot2<.ByServer> {
4  
  void init {
5  
    super.init();
6  
    makeByServer = () -> new ByServer;
7  
    useAGIBlueForDropPunctuation = false;
8  
    preprocessAtSelfToMyName = false;
9  
    dropPunctuation = false;
10  
    print("Total rules in all guilds: " + totalRuleCount());
11  
  }
12  
  
13  
  sclass Rule {
14  
    GlobalID globalID = aGlobalIDObj();
15  
    S text, comment;
16  
    
17  
    *() {} *(S *text) {}
18  
  }
19  
  
20  
  S renderRule(Rule r) {
21  
    ret r == null ? null : appendNewLineIfNempty(r.comment) + "Rule " + r.globalID + ": " + r.text;
22  
  }
23  
24  
  class ByServer extends DynTalkBot2<NLRulesBot.ByServer>.ByServer {
25  
    new L<Rule> rules;
26  
    new Set<Long> priorityChannels; // where we always respond
27  
    bool enableMagicQuotes = true;
28  
    new Map<Long, S> topicByChannel;
29  
    
30  
    new SimpleFactStore factStore;
31  
    new StringClustersWithIDs synonyms;
32  
    
33  
    delegate Cluster to StringClustersWithIDs.
34  
    
35  
    void _doneLoading { synonyms.onChange(r change); }
36  
    
37  
    S processSimplifiedLine(S s, O... _) {
38  
      try answer super.processSimplifiedLine(s, _);
39  
      ret fullTrim(processSimplifiedLine2(s, _));
40  
    }
41  
    
42  
    S processSimplifiedLine2(S s, O... _) {
43  
      lock myLock();
44  
45  
      try answer factStore_cmds(factStore, s, authed(_));
46  
      try answer philosophyBotWithFactStore_discordAnswer(
47  
      ai_weightChangeBot_theory(), factStore, s, _);
48  
      
49  
      long channelID = longPar channelID(_);
50  
      
51  
      new Matches m;
52  
      S s2 = dropMyPrefixOrNull(s);
53  
      if (s2 != null) s = s2;
54  
      else if (!contains(priorityChannels, channelID)) null;
55  
      optPar Message msg;
56  
      Message.Attachment attachment = msg == null ? null : first(msg.getAttachments());
57  
      if (attachment != null) {
58  
        File file = prepareCacheProgramFile(guildID + "/" + attachment.getFileName());
59  
        print("Downloading attachment: " + file);
60  
        deleteFile(file);
61  
        if (!attachment.download(file)) ret "Couldn't download attachment";
62  
        LS lines = tlft(loadTextFile(file));
63  
        print("First line: " + first(lines));
64  
        if (!swic(first(lines), "rule ")) print("File does not contain rules");
65  
        else {
66  
          new L<T3S> toImport; // (globalID, text, comment)
67  
          new LS comment;
68  
          for (S line : lines) {
69  
            LS l = regexpICFullMatch_groups("Rule ([a-z]+):(.*)$", line);
70  
            if (nempty(l)) {
71  
              toImport.add(t3(first(l), second(l), lines_rtrim(comment));
72  
              comment.clear();
73  
            } else
74  
              comment.add(line);
75  
          }
76  
          S found = "Found " + nRules(toImport) + " in attachment";
77  
          
78  
          int oldCount = l(rules);
79  
          Map<GlobalID, Rule> existing = indexByField globalID(rules);
80  
          int updated = 0, added = 0;
81  
          for (T3S rule : toImport) {
82  
            GlobalID id = GlobalID(rule.a);
83  
            S text = trim(rule.b);
84  
            Rule r = existing.get(id);
85  
            if (r == null) {
86  
              r = new Rule(text);
87  
              r.globalID = id;
88  
              r.comment = rule.c;
89  
              rules.add(r);
90  
              existing.put(id, r);
91  
              ++added;
92  
            } else if (neq(r.text, text)) {
93  
              r.comment = rule.c;
94  
              r.text = text;
95  
              ++updated;
96  
            }
97  
          }
98  
          
99  
          if (added+updated > 0) change();
100  
          ret found + ". Had: " + oldCount + ". Added: " + added + ". Updated: " + updated + ". New count: " + l(rules);
101  
        }
102  
      }
103  
104  
      // duplicated patterns go here
105  
      if "on * say *|on * or * say *|on keyword * and * say *|on keyword * and keyword * say *|on keyword * or * say *|on keyword * say *|on *, image-google X|on *, google X|when <*> comes online, say *|on bot keyword * and keyword *, say *|on conversation keyword * and *, say *|on topic * and *, say *|on topic * and keyword *, say *" {
106  
        Rule r = firstWhereIC(rules, text := s);
107  
        if (r != null) ret "Rule exists: " + r.globalID;
108  
        rules.add(r = new Rule(s));
109  
        change();
110  
        ret "Rule added with ID " + r.globalID;
111  
      }
112  
      
113  
      if "rules|rules as file" {
114  
        if (empty(rules)) ret "No rules defined yet";
115  
        uploadFileInChannel(channelID,
116  
          toUtf8(lines(map(r -> renderRule(r), rules))),
117  
          genericTextFileName(), null, null);
118  
        null;
119  
      }
120  
121  
      if "delete rule *|delete rule with id *" {
122  
        try answer checkAuth(_);
123  
        Rule r = firstWhere(rules, globalID := GlobalID($1));
124  
        if (r == null) ret "Rule " + $1 + " not found";
125  
        rules.remove(r);
126  
        change();
127  
        ret "Rule deleted, " + nRule(rules) + " remain";
128  
      }
129  
      
130  
      if "delete all rules" {
131  
        appendToTextFile(programFile("backups.txt"), guildStructure());
132  
        try answer checkAuth(_);
133  
        rules.clear();
134  
        change();
135  
        ret "All rules deleted";
136  
      }
137  
      
138  
      if "priority channel on" {
139  
        try answer checkAuth(_);
140  
        priorityChannels.add(channelID); change();
141  
        ret "OK";
142  
      }
143  
144  
      if "priority channel off" {
145  
        try answer checkAuth(_);
146  
        priorityChannels.remove(channelID); change();
147  
        ret "OK";
148  
      }
149  
      
150  
      if "disable magic quotes" {
151  
        try answer checkAuth(_);
152  
        enableMagicQuotes = false; change();
153  
        ret "OK";
154  
      }
155  
      
156  
      if "add synonym ...|add synonyms ..." {
157  
        LS tok = unquoteAll(wordTokC(m.rest()));
158  
        printStruct(+tok);
159  
        if (l(tok) <= 1) ret "Give me at least 2 synonyms, dude";
160  
        for (int i = 1; i < l(tok); i++)
161  
          synonyms.addPair(first(tok), tok.get(i));
162  
        ret "OK: " + synonyms.clusterWith(first(tok)).synonyms;
163  
      }
164  
      
165  
      if "delete synonym cluster *" {
166  
        Cluster c = synonyms.clusterWith($1);
167  
        if (c == null) ret "Cluster not found";
168  
        synonyms.remove(c);
169  
        ret "Synonym cluster deleted: " + c.synonyms;
170  
      }
171  
      
172  
      if "delete synonym * from cluster *" {
173  
        Cluster c = synonyms.clusterWith($1);
174  
        if (c == null) ret "Cluster not found";
175  
        if (!c.synonyms.remove($1)) ret format("Synonym * not found in " + c.synonyms);
176  
        change();
177  
        if (l(c.synonyms) <= 1) synonyms.remove(c);
178  
        ret "OK";
179  
      }
180  
      
181  
      if "list synonyms"
182  
        ret or2(lines(map(synonyms.clusters, c -> join(" = ", c.synonyms))), "No synonyms defined");
183  
        
184  
      if "list synonyms for *" {
185  
        Cl<Cluster> l = synonyms.searchForCluster($1);
186  
        ret or2(lines(map(l, c -> join(" = ", c.synonyms))), format("No synonyms found for *", $1));
187  
      }
188  
        
189  
      if "print log" {
190  
        try answer checkAuth(_);
191  
        ret str(_actualPrintLog());
192  
      }
193  
194  
      if "lottery print log" {
195  
        try answer checkAuth(_);
196  
        S mod = dm_findModule("#1025913/LotteryBot");
197  
        if (mod == null) ret "Not loaded";
198  
        ret mod + "\n" + str(dm_call(mod, '_actualPrintLog));
199  
      }
200  
      
201  
      if "lottery enabled" {
202  
        try answer checkAuth(_);
203  
        ret str(dm_get(dm_findModule("#1025913/LotteryBot"), 'enabled));
204  
      }
205  
      
206  
      if "lottery last error" {
207  
        try answer checkAuth(_);
208  
        ret str(dm_get(dm_findModule("#1025913/LotteryBot"), '_error));
209  
      }
210  
      
211  
      if "lottery reload" {
212  
        try answer checkAuth(_);
213  
        dm_reloadModule(dm_findModule("#1025913/LotteryBot"));
214  
        ret "OK (maybe)";
215  
      }
216  
      
217  
      if (eqic(s, "help"))
218  
        ret ltrim([[
219  
I execute simple English rules.
220  
221  
Stuff to say.
222  
@me `on "hello bot" say "who are you"`
223  
@me `on keyword "time" say "it is always time for a tea"`
224  
@me `on "what is X" google X`
225  
@me `on "show me X" image-google X`
226  
@me `on bot keyword "game" and keyword "sure" say "let's play tic tac toe"`
227  
@me `on topic "weather" and "nice", say "so the sun is shining?"`
228  
@me `when @user comes online, say "hello friend"`
229  
@me `rules` / `rules as file` - list rules
230  
@me `delete rule abcdefghijkl` - delete rule with ID
231  
232  
@me `add synonyms thx "thank you" thanks` - add synonyms
233  
@me `delete synonym cluster thx` - delete all synonyms of thx
234  
@me `delete synonym thx from cluster thanks` - delete a single synonym
235  
@me `list synonyms` / `list synonyms for "thank you"`
236  
237  
@me `topic` - show current topic for channel (see: conversation keyword)
238  
@me `forget the topic` / `forget <topic>` - forget current topic
239  
240  
@me `priority channel on` - respond to every msg
241  
@me `priority channel off` - respond to msgs with my name only
242  
@me `masters` / `add master <username>` / `delete master <username>`
243  
@me `help` / `support` / `source code`
244  
@me `download` - download my brain for this guild
245  
246  
To fill me with rules, just upload the file you got from `rules as file` to the channel.
247  
248  
[Bot made by https://BotCompany.de]
249  
  ]]).replace("@me", atSelf());
250  
  
251  
      // find rule to execute
252  
      
253  
      new LinkedHashSet<S> outs;
254  
      new LinkedHashSet<S> outs2; // higher priority (two matches)
255  
      
256  
      for (Rule r : rules) pcall {
257  
        // IMPORTANT: patterns are duplicated above
258  
        
259  
        if (matchX2("on conversation keyword * and *, say *|on topic * and *, say *|on topic * and keyword *, say *", r.text, m)) {
260  
          S in1 = $1, in2 = $2, out = $3;
261  
          if (findWithSynonyms(in1, s))
262  
            if (put_trueIfChanged(topicByChannel, channelID, in1)) change();
263  
          if (synonyms.eqicOrInSameCluster(topicByChannel.get(channelID), in1)
264  
            && findWithSynonyms(in2, s))
265  
            outs2.add(rewrite(out, _));
266  
        }
267  
        
268  
        if (match("on * say *", r.text, m)) {
269  
          S in = $1, out = $2;
270  
          if (matchWithSynonyms(in, s))
271  
            outs.add(rewrite(out, _));
272  
        }
273  
        
274  
        if (match("on * or * say *", r.text, m)) {
275  
          S in1 = $1, in2 = $2, out = $3;
276  
          if (matchWithSynonyms(in1, s) || matchWithSynonyms(in2, s))
277  
            outs.add(rewrite(out, _));
278  
        }
279  
        
280  
        if (match_vbar("on keyword * and * say *|on keyword * and keyword * say *", r.text, m)) {
281  
          S in1 = $1, in2 = $2, out = $3;
282  
          if (findWithSynonyms(in1, s) && findWithSynonyms(in2, s))
283  
            outs2.add(rewrite(out, _));
284  
        }
285  
        
286  
        if (match("on keyword * or * say *", r.text, m)) {
287  
          S in1 = $1, in2 = $2, out = $3;
288  
          if (findWithSynonyms(in1, s) || findWithSynonyms(in2, s))
289  
            outs2.add(rewrite(out, _));
290  
        }
291  
        
292  
        if (match("on bot keyword * and keyword *, say *", r.text, m)) {
293  
          ISaid lastSaid = lastSaidInChannel.get(channelID);
294  
          if (lastSaid != null) {
295  
            S in1 = $1, in2 = $2, out = $3;
296  
            if (findWithSynonyms(in1, lastSaid.text) && findWithSynonyms(in2, s))
297  
              outs2.add(rewrite(out, _));
298  
          }
299  
        }
300  
301  
        if (match("on keyword * say *", r.text, m)) {
302  
          S in = $1, out = $2;
303  
          if (findWithSynonyms(in, s))
304  
            outs.add(rewrite(out, _));
305  
        }
306  
        
307  
        if (match("on *, image-google X", r.text, m))
308  
          if (flexMatchIC(replaceWord($1, "X", "*"), s, m)) {
309  
            bool nsfw = discord_isNSFWChannel_gen(optPar channel(_));
310  
            print(+nsfw);
311  
            BufferedImage img = nsfw ? quickVisualize_nsfw($1) : quickVisualize($1);
312  
            if (img == null) ret "No image found, sorry!";
313  
            postImage(paramsToMap(_), uploadToImageServer(img, $1));
314  
            // null;
315  
          }
316  
          
317  
        if (match("on *, google X", r.text, m))
318  
          if (flexMatchIC(replaceWord($1, "X", "*"), s, m)) {
319  
            bool nsfw = discord_isNSFWChannel_gen(optPar channel(_));
320  
            print(+nsfw);
321  
            ret discord_google($1, results := 1, safeSearch := !nsfw);
322  
          }
323  
      }
324  
      
325  
      if "topic|conversation keyword" {
326  
        S topic = topicByChannel.get(channelID);
327  
        ret nempty(topic) ? "Current topic in this channel is: " + topic : "No topic set in this channel";
328  
      }
329  
      
330  
      if "forget topic|forget the topic"
331  
        ret forgetTopic(channelID);
332  
        
333  
      if "forget ..."
334  
        if (synonyms.eqicOrInSameCluster($1, topicByChannel.get(channelID)))
335  
          ret forgetTopic(channelID);
336  
337  
      if "download" {
338  
        optPar Guild guild;
339  
        optPar MessageChannel channel;
340  
        ret serveGuildBackup(channel, guild,
341  
          structure_nullingInstancesOfClass(_getClass(module()), ByServer.this));
342  
      }
343  
      
344  
      if "stats"
345  
        ret "I have " + nRules(rules) + " and know " + n2(synonyms.totalCount(), "synonym");
346  
        
347  
      if "masters"
348  
        ret renderMasters();
349  
      
350  
      try answer random(outs2);
351  
      try answer random(outs);
352  
      
353  
      if (enableMagicQuotes)
354  
        try answer answer_magicQuoteInput(s, useTranspiled := true);
355  
      null;
356  
    }
357  
    
358  
    S rewrite(S answer, O... _) {
359  
      if (containsIC(answer, "<user>")) {
360  
        optPar S name;
361  
        if (empty(name)) name = discord_optParUserName(_);
362  
        if (nempty(name))
363  
          answer = replaceIC(answer, "<user>", name);
364  
      }
365  
      ret answer;
366  
    }
367  
    
368  
    void onOnlineStatusChange(UserUpdateOnlineStatusEvent e) {
369  
      User user = e.getMember().getUser();
370  
      print("User status change: " + user + " " + e.getOldValue() + " -> " + e.getNewValue());
371  
      
372  
      // only react to user coming online
373  
      if (e.getOldValue() != OnlineStatus.OFFLINE) ret;
374  
      
375  
      long id = user.getIdLong();
376  
      
377  
      new LinkedHashSet<S> outs;
378  
      new Matches m;
379  
380  
      for (Rule r : rules) pcall {
381  
        // IMPORTANT: patterns are duplicated above
382  
        
383  
        if (match("when <*> comes online, say *", r.text, m)) {
384  
          long _id = parseLongOpt($1);
385  
          S out = $2;
386  
          if (id == _id)
387  
            outs.add(rewrite(out, name := user.getName()));
388  
        }
389  
      }
390  
      
391  
      postInChannel(preferredChannelID, random(outs));
392  
    }
393  
    
394  
    bool findWithSynonyms(S pat, S s) {
395  
      for (S pat2 : splitAtComma(pat))
396  
        if (!findWithSynonyms2(pat2, s))
397  
          false;
398  
      true;
399  
    }
400  
    
401  
    bool findWithSynonyms2(S pat2, S s) {
402  
      for (S pat3 : synonyms.extend(pat2))
403  
        if (find3(pat3, s))
404  
          true;
405  
      false;
406  
    }
407  
    
408  
    bool matchWithSynonyms(S pat, S s) {
409  
      for (S pat2 : synonyms.extend(pat))
410  
        if (match(pat2, s))
411  
          true;
412  
      false;
413  
    }
414  
    
415  
    S forgetTopic(long channelID) {
416  
      topicByChannel.remove(channelID);
417  
      ret "OK, let's talk about something else";
418  
    }
419  
  }
420  
  
421  
  long totalRuleCount() {
422  
    ret intSumAsLong(map(syncCloneValues(dataByServer), bs -> l(bs.rules)));
423  
  }
424  
  
425  
  S renderMasters() {
426  
    ret empty(authorizedUsers) ? "I have no masters." : "My masters are: " + ai_renderAndList(map discordAt(authorizedUsers));
427  
  }
428  
  
429  
  LS splitIntoLines(S s) {
430  
    ret tlft_honoringTokens(s);
431  
  }
432  
}

Author comment

Began life as a copy of #1025348

download  show line numbers  debug dex  old transpilations   

Travelled to 9 computer(s): bhatertpkbcr, dbzfplsxganw, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1025624
Snippet name: Katherine
Eternal ID of this version: #1025624/61
Text MD5: 0fff40582a324b98cebeeb99b5e135ac
Transpilation MD5: 5639ade72e9f1c03d6135efee16bfcb4
Author: stefan
Category: javax
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-08-13 16:27:18
Source code size: 15129 bytes / 432 lines
Pitched / IR pitched: No / No
Views / Downloads: 521 / 68295
Version history: 60 change(s)
Referenced in: [show references]