@2byte/tgbot-framework 1.0.19 → 1.0.20
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/package.json +1 -1
- package/src/core/App.ts +31 -25
- package/src/user/UserModel.ts +16 -2
- package/src/user/UserStore.ts +28 -26
- package/templates/bot/package.json +1 -1
package/package.json
CHANGED
package/src/core/App.ts
CHANGED
|
@@ -259,10 +259,12 @@ export class App {
|
|
|
259
259
|
|
|
260
260
|
async mainMiddleware() {
|
|
261
261
|
this.bot.use(async (ctx: Telegraf2byteContext, next: () => Promise<void>) => {
|
|
262
|
-
const
|
|
262
|
+
const genTgUsername = () => `unknown_${ctx.from?.id || "user"}`;
|
|
263
|
+
const tgId = this.getTgId(ctx);
|
|
264
|
+
const tgUsername = this.getTgUsername(ctx) || genTgUsername();
|
|
263
265
|
|
|
264
|
-
if (!
|
|
265
|
-
return ctx.reply("
|
|
266
|
+
if (!tgId) {
|
|
267
|
+
return ctx.reply("Unable to identify user");
|
|
266
268
|
}
|
|
267
269
|
|
|
268
270
|
if (!this.config.userStorage) {
|
|
@@ -272,6 +274,7 @@ export class App {
|
|
|
272
274
|
let startPayload: string | null = null;
|
|
273
275
|
let accessKey: string | null = null;
|
|
274
276
|
|
|
277
|
+
// If the message starts with /start, we can check for access key in the payload for private bots. The payload can be in format /start key=ACCESS_KEY or /start refid=USER_REF_ID or /start key=ACCESS_KEY refid=USER_REF_ID
|
|
275
278
|
if (ctx?.message?.text?.startsWith("/start")) {
|
|
276
279
|
startPayload = ctx?.message?.text?.split(" ")[1] || null;
|
|
277
280
|
accessKey =
|
|
@@ -279,9 +282,13 @@ export class App {
|
|
|
279
282
|
? startPayload.split("key=")[1] || null
|
|
280
283
|
: null;
|
|
281
284
|
}
|
|
285
|
+
// Also check for access key in message text for cases when user sends the access key separately after /start command
|
|
286
|
+
if (!accessKey && ctx?.message?.text && ctx?.message?.text?.length == 48) {
|
|
287
|
+
accessKey = ctx.message.text.trim();
|
|
288
|
+
}
|
|
282
289
|
|
|
283
|
-
// Check access by
|
|
284
|
-
if (!this.config.userStorage.exists(
|
|
290
|
+
// Check access by id and register user if not exists
|
|
291
|
+
if (!this.config.userStorage.exists(tgId) && !this.rememberUserById(tgId)) {
|
|
285
292
|
const isAuthByUsername = !this.config.accessPublic && !accessKey;
|
|
286
293
|
this.debugLog(
|
|
287
294
|
"Access control check. isAuthByUsername:",
|
|
@@ -293,12 +300,12 @@ export class App {
|
|
|
293
300
|
if (isAuthByUsername) {
|
|
294
301
|
const requestUsername = this.getTgUsername(ctx);
|
|
295
302
|
this.debugLog("Private access mode. Checking username:", requestUsername);
|
|
296
|
-
const
|
|
303
|
+
const accessUsernameAllowed =
|
|
297
304
|
this.config.envConfig.ACCESS_USERNAMES &&
|
|
298
305
|
this.config.envConfig.ACCESS_USERNAMES.split(",").map((name) => name.trim());
|
|
299
306
|
if (
|
|
300
|
-
|
|
301
|
-
|
|
307
|
+
accessUsernameAllowed &&
|
|
308
|
+
accessUsernameAllowed.every((name) => name.toLowerCase() !== requestUsername.toLowerCase())
|
|
302
309
|
) {
|
|
303
310
|
this.debugLog("Username access denied:", requestUsername);
|
|
304
311
|
return ctx.reply("Access denied. Your username is not in the access list.");
|
|
@@ -337,7 +344,7 @@ export class App {
|
|
|
337
344
|
user_refid: userRefIdFromStart,
|
|
338
345
|
tg_id: ctx.from.id,
|
|
339
346
|
tg_username: tgUsername,
|
|
340
|
-
tg_first_name: ctx.from.first_name ||
|
|
347
|
+
tg_first_name: ctx.from.first_name || "",
|
|
341
348
|
tg_last_name: ctx.from.last_name || "",
|
|
342
349
|
role: "user",
|
|
343
350
|
language: ctx.from.language_code || "en",
|
|
@@ -346,15 +353,15 @@ export class App {
|
|
|
346
353
|
);
|
|
347
354
|
}
|
|
348
355
|
|
|
349
|
-
ctx.user = this.config.userStorage.find(
|
|
356
|
+
ctx.user = this.config.userStorage.find(tgId);
|
|
350
357
|
ctx.userStorage = this.config.userStorage;
|
|
351
358
|
ctx.userSession = this.config.userStorage.findSession(ctx.user);
|
|
352
359
|
Object.assign(ctx, Telegraf2byteContextExtraMethods);
|
|
353
360
|
|
|
354
|
-
this.config.userStorage.upActive(
|
|
361
|
+
this.config.userStorage.upActive(tgId);
|
|
355
362
|
|
|
356
363
|
if (ctx.msgId) {
|
|
357
|
-
this.config.userStorage.storeMessageId(
|
|
364
|
+
this.config.userStorage.storeMessageId(tgId, ctx.msgId, 10);
|
|
358
365
|
}
|
|
359
366
|
|
|
360
367
|
return next();
|
|
@@ -1080,8 +1087,8 @@ export class App {
|
|
|
1080
1087
|
AccessKey.markUsed(accessKey, user.id);
|
|
1081
1088
|
}
|
|
1082
1089
|
if (this.config.userStorage) {
|
|
1083
|
-
this.config.userStorage.add(data.
|
|
1084
|
-
this.debugLog("User added to storage:", data.
|
|
1090
|
+
this.config.userStorage.add(data.tg_id, user);
|
|
1091
|
+
this.debugLog("User added to storage by tgId:", data.tg_id);
|
|
1085
1092
|
}
|
|
1086
1093
|
|
|
1087
1094
|
return user;
|
|
@@ -1092,26 +1099,25 @@ export class App {
|
|
|
1092
1099
|
}
|
|
1093
1100
|
|
|
1094
1101
|
/**
|
|
1095
|
-
* Remembers a user in storage by their Telegram
|
|
1096
|
-
* @param
|
|
1102
|
+
* Remembers a user in storage by their Telegram ID. If the user does not exist in storage, it attempts to fetch the user from the database and add them to storage.
|
|
1103
|
+
* @param tgId Telegram user ID of the user to remember.
|
|
1097
1104
|
* @returns A boolean indicating whether the user was successfully remembered (true) or not (false).
|
|
1098
1105
|
*/
|
|
1099
|
-
|
|
1100
|
-
if (this.config.userStorage && !this.config.userStorage.exists(
|
|
1101
|
-
this.debugLog("Warning: Remembering,
|
|
1102
|
-
this.debugLog("Remembering, Trying getting
|
|
1106
|
+
rememberUserById(tgId: number): boolean {
|
|
1107
|
+
if (this.config.userStorage && !this.config.userStorage.exists(tgId)) {
|
|
1108
|
+
this.debugLog("Warning: Remembering, tgId not found in storage:", tgId);
|
|
1109
|
+
this.debugLog("Remembering, Trying getting from database:", tgId);
|
|
1103
1110
|
|
|
1104
1111
|
// Try to get user from database and add to storage
|
|
1105
1112
|
UserModel.resolveDb();
|
|
1106
|
-
const userFromDb = UserModel.
|
|
1113
|
+
const userFromDb = UserModel.findByTgId(tgId);
|
|
1107
1114
|
|
|
1108
1115
|
if (userFromDb) {
|
|
1109
|
-
this.config.userStorage.add(
|
|
1110
|
-
this.debugLog("Success: User found in database and added to storage:",
|
|
1111
|
-
this.debugLog('Success: Remembered user "' + tgUsername + '"');
|
|
1116
|
+
this.config.userStorage.add(tgId, userFromDb);
|
|
1117
|
+
this.debugLog("Success: User found in database and added to storage by tgId:", tgId);
|
|
1112
1118
|
return true;
|
|
1113
1119
|
} else {
|
|
1114
|
-
this.debugLog("Warning: Remembering, User not found in database:",
|
|
1120
|
+
this.debugLog("Warning: Remembering, User not found in database for tgId:", tgId);
|
|
1115
1121
|
}
|
|
1116
1122
|
}
|
|
1117
1123
|
return false;
|
package/src/user/UserModel.ts
CHANGED
|
@@ -131,7 +131,7 @@ export class UserModel extends Model {
|
|
|
131
131
|
});
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
const user = this.
|
|
134
|
+
const user = this.findByTgId(params.tg_id);
|
|
135
135
|
|
|
136
136
|
if (user) {
|
|
137
137
|
return user;
|
|
@@ -168,6 +168,20 @@ export class UserModel extends Model {
|
|
|
168
168
|
return undefined;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
static findByTgId(tgId: number): UserModel | undefined {
|
|
172
|
+
if (this.db) {
|
|
173
|
+
const userData = this.queryOne(`SELECT * FROM ${this.tableName} WHERE tg_id = ?`, [tgId]);
|
|
174
|
+
|
|
175
|
+
if (userData) {
|
|
176
|
+
return UserModel.make(userData);
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
throw new Error("Database connection is not set.");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
|
|
171
185
|
static async findOnServer(tgUsername: string): Promise<UserModel> {
|
|
172
186
|
// Здесь должен быть запрос к API, но мы оставим это для будущей реализации
|
|
173
187
|
// const user = await api.fetch("user/get/" + tgUsername);
|
|
@@ -189,7 +203,7 @@ export class UserModel extends Model {
|
|
|
189
203
|
async refresh(): Promise<boolean> {
|
|
190
204
|
if (this.userSession) {
|
|
191
205
|
return this.userSession.add(
|
|
192
|
-
this.attributes.
|
|
206
|
+
this.attributes.tg_id,
|
|
193
207
|
await UserModel.findOnServer(this.attributes.tg_username)
|
|
194
208
|
);
|
|
195
209
|
}
|
package/src/user/UserStore.ts
CHANGED
|
@@ -27,11 +27,11 @@ export class UserStore {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async checkOrRegister(params: { tgUsername: string, tgId: number, tgName: string }): Promise<any> {
|
|
30
|
-
if (!this.usersMap.has(params.
|
|
30
|
+
if (!this.usersMap.has(String(params.tgId)) && this.api) {
|
|
31
31
|
let userData = null;
|
|
32
32
|
|
|
33
33
|
try {
|
|
34
|
-
let resApi = await this.api.fetch('user/get/' + params.
|
|
34
|
+
let resApi = await this.api.fetch('user/get/' + params.tgId);
|
|
35
35
|
userData = resApi.data;
|
|
36
36
|
} catch (err) {
|
|
37
37
|
let resApi = await this.api.fetch('user/register', 'post', {
|
|
@@ -44,7 +44,7 @@ export class UserStore {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (userData !== null) {
|
|
47
|
-
this.add(params.
|
|
47
|
+
this.add(params.tgId, userData);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
return userData;
|
|
@@ -52,48 +52,49 @@ export class UserStore {
|
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
find(
|
|
56
|
-
return this.usersMap.get(
|
|
55
|
+
find(tgId: number): UserModel {
|
|
56
|
+
return this.usersMap.get(String(tgId)) as UserModel;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
findSession(userModel: UserModel): UserSession {
|
|
60
60
|
return this.usersSession.get(userModel) || {};
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
exists(
|
|
64
|
-
return this.usersMap.has(
|
|
63
|
+
exists(tgId: number): boolean {
|
|
64
|
+
return this.usersMap.has(String(tgId));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
add(
|
|
67
|
+
add(tgId: number, data: UserModel | any): boolean {
|
|
68
|
+
const key = String(tgId);
|
|
68
69
|
if (data instanceof UserModel) {
|
|
69
|
-
this.usersMap.set(
|
|
70
|
+
this.usersMap.set(key, data);
|
|
70
71
|
} else {
|
|
71
|
-
this.usersMap.set(
|
|
72
|
+
this.usersMap.set(key, new UserModel(data));
|
|
72
73
|
}
|
|
73
|
-
|
|
74
|
-
this.usersSession.set(this.usersMap.get(
|
|
74
|
+
|
|
75
|
+
this.usersSession.set(this.usersMap.get(key) as UserModel, {});
|
|
75
76
|
|
|
76
77
|
return true;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
upActive(
|
|
80
|
-
const user = this.usersMap.get(
|
|
80
|
+
upActive(tgId: number): this {
|
|
81
|
+
const user = this.usersMap.get(String(tgId));
|
|
81
82
|
if (user) {
|
|
82
83
|
user.upActive();
|
|
83
84
|
}
|
|
84
85
|
return this;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
storeMessageId(
|
|
88
|
-
const user = this.usersMap.get(
|
|
88
|
+
storeMessageId(tgId: number, messageId: number, limit: number): this {
|
|
89
|
+
const user = this.usersMap.get(String(tgId));
|
|
89
90
|
if (user) {
|
|
90
91
|
user.storeMessageId(messageId, limit);
|
|
91
92
|
}
|
|
92
93
|
return this;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
getLastMessageId(
|
|
96
|
-
const user = this.usersMap.get(
|
|
96
|
+
getLastMessageId(tgId: number): number | undefined {
|
|
97
|
+
const user = this.usersMap.get(String(tgId));
|
|
97
98
|
if (user) {
|
|
98
99
|
const ids = user.lastMessageIds;
|
|
99
100
|
return ids.length ? ids[ids.length - 1] : undefined;
|
|
@@ -103,7 +104,7 @@ export class UserStore {
|
|
|
103
104
|
|
|
104
105
|
autocleanup(minutes: number = 1): void {
|
|
105
106
|
const getNotActiveUsers = (): [string, UserModel][] => {
|
|
106
|
-
return Array.from(this.usersMap).filter(([
|
|
107
|
+
return Array.from(this.usersMap).filter(([_key, data]) => {
|
|
107
108
|
return (
|
|
108
109
|
data.serviceAttributes.lastActive.getTime() <= Date.now() - minutes * 60 * 1000
|
|
109
110
|
);
|
|
@@ -111,17 +112,18 @@ export class UserStore {
|
|
|
111
112
|
};
|
|
112
113
|
|
|
113
114
|
setInterval(() => {
|
|
114
|
-
getNotActiveUsers().forEach(([
|
|
115
|
-
this.usersMap.delete(
|
|
115
|
+
getNotActiveUsers().forEach(([key]) => {
|
|
116
|
+
this.usersMap.delete(key);
|
|
116
117
|
});
|
|
117
118
|
}, 60 * 1000);
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
deleteByUsername(tgUsername: string): boolean {
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
122
|
+
const entry = Array.from(this.usersMap).find(([_, user]) => user.username === tgUsername);
|
|
123
|
+
if (entry) {
|
|
124
|
+
const [key, user] = entry;
|
|
123
125
|
this.usersSession.delete(user);
|
|
124
|
-
return this.usersMap.delete(
|
|
126
|
+
return this.usersMap.delete(key);
|
|
125
127
|
}
|
|
126
128
|
return false;
|
|
127
129
|
}
|
|
@@ -129,9 +131,9 @@ export class UserStore {
|
|
|
129
131
|
deleteById(userId: number): boolean {
|
|
130
132
|
const userEntry = Array.from(this.usersMap).find(([_, user]) => user.id === userId);
|
|
131
133
|
if (userEntry) {
|
|
132
|
-
const [
|
|
134
|
+
const [key, user] = userEntry;
|
|
133
135
|
this.usersSession.delete(user);
|
|
134
|
-
return this.usersMap.delete(
|
|
136
|
+
return this.usersMap.delete(key);
|
|
135
137
|
}
|
|
136
138
|
return false;
|
|
137
139
|
}
|