-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
123 lines (107 loc) · 4.03 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
interface Message {
text: string; // original text of the message, unchanged
interpreted: string; // interpreted text of the message (how RS sees it: lowercase, no special chars, etc.)
pattern: string; // which of the patterns you supplied to `jive.hear(pattern1, pattern2, ...)` matched the message text
patternIndex: number; // the index (starting with 0) of the pattern that matched (always 0 if you only supply one)
// replacements: { [placeholder: string]: string[] }; // e.g., if you did `jive.hear('so [*] uh [*] hi * i am [*] hungry are (you|yall) too')`,
} // and the user says 'So uh, oh well. Hi Keegan! I am hungry. Are you, too?', then
// `replacements` will be `{ '[*]': ['', 'oh well', ''], '*': ['keegan'], '(you|yall)': ['you'] }`
interface Trigger {
patterns: string[];
callback(message: Message): void|Promise<void>;
}
interface TriggerTier {
current: Trigger[];
parent: TriggerTier|null;
}
interface TriggerMatchInfo {
trigger: Trigger;
interpreted: Message['interpreted'];
pattern: Message['pattern'];
patternIndex: Message['patternIndex'];
// replacements: Message['replacements'];
}
const patternToRegex = (pattern: string): RegExp => {
const regexString = pattern
.toLowerCase()
.replace(/ +/g, ' ') // condense whitespace
.replace(/[^a-z0-9*\[\]\(\)| ]/g, '') // remove invalid characters
.replace(/\*/g, '.*') // wildcards
.replace(/ \[([^\]]*)\]/g, '( $1)?') // optional groups anywhere but the beginning of the string
.replace(/\[([^\]]*)\] /g, '($1 )?') // optional group at the beginning of the string
.trim();
return new RegExp(`^${regexString}$`);
};
const formatTextForTrigger = (text: string): string => {
return text.toLowerCase().replace(/[^a-z0-9 ]/ig, '');
};
const matchTextToTrigger = (text: string, triggers: Trigger[]): null|TriggerMatchInfo => {
const interpreted = formatTextForTrigger(text);
for (const trigger of triggers) {
const { patterns } = trigger;
for (let patternIndex = 0; patternIndex < patterns.length; patternIndex++) {
const pattern = patterns[patternIndex];
const match = interpreted.match(patternToRegex(pattern));
if (match) {
return {
trigger,
interpreted,
pattern,
patternIndex,
}
}
}
}
return null;
}
class JiveScript {
private triggerTier: TriggerTier;
public lastResponse?: string;
constructor() {
this.triggerTier = {
current: [],
parent: null,
}
}
private get triggers(): Trigger[] {
return this.triggerTier.current;
}
private matchTrigger(text: string): null|TriggerMatchInfo {
const triggerMatch = matchTextToTrigger(text, this.triggers);
if (triggerMatch || !this.triggerTier.parent) return triggerMatch;
this.triggerTier = {
current: this.triggerTier.parent.current,
parent: this.triggerTier.parent.parent,
};
return this.matchTrigger(text);
}
tell(text: string): Promise<string> {
return new Promise((resolve, reject) => {
const triggerMatch = this.matchTrigger(text);
if (!triggerMatch) {
resolve('Error: No matching trigger found!');
} else {
const message = Object.assign({ text }, triggerMatch);
const cbReturn = triggerMatch.trigger.callback(message);
const cbPromise = cbReturn && cbReturn.then ? cbReturn : new Promise<void>(res => res());
cbPromise.then(() => resolve(this.lastResponse || 'Error: no response to matched trigger!'));
}
});
}
hear(pattern: string|string[], callback: Trigger['callback']): void {
const patterns = typeof pattern === 'string' ? [pattern] : pattern;
this.triggers.push({
patterns,
callback,
});
}
say(responseText: string, callback?: () => void|Promise<void>): void {
this.lastResponse = responseText;
this.triggerTier = {
current: [],
parent: this.triggerTier,
}
if (callback) callback();
};
}
export default JiveScript;