@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/src/client.ts CHANGED
@@ -1,10 +1,7 @@
1
- import * as Micro from "effect/Micro"
2
- import * as Context from "effect/Context"
3
-
4
1
  import type { Api } from "@effect-ak/tg-bot-api"
5
2
  import { executeTgBotMethod } from "./execute"
6
- import { TgBotApiToken } from "./config"
7
- import { GetFile, ClientFileService } from "./client-file"
3
+ import type { TgBotConfig } from "./config"
4
+ import { getFile as getFileImpl, type GetFile } from "./client-file"
8
5
 
9
6
  export interface TgBotClient {
10
7
  readonly execute: <M extends keyof Api>(
@@ -14,33 +11,24 @@ export interface TgBotClient {
14
11
  readonly getFile: (input: GetFile) => Promise<File>
15
12
  }
16
13
 
17
- interface MakeTgClient {
14
+ export interface MakeTgClient {
18
15
  bot_token: string
16
+ base_url?: string
19
17
  }
20
18
 
21
19
  export function makeTgBotClient(config: MakeTgClient): TgBotClient {
22
- return createEffect(config).pipe(Micro.runSync)
23
- }
24
-
25
- const createEffect = ({ bot_token }: MakeTgClient) =>
26
- Micro.gen(function* () {
27
- const file = yield* Micro.service(ClientFileService)
28
- const context = Context.make(TgBotApiToken, bot_token)
29
-
30
- const execute = <M extends keyof Api>(
31
- method: M,
32
- input: Parameters<Api[M]>[0]
33
- ) =>
34
- executeTgBotMethod(method, input).pipe(
35
- Micro.provideContext(context),
36
- Micro.runPromise
37
- )
20
+ const tgConfig: TgBotConfig = {
21
+ botToken: config.bot_token,
22
+ ...(config.base_url ? { baseUrl: config.base_url } : {})
23
+ }
38
24
 
39
- const getFile = (input: GetFile) =>
40
- file.getFile(input).pipe(Micro.provideContext(context), Micro.runPromise)
25
+ const execute = <M extends keyof Api>(
26
+ method: M,
27
+ input: Parameters<Api[M]>[0]
28
+ ) => executeTgBotMethod({ config: tgConfig, method, input })
41
29
 
42
- return {
43
- execute,
44
- getFile
45
- }
46
- }).pipe(Micro.provideContext(ClientFileService.live()))
30
+ return {
31
+ execute,
32
+ getFile: (input: GetFile) => getFileImpl(input, { config: tgConfig, execute })
33
+ }
34
+ }
package/src/config.ts CHANGED
@@ -1,13 +1,10 @@
1
- import * as Context from "effect/Context"
2
-
3
1
  import { TG_BOT_API_URL } from "./const"
4
2
 
5
- export class TgBotApiBaseUrl extends Context.Reference<TgBotApiBaseUrl>()(
6
- "TgBotApiBaseUrl",
7
- { defaultValue: () => TG_BOT_API_URL }
8
- ) {}
3
+ export interface TgBotConfig {
4
+ botToken: string
5
+ baseUrl?: string
6
+ }
9
7
 
10
- export class TgBotApiToken extends Context.Tag("TgBotApiToken")<
11
- TgBotApiToken,
12
- string
13
- >() {}
8
+ export const getBaseUrl = (config?: Pick<TgBotConfig, "baseUrl">): string => {
9
+ return config?.baseUrl ?? TG_BOT_API_URL
10
+ }
package/src/errors.ts CHANGED
@@ -1,14 +1,18 @@
1
- import * as Data from "effect/Data"
1
+ type ErrorReason =
2
+ | { _tag: "NotOkResponse"; errorCode?: number; details?: string }
3
+ | { _tag: "UnexpectedResponse"; response: unknown }
4
+ | { _tag: "ClientInternalError"; cause: unknown }
5
+ | { _tag: "UnableToGetFile"; cause: unknown }
6
+ | { _tag: "BotHandlerError"; cause: unknown }
7
+ | { _tag: "NotJsonResponse"; response: unknown }
2
8
 
3
- type ErrorReason = Data.TaggedEnum<{
4
- NotOkResponse: { errorCode?: number; details?: string }
5
- UnexpectedResponse: { response: unknown }
6
- ClientInternalError: { cause: unknown }
7
- UnableToGetFile: { cause: unknown }
8
- BotHandlerError: { cause: unknown }
9
- NotJsonResponse: { response: unknown }
10
- }>
9
+ export class TgBotClientError extends Error {
10
+ readonly _tag = "TgBotClientError"
11
+ readonly cause: ErrorReason
11
12
 
12
- export class TgBotClientError extends Data.TaggedError("TgBotClientError")<{
13
- cause: ErrorReason
14
- }> {}
13
+ constructor(options: { cause: ErrorReason }) {
14
+ super(`TgBotClientError: ${options.cause._tag}`)
15
+ this.cause = options.cause
16
+ this.name = "TgBotClientError"
17
+ }
18
+ }
package/src/execute.ts CHANGED
@@ -1,65 +1,62 @@
1
- import * as String from "effect/String"
2
- import * as Micro from "effect/Micro"
3
1
  import type { Api } from "@effect-ak/tg-bot-api"
4
2
 
5
3
  import { TgBotClientError } from "./errors"
6
4
  import { isFileContent, isTgBotApiResponse } from "./guards"
7
- import { TgBotApiBaseUrl, TgBotApiToken } from "./config"
5
+ import type { TgBotConfig } from "./config"
6
+ import { getBaseUrl } from "./config"
7
+ import { snakeToCamel } from "./utils"
8
8
 
9
- export const executeTgBotMethod = <M extends keyof Api>(
10
- method: M,
9
+ export async function executeTgBotMethod<M extends keyof Api>(params: {
10
+ config: TgBotConfig
11
+ method: M
11
12
  input: Parameters<Api[M]>[0]
12
- ): Micro.Micro<ReturnType<Api[M]>, TgBotClientError, TgBotApiToken> =>
13
- Micro.gen(function* () {
14
- const botToken = yield* Micro.service(TgBotApiToken)
15
- const baseUrl = yield* Micro.service(TgBotApiBaseUrl)
13
+ }): Promise<ReturnType<Api[M]>> {
14
+ const { config, method, input } = params
15
+ const baseUrl = getBaseUrl(config)
16
+ const botToken = config.botToken
16
17
 
17
- const httpResponse = yield* Micro.tryPromise({
18
- try: () =>
19
- fetch(`${baseUrl}/bot${botToken}/${String.snakeToCamel(method)}`, {
20
- body: makePayload(input) ?? null,
21
- method: "POST"
22
- }),
23
- catch: (cause) =>
24
- new TgBotClientError({
25
- cause: { _tag: "ClientInternalError", cause }
26
- })
18
+ let httpResponse: Response
19
+ try {
20
+ httpResponse = await fetch(
21
+ `${baseUrl}/bot${botToken}/${snakeToCamel(method)}`,
22
+ {
23
+ body: makePayload(input) ?? null,
24
+ method: "POST"
25
+ }
26
+ )
27
+ } catch (cause) {
28
+ throw new TgBotClientError({
29
+ cause: { _tag: "ClientInternalError", cause }
27
30
  })
31
+ }
28
32
 
29
- const response = yield* Micro.tryPromise({
30
- try: () => httpResponse.json(),
31
- catch: () =>
32
- new TgBotClientError({
33
- cause: { _tag: "NotJsonResponse", response: httpResponse }
34
- })
33
+ let response: unknown
34
+ try {
35
+ response = await httpResponse.json()
36
+ } catch {
37
+ throw new TgBotClientError({
38
+ cause: { _tag: "NotJsonResponse", response: httpResponse }
35
39
  })
40
+ }
36
41
 
37
- if (!isTgBotApiResponse(response)) {
38
- return yield* Micro.fail(
39
- new TgBotClientError({
40
- cause: { _tag: "UnexpectedResponse", response }
41
- })
42
- )
43
- }
42
+ if (!isTgBotApiResponse(response)) {
43
+ throw new TgBotClientError({
44
+ cause: { _tag: "UnexpectedResponse", response }
45
+ })
46
+ }
44
47
 
45
- if (!httpResponse.ok) {
46
- return yield* Micro.fail(
47
- new TgBotClientError({
48
- cause: {
49
- _tag: "NotOkResponse",
50
- ...(response.error_code
51
- ? { errorCode: response.error_code }
52
- : undefined),
53
- ...(response.description
54
- ? { details: response.description }
55
- : undefined)
56
- }
57
- })
58
- )
59
- }
48
+ if (!httpResponse.ok) {
49
+ throw new TgBotClientError({
50
+ cause: {
51
+ _tag: "NotOkResponse",
52
+ ...(response.error_code ? { errorCode: response.error_code } : {}),
53
+ ...(response.description ? { details: response.description } : {})
54
+ }
55
+ })
56
+ }
60
57
 
61
- return response.result as ReturnType<Api[M]>
62
- })
58
+ return response.result as ReturnType<Api[M]>
59
+ }
63
60
 
64
61
  export const makePayload = (body: object): FormData | undefined => {
65
62
  const entries = Object.entries(body)
package/src/index.ts CHANGED
@@ -5,3 +5,4 @@ export * from "./config"
5
5
  export * from "./errors"
6
6
  export * from "./guards"
7
7
  export * from "./const"
8
+ export * from "./utils"
package/src/utils.ts ADDED
@@ -0,0 +1,3 @@
1
+ export const snakeToCamel = (str: string): string => {
2
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase())
3
+ }