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