Download Jar. Libraryless. Click here for Pure Java version (14003L/99K).
1 | !7 |
2 | |
3 | sS dbBotID = #1026409; |
4 | |
5 | static Env env; |
6 | static new ThreadLocal<Session> session; |
7 | static new ThreadLocal<Out> out; |
8 | |
9 | sclass Out { |
10 | S buttonsIntro; |
11 | LS buttons; |
12 | S placeholder; |
13 | S defaultInput; |
14 | } |
15 | |
16 | sbool testFunctions = false; |
17 | |
18 | static interface Env { |
19 | void sayAsync(S session, S msg); |
20 | } |
21 | |
22 | concept Session { |
23 | S cookie; |
24 | S language; |
25 | S lastQuestion; |
26 | FormInFlight form, lastForm; |
27 | |
28 | void cancelForm { |
29 | cset(this, lastForm := form, form := null); |
30 | } |
31 | |
32 | S language() { ret or2(language, 'en); } |
33 | } |
34 | |
35 | sclass FormStep { |
36 | S key; |
37 | S displayText, desc; |
38 | S defaultValue; |
39 | S placeholder; // null for same as displayText, "" for none |
40 | LS buttons; |
41 | bool allowFreeText; // only matters when there are buttons |
42 | S value; |
43 | |
44 | // called before data entry |
45 | void update(Runnable onChange) {} |
46 | |
47 | // called after data entry |
48 | // return error message or null |
49 | // call session->cancelForm(); to cancel the form (and make sure to return a text) |
50 | S verifyData(S s) { null; } |
51 | } |
52 | |
53 | sclass FormInFlight { |
54 | S hashTag; |
55 | new L<FormStep> steps; |
56 | int stepIndex; // in steps list |
57 | |
58 | FormStep currentStep() { |
59 | ret get(steps, stepIndex); |
60 | } |
61 | |
62 | void update(Runnable onChange) { |
63 | if (currentStep() != null) currentStep().update(onChange); |
64 | } |
65 | |
66 | S complete() { ret "Form complete"; } |
67 | S cancel() { ret "Request cancelled"; } |
68 | } |
69 | |
70 | sclass MainForm > FormInFlight { |
71 | FormStep stepInterested, stepMailAddress, stepDevice, stepConfirm; |
72 | |
73 | *() { |
74 | hashTag = "#mainForm"; |
75 | |
76 | steps.add(stepInterested = setAll(new StepInterested, |
77 | key := "interested", |
78 | desc := "Interested in free trial", |
79 | displayText := "Are you after a free trial of our IPTV services?", |
80 | buttons := ll("Yes, please!", "No, thanks."), |
81 | allowFreeText := true)); |
82 | |
83 | steps.add(stepMailAddress = setAll(new StepMail, |
84 | key := "mailAddress", |
85 | desc := "E-mail address", |
86 | displayText := "Please provide your e-mail address so we can contact you!", |
87 | placeholder := "What is your e-mail address?",)); |
88 | |
89 | steps.add(stepDevice = nu FormStep( |
90 | key := "device", |
91 | desc := "Device", |
92 | displayText := "Which IPTV-capable device do you own?", |
93 | buttons := ll("Android box", "Android phone", "Android tablet", "Firestick", "Smart TV", "None/Unsure"), |
94 | allowFreeText := true)); |
95 | |
96 | steps.add(stepConfirm = setAll(new StepConfirm, |
97 | key := "confirm", |
98 | placeholder := "", |
99 | buttons := ll("Yes") |
100 | )); |
101 | } |
102 | |
103 | class StepInterested extends FormStep { |
104 | S verifyData(S s) { |
105 | s = trim(s); |
106 | if (isNo(s) || matchStart("not", s)) { |
107 | session->cancelForm(); |
108 | ret answer("#notInterestedInTrial"); |
109 | } |
110 | if (!isYes(s)) { |
111 | // cancel form, interpret as normal input |
112 | session->cancelForm(); |
113 | ret answer(s); |
114 | } |
115 | null; |
116 | } |
117 | } |
118 | |
119 | class StepMail extends FormStep { |
120 | @Override |
121 | S verifyData(S s) { |
122 | ret callPrint1x isValidEmailAddress_simple('isValidEmailAddress_simple, trim(s)) ? null : "Please enter a valid email address."; |
123 | } |
124 | } |
125 | |
126 | class StepConfirm extends FormStep { |
127 | void update(Runnable onChange) { |
128 | if (set_trueIfChanged(this, displayText := |
129 | summary() + "\n\n" + "Submit contact request?")) callF(onChange); |
130 | } |
131 | } |
132 | |
133 | S summary() { |
134 | print("Will send to: " + sendToViberIDs()); |
135 | ret mapToLines_rtrim(listMinus(steps, stepConfirm), step -> |
136 | (nempty(step.desc) |
137 | ? addSuffix(step.desc, ":") : step.displayText) |
138 | + " " + step.value); |
139 | } |
140 | |
141 | LS sendToViberIDs() { |
142 | ret llNempties(loadTextFileTrim(javaxSecretDir("iptv-viber-id.txt")), |
143 | stefanViberID()); |
144 | } |
145 | |
146 | S complete() { |
147 | LS viberIDs = sendToViberIDs(); |
148 | S text = joinWithEmptyLines( |
149 | "Contact request over smart-iptv-solutions.co.uk (" + timeInTimeZoneWithDate_24(ukTimeZone_string()) + ")", |
150 | summary()); |
151 | pcall { |
152 | for (S viberID : sendToViberIDs()) |
153 | viber_sendMessage(mandatoryViberToken(), |
154 | /*"BotCompany"*/"smart-iptv-solutions.co.uk", |
155 | viberID, |
156 | text); |
157 | |
158 | ret "Thank you, your contact request was sent!"; |
159 | } |
160 | ret "Sorry, an error occurred sending the message."; |
161 | } |
162 | |
163 | S cancel() { ret "Contact request cancelled"; } |
164 | } |
165 | |
166 | sbool debug; |
167 | |
168 | svoid setSession(S cookie, SS params) { |
169 | session.set(uniq_sync(Session, +cookie)); |
170 | S lang = mapGet(params, 'language); |
171 | if (nempty(lang)) |
172 | cset(session!, language := lang); |
173 | S lang_default = mapGet(params, 'language_default); |
174 | if (nempty(lang_default)) { |
175 | print("lang_default=" + lang_default + ", have: " + session->language); |
176 | if (empty(session->language)) { |
177 | print("Setting language."); |
178 | cset(session!, language := lang_default); |
179 | } |
180 | } |
181 | } |
182 | |
183 | svoid clearSession(S cookie) { |
184 | deleteConcepts(Session, +cookie); |
185 | } |
186 | |
187 | p { |
188 | db(); |
189 | |
190 | // for testing on desktop |
191 | if (isMain()) { veryBigConsole(); bot(); } |
192 | } |
193 | |
194 | sS returnQuestion(S q) { |
195 | cset(session!, lastQuestion := q); |
196 | ret q; |
197 | } |
198 | |
199 | sS answer(S s) { |
200 | if (session! == null) setSession("default", new Map); |
201 | out.set(new Out); |
202 | if (creator() == null) if "debug" set debug; |
203 | |
204 | S lq = session->lastQuestion; |
205 | cset(session!, lastQuestion := null); |
206 | |
207 | FormInFlight form = session->form; |
208 | if (form != null && form.currentStep() != null) { |
209 | if (eqicOneOf(s, "cancel", "Abbrechen", unicode_crossProduct())) { |
210 | S answer = form.cancel(); |
211 | session->cancelForm(); |
212 | ret answer; |
213 | } |
214 | else if (eqicOneOf(s, "back", "zurück", unicode_undoArrow()) && form.stepIndex > 0) { |
215 | --form.stepIndex; |
216 | session->change(); |
217 | ret deliverAnswerAndFormStep(""); |
218 | } else { |
219 | FormStep step = form.currentStep(); |
220 | if (!step.allowFreeText && nempty(step.buttons) && !cic(step.buttons, s)) |
221 | ret deliverAnswerAndFormStep(de() ? "Bitte wählen Sie eine Option!" : "Please choose an option."); |
222 | print("Verifying data " + quote(s) + " in step " + step); |
223 | S error = step.verifyData(s); |
224 | if (error != null) |
225 | ret deliverAnswerAndFormStep(error); |
226 | |
227 | step.value = s; |
228 | ++form.stepIndex; |
229 | session->change(); |
230 | if (form.currentStep() == null) { |
231 | S answer = form.complete(); |
232 | session->cancelForm(); |
233 | ret answer; |
234 | } |
235 | ret deliverAnswerAndFormStep(""); |
236 | } |
237 | } |
238 | |
239 | if (testFunctions) { |
240 | if (eq(lq, "Would you like some tea?")) { |
241 | if "yes" ret "Here's your tea."; |
242 | if "no" ret "Very well then."; |
243 | } |
244 | |
245 | if "test buttons" { |
246 | out->buttons = ll("Yes", "No"); |
247 | ret returnQuestion("Would you like some tea?"); |
248 | } |
249 | |
250 | if "say something later" { |
251 | if (env == null) ret("No env"); |
252 | final S mySession = session->cookie; |
253 | thread { |
254 | sleepSeconds(5); |
255 | env.sayAsync(mySession, "Here it is."); |
256 | } |
257 | ret "OK, will do it in 5 seconds"; |
258 | } |
259 | } |
260 | |
261 | // get answer from bot, switch language |
262 | |
263 | O bot = dbBot(); |
264 | S a = (S) call(bot, 'answer, s, session->language()); |
265 | S lang = cast getThreadLocal(((ThreadLocal) get(bot, 'language_out))); |
266 | if (nempty(lang)) cset(session!, language := lang); |
267 | |
268 | S a2 = replaceAll(a, "\\s*#mainForm\\b", ""); |
269 | if (neq(a, a2)) { |
270 | form = new MainForm; |
271 | print("Made form for cookie " + session->cookie + ": " + sfu(form)); |
272 | cset(session!, +form); |
273 | } |
274 | |
275 | ret deliverAnswerAndFormStep(a2); |
276 | } |
277 | |
278 | sS deliverAnswerAndFormStep(S answer) { |
279 | if (session->form != null) session->form.update(r { session->change() }); |
280 | /*if (form.shouldCancel()) { |
281 | session->cancelForm(); |
282 | ret answer; |
283 | } |
284 | */ |
285 | FormInFlight form = session->form; // form may have cancelled itself in update |
286 | if (form == null || form.currentStep() == null) ret answer; |
287 | |
288 | FormStep step = form.currentStep(); |
289 | if (empty(answer)) |
290 | answer = step.displayText; |
291 | else |
292 | out->buttonsIntro = step.displayText; |
293 | out->placeholder = or(step.placeholder, step.displayText); |
294 | print("Step " + form.stepIndex + ": " + sfu(step)); |
295 | out->defaultInput = or2(step.value, step.defaultValue); |
296 | out->buttons = cloneList(step.buttons); |
297 | if (form.stepIndex > 0) out->buttons.add(de() ? "Zurück" : "Back"); |
298 | out->buttons.add(de() ? "Abbrechen" : "Cancel"); |
299 | ret answer; |
300 | } |
301 | |
302 | sS initialMessage() { |
303 | print("initialMessage, lang=" + session->language()); |
304 | ret or2(answer("#greeting"), "Hello"); |
305 | } |
306 | |
307 | sO dbBot() { |
308 | ret getBot(dbBotID); |
309 | } |
310 | |
311 | sbool de() { |
312 | ret eqic(session->language(), "de"); |
313 | } |
314 | |
315 | sbool botOn() { |
316 | ret isTrue(call(dbBot(), "botOn")); |
317 | } |
318 | |
319 | sbool botAutoOpen() { |
320 | ret isTrue(call(dbBot(), "botAutoOpen")); |
321 | } |
Began life as a copy of #1026222
download show line numbers debug dex old transpilations
Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1026410 |
Snippet name: | Tomii Boi Thought Bot [LIVE] |
Eternal ID of this version: | #1026410/1 |
Text MD5: | 018d79e4f3780dffc7dd2095d188c62b |
Transpilation MD5: | 4050651961a71c7af1b9c4af2da71a81 |
Author: | stefan |
Category: | javax / a.i. |
Type: | JavaX source code (desktop) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2020-01-03 00:55:44 |
Source code size: | 8803 bytes / 321 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 353 / 1165 |
Referenced in: | [show references] |