Skip to content

Commit 45d9f7c

Browse files
committed
refactor BotApi to adhere to Effect's best practices
1 parent 67bd5f9 commit 45d9f7c

26 files changed

Lines changed: 178 additions & 350 deletions

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
Effectful library to interact with Telegram Bot API.
1+
Effectful library for crafting Telegram bots.
22

3-
> ⚠️ Work in progress.
3+
> [!WARNING]
4+
> Work in progress.
5+
6+
## Features
7+
8+
- Effectful and typesafe API.
9+
- Up-to-date and documented Bot API methods and types.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"private": false,
66
"scripts": {
77
"gen": "bun run scripts/gen-bot-api.ts",
8+
"typecheck": "tsc",
89
"lint": "eslint .",
910
"lint:fix": "eslint . --fix"
1011
},

scripts/gen-bot-api.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { Project, Writers } from 'ts-morph'
55

66
async function main() {
77
const proj = new Project()
8-
genTypes(proj.createSourceFile('./src/bot-api/internal/types.gen.ts', {}, { overwrite: true }))
9-
genMethods(proj.createSourceFile('./src/bot-api/internal/methods.gen.ts', {}, { overwrite: true }))
10-
genBotApi(proj.createSourceFile('./src/bot-api/internal/botApi.gen.ts', {}, { overwrite: true }))
8+
genTypes(proj.createSourceFile('./src/internal/botApiTypes.gen.ts', {}, { overwrite: true }))
9+
genMethods(proj.createSourceFile('./src/internal/botApiMethods.gen.ts', {}, { overwrite: true }))
10+
genBotApiShape(proj.createSourceFile('./src/internal/botApiShape.gen.ts', {}, { overwrite: true }))
1111
await proj.save()
1212
}
1313

@@ -49,12 +49,12 @@ function genMethods(f: SourceFile): void {
4949
inputFile: 'InputFile',
5050
}
5151
f.addImportDeclaration({
52-
moduleSpecifier: './types.gen.ts',
52+
moduleSpecifier: './botApiTypes.gen.ts',
5353
isTypeOnly: true,
5454
namespaceImport: 'Types',
5555
})
5656
f.addImportDeclaration({
57-
moduleSpecifier: '../InputFile.ts',
57+
moduleSpecifier: './inputFile.ts',
5858
isTypeOnly: true,
5959
namedImports: ['InputFile'],
6060
})
@@ -93,7 +93,7 @@ function genMethods(f: SourceFile): void {
9393
}
9494
}
9595

96-
function genBotApi(f: SourceFile): void {
96+
function genBotApiShape(f: SourceFile): void {
9797
f.addImportDeclarations([
9898
{
9999
isTypeOnly: true,
@@ -104,7 +104,7 @@ function genBotApi(f: SourceFile): void {
104104
f.addInterface({
105105
isExported: true,
106106
isDefaultExport: false,
107-
name: 'BotApi',
107+
name: 'BotApiShape',
108108
properties: Object
109109
.values(methods)
110110
.map(({ name, description }) => ({

src/BotApi.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import type { BotApiShape } from './internal/botApiShape.gen'
2+
import * as Context from 'effect/Context'
3+
import * as Layer from 'effect/Layer'
4+
import * as internal from './internal/botApi'
5+
6+
export class BotApi extends Context.Tag('@grom.js/effectg/BotApi')<
7+
BotApi,
8+
BotApiShape
9+
>() {}
10+
11+
export const make = internal.make
12+
export const layer = Layer.effect(BotApi, make)

src/BotApiError.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type * as Types from './internal/botApiTypes.gen'
2+
import * as Data from 'effect/Data'
3+
4+
/**
5+
* Error returned from the Bot API server in case of unsuccessful method call.
6+
*/
7+
export class BotApiError extends Data.TaggedError('@grom.js/effectg/BotApiError')<{
8+
code: number
9+
description: string
10+
parameters?: Types.ResponseParameters
11+
}> {
12+
override get message() {
13+
return `(${this.code}) ${this.description}`
14+
}
15+
}

src/BotApiTransport.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type * as Effect from 'effect/Effect'
2+
import type { BotApiTransportError } from './BotApiTransportError'
3+
import type * as Types from './internal/botApiTypes.gen'
4+
import { Layer } from 'effect'
5+
import * as Context from 'effect/Context'
6+
import * as internal from './internal/botApiTransport'
7+
8+
export class BotApiTransport extends Context.Tag('@grom.js/effectg/BotApiTransport')<
9+
BotApiTransport,
10+
{
11+
sendRequest: (
12+
method: string,
13+
params: unknown,
14+
) => Effect.Effect<BotApiResponse, BotApiTransportError>
15+
}
16+
>() {
17+
}
18+
19+
/**
20+
* @see https://core.telegram.org/bots/api#making-requests
21+
*/
22+
export type BotApiResponse
23+
= {
24+
ok: true
25+
result: unknown
26+
description?: string
27+
} | {
28+
ok: false
29+
error_code: number
30+
description: string
31+
parameters?: Types.ResponseParameters
32+
}
33+
34+
export const makeWith = internal.makeWith
35+
36+
export const layerProd = (token: string) => Layer.effect(
37+
BotApiTransport,
38+
makeWith({
39+
makeUrl: method => new URL(`https://api.telegram.org/bot${token}/${method}`),
40+
}),
41+
)
42+
43+
export const layerTest = (token: string) => Layer.effect(
44+
BotApiTransport,
45+
makeWith({
46+
makeUrl: method => new URL(`https://api.telegram.org/bot${token}/test/${method}`),
47+
}),
48+
)

src/BotApiTransportError.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Data from 'effect/Data'
2+
3+
/**
4+
* Error caused by the transport when accessing Bot API.
5+
*/
6+
export class BotApiTransportError extends Data.TaggedError('@grom.js/effectg/BotApiTransportError')<{
7+
cause: unknown
8+
}> {}

src/bot-api/BotApi.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/bot-api/internal/botApi.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/bot-api/internal/botApiError.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)