Uses 5614K of libraries. Click here for Pure Java version (20720L/155K).
1 | abstract sclass DynNLRulesBot<A extends DynNLRulesBot.ByServer> extends DynTalkBot2<A> {
|
2 | void init {
|
3 | super.init(); |
4 | useAGIBlueForDropPunctuation = false; |
5 | preprocessAtSelfToMyName = false; |
6 | dropPunctuation = false; |
7 | print("Total rules in all guilds: " + totalRuleCount());
|
8 | } |
9 | |
10 | sclass Rule {
|
11 | GlobalID globalID = aGlobalIDObj(); |
12 | S text; |
13 | |
14 | *() {} *(S *text) {}
|
15 | } |
16 | |
17 | S renderRule(Rule r) {
|
18 | ret r == null ? null : "Rule " + r.globalID + ": " + r.text; |
19 | } |
20 | |
21 | class ByServer extends DynTalkBot2.ByServer {
|
22 | new L<Rule> rules; |
23 | new Set<Long> priorityChannels; // where we always respond |
24 | bool enableMagicQuotes = true; |
25 | |
26 | synchronized S processSimplifiedLine(S s, O... _) {
|
27 | try answer super.processSimplifiedLine(s, _); |
28 | |
29 | print("s=" + s);
|
30 | new Matches m; |
31 | S s2 = dropMyPrefixOrNull(s); |
32 | if (s2 != null) s = s2; |
33 | else if (!contains(priorityChannels, optPar channelID(_))) null; |
34 | optPar Message msg; |
35 | print("msg=" + msg);
|
36 | Message.Attachment attachment = msg == null ? null : first(msg.getAttachments()); |
37 | if (attachment != null) {
|
38 | long guildID = idForByServer(this); |
39 | File file = prepareCacheProgramFile(guildID + "/" + attachment.getFileName()); |
40 | print("Downloading attachment: " + file);
|
41 | deleteFile(file); |
42 | if (!attachment.download(file)) ret "Couldn't download attachment"; |
43 | LS lines = tlft(loadTextFile(file)); |
44 | print("First line: " + first(lines));
|
45 | if (!swic(first(lines), "rule ")) print("Ignoring attachment");
|
46 | else {
|
47 | new LPairS toImport; // (globalID, text) |
48 | for (S line : lines) {
|
49 | LS l = regexpICFullMatch_groups("Rule ([a-z]+):(.*)$", line);
|
50 | if (nempty(l)) |
51 | toImport.add(listToPair(l)); |
52 | } |
53 | S found = "Found " + nRules(toImport) + " in attachment"; |
54 | |
55 | int oldCount = l(rules); |
56 | Map<GlobalID, Rule> existing = indexByField globalID(rules); |
57 | int updated = 0, added = 0; |
58 | for (PairS rule : toImport) {
|
59 | GlobalID id = GlobalID(rule.a); |
60 | S text = trim(rule.b); |
61 | Rule r = existing.get(id); |
62 | if (r == null) {
|
63 | r = new Rule(text); |
64 | r.globalID = id; |
65 | rules.add(r); |
66 | existing.put(id, r); |
67 | ++added; |
68 | } else if (neq(r.text, text)) {
|
69 | r.text = text; |
70 | ++updated; |
71 | } |
72 | } |
73 | |
74 | if (added+updated > 0) change(); |
75 | ret found + ". Had: " + oldCount + ". Added: " + added + ". Updated: " + updated + ". New count: " + l(rules); |
76 | } |
77 | |
78 | } |
79 | |
80 | // duplicated patterns go here |
81 | 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 *" {
|
82 | Rule r = firstWhereIC(rules, text := s); |
83 | if (r != null) ret "Rule exists: " + r.globalID; |
84 | rules.add(r = new Rule(s)); |
85 | change(); |
86 | ret "Rule added with ID " + r.globalID; |
87 | } |
88 | |
89 | if "rules" |
90 | ret or2(lines(map(r -> renderRule(r), rules)), "No rules defined yet"); |
91 | |
92 | if "rules as file" {
|
93 | uploadFileInChannel(longPar channelID(_), |
94 | toUtf8(lines(map(r -> renderRule(r), rules))), |
95 | genericTextFileName(), null, null); |
96 | null; |
97 | } |
98 | |
99 | if "delete rule *|delete rule with id *" {
|
100 | try answer checkAuth(_); |
101 | Rule r = firstWhere(rules, globalID := GlobalID($1)); |
102 | if (r == null) ret "Rule " + $1 + " not found"; |
103 | rules.remove(r); |
104 | change(); |
105 | ret "Rule deleted, " + nRule(rules) + " remain"; |
106 | } |
107 | |
108 | if "delete all rules" {
|
109 | appendToTextFile(programFile("backups.txt"), guildStructure());
|
110 | try answer checkAuth(_); |
111 | rules.clear(); |
112 | change(); |
113 | ret "All rules deleted"; |
114 | } |
115 | |
116 | if "priority channel on" {
|
117 | try answer checkAuth(_); |
118 | priorityChannels.add(longOptPar channelID(_)); change(); |
119 | ret "OK"; |
120 | } |
121 | |
122 | if "priority channel off" {
|
123 | try answer checkAuth(_); |
124 | priorityChannels.remove(optPar channelID(_)); change(); |
125 | ret "OK"; |
126 | } |
127 | |
128 | if "disable magic quotes" {
|
129 | try answer checkAuth(_); |
130 | enableMagicQuotes = false; change(); |
131 | ret "OK"; |
132 | } |
133 | |
134 | if (eqic(s, "help")) |
135 | ret ltrim([[ |
136 | I execute simple English rules. |
137 | |
138 | Stuff to say. |
139 | @me `on "hello bot" say "who are you"` |
140 | @me `on keyword "time" say "it is always time for a tea"` |
141 | @me `on "what is X" google X` |
142 | @me `on "show me X" image-google X` |
143 | @me `when @user comes online, say "hello friend"` |
144 | @me `rules` / `rules as file` - list rules |
145 | @me `delete rule abcdefghijkl` - delete rule with ID |
146 | @me `priority channel on` - respond to every msg |
147 | @me `priority channel off` - respond to msgs with my name only |
148 | @me `masters` / `add master <username>` / `delete master <username>` |
149 | @me `help` / `support` / `source code` |
150 | @me `download` - download my brain for this guild |
151 | |
152 | To fill me with rules, just upload the file you got from `rules as file` to the channel. |
153 | |
154 | [Bot made by https://BotCompany.de] |
155 | ]]).replace("@me", atSelf());
|
156 | |
157 | // find rule to execute |
158 | |
159 | new LinkedHashSet<S> outs; |
160 | |
161 | for (Rule r : rules) pcall {
|
162 | // IMPORTANT: patterns are duplicated above |
163 | |
164 | if (match("on * say *", r.text, m)) {
|
165 | S in = $1, out = $2; |
166 | if (match(in, s)) |
167 | outs.add(rewrite(out, _)); |
168 | } |
169 | |
170 | if (match("on * or * say *", r.text, m)) {
|
171 | S in1 = $1, in2 = $2, out = $3; |
172 | if (match(in1, s) || match(in2, s)) |
173 | outs.add(rewrite(out, _)); |
174 | } |
175 | |
176 | if (match("on keyword * and * say *", r.text, m)) {
|
177 | S in1 = $1, in2 = $2, out = $3; |
178 | if (find3(in1, s) && find3(in2, s)) |
179 | outs.add(rewrite(out, _)); |
180 | } |
181 | |
182 | if (match("on keyword * or * say *", r.text, m)) {
|
183 | S in1 = $1, in2 = $2, out = $3; |
184 | if (find3(in1, s) || find3(in2, s)) |
185 | outs.add(rewrite(out, _)); |
186 | } |
187 | |
188 | if (match("on keyword * say *", r.text, m)) {
|
189 | S in = $1, out = $2; |
190 | if (find3(in, s)) |
191 | outs.add(rewrite(out, _)); |
192 | } |
193 | |
194 | if (match("on *, image-google X", r.text, m))
|
195 | if (flexMatchIC(replaceWord($1, "X", "*"), s, m)) {
|
196 | bool nsfw = discord_isNSFWChannel_gen(optPar channel(_)); |
197 | print(+nsfw); |
198 | BufferedImage img = nsfw ? quickVisualize_nsfw($1) : quickVisualize($1); |
199 | if (img == null) ret "No image found, sorry!"; |
200 | postImage(paramsToMap(_), uploadToImageServer(img, $1)); |
201 | } |
202 | |
203 | if (match("on *, google X", r.text, m))
|
204 | if (flexMatchIC(replaceWord($1, "X", "*"), s, m)) {
|
205 | bool nsfw = discord_isNSFWChannel_gen(optPar channel(_)); |
206 | print(+nsfw); |
207 | ret discord_google($1, results := 1, safeSearch := !nsfw); |
208 | } |
209 | } |
210 | |
211 | if "download" {
|
212 | optPar Guild guild; |
213 | optPar MessageChannel channel; |
214 | ret serveGuildBackup(channel, guild, |
215 | structure_nullingInstancesOfClass(_getClass(module()), ByServer.this)); |
216 | } |
217 | |
218 | if "stats" |
219 | ret "I have " + nRules(rules); |
220 | |
221 | try answer random(outs); |
222 | |
223 | if (enableMagicQuotes) |
224 | try answer answer_magicQuoteInput(s, useTranspiled := true); |
225 | null; |
226 | } |
227 | |
228 | S rewrite(S answer, O... _) {
|
229 | if (containsIC(answer, "<user>")) {
|
230 | optPar S name; |
231 | if (empty(name)) name = discord_optParUserName(_); |
232 | if (nempty(name)) |
233 | answer = replaceIC(answer, "<user>", name); |
234 | } |
235 | ret answer; |
236 | } |
237 | |
238 | void onOnlineStatusChange(UserUpdateOnlineStatusEvent e) {
|
239 | User user = e.getMember().getUser(); |
240 | print("User status change: " + user + " " + e.getOldValue() + " -> " + e.getNewValue());
|
241 | |
242 | // only react to user coming online |
243 | if (e.getOldValue() != OnlineStatus.OFFLINE) ret; |
244 | |
245 | long id = user.getIdLong(); |
246 | |
247 | new LinkedHashSet<S> outs; |
248 | new Matches m; |
249 | |
250 | for (Rule r : rules) pcall {
|
251 | // IMPORTANT: patterns are duplicated above |
252 | |
253 | if (match("when <*> comes online, say *", r.text, m)) {
|
254 | long _id = parseLongOpt($1); |
255 | S out = $2; |
256 | if (id == _id) |
257 | outs.add(rewrite(out, name := user.getName())); |
258 | } |
259 | } |
260 | |
261 | postInChannel(preferredChannelID, random(outs)); |
262 | } |
263 | } |
264 | |
265 | long totalRuleCount() {
|
266 | ret intSumAsLong(map(syncCloneValues(dataByServer), bs -> l(bs.rules))); |
267 | } |
268 | } |
Began life as a copy of #1025348
download show line numbers debug dex old transpilations
Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt
No comments. add comment
| Snippet ID: | #1025775 |
| Snippet name: | DynNLRulesBot |
| Eternal ID of this version: | #1025775/5 |
| Text MD5: | 485469efc302f8276e2ff4739b2b964d |
| Transpilation MD5: | 163fd6b258feb9476b38efaab7b0bc95 |
| Author: | stefan |
| Category: | javax |
| Type: | JavaX fragment (include) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2019-10-18 23:25:42 |
| Source code size: | 9086 bytes / 268 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 464 / 845 |
| Version history: | 4 change(s) |
| Referenced in: | [show references] |