gramstax 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.cjs +2 -0
- package/dist/src/index.cjs.map +1 -0
- package/dist/src/{core/bot.d.ts → index.d.cts} +500 -12
- package/dist/src/index.d.ts +1292 -5
- package/dist/src/index.js +2 -4
- package/dist/src/index.js.map +1 -0
- package/package.json +10 -9
- package/dist/package.json +0 -52
- package/dist/src/base/general.d.ts +0 -7
- package/dist/src/base/general.d.ts.map +0 -1
- package/dist/src/base/general.js +0 -15
- package/dist/src/base/guard.d.ts +0 -13
- package/dist/src/base/guard.d.ts.map +0 -1
- package/dist/src/base/guard.js +0 -8
- package/dist/src/base/index.d.ts +0 -4
- package/dist/src/base/index.d.ts.map +0 -1
- package/dist/src/base/index.js +0 -3
- package/dist/src/base/page.d.ts +0 -263
- package/dist/src/base/page.d.ts.map +0 -1
- package/dist/src/base/page.js +0 -805
- package/dist/src/cache/external.d.ts +0 -10
- package/dist/src/cache/external.d.ts.map +0 -1
- package/dist/src/cache/external.js +0 -16
- package/dist/src/cache/index.d.ts +0 -2
- package/dist/src/cache/index.d.ts.map +0 -1
- package/dist/src/cache/index.js +0 -1
- package/dist/src/core/bot.d.ts.map +0 -1
- package/dist/src/core/bot.js +0 -465
- package/dist/src/core/ctx.d.ts +0 -60
- package/dist/src/core/ctx.d.ts.map +0 -1
- package/dist/src/core/ctx.js +0 -175
- package/dist/src/core/index.d.ts +0 -3
- package/dist/src/core/index.d.ts.map +0 -1
- package/dist/src/core/index.js +0 -2
- package/dist/src/grammy/index.d.ts +0 -2
- package/dist/src/grammy/index.d.ts.map +0 -1
- package/dist/src/grammy/index.js +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/template/engine.d.ts +0 -34
- package/dist/src/template/engine.d.ts.map +0 -1
- package/dist/src/template/engine.js +0 -122
- package/dist/src/template/index.d.ts +0 -3
- package/dist/src/template/index.d.ts.map +0 -1
- package/dist/src/template/index.js +0 -2
- package/dist/src/template/manager.d.ts +0 -111
- package/dist/src/template/manager.d.ts.map +0 -1
- package/dist/src/template/manager.js +0 -237
- package/src/base/general.ts +0 -17
- package/src/base/guard.ts +0 -10
- package/src/base/index.ts +0 -3
- package/src/base/page.ts +0 -1111
- package/src/cache/external.ts +0 -15
- package/src/cache/index.ts +0 -1
- package/src/core/bot.ts +0 -535
- package/src/core/ctx.ts +0 -177
- package/src/core/index.ts +0 -2
- package/src/grammy/index.ts +0 -1
- package/src/index.ts +0 -4
- package/src/template/engine.ts +0 -167
- package/src/template/index.ts +0 -2
- package/src/template/manager.ts +0 -280
- package/src/types/page.d.ts +0 -4
package/src/core/ctx.ts
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import { CacheExternal } from "../cache"
|
|
2
|
-
import { setTimeout } from "node:timers/promises"
|
|
3
|
-
import { BaseGeneral } from "../base"
|
|
4
|
-
import { TemplateManager } from "../template"
|
|
5
|
-
import { InlineKeyboard, Keyboard, type Context } from "grammy"
|
|
6
|
-
|
|
7
|
-
export class Ctx extends BaseGeneral {
|
|
8
|
-
public data = (this as any).constructor.data as { name: string; callbackData: (...args: any) => string | any } // for internal access (this.**). example this.data.callbackData
|
|
9
|
-
|
|
10
|
-
public get isPremium() {
|
|
11
|
-
return this.ct.from?.is_premium ?? false
|
|
12
|
-
}
|
|
13
|
-
public get isBot() {
|
|
14
|
-
return this.ct.from?.is_bot ?? false
|
|
15
|
-
}
|
|
16
|
-
public get userid() {
|
|
17
|
-
return this.ct.from?.id as number
|
|
18
|
-
}
|
|
19
|
-
public get username() {
|
|
20
|
-
return this.ct.from?.username as string
|
|
21
|
-
}
|
|
22
|
-
public get fullname() {
|
|
23
|
-
return `${this.ct.from?.first_name || ``}${this.ct.from?.last_name || ``}`
|
|
24
|
-
}
|
|
25
|
-
public get firstname() {
|
|
26
|
-
return this.ct.from?.first_name as string
|
|
27
|
-
}
|
|
28
|
-
public get lastname() {
|
|
29
|
-
return this.ct.from?.last_name as string
|
|
30
|
-
}
|
|
31
|
-
public get msgText() {
|
|
32
|
-
return this.ct.message?.text as string
|
|
33
|
-
}
|
|
34
|
-
public get msgTextIntent() {
|
|
35
|
-
return this.msgText && this.msgText.indexOf(` `) != -1 ? (this.msgText.split(` `)[0] as string) : ``
|
|
36
|
-
}
|
|
37
|
-
public get msgTextPayloads() {
|
|
38
|
-
return this.msgText && this.msgText.indexOf(` `) != -1 ? (this.msgText.split(` `).slice(1) as any[]) : ([] as any[])
|
|
39
|
-
}
|
|
40
|
-
public get msgTextPayload() {
|
|
41
|
-
return this.msgTextPayloads[0] as string
|
|
42
|
-
}
|
|
43
|
-
public get msgCaption() {
|
|
44
|
-
return this.ct.message?.caption as string
|
|
45
|
-
}
|
|
46
|
-
public get msgCaptionIntent() {
|
|
47
|
-
return this.msgCaption && this.msgCaption.indexOf(` `) != -1 ? (this.msgCaption.split(` `)[0] as string) : ``
|
|
48
|
-
}
|
|
49
|
-
public get msgPhoto() {
|
|
50
|
-
return this.ct.message?.photo
|
|
51
|
-
}
|
|
52
|
-
public get msgVideo() {
|
|
53
|
-
return this.ct.message?.video
|
|
54
|
-
}
|
|
55
|
-
public get msgAudio() {
|
|
56
|
-
return this.ct.message?.audio
|
|
57
|
-
}
|
|
58
|
-
public get callbackData() {
|
|
59
|
-
return this.ct.callbackQuery?.data as string
|
|
60
|
-
}
|
|
61
|
-
public get callbackDataIntent() {
|
|
62
|
-
return this.callbackData && this.callbackData.indexOf(`:`) != -1 ? (this.callbackData.split(`:`)[0] as string) : ``
|
|
63
|
-
}
|
|
64
|
-
public get callbackDataPayloads() {
|
|
65
|
-
return this.callbackData && this.callbackData.indexOf(`:`) != -1 ? (this.callbackData.split(`:`).slice(1) as any[]) : ([] as any[])
|
|
66
|
-
}
|
|
67
|
-
public get callbackDataPayload() {
|
|
68
|
-
return this.callbackDataPayloads?.[0] as string
|
|
69
|
-
}
|
|
70
|
-
public get callbackDataParts() {
|
|
71
|
-
return this.callbackDataPayload && this.callbackDataPayload.indexOf(`+`) != -1 ? (this.callbackDataPayload.split(`+`) as any[]) : ([] as any[])
|
|
72
|
-
}
|
|
73
|
-
public get languageCode() {
|
|
74
|
-
return this.ct.from?.language_code as string
|
|
75
|
-
}
|
|
76
|
-
public set languageCode(v: any) {
|
|
77
|
-
;(this as any).ct.from.language_code = v
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
public session: { method?: string; params?: any } = {}
|
|
81
|
-
public constructor(public ct: Context, public templateManager: TemplateManager, public cacheSession: CacheExternal, public cacheKeyboard: Map<any, any>) {
|
|
82
|
-
super()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
public async broadcast(ids: any[], languageCode: any[], keyboard?: Keyboard | InlineKeyboard, data?: Record<string, any>, baseId?: string, options?: any) {
|
|
86
|
-
const limitFree = 30
|
|
87
|
-
const msgs = languageCode.map((it) => this.templateManager.getMessage(this.data.name, baseId, it, data)) as any[]
|
|
88
|
-
for (let index = 0; index < ids.length; index++) {
|
|
89
|
-
const id = ids[index]
|
|
90
|
-
const msg = msgs[index]
|
|
91
|
-
await this.ct.api.sendMessage(id, msg, {
|
|
92
|
-
parse_mode: `HTML`,
|
|
93
|
-
reply_markup: keyboard,
|
|
94
|
-
...options
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
if (index + 1 == limitFree) {
|
|
98
|
-
await setTimeout(1200)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public async reply(keyboard?: Keyboard | InlineKeyboard, data?: Record<string, any>, baseId?: string, options?: any) {
|
|
104
|
-
const msg = this.templateManager.getMessage(this.data.name, baseId, this.languageCode, data)
|
|
105
|
-
return await this.ct.reply(msg, {
|
|
106
|
-
parse_mode: `HTML`,
|
|
107
|
-
reply_markup: keyboard,
|
|
108
|
-
...options
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public async replyAction(type: Parameters<typeof this.ct.replyWithChatAction>[0]) {
|
|
113
|
-
return await this.ct.replyWithChatAction(type)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
public async edit(keyboard?: Keyboard | InlineKeyboard, data?: Record<string, any>, baseId?: string, options?: any) {
|
|
117
|
-
try {
|
|
118
|
-
const msg = this.templateManager.getMessage(this.data.name, baseId, this.languageCode, data)
|
|
119
|
-
return await this.ct.editMessageText(msg, {
|
|
120
|
-
parse_mode: `HTML`,
|
|
121
|
-
reply_markup: keyboard,
|
|
122
|
-
...options
|
|
123
|
-
})
|
|
124
|
-
} catch (error) {
|
|
125
|
-
await this.reply(keyboard, data, baseId, options)
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
public async delete() {
|
|
130
|
-
return await this.ct.deleteMessage()
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
public async callbackQueryAnswer(options?: any) {
|
|
134
|
-
try {
|
|
135
|
-
return await this.ct.answerCallbackQuery(options)
|
|
136
|
-
} catch {
|
|
137
|
-
return null
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
public async sessionSet(method: string, params: any, ...moreRoot: any) {
|
|
142
|
-
try {
|
|
143
|
-
const value = { method, params, ...moreRoot }
|
|
144
|
-
const result = await this.cacheSession.set(`${this.userid}`, { method, params, ...moreRoot })
|
|
145
|
-
if (result) {
|
|
146
|
-
this.session = value
|
|
147
|
-
}
|
|
148
|
-
return result
|
|
149
|
-
} catch {
|
|
150
|
-
return null
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
public async sessionGet(): Promise<{ method: string; params: any; [moreRoot: string]: any } | null> {
|
|
155
|
-
try {
|
|
156
|
-
const result = await this.cacheSession.get(`${this.userid}`)
|
|
157
|
-
if (result) {
|
|
158
|
-
this.session = result
|
|
159
|
-
}
|
|
160
|
-
return result || null
|
|
161
|
-
} catch {
|
|
162
|
-
return null
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
public async sessionClear() {
|
|
167
|
-
try {
|
|
168
|
-
const result = await this.cacheSession.delete(`${this.userid}`)
|
|
169
|
-
if (result) {
|
|
170
|
-
this.session = {}
|
|
171
|
-
}
|
|
172
|
-
return result
|
|
173
|
-
} catch {
|
|
174
|
-
return null
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
package/src/core/index.ts
DELETED
package/src/grammy/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "grammy"
|
package/src/index.ts
DELETED
package/src/template/engine.ts
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
export class TemplateEngine {
|
|
2
|
-
public static _escape(str: string): string {
|
|
3
|
-
return str
|
|
4
|
-
.replace(/\\/g, `\\\\`) // escape backslash: \ → \\
|
|
5
|
-
.replace(/`/g, `\\\``) // escape backtick: ` → \`
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
public static _processVariables(template: string) {
|
|
9
|
-
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => `\${(data && data["${key}"]) ?? ""}`)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
public static _processBaseBlocks(template: string) {
|
|
13
|
-
const baseRegex = /<base\b[^>]*?(?:\sid=(["']?)([^"'\s>]+)\1)?[^>]*?>([\s\S]*?)<\/base>/g
|
|
14
|
-
const result: {
|
|
15
|
-
[baseId: string]: string
|
|
16
|
-
} = {}
|
|
17
|
-
|
|
18
|
-
for (const match of template.matchAll(baseRegex)) {
|
|
19
|
-
const idValue = match[2] ?? `default`
|
|
20
|
-
const inner = match[3] ?? ``
|
|
21
|
-
const cleaned = inner
|
|
22
|
-
.replace(/^[\t ]+/gm, ``)
|
|
23
|
-
.replace(/^\n+/, ``)
|
|
24
|
-
.replace(/\n+$/, ``)
|
|
25
|
-
.trim()
|
|
26
|
-
|
|
27
|
-
result[idValue] = cleaned
|
|
28
|
-
}
|
|
29
|
-
return result
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public static _processScriptBlocks(template: string) {
|
|
33
|
-
return template.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gi, (_, rawCode) => {
|
|
34
|
-
const innerCode = rawCode.trim()
|
|
35
|
-
if (!innerCode) return ``
|
|
36
|
-
|
|
37
|
-
return `\${(() => { if (!data) return ''; try { with(data) { ${innerCode} } } catch(e) { return '' } })()}`
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
public static _processSpaceBlocks(template: string) {
|
|
42
|
-
return template.replace(/^[ \t]*<space\s*\/>[ \t]*$/gim, ``)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
public static _processDivBlocks(template: string) {
|
|
46
|
-
return template.replace(/<div>([\s\S]*?)<\/div>/gi, (_, inner) => {
|
|
47
|
-
const cleaned = inner
|
|
48
|
-
.replace(/^[\t ]+/gm, ``)
|
|
49
|
-
.replace(/^\n+/, ``)
|
|
50
|
-
.replace(/\n+$/, ``)
|
|
51
|
-
return cleaned
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public static _processMessageBlocks(template: string) {
|
|
56
|
-
const messageRegex = /<message\s+lang="([^"]+)"\s*>([\s\S]*?)<\/message>/gi
|
|
57
|
-
const result: {
|
|
58
|
-
[lang: string]: string
|
|
59
|
-
} = {}
|
|
60
|
-
|
|
61
|
-
for (const [, lang, rawContent] of template.matchAll(messageRegex) as any) {
|
|
62
|
-
const cleaned = rawContent
|
|
63
|
-
.replace(/^[\t ]+/gm, ``)
|
|
64
|
-
.replace(/^\n+/, ``)
|
|
65
|
-
.replace(/\n+$/, ``)
|
|
66
|
-
.trim()
|
|
67
|
-
|
|
68
|
-
result[lang] = cleaned
|
|
69
|
-
}
|
|
70
|
-
return result
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
public static _processKeyboardBlocks(template: string) {
|
|
74
|
-
const keyboardRegex = /<keyboard\b[^>]*\blang="([^"]*)"[^>]*?(?:\sfor="([^"]*)")?[^>]*>([\s\S]*?)<\/keyboard>/gi
|
|
75
|
-
const result: {
|
|
76
|
-
[lang: string]: {
|
|
77
|
-
inline?: string
|
|
78
|
-
default?: string
|
|
79
|
-
}
|
|
80
|
-
} = {}
|
|
81
|
-
|
|
82
|
-
for (const [, lang, forAttr, rawContent] of template.matchAll(keyboardRegex) as any) {
|
|
83
|
-
const mode = forAttr === `default` ? `default` : `inline`
|
|
84
|
-
const cleaned = rawContent.trim()
|
|
85
|
-
|
|
86
|
-
if (!result[lang]) {
|
|
87
|
-
result[lang] = {}
|
|
88
|
-
}
|
|
89
|
-
result[lang][mode] = cleaned
|
|
90
|
-
}
|
|
91
|
-
return result
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public static _compileToFunction<T extends boolean>(template: T extends true ? string[] : string, isArray: T): T extends true ? (data?: Record<string, any>) => string[] : (data?: Record<string, any>) => string {
|
|
95
|
-
let singleOrCombined: string
|
|
96
|
-
if (isArray) {
|
|
97
|
-
const multiTemplate = template as string[]
|
|
98
|
-
singleOrCombined = multiTemplate.map((t) => `\`${t}\``).join(`,`)
|
|
99
|
-
} else {
|
|
100
|
-
singleOrCombined = `\`${template as string}\``
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let processed = singleOrCombined
|
|
104
|
-
processed = this._processScriptBlocks(this._processDivBlocks(this._processSpaceBlocks(processed)))
|
|
105
|
-
|
|
106
|
-
if (isArray) {
|
|
107
|
-
const functionBody = `return [${processed}]`
|
|
108
|
-
return new Function(`data`, functionBody) as any
|
|
109
|
-
} else {
|
|
110
|
-
const functionBody = `return ${processed}`
|
|
111
|
-
return new Function(`data`, functionBody) as any
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
public static compileMessages(template: string) {
|
|
116
|
-
const baseBlocks = this._processBaseBlocks(template)
|
|
117
|
-
const result: {
|
|
118
|
-
[baseId: string]: {
|
|
119
|
-
[lang: string]: (data?: Record<string, any>) => string
|
|
120
|
-
}
|
|
121
|
-
} = {}
|
|
122
|
-
|
|
123
|
-
for (const [baseId, baseContent] of Object.entries(baseBlocks)) {
|
|
124
|
-
const messages = this._processMessageBlocks(baseContent)
|
|
125
|
-
result[baseId] = {}
|
|
126
|
-
|
|
127
|
-
for (const [lang, content] of Object.entries(messages)) {
|
|
128
|
-
const processed = this._processVariables(this._processScriptBlocks(content))
|
|
129
|
-
result[baseId][lang] = this._compileToFunction(processed, false)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return result
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
public static compileKeyboards(template: string) {
|
|
137
|
-
const baseBlocks = this._processBaseBlocks(template)
|
|
138
|
-
const result: {
|
|
139
|
-
[baseId: string]: {
|
|
140
|
-
[lang: string]: {
|
|
141
|
-
inline?: (data?: Record<string, any>) => string[]
|
|
142
|
-
default?: (data?: Record<string, any>) => string[]
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
} = {}
|
|
146
|
-
|
|
147
|
-
for (const [baseId, baseContent] of Object.entries(baseBlocks)) {
|
|
148
|
-
const keyboards = this._processKeyboardBlocks(baseContent)
|
|
149
|
-
result[baseId] = {}
|
|
150
|
-
|
|
151
|
-
for (const [lang, modes] of Object.entries(keyboards)) {
|
|
152
|
-
result[baseId][lang] = {}
|
|
153
|
-
|
|
154
|
-
for (const [mode, content] of Object.entries(modes)) {
|
|
155
|
-
// Pisahkan label, handle escaped comma
|
|
156
|
-
const labels = content.split(/(?<!\\),\s*/).map((label: string) => label.replace(/\\,/g, `,`).trim())
|
|
157
|
-
|
|
158
|
-
const processedLabels = labels.map((label) => this._processVariables(this._processScriptBlocks(label)))
|
|
159
|
-
|
|
160
|
-
result[baseId][lang][mode as `inline` | `default`] = this._compileToFunction(processedLabels, true)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return result
|
|
166
|
-
}
|
|
167
|
-
}
|
package/src/template/index.ts
DELETED
package/src/template/manager.ts
DELETED
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
import { TemplateEngine } from "."
|
|
2
|
-
import { readFile, mkdir } from "node:fs/promises"
|
|
3
|
-
import { watch, FSWatcher } from "chokidar"
|
|
4
|
-
import { basename, join, extname } from "node:path"
|
|
5
|
-
import { readdirSync, readFileSync, existsSync, statSync, writeFileSync } from "node:fs"
|
|
6
|
-
|
|
7
|
-
/** Template file interface for better type safety */
|
|
8
|
-
type ICompiledTemplate = {
|
|
9
|
-
message: ReturnType<typeof TemplateEngine.compileMessages>
|
|
10
|
-
keyboard: ReturnType<typeof TemplateEngine.compileKeyboards>
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Template loading options */
|
|
14
|
-
type ITemplateOptions = {
|
|
15
|
-
/** Enable file watching for hot reloading (if have templatePath) */
|
|
16
|
-
enableWatch?: boolean
|
|
17
|
-
/** Base path for template files (set null if dont want read template from file) */
|
|
18
|
-
path?: string | undefined | null
|
|
19
|
-
/** Force language params to this value */
|
|
20
|
-
forceLanguage?: string | undefined | null
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** Template management system */
|
|
24
|
-
export class TemplateManager {
|
|
25
|
-
public _map = new Map<string, ICompiledTemplate>()
|
|
26
|
-
public _engine = TemplateEngine
|
|
27
|
-
public _watcher: FSWatcher | null = null
|
|
28
|
-
public _supportedExtensions = new Set([`.html`])
|
|
29
|
-
public readonly _path: string | null
|
|
30
|
-
public readonly _forceLanguage: string | null
|
|
31
|
-
public readonly _isWatchEnabled: boolean
|
|
32
|
-
public constructor(options: ITemplateOptions = {}) {
|
|
33
|
-
const { enableWatch = false, path = null, forceLanguage = null } = options
|
|
34
|
-
|
|
35
|
-
this._path = path
|
|
36
|
-
this._forceLanguage = forceLanguage
|
|
37
|
-
this._isWatchEnabled = enableWatch
|
|
38
|
-
|
|
39
|
-
this._validatePath()
|
|
40
|
-
this._initializeWatcher()
|
|
41
|
-
this._loadAllFiles()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Validate and ensure template directory exists. @returns Void */
|
|
45
|
-
public _validatePath(): void {
|
|
46
|
-
try {
|
|
47
|
-
if (this._path === null || !existsSync(this._path)) {
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const stats = statSync(this._path)
|
|
52
|
-
if (!stats.isDirectory()) {
|
|
53
|
-
throw new Error(`Template path is not a directory: ${this._path}`)
|
|
54
|
-
}
|
|
55
|
-
} catch (error) {
|
|
56
|
-
throw new Error(`Failed to validate Template path: ${error}`)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/** Initialize file system watcher for hot reloading. @returns Void */
|
|
61
|
-
public _initializeWatcher(): void {
|
|
62
|
-
if (this._path === null || !this._isWatchEnabled) {
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
this._watcher = watch(this._path, {
|
|
67
|
-
persistent: true,
|
|
68
|
-
ignoreInitial: true,
|
|
69
|
-
awaitWriteFinish: {
|
|
70
|
-
stabilityThreshold: 100,
|
|
71
|
-
pollInterval: 50
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
this._watcher!.on(`change`, this._handleFileChange.bind(this))
|
|
76
|
-
this._watcher!.on(`add`, this._handleFileAdd.bind(this))
|
|
77
|
-
this._watcher!.on(`unlink`, this._handleFileDelete.bind(this))
|
|
78
|
-
this._watcher!.on(`error`, this._handleWatchError.bind(this))
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Handle file change events */
|
|
82
|
-
public async _handleFileChange(filepath: string): Promise<void> {
|
|
83
|
-
const filename = basename(filepath)
|
|
84
|
-
if (this._isValidFile(filename)) {
|
|
85
|
-
await this._loadFile(filename, false)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Handle file add events */
|
|
90
|
-
public async _handleFileAdd(filePath: string): Promise<void> {
|
|
91
|
-
const fileName = basename(filePath)
|
|
92
|
-
if (this._isValidFile(fileName)) {
|
|
93
|
-
await this._loadFile(fileName, false)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/** Handle file delete events */
|
|
98
|
-
public _handleFileDelete(filePath: string): void {
|
|
99
|
-
const fileName = basename(filePath)
|
|
100
|
-
const fileKey = this._getKey(fileName)
|
|
101
|
-
if (this._map.has(fileKey)) {
|
|
102
|
-
this._map.delete(fileKey)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Handle watcher errors */
|
|
107
|
-
public _handleWatchError(error: any): void {
|
|
108
|
-
// you can overrides
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Load all Template files during initialization */
|
|
112
|
-
public _loadAllFiles(): void {
|
|
113
|
-
if (this._path === null || !existsSync(this._path)) {
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
const files = readdirSync(this._path, { encoding: `utf8` })
|
|
117
|
-
const validFiles = files.filter((file) => this._isValidFile(file))
|
|
118
|
-
for (const fileName of validFiles) {
|
|
119
|
-
this._loadFile(fileName, true)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/** Load and compile a single template file */
|
|
124
|
-
public async _loadFile(filename: string, useSync: boolean = true): Promise<void> {
|
|
125
|
-
if (this._path === null) {
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
const path = join(this._path, filename)
|
|
129
|
-
const key = this._getKey(filename)
|
|
130
|
-
const raw = useSync ? readFileSync(path, { encoding: `utf8` }) : await readFile(path, { encoding: `utf8` })
|
|
131
|
-
this.compile(key, raw)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
public _isValidFile(filename: string): boolean {
|
|
135
|
-
const extension = extname(filename).toLowerCase()
|
|
136
|
-
return this._supportedExtensions.has(extension)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
public _getKey(filename: string): string {
|
|
140
|
-
return filename.replace(/\.[^/.]+$/, ``)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
public compile(key: string, rawTemplate: string, keepIfExist?: Partial<{ [K in keyof ICompiledTemplate]: boolean }>) {
|
|
144
|
-
const existing = this._map.get(key)
|
|
145
|
-
const compiled = existing || ({} as ICompiledTemplate)
|
|
146
|
-
let updated = false
|
|
147
|
-
|
|
148
|
-
if (keepIfExist === undefined || keepIfExist?.message === false || existing?.message === undefined) {
|
|
149
|
-
compiled.message = TemplateEngine.compileMessages(rawTemplate)
|
|
150
|
-
updated = true
|
|
151
|
-
}
|
|
152
|
-
if (keepIfExist === undefined || keepIfExist?.keyboard === false || existing?.keyboard === undefined) {
|
|
153
|
-
compiled.keyboard = TemplateEngine.compileKeyboards(rawTemplate)
|
|
154
|
-
updated = true
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (updated) {
|
|
158
|
-
this._map.set(key, compiled)
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
/**
|
|
163
|
-
* Get compiled message template
|
|
164
|
-
* @param key Template identifier (filename without extension)
|
|
165
|
-
* @param baseId Base id attr from block
|
|
166
|
-
* @param language Target language code
|
|
167
|
-
* @param data Template variables
|
|
168
|
-
* @returns Rendered message string
|
|
169
|
-
*/
|
|
170
|
-
public getMessage(key: string, baseId: string = `default`, language: string = `en`, data?: Record<string, any>): string {
|
|
171
|
-
const template = this._map.get(key)
|
|
172
|
-
const func = template?.message?.[baseId]?.[this._forceLanguage || language]
|
|
173
|
-
if (!func) {
|
|
174
|
-
return ``
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
return func(data)
|
|
179
|
-
} catch (error) {
|
|
180
|
-
return ``
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Get compiled keyboard template
|
|
186
|
-
* @param key Template identifier (filename without extension)
|
|
187
|
-
* @param baseId Base id attr from block
|
|
188
|
-
* @param language Target language code
|
|
189
|
-
* @param data Template variables
|
|
190
|
-
* @returns Array of rendered button labels
|
|
191
|
-
*/
|
|
192
|
-
public getKeyboard(key: string, baseId: string = `default`, language: string = `en`, display: `inline` | `default` = `inline`, data?: Record<string, any>): string[] {
|
|
193
|
-
const template = this._map.get(key)
|
|
194
|
-
const func = template?.keyboard?.[baseId]?.[this._forceLanguage || language]?.[display]
|
|
195
|
-
if (!func) {
|
|
196
|
-
return []
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
return func(data)
|
|
201
|
-
} catch (error) {
|
|
202
|
-
return []
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Update message template in memory
|
|
208
|
-
* @param key Template identifier (filename without extension)
|
|
209
|
-
* @param rawTemplate Raw template string
|
|
210
|
-
*/
|
|
211
|
-
public setMessage(key: string, rawTemplate: string): void {
|
|
212
|
-
this.compile(key, rawTemplate, { keyboard: true })
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Update keyboard template in memory
|
|
217
|
-
* @param key Template identifier (filename without extension)
|
|
218
|
-
* @param rawTemplate Raw template string
|
|
219
|
-
*/
|
|
220
|
-
public setKeyboard(key: string, rawTemplate: string): void {
|
|
221
|
-
this.compile(key, rawTemplate, { message: true })
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Write template to file and update cache
|
|
226
|
-
* @param filename Target filename (with extension)
|
|
227
|
-
* @param rawTemplate Template to write
|
|
228
|
-
* @returns Promise that resolves when file is written
|
|
229
|
-
*/
|
|
230
|
-
public async write(filename: string, rawTemplate: string): Promise<void> {
|
|
231
|
-
if (this._path === null) {
|
|
232
|
-
return
|
|
233
|
-
}
|
|
234
|
-
const filePath = join(this._path, filename)
|
|
235
|
-
await mkdir(this._path, { recursive: true })
|
|
236
|
-
writeFileSync(filePath, rawTemplate, { encoding: `utf8` })
|
|
237
|
-
await this._loadFile(filename, false)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Get list of available template
|
|
242
|
-
* @returns Array of template names
|
|
243
|
-
*/
|
|
244
|
-
public getAvailable(): string[] {
|
|
245
|
-
return Array.from(this._map.keys())
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Check if template exists
|
|
250
|
-
* @param key Template identifier
|
|
251
|
-
* @returns True if template exists
|
|
252
|
-
*/
|
|
253
|
-
public has(key: string): boolean {
|
|
254
|
-
return this._map.has(key)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Get cache statistics
|
|
259
|
-
* @returns Object with cache information
|
|
260
|
-
*/
|
|
261
|
-
public getCacheStats(): { templateCount: number; totalSize: number } {
|
|
262
|
-
return {
|
|
263
|
-
templateCount: this._map.size,
|
|
264
|
-
totalSize: JSON.stringify(this._map.entries().toArray(), (_, value) => {
|
|
265
|
-
return typeof value === `function` ? value.toString() : value
|
|
266
|
-
}).length
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Cleanup resources and close watchers
|
|
272
|
-
* @returns Promise void
|
|
273
|
-
*/
|
|
274
|
-
public async destroy(): Promise<void> {
|
|
275
|
-
if (this._watcher) {
|
|
276
|
-
await this._watcher.close()
|
|
277
|
-
}
|
|
278
|
-
this._map.clear()
|
|
279
|
-
}
|
|
280
|
-
}
|
package/src/types/page.d.ts
DELETED