@effect-ak/tg-bot-client 1.0.0 → 1.2.0

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.d.cts CHANGED
@@ -1,60 +1,33 @@
1
- import * as Micro from 'effect/Micro';
2
- import { Api, Update } from '@effect-ak/tg-bot-api';
3
- import * as effect_Cause from 'effect/Cause';
4
- import * as effect_Types from 'effect/Types';
5
- import * as Data from 'effect/Data';
6
- import * as Context from 'effect/Context';
1
+ import { Api } from '@effect-ak/tg-bot-api';
7
2
 
8
- type ErrorReason = Data.TaggedEnum<{
9
- NotOkResponse: {
10
- errorCode?: number;
11
- details?: string;
12
- };
13
- UnexpectedResponse: {
14
- response: unknown;
15
- };
16
- ClientInternalError: {
17
- cause: unknown;
18
- };
19
- UnableToGetFile: {
20
- cause: unknown;
21
- };
22
- BotHandlerError: {
23
- cause: unknown;
24
- };
25
- NotJsonResponse: {
26
- response: unknown;
27
- };
28
- }>;
29
- declare const TgBotClientError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
30
- readonly _tag: "TgBotClientError";
31
- } & Readonly<A>;
32
- declare class TgBotClientError extends TgBotClientError_base<{
33
- cause: ErrorReason;
34
- }> {
3
+ interface TgBotConfig {
4
+ botToken: string;
5
+ baseUrl?: string;
35
6
  }
7
+ declare const getBaseUrl: (config?: Pick<TgBotConfig, "baseUrl">) => string;
36
8
 
37
- declare const TgBotApiBaseUrl_base: Context.ReferenceClass<TgBotApiBaseUrl, "TgBotApiBaseUrl", string>;
38
- declare class TgBotApiBaseUrl extends TgBotApiBaseUrl_base {
39
- }
40
- declare const TgBotApiToken_base: Context.TagClass<TgBotApiToken, "TgBotApiToken", string>;
41
- declare class TgBotApiToken extends TgBotApiToken_base {
42
- }
43
-
44
- declare const executeTgBotMethod: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Micro.Micro<ReturnType<Api[M]>, TgBotClientError, TgBotApiToken>;
9
+ declare function executeTgBotMethod<M extends keyof Api>(params: {
10
+ config: TgBotConfig;
11
+ method: M;
12
+ input: Parameters<Api[M]>[0];
13
+ }): Promise<ReturnType<Api[M]>>;
45
14
  declare const makePayload: (body: object) => FormData | undefined;
46
15
 
47
- declare const ClientFileService_base: Context.TagClass<ClientFileService, "ClientFileService", {
48
- getFile: (input: GetFile) => ReturnType<typeof getFile>;
49
- }>;
50
- declare class ClientFileService extends ClientFileService_base {
51
- static live: () => Context.Context<ClientFileService>;
52
- }
53
16
  interface GetFile {
54
17
  fileId: string;
55
18
  type?: string;
56
19
  }
57
- declare const getFile: ({ fileId, type }: GetFile) => Micro.Micro<File, TgBotClientError, TgBotApiToken>;
20
+ interface FileBytes {
21
+ content: ArrayBuffer;
22
+ file_name: string;
23
+ base64String: () => string;
24
+ }
25
+ interface FileContext {
26
+ config: TgBotConfig;
27
+ execute: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Promise<ReturnType<Api[M]>>;
28
+ }
29
+ declare const getFileBytes: (fileId: string, context: FileContext) => Promise<FileBytes>;
30
+ declare const getFile: (input: GetFile, context: FileContext) => Promise<File>;
58
31
 
59
32
  interface TgBotClient {
60
33
  readonly execute: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Promise<ReturnType<Api[M]>>;
@@ -62,9 +35,38 @@ interface TgBotClient {
62
35
  }
63
36
  interface MakeTgClient {
64
37
  bot_token: string;
38
+ base_url?: string;
65
39
  }
66
40
  declare function makeTgBotClient(config: MakeTgClient): TgBotClient;
67
41
 
42
+ type ErrorReason = {
43
+ _tag: "NotOkResponse";
44
+ errorCode?: number;
45
+ details?: string;
46
+ } | {
47
+ _tag: "UnexpectedResponse";
48
+ response: unknown;
49
+ } | {
50
+ _tag: "ClientInternalError";
51
+ cause: unknown;
52
+ } | {
53
+ _tag: "UnableToGetFile";
54
+ cause: unknown;
55
+ } | {
56
+ _tag: "BotHandlerError";
57
+ cause: unknown;
58
+ } | {
59
+ _tag: "NotJsonResponse";
60
+ response: unknown;
61
+ };
62
+ declare class TgBotClientError extends Error {
63
+ readonly _tag = "TgBotClientError";
64
+ readonly cause: ErrorReason;
65
+ constructor(options: {
66
+ cause: ErrorReason;
67
+ });
68
+ }
69
+
68
70
  interface FileContent {
69
71
  file_content: Uint8Array<ArrayBuffer>;
70
72
  file_name: string;
@@ -77,7 +79,6 @@ interface TgBotApiResponseSchema {
77
79
  result?: unknown;
78
80
  }
79
81
  declare const isTgBotApiResponse: (input: unknown) => input is TgBotApiResponseSchema;
80
- declare const isTgBotApiUpdate: (input: unknown) => input is Update;
81
82
 
82
83
  declare const TG_BOT_API_URL = "https://api.telegram.org";
83
84
  declare const MESSAGE_EFFECTS: {
@@ -92,4 +93,6 @@ type MessageEffect = keyof typeof MESSAGE_EFFECTS;
92
93
  declare const messageEffectIdCodes: MessageEffect[];
93
94
  declare const isMessageEffect: (input: unknown) => input is MessageEffect;
94
95
 
95
- export { ClientFileService, type FileContent, type GetFile, MESSAGE_EFFECTS, type MessageEffect, TG_BOT_API_URL, TgBotApiBaseUrl, type TgBotApiResponseSchema, TgBotApiToken, type TgBotClient, TgBotClientError, executeTgBotMethod, isFileContent, isMessageEffect, isTgBotApiResponse, isTgBotApiUpdate, makePayload, makeTgBotClient, messageEffectIdCodes };
96
+ declare const snakeToCamel: (str: string) => string;
97
+
98
+ export { type FileBytes, type FileContent, type GetFile, MESSAGE_EFFECTS, type MakeTgClient, type MessageEffect, TG_BOT_API_URL, type TgBotApiResponseSchema, type TgBotClient, TgBotClientError, type TgBotConfig, executeTgBotMethod, getBaseUrl, getFile, getFileBytes, isFileContent, isMessageEffect, isTgBotApiResponse, makePayload, makeTgBotClient, messageEffectIdCodes, snakeToCamel };
package/dist/index.d.ts CHANGED
@@ -1,60 +1,33 @@
1
- import * as Micro from 'effect/Micro';
2
- import { Api, Update } from '@effect-ak/tg-bot-api';
3
- import * as effect_Cause from 'effect/Cause';
4
- import * as effect_Types from 'effect/Types';
5
- import * as Data from 'effect/Data';
6
- import * as Context from 'effect/Context';
1
+ import { Api } from '@effect-ak/tg-bot-api';
7
2
 
8
- type ErrorReason = Data.TaggedEnum<{
9
- NotOkResponse: {
10
- errorCode?: number;
11
- details?: string;
12
- };
13
- UnexpectedResponse: {
14
- response: unknown;
15
- };
16
- ClientInternalError: {
17
- cause: unknown;
18
- };
19
- UnableToGetFile: {
20
- cause: unknown;
21
- };
22
- BotHandlerError: {
23
- cause: unknown;
24
- };
25
- NotJsonResponse: {
26
- response: unknown;
27
- };
28
- }>;
29
- declare const TgBotClientError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
30
- readonly _tag: "TgBotClientError";
31
- } & Readonly<A>;
32
- declare class TgBotClientError extends TgBotClientError_base<{
33
- cause: ErrorReason;
34
- }> {
3
+ interface TgBotConfig {
4
+ botToken: string;
5
+ baseUrl?: string;
35
6
  }
7
+ declare const getBaseUrl: (config?: Pick<TgBotConfig, "baseUrl">) => string;
36
8
 
37
- declare const TgBotApiBaseUrl_base: Context.ReferenceClass<TgBotApiBaseUrl, "TgBotApiBaseUrl", string>;
38
- declare class TgBotApiBaseUrl extends TgBotApiBaseUrl_base {
39
- }
40
- declare const TgBotApiToken_base: Context.TagClass<TgBotApiToken, "TgBotApiToken", string>;
41
- declare class TgBotApiToken extends TgBotApiToken_base {
42
- }
43
-
44
- declare const executeTgBotMethod: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Micro.Micro<ReturnType<Api[M]>, TgBotClientError, TgBotApiToken>;
9
+ declare function executeTgBotMethod<M extends keyof Api>(params: {
10
+ config: TgBotConfig;
11
+ method: M;
12
+ input: Parameters<Api[M]>[0];
13
+ }): Promise<ReturnType<Api[M]>>;
45
14
  declare const makePayload: (body: object) => FormData | undefined;
46
15
 
47
- declare const ClientFileService_base: Context.TagClass<ClientFileService, "ClientFileService", {
48
- getFile: (input: GetFile) => ReturnType<typeof getFile>;
49
- }>;
50
- declare class ClientFileService extends ClientFileService_base {
51
- static live: () => Context.Context<ClientFileService>;
52
- }
53
16
  interface GetFile {
54
17
  fileId: string;
55
18
  type?: string;
56
19
  }
57
- declare const getFile: ({ fileId, type }: GetFile) => Micro.Micro<File, TgBotClientError, TgBotApiToken>;
20
+ interface FileBytes {
21
+ content: ArrayBuffer;
22
+ file_name: string;
23
+ base64String: () => string;
24
+ }
25
+ interface FileContext {
26
+ config: TgBotConfig;
27
+ execute: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Promise<ReturnType<Api[M]>>;
28
+ }
29
+ declare const getFileBytes: (fileId: string, context: FileContext) => Promise<FileBytes>;
30
+ declare const getFile: (input: GetFile, context: FileContext) => Promise<File>;
58
31
 
59
32
  interface TgBotClient {
60
33
  readonly execute: <M extends keyof Api>(method: M, input: Parameters<Api[M]>[0]) => Promise<ReturnType<Api[M]>>;
@@ -62,9 +35,38 @@ interface TgBotClient {
62
35
  }
63
36
  interface MakeTgClient {
64
37
  bot_token: string;
38
+ base_url?: string;
65
39
  }
66
40
  declare function makeTgBotClient(config: MakeTgClient): TgBotClient;
67
41
 
42
+ type ErrorReason = {
43
+ _tag: "NotOkResponse";
44
+ errorCode?: number;
45
+ details?: string;
46
+ } | {
47
+ _tag: "UnexpectedResponse";
48
+ response: unknown;
49
+ } | {
50
+ _tag: "ClientInternalError";
51
+ cause: unknown;
52
+ } | {
53
+ _tag: "UnableToGetFile";
54
+ cause: unknown;
55
+ } | {
56
+ _tag: "BotHandlerError";
57
+ cause: unknown;
58
+ } | {
59
+ _tag: "NotJsonResponse";
60
+ response: unknown;
61
+ };
62
+ declare class TgBotClientError extends Error {
63
+ readonly _tag = "TgBotClientError";
64
+ readonly cause: ErrorReason;
65
+ constructor(options: {
66
+ cause: ErrorReason;
67
+ });
68
+ }
69
+
68
70
  interface FileContent {
69
71
  file_content: Uint8Array<ArrayBuffer>;
70
72
  file_name: string;
@@ -77,7 +79,6 @@ interface TgBotApiResponseSchema {
77
79
  result?: unknown;
78
80
  }
79
81
  declare const isTgBotApiResponse: (input: unknown) => input is TgBotApiResponseSchema;
80
- declare const isTgBotApiUpdate: (input: unknown) => input is Update;
81
82
 
82
83
  declare const TG_BOT_API_URL = "https://api.telegram.org";
83
84
  declare const MESSAGE_EFFECTS: {
@@ -92,4 +93,6 @@ type MessageEffect = keyof typeof MESSAGE_EFFECTS;
92
93
  declare const messageEffectIdCodes: MessageEffect[];
93
94
  declare const isMessageEffect: (input: unknown) => input is MessageEffect;
94
95
 
95
- export { ClientFileService, type FileContent, type GetFile, MESSAGE_EFFECTS, type MessageEffect, TG_BOT_API_URL, TgBotApiBaseUrl, type TgBotApiResponseSchema, TgBotApiToken, type TgBotClient, TgBotClientError, executeTgBotMethod, isFileContent, isMessageEffect, isTgBotApiResponse, isTgBotApiUpdate, makePayload, makeTgBotClient, messageEffectIdCodes };
96
+ declare const snakeToCamel: (str: string) => string;
97
+
98
+ export { type FileBytes, type FileContent, type GetFile, MESSAGE_EFFECTS, type MakeTgClient, type MessageEffect, TG_BOT_API_URL, type TgBotApiResponseSchema, type TgBotClient, TgBotClientError, type TgBotConfig, executeTgBotMethod, getBaseUrl, getFile, getFileBytes, isFileContent, isMessageEffect, isTgBotApiResponse, makePayload, makeTgBotClient, messageEffectIdCodes, snakeToCamel };
package/dist/index.js CHANGED
@@ -1,19 +1,17 @@
1
- // src/execute.ts
2
- import * as String from "effect/String";
3
- import * as Micro from "effect/Micro";
4
-
5
1
  // src/errors.ts
6
- import * as Data from "effect/Data";
7
- var TgBotClientError = class extends Data.TaggedError("TgBotClientError") {
2
+ var TgBotClientError = class extends Error {
3
+ _tag = "TgBotClientError";
4
+ cause;
5
+ constructor(options) {
6
+ super(`TgBotClientError: ${options.cause._tag}`);
7
+ this.cause = options.cause;
8
+ this.name = "TgBotClientError";
9
+ }
8
10
  };
9
11
 
10
12
  // src/guards.ts
11
13
  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";
12
14
  var isTgBotApiResponse = (input) => typeof input == "object" && input != null && "ok" in input && typeof input.ok == "boolean";
13
- var isTgBotApiUpdate = (input) => typeof input == "object" && input != null && "update_id" in input && typeof input.update_id == "number";
14
-
15
- // src/config.ts
16
- import * as Context from "effect/Context";
17
15
 
18
16
  // src/const.ts
19
17
  var TG_BOT_API_URL = "https://api.telegram.org";
@@ -33,53 +31,58 @@ var isMessageEffect = (input) => {
33
31
  };
34
32
 
35
33
  // src/config.ts
36
- var TgBotApiBaseUrl = class extends Context.Reference()(
37
- "TgBotApiBaseUrl",
38
- { defaultValue: () => TG_BOT_API_URL }
39
- ) {
34
+ var getBaseUrl = (config) => {
35
+ return config?.baseUrl ?? TG_BOT_API_URL;
40
36
  };
41
- var TgBotApiToken = class extends Context.Tag("TgBotApiToken")() {
37
+
38
+ // src/utils.ts
39
+ var snakeToCamel = (str) => {
40
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
42
41
  };
43
42
 
44
43
  // src/execute.ts
45
- var executeTgBotMethod = (method, input) => Micro.gen(function* () {
46
- const botToken = yield* Micro.service(TgBotApiToken);
47
- const baseUrl = yield* Micro.service(TgBotApiBaseUrl);
48
- const httpResponse = yield* Micro.tryPromise({
49
- try: () => fetch(`${baseUrl}/bot${botToken}/${String.snakeToCamel(method)}`, {
50
- body: makePayload(input) ?? null,
51
- method: "POST"
52
- }),
53
- catch: (cause) => new TgBotClientError({
44
+ async function executeTgBotMethod(params) {
45
+ const { config, method, input } = params;
46
+ const baseUrl = getBaseUrl(config);
47
+ const botToken = config.botToken;
48
+ let httpResponse;
49
+ try {
50
+ httpResponse = await fetch(
51
+ `${baseUrl}/bot${botToken}/${snakeToCamel(method)}`,
52
+ {
53
+ body: makePayload(input) ?? null,
54
+ method: "POST"
55
+ }
56
+ );
57
+ } catch (cause) {
58
+ throw new TgBotClientError({
54
59
  cause: { _tag: "ClientInternalError", cause }
55
- })
56
- });
57
- const response = yield* Micro.tryPromise({
58
- try: () => httpResponse.json(),
59
- catch: () => new TgBotClientError({
60
+ });
61
+ }
62
+ let response;
63
+ try {
64
+ response = await httpResponse.json();
65
+ } catch {
66
+ throw new TgBotClientError({
60
67
  cause: { _tag: "NotJsonResponse", response: httpResponse }
61
- })
62
- });
68
+ });
69
+ }
63
70
  if (!isTgBotApiResponse(response)) {
64
- return yield* Micro.fail(
65
- new TgBotClientError({
66
- cause: { _tag: "UnexpectedResponse", response }
67
- })
68
- );
71
+ throw new TgBotClientError({
72
+ cause: { _tag: "UnexpectedResponse", response }
73
+ });
69
74
  }
70
75
  if (!httpResponse.ok) {
71
- return yield* Micro.fail(
72
- new TgBotClientError({
73
- cause: {
74
- _tag: "NotOkResponse",
75
- ...response.error_code ? { errorCode: response.error_code } : void 0,
76
- ...response.description ? { details: response.description } : void 0
77
- }
78
- })
79
- );
76
+ throw new TgBotClientError({
77
+ cause: {
78
+ _tag: "NotOkResponse",
79
+ ...response.error_code ? { errorCode: response.error_code } : {},
80
+ ...response.description ? { details: response.description } : {}
81
+ }
82
+ });
80
83
  }
81
84
  return response.result;
82
- });
85
+ }
83
86
  var makePayload = (body) => {
84
87
  const entries = Object.entries(body);
85
88
  if (entries.length == 0) return void 0;
@@ -98,83 +101,69 @@ var makePayload = (body) => {
98
101
  };
99
102
 
100
103
  // src/client-file.ts
101
- import * as Micro2 from "effect/Micro";
102
- import * as Context2 from "effect/Context";
103
- var ClientFileService = class _ClientFileService extends Context2.Tag("ClientFileService")() {
104
- static live = () => {
105
- return _ClientFileService.context({
106
- getFile
107
- });
108
- };
109
- };
110
- var getFile = ({ fileId, type }) => getFileBytes(fileId).pipe(
111
- Micro2.andThen(
112
- ({ content, file_name }) => new File([content], file_name, {
113
- ...type ? { type } : void 0
114
- })
115
- )
116
- );
117
- var getFileBytes = (fileId) => Micro2.gen(function* () {
118
- const response = yield* executeTgBotMethod("get_file", { file_id: fileId });
104
+ var getFileBytes = async (fileId, context) => {
105
+ const { config, execute } = context;
106
+ const response = await execute("get_file", { file_id: fileId });
119
107
  const file_path = response.file_path;
120
- if (!file_path || file_path.length == 0) {
121
- return yield* Micro2.fail(
122
- new TgBotClientError({
123
- cause: {
124
- _tag: "UnableToGetFile",
125
- cause: "File path not defined"
126
- }
127
- })
128
- );
108
+ if (!file_path || file_path.length === 0) {
109
+ throw new TgBotClientError({
110
+ cause: {
111
+ _tag: "UnableToGetFile",
112
+ cause: "File path not defined"
113
+ }
114
+ });
129
115
  }
130
116
  const file_name = file_path.replaceAll("/", "-");
131
- const baseUrl = yield* Micro2.service(TgBotApiBaseUrl);
132
- const botToken = yield* Micro2.service(TgBotApiToken);
117
+ const baseUrl = getBaseUrl(config);
118
+ const botToken = config.botToken;
133
119
  const url = `${baseUrl}/file/bot${botToken}/${file_path}`;
134
- const content = yield* Micro2.tryPromise({
135
- try: () => fetch(url).then((_) => _.arrayBuffer()),
136
- catch: (cause) => new TgBotClientError({
120
+ let content;
121
+ try {
122
+ content = await fetch(url).then((_) => _.arrayBuffer());
123
+ } catch (cause) {
124
+ throw new TgBotClientError({
137
125
  cause: { _tag: "UnableToGetFile", cause }
138
- })
139
- });
126
+ });
127
+ }
128
+ const base64String = () => Buffer.from(content).toString("base64");
140
129
  return {
141
130
  content,
142
- file_name
131
+ file_name,
132
+ base64String
143
133
  };
144
- });
134
+ };
135
+ var getFile = async (input, context) => {
136
+ const { content, file_name } = await getFileBytes(input.fileId, context);
137
+ return new File([content], file_name, {
138
+ ...input.type ? { type: input.type } : {}
139
+ });
140
+ };
145
141
 
146
142
  // src/client.ts
147
- import * as Micro3 from "effect/Micro";
148
- import * as Context3 from "effect/Context";
149
143
  function makeTgBotClient(config) {
150
- return createEffect(config).pipe(Micro3.runSync);
151
- }
152
- var createEffect = ({ bot_token }) => Micro3.gen(function* () {
153
- const file = yield* Micro3.service(ClientFileService);
154
- const context = Context3.make(TgBotApiToken, bot_token);
155
- const execute = (method, input) => executeTgBotMethod(method, input).pipe(
156
- Micro3.provideContext(context),
157
- Micro3.runPromise
158
- );
159
- const getFile2 = (input) => file.getFile(input).pipe(Micro3.provideContext(context), Micro3.runPromise);
144
+ const tgConfig = {
145
+ botToken: config.bot_token,
146
+ ...config.base_url ? { baseUrl: config.base_url } : {}
147
+ };
148
+ const execute = (method, input) => executeTgBotMethod({ config: tgConfig, method, input });
160
149
  return {
161
150
  execute,
162
- getFile: getFile2
151
+ getFile: (input) => getFile(input, { config: tgConfig, execute })
163
152
  };
164
- }).pipe(Micro3.provideContext(ClientFileService.live()));
153
+ }
165
154
  export {
166
- ClientFileService,
167
155
  MESSAGE_EFFECTS,
168
156
  TG_BOT_API_URL,
169
- TgBotApiBaseUrl,
170
- TgBotApiToken,
171
157
  TgBotClientError,
172
158
  executeTgBotMethod,
159
+ getBaseUrl,
160
+ getFile,
161
+ getFileBytes,
173
162
  isFileContent,
174
163
  isMessageEffect,
175
164
  isTgBotApiResponse,
176
- isTgBotApiUpdate,
177
165
  makePayload,
178
166
  makeTgBotClient,
179
- messageEffectIdCodes
167
+ messageEffectIdCodes,
168
+ snakeToCamel
180
169
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@effect-ak/tg-bot-client",
3
3
  "type": "module",
4
4
  "description": "Type-safe HTTP client for Telegram Bot API",
5
- "version": "1.0.0",
5
+ "version": "1.2.0",
6
6
  "license": "MIT",
7
7
  "author": {
8
8
  "name": "Aleksandr Kondaurov",
@@ -32,9 +32,6 @@
32
32
  "dependencies": {
33
33
  "@effect-ak/tg-bot-api": "0.9.2"
34
34
  },
35
- "peerDependencies": {
36
- "effect": "^3.12.0"
37
- },
38
35
  "scripts": {
39
36
  "build": "tsup"
40
37
  }
@@ -1,70 +1,73 @@
1
- import * as Micro from "effect/Micro"
2
- import * as Context from "effect/Context"
3
-
1
+ import type { Api } from "@effect-ak/tg-bot-api"
4
2
  import { TgBotClientError } from "./errors"
5
- import { TgBotApiBaseUrl, TgBotApiToken } from "./config"
6
- import { executeTgBotMethod } from "./execute"
7
-
8
- export class ClientFileService extends Context.Tag("ClientFileService")<
9
- ClientFileService,
10
- {
11
- getFile: (input: GetFile) => ReturnType<typeof getFile>
12
- }
13
- >() {
14
- static live = () => {
15
- return ClientFileService.context({
16
- getFile
17
- })
18
- }
19
- }
3
+ import type { TgBotConfig } from "./config"
4
+ import { getBaseUrl } from "./config"
20
5
 
21
6
  export interface GetFile {
22
7
  fileId: string
23
8
  type?: string
24
9
  }
25
10
 
26
- const getFile = ({ fileId, type }: GetFile) =>
27
- getFileBytes(fileId).pipe(
28
- Micro.andThen(
29
- ({ content, file_name }) =>
30
- new File([content], file_name, {
31
- ...(type ? { type } : undefined)
32
- })
33
- )
34
- )
11
+ export interface FileBytes {
12
+ content: ArrayBuffer
13
+ file_name: string
14
+ base64String: () => string
15
+ }
35
16
 
36
- const getFileBytes = (fileId: string) =>
37
- Micro.gen(function* () {
38
- const response = yield* executeTgBotMethod("get_file", { file_id: fileId })
39
- const file_path = response.file_path
17
+ interface FileContext {
18
+ config: TgBotConfig
19
+ execute: <M extends keyof Api>(
20
+ method: M,
21
+ input: Parameters<Api[M]>[0]
22
+ ) => Promise<ReturnType<Api[M]>>
23
+ }
40
24
 
41
- if (!file_path || file_path.length == 0) {
42
- return yield* Micro.fail(
43
- new TgBotClientError({
44
- cause: {
45
- _tag: "UnableToGetFile",
46
- cause: "File path not defined"
47
- }
48
- })
49
- )
50
- }
25
+ export const getFileBytes = async (
26
+ fileId: string,
27
+ context: FileContext
28
+ ): Promise<FileBytes> => {
29
+ const { config, execute } = context
30
+ const response = await execute("get_file", { file_id: fileId })
31
+ const file_path = response.file_path
51
32
 
52
- const file_name = file_path.replaceAll("/", "-")
53
- const baseUrl = yield* Micro.service(TgBotApiBaseUrl)
54
- const botToken = yield* Micro.service(TgBotApiToken)
33
+ if (!file_path || file_path.length === 0) {
34
+ throw new TgBotClientError({
35
+ cause: {
36
+ _tag: "UnableToGetFile",
37
+ cause: "File path not defined"
38
+ }
39
+ })
40
+ }
55
41
 
56
- const url = `${baseUrl}/file/bot${botToken}/${file_path}`
42
+ const file_name = file_path.replaceAll("/", "-")
43
+ const baseUrl = getBaseUrl(config)
44
+ const botToken = config.botToken
45
+ const url = `${baseUrl}/file/bot${botToken}/${file_path}`
57
46
 
58
- const content = yield* Micro.tryPromise({
59
- try: () => fetch(url).then((_) => _.arrayBuffer()),
60
- catch: (cause) =>
61
- new TgBotClientError({
62
- cause: { _tag: "UnableToGetFile", cause }
63
- })
47
+ let content: ArrayBuffer
48
+ try {
49
+ content = await fetch(url).then((_) => _.arrayBuffer())
50
+ } catch (cause) {
51
+ throw new TgBotClientError({
52
+ cause: { _tag: "UnableToGetFile", cause }
64
53
  })
54
+ }
55
+
56
+ const base64String = () => Buffer.from(content).toString("base64")
65
57
 
66
- return {
67
- content,
68
- file_name
69
- }
58
+ return {
59
+ content,
60
+ file_name,
61
+ base64String
62
+ }
63
+ }
64
+
65
+ export const getFile = async (
66
+ input: GetFile,
67
+ context: FileContext
68
+ ): Promise<File> => {
69
+ const { content, file_name } = await getFileBytes(input.fileId, context)
70
+ return new File([content], file_name, {
71
+ ...(input.type ? { type: input.type } : {})
70
72
  })
73
+ }