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

338
LINES

< > BotCompany Repo | #1025348 // NL Rules Bot [LIVE in Gazelle, copy of Katherine]

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

Uses 6525K of libraries. Click here for Pure Java version (23606L/135K).

!7

cmodule NLRulesBot extends DynTalkBot2<.ByServer> {
  void init {
    super.init();
    makeByServer = () -> new ByServer;
    useAGIBlueForDropPunctuation = false;
    preprocessAtSelfToMyName = false;
    dropPunctuation = false;
    print("Total rules in all guilds: " + totalRuleCount());
  }
  
  sclass Rule {
    GlobalID globalID = aGlobalIDObj();
    S text;
    
    *() {} *(S *text) {}
  }
  
  S renderRule(Rule r) {
    ret r == null ? null : "Rule " + r.globalID + ": " + r.text;
  }

  class ByServer extends DynTalkBot2<NLRulesBot.ByServer>.ByServer {
    new L<Rule> rules;
    new Set<Long> priorityChannels; // where we always respond
    bool enableMagicQuotes = true;
    
    new SimpleFactStore factStore;
    new StringClustersWithIDs synonyms;
    
    delegate Cluster to StringClustersWithIDs.
    
    void _doneLoading { synonyms.onChange(r change); }
    
    synchronized S processSimplifiedLine(S s, O... _) {
      try answer super.processSimplifiedLine(s, _);
      
      try answer factStore_cmds(factStore, s, authed(_));
      try answer philosophyBotWithFactStore_discordAnswer(
      ai_weightChangeBot_theory(), factStore, s, _);
      
      long channelID = longPar channelID(_);
      
      new Matches m;
      S s2 = dropMyPrefixOrNull(s);
      if (s2 != null) s = s2;
      else if (!contains(priorityChannels, channelID)) null;
      optPar Message msg;
      Message.Attachment attachment = msg == null ? null : first(msg.getAttachments());
      if (attachment != null) {
        long guildID = idForByServer(this);
        File file = prepareCacheProgramFile(guildID + "/" + attachment.getFileName());
        print("Downloading attachment: " + file);
        deleteFile(file);
        if (!attachment.download(file)) ret "Couldn't download attachment";
        LS lines = tlft(loadTextFile(file));
        print("First line: " + first(lines));
        if (!swic(first(lines), "rule ")) print("Ignoring attachment");
        else {
          new LPairS toImport; // (globalID, text)
          for (S line : lines) {
            LS l = regexpICFullMatch_groups("Rule ([a-z]+):(.*)$", line);
            if (nempty(l))
              toImport.add(listToPair(l));
          }
          S found = "Found " + nRules(toImport) + " in attachment";
          
          int oldCount = l(rules);
          Map<GlobalID, Rule> existing = indexByField globalID(rules);
          int updated = 0, added = 0;
          for (PairS rule : toImport) {
            GlobalID id = GlobalID(rule.a);
            S text = trim(rule.b);
            Rule r = existing.get(id);
            if (r == null) {
              r = new Rule(text);
              r.globalID = id;
              rules.add(r);
              existing.put(id, r);
              ++added;
            } else if (neq(r.text, text)) {
              r.text = text;
              ++updated;
            }
          }
          
          if (added+updated > 0) change();
          ret found + ". Had: " + oldCount + ". Added: " + added + ". Updated: " + updated + ". New count: " + l(rules);
        }
      }

      // duplicated patterns go here
      if "on * say *|on * or * say *|on keyword * and * 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 *" {
        Rule r = firstWhereIC(rules, text := s);
        if (r != null) ret "Rule exists: " + r.globalID;
        rules.add(r = new Rule(s));
        change();
        ret "Rule added with ID " + r.globalID;
      }
      
      if "rules"
        ret or2(lines(map(r -> renderRule(r), rules)), "No rules defined yet");
        
      if "rules as file" {
        uploadFileInChannel(channelID,
          toUtf8(lines(map(r -> renderRule(r), rules))),
          genericTextFileName(), null, null);
        null;
      }

      if "delete rule *|delete rule with id *" {
        try answer checkAuth(_);
        Rule r = firstWhere(rules, globalID := GlobalID($1));
        if (r == null) ret "Rule " + $1 + " not found";
        rules.remove(r);
        change();
        ret "Rule deleted, " + nRule(rules) + " remain";
      }
      
      if "delete all rules" {
        appendToTextFile(programFile("backups.txt"), guildStructure());
        try answer checkAuth(_);
        rules.clear();
        change();
        ret "All rules deleted";
      }
      
      if "priority channel on" {
        try answer checkAuth(_);
        priorityChannels.add(channelID); change();
        ret "OK";
      }

      if "priority channel off" {
        try answer checkAuth(_);
        priorityChannels.remove(channelID); change();
        ret "OK";
      }
      
      if "disable magic quotes" {
        try answer checkAuth(_);
        enableMagicQuotes = false; change();
        ret "OK";
      }
      
      if "add synonym ...|add synonyms ..." {
        LS tok = unquoteAll(wordTokC(m.rest()));
        if (l(tok) <= 1) ret "Give me at least 2 synonyms, dude";
        for (int i = 1; i < l(tok); i++)
          synonyms.addPair(first(tok), tok.get(i));
        ret "OK: " + synonyms.clusterWith(first(tok)).synonyms;
      }
      
      if "delete synonym cluster *" {
        Cluster c = synonyms.clusterWith($1);
        if (c == null) ret "Cluster not found";
        synonyms.remove(c);
        ret "Synonym cluster deleted: " + c.synonyms;
      }
      
      if "delete synonym * from cluster *" {
        Cluster c = synonyms.clusterWith($1);
        if (c == null) ret "Cluster not found";
        if (!c.synonyms.remove($1)) ret format("Synonym * not found in " + c.synonyms);
        change();
        if (l(c.synonyms) <= 1) synonyms.remove(c);
        ret "OK";
      }
      
      if "list synonyms"
        ret or2(lines(map(synonyms.clusters, c -> join(" = ", c.synonyms))), "No synonyms defined");

      if (eqic(s, "help"))
        ret ltrim([[
I execute simple English rules.

Stuff to say.
@me `on "hello bot" say "who are you"`
@me `on keyword "time" say "it is always time for a tea"`
@me `on "what is X" google X`
@me `on "show me X" image-google X`
@me `on bot keyword "game" and keyword "sure" say "let's play tic tac toe"`
@me `when @user comes online, say "hello friend"`
@me `rules` / `rules as file` - list rules
@me `delete rule abcdefghijkl` - delete rule with ID

@me `add synonyms thx "thank you" thanks` - add synonyms
@me `delete synonym cluster thx` - delete all synonyms of thx
@me `delete synonym thx from cluster thanks` - delete a single synonym
@me `list synonyms`

@me `priority channel on` - respond to every msg
@me `priority channel off` - respond to msgs with my name only
@me `masters` / `add master <username>` / `delete master <username>`
@me `help` / `support` / `source code`
@me `download` - download my brain for this guild

To fill me with rules, just upload the file you got from `rules as file` to the channel.

[Bot made by https://BotCompany.de]
  ]]).replace("@me", atSelf());
  
      // find rule to execute
      
      new LinkedHashSet<S> outs;
      
      for (Rule r : rules) pcall {
        // IMPORTANT: patterns are duplicated above
        
        if (match("on * say *", r.text, m)) {
          S in = $1, out = $2;
          if (matchWithSynonyms(in, s))
            outs.add(rewrite(out, _));
        }
        
        if (match("on * or * say *", r.text, m)) {
          S in1 = $1, in2 = $2, out = $3;
          if (matchWithSynonyms(in1, s) || matchWithSynonyms(in2, s))
            outs.add(rewrite(out, _));
        }
        
        if (match("on keyword * and * say *", r.text, m)) {
          S in1 = $1, in2 = $2, out = $3;
          if (findWithSynonyms(in1, s) && findWithSynonyms(in2, s))
            outs.add(rewrite(out, _));
        }
        
        if (match("on keyword * or * say *", r.text, m)) {
          S in1 = $1, in2 = $2, out = $3;
          if (findWithSynonyms(in1, s) || findWithSynonyms(in2, s))
            outs.add(rewrite(out, _));
        }
        
        if (match("on bot keyword * and keyword *, say *", r.text, m)) {
          ISaid lastSaid = lastSaidInChannel.get(channelID);
          if (lastSaid != null) {
            S in1 = $1, in2 = $2, out = $3;
            if (findWithSynonyms(in1, lastSaid.text) && findWithSynonyms(in2, s))
              outs.add(rewrite(out, _));
          }
        }

        if (match("on keyword * say *", r.text, m)) {
          S in = $1, out = $2;
          if (findWithSynonyms(in, s))
            outs.add(rewrite(out, _));
        }
        
        if (match("on *, image-google X", r.text, m))
          if (flexMatchIC(replaceWord($1, "X", "*"), s, m)) {
            bool nsfw = discord_isNSFWChannel_gen(optPar channel(_));
            print(+nsfw);
            BufferedImage img = nsfw ? quickVisualize_nsfw($1) : quickVisualize($1);
            if (img == null) ret "No image found, sorry!";
            postImage(paramsToMap(_), uploadToImageServer(img, $1));
          }
          
        if (match("on *, google X", r.text, m))
          if (flexMatchIC(replaceWord($1, "X", "*"), s, m)) {
            bool nsfw = discord_isNSFWChannel_gen(optPar channel(_));
            print(+nsfw);
            ret discord_google($1, results := 1, safeSearch := !nsfw);
          }
      }
      
      if "download" {
        optPar Guild guild;
        optPar MessageChannel channel;
        ret serveGuildBackup(channel, guild,
          structure_nullingInstancesOfClass(_getClass(module()), ByServer.this));
      }
      
      if "stats"
        ret "I have " + nRules(rules) + " and know " + n2(synonyms.totalCount(), "synonym");
      
      try answer random(outs);
      
      if (enableMagicQuotes)
        try answer answer_magicQuoteInput(s, useTranspiled := true);
      null;
    }
    
    S rewrite(S answer, O... _) {
      if (containsIC(answer, "<user>")) {
        optPar S name;
        if (empty(name)) name = discord_optParUserName(_);
        if (nempty(name))
          answer = replaceIC(answer, "<user>", name);
      }
      ret answer;
    }
    
    void onOnlineStatusChange(UserUpdateOnlineStatusEvent e) {
      User user = e.getMember().getUser();
      print("User status change: " + user + " " + e.getOldValue() + " -> " + e.getNewValue());
      
      // only react to user coming online
      if (e.getOldValue() != OnlineStatus.OFFLINE) ret;
      
      long id = user.getIdLong();
      
      new LinkedHashSet<S> outs;
      new Matches m;

      for (Rule r : rules) pcall {
        // IMPORTANT: patterns are duplicated above
        
        if (match("when <*> comes online, say *", r.text, m)) {
          long _id = parseLongOpt($1);
          S out = $2;
          if (id == _id)
            outs.add(rewrite(out, name := user.getName()));
        }
      }
      
      postInChannel(preferredChannelID, random(outs));
    }
    
    bool findWithSynonyms(S pat, S s) {
      for (S pat2 : synonyms.extend(pat))
        if (find3(pat2, s))
          true;
      false;
    }
    
    bool matchWithSynonyms(S pat, S s) {
      for (S pat2 : synonyms.extend(pat))
        if (match(pat2, s))
          true;
      false;
    }
  }
  
  long totalRuleCount() {
    ret intSumAsLong(map(syncCloneValues(dataByServer), bs -> l(bs.rules)));
  }
}

Author comment

Began life as a copy of #1025304

download  show line numbers  debug dex  old transpilations   

Travelled to 8 computer(s): bhatertpkbcr, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, whxojlpjdney

No comments. add comment

Snippet ID: #1025348
Snippet name: NL Rules Bot [LIVE in Gazelle, copy of Katherine]
Eternal ID of this version: #1025348/95
Text MD5: 30c5362559a2ac9eb9e8ce229a0cba63
Transpilation MD5: e2c67f92b82ac91e415da06c936e1b7a
Author: stefan
Category: javax
Type: JavaX source code (Dynamic Module)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-10-23 12:31:29
Source code size: 11655 bytes / 338 lines
Pitched / IR pitched: No / No
Views / Downloads: 383 / 25417
Version history: 94 change(s)
Referenced in: [show references]