-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMessageCommandParser.ts
More file actions
117 lines (104 loc) · 3.02 KB
/
Copy pathMessageCommandParser.ts
File metadata and controls
117 lines (104 loc) · 3.02 KB
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
import type { MessageContextJSON } from "@structures/builder/Context.js";
import { closest, distance } from "fastest-levenshtein";
interface IntegerOptionConfig {
key: string;
fallbackAliases?: string[];
defaultValue?: number;
startIndex?: number;
useFuzzy?: boolean;
maxDistance?: number;
}
interface MatchOptions {
useFuzzy?: boolean;
maxDistance?: number;
}
export class MessageCommandParser {
readonly args: string[];
readonly normalizedArgs: string[];
constructor(private readonly ctx: MessageContextJSON) {
this.args = ctx.args;
this.normalizedArgs = this.args.map((arg) =>
MessageCommandParser.normalizeToken(arg)
);
}
static normalizeToken(value: string): string {
return String(value)
.trim()
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "");
}
getAliases(key: string, fallbackAliases: string[] = []): string[] {
return this.ctx
.getLocalizationAliases(key, fallbackAliases)
.map((value) => MessageCommandParser.normalizeToken(value))
.filter(
(value, index, arr) => value.length > 0 && arr.indexOf(value) === index
);
}
private findClosestAlias(
token: string,
aliases: string[],
maxDistance: number = 1
): string | null {
if (!token || aliases.length === 0) return null;
const best = closest(token, aliases);
if (!best) return null;
const d = distance(token, best);
return d <= maxDistance ? best : null;
}
matchesArg(
index: number,
key: string,
fallbackAliases: string[] = [],
options: MatchOptions = {}
): boolean {
const token = this.normalizedArgs[index];
if (!token) return false;
const aliases = this.getAliases(key, fallbackAliases);
const aliasSet = new Set(aliases);
if (aliasSet.has(token)) return true;
if (!options.useFuzzy) return false;
return (
this.findClosestAlias(token, aliases, options.maxDistance ?? 1) !== null
);
}
parseIntegerOption(config: IntegerOptionConfig): number {
const {
key,
fallbackAliases = [],
defaultValue = 0,
startIndex = 0,
useFuzzy = false,
maxDistance = 1,
} = config;
const aliasList = this.getAliases(key, fallbackAliases);
const aliases = new Set(aliasList);
const directValue = Number.parseInt(
this.normalizedArgs[startIndex + 1] ?? "",
10
);
if (Number.isFinite(directValue) && directValue >= 0) {
return directValue;
}
for (let i = startIndex + 1; i < this.normalizedArgs.length; i += 1) {
const token = this.normalizedArgs[i];
const [keyPart, valuePart] = token.split(":");
const nearestKey = useFuzzy
? this.findClosestAlias(keyPart, aliasList, maxDistance)
: null;
if (
aliases.has(token) ||
(useFuzzy && this.findClosestAlias(token, aliasList, maxDistance))
) {
const parsed = Number.parseInt(this.normalizedArgs[i + 1] ?? "", 10);
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
}
if (valuePart && (aliases.has(keyPart) || nearestKey)) {
const parsed = Number.parseInt(valuePart, 10);
if (Number.isFinite(parsed) && parsed >= 0) return parsed;
}
}
return defaultValue;
}
}