@effect-ak/tg-bot-client 0.3.0 → 0.3.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/index.js CHANGED
@@ -32,6 +32,8 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  BotFactoryService: () => BotFactoryService,
34
34
  BotFactoryServiceDefault: () => BotFactoryServiceDefault,
35
+ BotUpdatePollerService: () => BotUpdatePollerService,
36
+ BotUpdatesPollerServiceDefault: () => BotUpdatesPollerServiceDefault,
35
37
  MESSAGE_EFFECTS: () => MESSAGE_EFFECTS,
36
38
  defaultBaseUrl: () => defaultBaseUrl,
37
39
  isMessageEffect: () => isMessageEffect,
@@ -41,8 +43,87 @@ __export(src_exports, {
41
43
  });
42
44
  module.exports = __toCommonJS(src_exports);
43
45
 
44
- // src/client/_client.ts
45
- var Micro5 = __toESM(require("effect/Micro"));
46
+ // src/bot/factory/_service.ts
47
+ var Micro7 = __toESM(require("effect/Micro"));
48
+ var Context3 = __toESM(require("effect/Context"));
49
+
50
+ // src/bot/update-poller/_service.ts
51
+ var Micro4 = __toESM(require("effect/Micro"));
52
+ var Context2 = __toESM(require("effect/Context"));
53
+
54
+ // src/bot/update-poller/poll-and-handle.ts
55
+ var Micro3 = __toESM(require("effect/Micro"));
56
+
57
+ // src/bot/update-poller/settings.ts
58
+ var makeSettingsFrom = (input) => {
59
+ let limit = input.batch_size ?? 10;
60
+ let timeout = input.timeout ?? 10;
61
+ let max_empty_responses = input.max_empty_responses;
62
+ let update_types = input.update_types;
63
+ let log_level = input.log_level;
64
+ if (limit < 10 || limit > 100) {
65
+ console.warn("Wrong limit, must be in [10..100], using 10 instead");
66
+ limit = 10;
67
+ }
68
+ if (timeout < 2 || timeout > 10) {
69
+ console.warn("Wrong timeout, must be in [2..10], using 2 instead");
70
+ limit = 10;
71
+ }
72
+ if (max_empty_responses && max_empty_responses < 2) {
73
+ console.warn("Wrong max_empty_responses, must be in [2..infinity], using infinity");
74
+ max_empty_responses = void 0;
75
+ }
76
+ if (max_empty_responses && max_empty_responses < 2) {
77
+ console.warn("Wrong max_empty_responses, must be in [2..infinity], using infinity");
78
+ max_empty_responses = void 0;
79
+ }
80
+ if (!update_types) {
81
+ console.info("Handling only messages, ignoring others");
82
+ update_types = ["message"];
83
+ }
84
+ if (!log_level) {
85
+ log_level = "info";
86
+ }
87
+ return {
88
+ limit,
89
+ timeout,
90
+ max_empty_responses,
91
+ update_types,
92
+ log_level
93
+ };
94
+ };
95
+
96
+ // src/bot/update-poller/fetch-updates.ts
97
+ var Micro2 = __toESM(require("effect/Micro"));
98
+
99
+ // src/bot/message-handler/utils.ts
100
+ var extractUpdate = (input) => {
101
+ for (const [field, value] of Object.entries(input)) {
102
+ if (field == "update_id") {
103
+ continue;
104
+ }
105
+ return {
106
+ type: field,
107
+ ...value
108
+ };
109
+ }
110
+ return;
111
+ };
112
+
113
+ // src/client/execute-request/execute.ts
114
+ var Micro = __toESM(require("effect/Micro"));
115
+ var String = __toESM(require("effect/String"));
116
+
117
+ // src/client/errors.ts
118
+ var Data = __toESM(require("effect/Data"));
119
+ var TgBotClientError = class _TgBotClientError extends Data.TaggedError("TgBotClientError") {
120
+ static missingSuccess = new _TgBotClientError({
121
+ reason: {
122
+ type: "ClientInternalError",
123
+ cause: "Expected 'success' to be defined"
124
+ }
125
+ });
126
+ };
46
127
 
47
128
  // src/client/config.ts
48
129
  var Context = __toESM(require("effect/Context"));
@@ -70,25 +151,6 @@ var makeTgBotClientConfig = (input) => TgBotClientConfig.of({
70
151
  var TgBotClientConfig = class extends Context.Tag("TgBotClientConfig")() {
71
152
  };
72
153
 
73
- // src/client/execute-request/_service.ts
74
- var Micro2 = __toESM(require("effect/Micro"));
75
- var Context2 = __toESM(require("effect/Context"));
76
-
77
- // src/client/execute-request/execute.ts
78
- var Micro = __toESM(require("effect/Micro"));
79
- var String = __toESM(require("effect/String"));
80
-
81
- // src/client/errors.ts
82
- var Data = __toESM(require("effect/Data"));
83
- var TgBotClientError = class _TgBotClientError extends Data.TaggedError("TgBotClientError") {
84
- static missingSuccess = new _TgBotClientError({
85
- reason: {
86
- type: "ClientInternalError",
87
- cause: "Expected 'success' to be defined"
88
- }
89
- });
90
- };
91
-
92
154
  // src/client/guards.ts
93
155
  var isFileContent = (input) => typeof input == "object" && input != null && ("file_content" in input && input.file_content instanceof Uint8Array) && ("file_name" in input && typeof input.file_name == "string");
94
156
  var isTgBotApiResponse = (input) => typeof input == "object" && input != null && ("ok" in input && typeof input.ok == "boolean");
@@ -113,7 +175,8 @@ var makePayload = (body) => {
113
175
  };
114
176
 
115
177
  // src/client/execute-request/execute.ts
116
- var execute = (config, method, input) => Micro.gen(function* () {
178
+ var execute = (method, input) => Micro.gen(function* () {
179
+ const config = yield* Micro.service(TgBotClientConfig);
117
180
  const httpResponse = yield* Micro.tryPromise({
118
181
  try: () => fetch(
119
182
  `${config.base_url}/bot${config.bot_token}/${String.snakeToCamel(method)}`,
@@ -149,143 +212,17 @@ var execute = (config, method, input) => Micro.gen(function* () {
149
212
  return response.result;
150
213
  });
151
214
 
152
- // src/client/execute-request/_service.ts
153
- var ClientExecuteRequestService = class extends Context2.Tag("ClientExecuteRequestService")() {
154
- };
155
- var ClientExecuteRequestServiceDefault = Micro2.gen(function* () {
156
- const config = yield* Micro2.service(TgBotClientConfig);
157
- return {
158
- execute: (method, input) => execute(config, method, input)
159
- };
160
- });
161
-
162
- // src/client/file/_service.ts
163
- var Micro4 = __toESM(require("effect/Micro"));
164
- var Context3 = __toESM(require("effect/Context"));
165
-
166
- // src/client/file/get-file.ts
167
- var Micro3 = __toESM(require("effect/Micro"));
168
- var getFile = (fileId, config, execute2) => Micro3.gen(function* () {
169
- const response = yield* execute2.execute("get_file", { file_id: fileId });
170
- const file_path = response.file_path;
171
- if (!file_path || file_path.length == 0) {
172
- return yield* Micro3.fail(
173
- new TgBotClientError({
174
- reason: {
175
- type: "UnableToGetFile",
176
- cause: "File path not defined"
177
- }
178
- })
179
- );
180
- }
181
- const file_name = file_path.replaceAll("/", "-");
182
- const url = `${config.base_url}/file/bot${config.bot_token}/${file_path}`;
183
- const fileContent = yield* Micro3.tryPromise({
184
- try: () => fetch(url).then((_) => _.arrayBuffer()),
185
- catch: (cause) => new TgBotClientError({
186
- reason: { type: "UnableToGetFile", cause }
187
- })
188
- });
189
- const file = new File([new Uint8Array(fileContent)], file_name);
190
- return file;
191
- });
192
-
193
- // src/client/file/_service.ts
194
- var ClientFileService = class extends Context3.Tag("ClientFileService")() {
195
- };
196
- var ClientFileServiceDefault = Micro4.gen(function* () {
197
- const config = yield* Micro4.service(TgBotClientConfig);
198
- const execute2 = yield* Micro4.service(ClientExecuteRequestService);
199
- return {
200
- getFile: (input) => getFile(input.file_id, config, execute2)
201
- };
202
- }).pipe(
203
- Micro4.provideServiceEffect(ClientExecuteRequestService, ClientExecuteRequestServiceDefault)
204
- );
205
-
206
- // src/client/_client.ts
207
- var makeTgBotClient = (input) => {
208
- const config = makeTgBotClientConfig(input);
209
- const client = Micro5.gen(function* () {
210
- const execute2 = yield* Micro5.service(ClientExecuteRequestService);
211
- const file = yield* Micro5.service(ClientFileService);
212
- return {
213
- execute: (method, input2) => execute2.execute(method, input2).pipe(Micro5.runPromise),
214
- getFile: (input2) => file.getFile(input2).pipe(Micro5.runPromise)
215
- };
216
- }).pipe(
217
- Micro5.provideServiceEffect(ClientExecuteRequestService, ClientExecuteRequestServiceDefault),
218
- Micro5.provideServiceEffect(ClientFileService, ClientFileServiceDefault),
219
- Micro5.provideService(TgBotClientConfig, config),
220
- Micro5.runSync
221
- );
222
- return client;
223
- };
224
-
225
- // src/bot/run.ts
226
- var Micro12 = __toESM(require("effect/Micro"));
227
-
228
- // src/bot/factory/_service.ts
229
- var Micro11 = __toESM(require("effect/Micro"));
230
- var Context5 = __toESM(require("effect/Context"));
231
-
232
- // src/bot/update-poller/_service.ts
233
- var Micro8 = __toESM(require("effect/Micro"));
234
- var Context4 = __toESM(require("effect/Context"));
235
-
236
- // src/bot/update-poller/poll-and-handle.ts
237
- var Micro7 = __toESM(require("effect/Micro"));
238
-
239
- // src/bot/update-poller/settings.ts
240
- var makeSettingsFrom = (input) => {
241
- let limit = input.batch_size ?? 10;
242
- let timeout = input.timeout ?? 10;
243
- let max_empty_responses = input.max_empty_responses;
244
- if (limit < 10 || limit > 100) {
245
- console.warn("Wrong limit, must be in [10..100], using 10 instead");
246
- limit = 10;
247
- }
248
- if (timeout < 2 || timeout > 10) {
249
- console.warn("Wrong timeout, must be in [2..10], using 2 instead");
250
- limit = 10;
251
- }
252
- if (max_empty_responses && max_empty_responses < 2) {
253
- console.warn("Wrong max_empty_responses, must be in [2..infinity], using infinity");
254
- max_empty_responses = void 0;
255
- }
256
- return {
257
- limit,
258
- timeout,
259
- max_empty_responses
260
- };
261
- };
262
-
263
- // src/bot/update-poller/fetch-updates.ts
264
- var Micro6 = __toESM(require("effect/Micro"));
265
-
266
- // src/bot/message-handler/utils.ts
267
- var extractUpdate = (input) => {
268
- for (const [field, value] of Object.entries(input)) {
269
- if (field == "update_id") {
270
- continue;
271
- }
272
- return {
273
- type: field,
274
- ...value
275
- };
276
- }
277
- return void 0;
278
- };
279
-
280
215
  // src/bot/update-poller/fetch-updates.ts
281
- var fetchUpdates = ({ state, settings, execute: execute2, handlers }) => Micro6.gen(function* () {
216
+ var fetchUpdates = ({ state, settings, handlers }) => Micro2.gen(function* () {
282
217
  const updateId = state.lastUpdateId;
283
- console.info("getting updates", state);
284
- const updates = yield* execute2("get_updates", {
218
+ if (settings.log_level == "debug") {
219
+ console.debug("getting updates", state);
220
+ }
221
+ const updates = yield* execute("get_updates", {
285
222
  ...settings,
286
223
  ...updateId ? { offset: updateId } : void 0
287
224
  }).pipe(
288
- Micro6.andThen((_) => _.sort((_2) => _2.update_id))
225
+ Micro2.andThen((_) => _.sort((_2) => _2.update_id))
289
226
  );
290
227
  let lastSuccessId = void 0;
291
228
  let hasError = false;
@@ -298,34 +235,49 @@ var fetchUpdates = ({ state, settings, execute: execute2, handlers }) => Micro6.
298
235
  }
299
236
  const handler = handlers[`on_${update.type}`];
300
237
  if (!handler) {
301
- console.warn("Handler for update not defined", update);
302
- hasError = true;
303
- break;
238
+ if (settings.update_types.includes(update.type)) {
239
+ console.error("Handler for update not defined", update);
240
+ hasError = true;
241
+ break;
242
+ } else {
243
+ if (settings.log_level == "debug") {
244
+ console.debug("Ignored update", update);
245
+ }
246
+ lastSuccessId = updateObject.update_id;
247
+ continue;
248
+ }
249
+ }
250
+ if (update.type == "message" && "text" in update) {
251
+ console.info("Got new message", {
252
+ chatId: update.chat.id,
253
+ chatType: update.chat.type,
254
+ message: `${update.text.slice(0, 5)}...`
255
+ });
304
256
  }
305
257
  const handleResult = handler(update);
306
- if ("chat" in update) {
307
- const response = yield* execute2(`send_${handleResult.type}`, {
258
+ if ("chat" in update && handleResult) {
259
+ const response = yield* execute(`send_${handleResult.type}`, {
308
260
  ...handleResult,
309
261
  chat_id: update.chat.id
310
262
  });
311
- console.log("bot response", response);
263
+ if (settings.log_level == "debug" && "text") {
264
+ console.debug("bot response", response);
265
+ }
312
266
  }
313
- if (!handleResult) {
314
- hasError = true;
315
- console.log(handleResult);
316
- break;
267
+ if (!handleResult && settings.log_level == "debug") {
268
+ console.debug("handler returned no response for update", { update });
317
269
  }
318
270
  ;
319
271
  lastSuccessId = updateObject.update_id;
320
272
  }
321
273
  if (hasError && lastSuccessId) {
322
- const resp = (
323
- //commit successfully handled messages
324
- yield* execute2("get_updates", {
325
- offset: lastSuccessId,
326
- limit: 0
327
- })
328
- );
274
+ yield* execute("get_updates", {
275
+ offset: lastSuccessId,
276
+ limit: 0
277
+ });
278
+ if (settings.log_level == "debug") {
279
+ console.debug("committed offset", lastSuccessId);
280
+ }
329
281
  }
330
282
  return { updates, lastSuccessId, hasError };
331
283
  });
@@ -337,18 +289,17 @@ var pollAndHandle = (input) => {
337
289
  emptyResponses: 0
338
290
  };
339
291
  const settings = makeSettingsFrom(input.settings);
340
- return Micro7.delay(1e3)(
292
+ return Micro3.delay(1e3)(
341
293
  fetchUpdates({
342
294
  state,
343
295
  settings,
344
- execute: input.execute,
345
296
  handlers: input.settings
346
297
  })
347
298
  ).pipe(
348
- Micro7.repeat({
299
+ Micro3.repeat({
349
300
  while: ({ updates, lastSuccessId, hasError }) => {
350
301
  if (hasError) {
351
- console.warn("error in handler, quitting");
302
+ console.info("error in handler, quitting");
352
303
  return false;
353
304
  }
354
305
  if (updates.length == 0) {
@@ -371,22 +322,19 @@ var pollAndHandle = (input) => {
371
322
  };
372
323
 
373
324
  // src/bot/update-poller/_service.ts
374
- var BotUpdatePollerService = class extends Context4.Tag("BotUpdatePollerService")() {
325
+ var BotUpdatePollerService = class extends Context2.Tag("BotUpdatePollerService")() {
375
326
  };
376
- var BotUpdatesPollerServiceDefault = Micro8.gen(function* () {
327
+ var BotUpdatesPollerServiceDefault = Micro4.gen(function* () {
377
328
  console.log("Initiating BotUpdatesPollerServiceDefault");
378
329
  const state = {
379
330
  fiber: void 0
380
331
  };
381
- const client = yield* Micro8.service(ClientExecuteRequestService);
382
- const runBot = (messageHandler) => Micro8.gen(function* () {
383
- console.log(state);
332
+ const runBot = (messageHandler) => Micro4.gen(function* () {
384
333
  const startFiber = pollAndHandle({
385
- settings: messageHandler,
386
- execute: client.execute
334
+ settings: messageHandler
387
335
  }).pipe(
388
- Micro8.forkDaemon,
389
- Micro8.tap(
336
+ Micro4.forkDaemon,
337
+ Micro4.tap(
390
338
  (fiber) => fiber.addObserver((exit) => {
391
339
  console.log("bot's fiber has been closed", exit);
392
340
  if (messageHandler.onExit) {
@@ -397,83 +345,156 @@ var BotUpdatesPollerServiceDefault = Micro8.gen(function* () {
397
345
  );
398
346
  if (state.fiber) {
399
347
  console.log("killing previous bot's fiber");
400
- yield* Micro8.fiberInterrupt(state.fiber);
348
+ yield* Micro4.fiberInterrupt(state.fiber);
401
349
  }
402
350
  state.fiber = yield* startFiber;
403
- console.log("Reading bot's updates.....", state.fiber == null);
351
+ console.log("Fetching bot updates via long polling...");
404
352
  return state.fiber;
405
353
  });
406
354
  return {
407
355
  runBot
408
356
  };
409
- }).pipe(
410
- Micro8.provideServiceEffect(ClientExecuteRequestService, ClientExecuteRequestServiceDefault)
411
- );
357
+ });
412
358
 
413
359
  // src/bot/factory/client-config.ts
414
- var Micro9 = __toESM(require("effect/Micro"));
415
- var makeClientConfigFrom = (input) => Micro9.gen(function* () {
360
+ var Micro5 = __toESM(require("effect/Micro"));
361
+ var makeClientConfigFrom = (input) => Micro5.gen(function* () {
416
362
  if (input.type == "config") {
417
363
  return makeTgBotClientConfig(input);
418
364
  }
419
- const config = yield* Micro9.tryPromise({
365
+ const config = yield* Micro5.tryPromise({
420
366
  try: async () => {
421
367
  const { readFileSync } = await import("fs");
422
368
  return JSON.parse(await readFileSync("config.json", "utf-8"));
423
369
  },
424
370
  catch: (error) => {
425
- console.warn(error);
371
+ console.warn("invalid tg bot config", error);
426
372
  return "ReadingConfigError";
427
373
  }
428
374
  });
429
375
  if (!isTgBotClientSettingsInput(config)) {
430
- return yield* Micro9.fail("InvalidConfig");
376
+ return yield* Micro5.fail("InvalidConfig");
431
377
  }
432
378
  return makeTgBotClientConfig(config);
433
379
  });
434
380
 
435
381
  // src/bot/factory/make-bot.ts
436
- var Micro10 = __toESM(require("effect/Micro"));
437
- var makeBot = (messageHandler) => Micro10.gen(function* () {
438
- const { runBot } = yield* Micro10.service(BotUpdatePollerService);
382
+ var Micro6 = __toESM(require("effect/Micro"));
383
+ var makeBot = (messageHandler) => Micro6.gen(function* () {
384
+ const { runBot } = yield* Micro6.service(BotUpdatePollerService);
385
+ const fiber = yield* runBot(messageHandler);
386
+ const interrupt = Micro6.fiberInterrupt(fiber);
439
387
  return {
440
- fiber: yield* runBot(messageHandler),
441
- runBot
388
+ runBot,
389
+ interrupt
442
390
  };
443
391
  }).pipe(
444
- Micro10.tapError((error) => {
392
+ Micro6.tapError((error) => {
445
393
  console.error(error);
446
- return Micro10.void;
394
+ return Micro6.void;
447
395
  })
448
396
  );
449
397
 
450
398
  // src/bot/factory/_service.ts
451
- var BotFactoryService = class extends Context5.Tag("BotFactoryService")() {
399
+ var BotFactoryService = class extends Context3.Tag("BotFactoryService")() {
452
400
  };
453
401
  var BotFactoryServiceDefault = {
454
402
  makeBot,
455
- runBot: (input) => Micro11.gen(function* () {
456
- console.log("client");
457
- const client = yield* makeClientConfigFrom(input);
403
+ runBot: (input) => Micro7.gen(function* () {
404
+ const client = Context3.make(TgBotClientConfig, yield* makeClientConfigFrom(input));
458
405
  const poller = yield* BotUpdatesPollerServiceDefault.pipe(
459
- Micro11.provideService(TgBotClientConfig, client)
406
+ Micro7.provideContext(client)
460
407
  );
461
408
  const bot = yield* makeBot(input).pipe(
462
- Micro11.provideService(BotUpdatePollerService, poller)
409
+ Micro7.provideContext(client),
410
+ Micro7.provideService(BotUpdatePollerService, poller)
411
+ );
412
+ const reload = (input2) => bot.runBot(input2).pipe(
413
+ Micro7.provideContext(client)
463
414
  );
464
- const reload = (input2) => bot.runBot(input2).pipe(Micro11.runPromise);
465
415
  return {
466
- reload
416
+ reload,
417
+ bot
467
418
  };
468
419
  })
469
420
  };
470
421
 
471
422
  // src/bot/run.ts
472
- var runTgChatBot = (input) => BotFactoryServiceDefault.runBot(input).pipe(Micro12.runPromise);
423
+ var Micro8 = __toESM(require("effect/Micro"));
424
+ var runTgChatBot = (input) => BotFactoryServiceDefault.runBot(input).pipe(Micro8.runPromise);
425
+
426
+ // src/client/_client.ts
427
+ var Micro11 = __toESM(require("effect/Micro"));
428
+
429
+ // src/client/file/_service.ts
430
+ var Micro10 = __toESM(require("effect/Micro"));
431
+ var Context4 = __toESM(require("effect/Context"));
432
+
433
+ // src/client/file/get-file.ts
434
+ var Micro9 = __toESM(require("effect/Micro"));
435
+ var getFile = (fileId) => Micro9.gen(function* () {
436
+ const response = yield* execute("get_file", { file_id: fileId });
437
+ const config = yield* Micro9.service(TgBotClientConfig);
438
+ const file_path = response.file_path;
439
+ if (!file_path || file_path.length == 0) {
440
+ return yield* Micro9.fail(
441
+ new TgBotClientError({
442
+ reason: {
443
+ type: "UnableToGetFile",
444
+ cause: "File path not defined"
445
+ }
446
+ })
447
+ );
448
+ }
449
+ const file_name = file_path.replaceAll("/", "-");
450
+ const url = `${config.base_url}/file/bot${config.bot_token}/${file_path}`;
451
+ const fileContent = yield* Micro9.tryPromise({
452
+ try: () => fetch(url).then((_) => _.arrayBuffer()),
453
+ catch: (cause) => new TgBotClientError({
454
+ reason: { type: "UnableToGetFile", cause }
455
+ })
456
+ });
457
+ const file = new File([new Uint8Array(fileContent)], file_name);
458
+ return file;
459
+ });
460
+
461
+ // src/client/file/_service.ts
462
+ var ClientFileService = class extends Context4.Tag("ClientFileService")() {
463
+ };
464
+ var ClientFileServiceDefault = Micro10.gen(function* () {
465
+ return {
466
+ getFile: (input) => getFile(input.file_id)
467
+ };
468
+ });
469
+
470
+ // src/client/_client.ts
471
+ var makeTgBotClient = (input) => {
472
+ const config = makeTgBotClientConfig(input);
473
+ const client = Micro11.gen(function* () {
474
+ const file = yield* Micro11.service(ClientFileService);
475
+ return {
476
+ execute: (method, input2) => execute(method, input2).pipe(
477
+ Micro11.provideService(TgBotClientConfig, config),
478
+ Micro11.runPromise
479
+ ),
480
+ getFile: (input2) => file.getFile(input2).pipe(
481
+ Micro11.provideService(TgBotClientConfig, config),
482
+ Micro11.runPromise
483
+ )
484
+ };
485
+ }).pipe(
486
+ Micro11.provideServiceEffect(ClientFileService, ClientFileServiceDefault),
487
+ Micro11.provideService(TgBotClientConfig, config),
488
+ Micro11.runSync
489
+ );
490
+ return client;
491
+ };
473
492
  // Annotate the CommonJS export names for ESM import in node:
474
493
  0 && (module.exports = {
475
494
  BotFactoryService,
476
495
  BotFactoryServiceDefault,
496
+ BotUpdatePollerService,
497
+ BotUpdatesPollerServiceDefault,
477
498
  MESSAGE_EFFECTS,
478
499
  defaultBaseUrl,
479
500
  isMessageEffect,