@grom.js/effect-tg 0.8.0 → 0.9.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/README.md +162 -5
- package/dist/BotApi.d.ts +10 -12
- package/dist/BotApi.d.ts.map +1 -1
- package/dist/BotApi.js +2 -2
- package/dist/BotApi.js.map +1 -1
- package/dist/BotApiError.d.ts +46 -71
- package/dist/BotApiError.d.ts.map +1 -1
- package/dist/BotApiError.js +59 -41
- package/dist/BotApiError.js.map +1 -1
- package/dist/BotApiTransport.d.ts +10 -17
- package/dist/BotApiTransport.d.ts.map +1 -1
- package/dist/BotApiTransport.js +2 -7
- package/dist/BotApiTransport.js.map +1 -1
- package/dist/BotApiUrl.d.ts +6 -8
- package/dist/BotApiUrl.d.ts.map +1 -1
- package/dist/BotApiUrl.js.map +1 -1
- package/dist/Content.d.ts.map +1 -1
- package/dist/Dialog.d.ts +96 -41
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Dialog.js +60 -25
- package/dist/Dialog.js.map +1 -1
- package/dist/File.d.ts +1 -2
- package/dist/File.d.ts.map +1 -1
- package/dist/File.js +1 -1
- package/dist/File.js.map +1 -1
- package/dist/Markup.d.ts.map +1 -1
- package/dist/Runner.d.ts +1 -1
- package/dist/Runner.d.ts.map +1 -1
- package/dist/Send.d.ts +57 -130
- package/dist/Send.d.ts.map +1 -1
- package/dist/Send.js +65 -128
- package/dist/Send.js.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/internal/botApi.d.ts +3 -1
- package/dist/internal/botApi.d.ts.map +1 -1
- package/dist/internal/botApi.gen.d.ts +6501 -0
- package/dist/internal/botApi.gen.d.ts.map +1 -0
- package/dist/internal/botApi.gen.js +2 -0
- package/dist/internal/botApi.gen.js.map +1 -0
- package/dist/internal/botApi.js +2 -6
- package/dist/internal/botApi.js.map +1 -1
- package/dist/internal/botApiError.d.ts +5 -2
- package/dist/internal/botApiError.d.ts.map +1 -1
- package/dist/internal/botApiError.js +8 -18
- package/dist/internal/botApiError.js.map +1 -1
- package/dist/internal/botApiTransport.d.ts +5 -2
- package/dist/internal/botApiTransport.d.ts.map +1 -1
- package/dist/internal/botApiTransport.js +14 -10
- package/dist/internal/botApiTransport.js.map +1 -1
- package/dist/internal/dialog.d.ts +27 -10
- package/dist/internal/dialog.d.ts.map +1 -1
- package/dist/internal/dialog.js +88 -14
- package/dist/internal/dialog.js.map +1 -1
- package/dist/internal/file.d.ts +1 -1
- package/dist/internal/file.d.ts.map +1 -1
- package/dist/internal/file.js +1 -1
- package/dist/internal/file.js.map +1 -1
- package/dist/internal/runner.d.ts +4 -5
- package/dist/internal/runner.d.ts.map +1 -1
- package/dist/internal/runner.js +14 -19
- package/dist/internal/runner.js.map +1 -1
- package/dist/internal/send.d.ts +4 -4
- package/dist/internal/send.d.ts.map +1 -1
- package/dist/internal/send.js +62 -51
- package/dist/internal/send.js.map +1 -1
- package/package.json +11 -9
- package/src/BotApi.ts +38 -31
- package/src/BotApiError.ts +109 -63
- package/src/BotApiTransport.ts +18 -20
- package/src/BotApiUrl.ts +6 -8
- package/src/Content.ts +14 -14
- package/src/Dialog.ts +164 -42
- package/src/File.ts +3 -4
- package/src/Markup.ts +5 -5
- package/src/Send.ts +114 -202
- package/src/Text.ts +5 -5
- package/src/internal/botApi.gen.ts +6783 -0
- package/src/internal/botApi.ts +7 -11
- package/src/internal/botApiError.ts +15 -20
- package/src/internal/botApiTransport.ts +25 -17
- package/src/internal/dialog.ts +109 -26
- package/src/internal/file.ts +1 -1
- package/src/internal/runner.ts +34 -38
- package/src/internal/send.ts +161 -132
- package/dist/internal/botApiMethods.gen.d.ts +0 -2110
- package/dist/internal/botApiMethods.gen.d.ts.map +0 -1
- package/dist/internal/botApiMethods.gen.js +0 -2
- package/dist/internal/botApiMethods.gen.js.map +0 -1
- package/dist/internal/botApiShape.gen.d.ts +0 -406
- package/dist/internal/botApiShape.gen.d.ts.map +0 -1
- package/dist/internal/botApiShape.gen.js +0 -2
- package/dist/internal/botApiShape.gen.js.map +0 -1
- package/dist/internal/botApiTypes.gen.d.ts +0 -3986
- package/dist/internal/botApiTypes.gen.d.ts.map +0 -1
- package/dist/internal/botApiTypes.gen.js +0 -2
- package/dist/internal/botApiTypes.gen.js.map +0 -1
- package/src/internal/botApiMethods.gen.ts +0 -2111
- package/src/internal/botApiShape.gen.ts +0 -406
- package/src/internal/botApiTypes.gen.ts +0 -4264
package/src/internal/botApi.ts
CHANGED
|
@@ -3,9 +3,11 @@ import type * as BotApiTransport from '../BotApiTransport.ts'
|
|
|
3
3
|
import * as Effect from 'effect/Effect'
|
|
4
4
|
import * as BotApiError from '../BotApiError.ts'
|
|
5
5
|
|
|
6
|
-
export const make = (
|
|
7
|
-
transport
|
|
8
|
-
|
|
6
|
+
export const make = ({
|
|
7
|
+
transport,
|
|
8
|
+
}: {
|
|
9
|
+
transport: BotApiTransport.Service
|
|
10
|
+
}): BotApi.Service => (
|
|
9
11
|
new Proxy({}, {
|
|
10
12
|
get: (_target, prop) => {
|
|
11
13
|
if (typeof prop !== 'string') {
|
|
@@ -18,15 +20,9 @@ export const make = (
|
|
|
18
20
|
if (response.ok) {
|
|
19
21
|
return response.result
|
|
20
22
|
}
|
|
21
|
-
return yield* Effect.fail(
|
|
22
|
-
new BotApiError.BotApiError({
|
|
23
|
-
code: response.error_code,
|
|
24
|
-
description: response.description,
|
|
25
|
-
parameters: response.parameters,
|
|
26
|
-
}),
|
|
27
|
-
)
|
|
23
|
+
return yield* Effect.fail(BotApiError.fromResponse(response))
|
|
28
24
|
},
|
|
29
25
|
)
|
|
30
26
|
},
|
|
31
|
-
}) as BotApi.
|
|
27
|
+
}) as BotApi.Service
|
|
32
28
|
)
|
|
@@ -1,36 +1,31 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as BotApiError from '../BotApiError.ts'
|
|
1
|
+
import type { MethodFailureReason } from '../BotApiError.ts'
|
|
3
2
|
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
cause: error,
|
|
13
|
-
retryAfter: Duration.seconds(params.retry_after),
|
|
14
|
-
})
|
|
15
|
-
}
|
|
3
|
+
export const guessReason = ({
|
|
4
|
+
code,
|
|
5
|
+
description,
|
|
6
|
+
}: {
|
|
7
|
+
code: number
|
|
8
|
+
description: string
|
|
9
|
+
}): MethodFailureReason => {
|
|
10
|
+
const msg = description.toLowerCase()
|
|
16
11
|
if (code === 403) {
|
|
17
12
|
if (msg.includes('bot was blocked by the user')) {
|
|
18
|
-
return
|
|
13
|
+
return 'BotBlockedByUser'
|
|
19
14
|
}
|
|
20
15
|
}
|
|
21
16
|
if (code === 400) {
|
|
22
17
|
if (msg.includes('message is not modified')) {
|
|
23
|
-
return
|
|
18
|
+
return 'MessageNotModified'
|
|
24
19
|
}
|
|
25
20
|
if (msg.includes('reply markup too long')) {
|
|
26
|
-
return
|
|
21
|
+
return 'ReplyMarkupTooLong'
|
|
27
22
|
}
|
|
28
23
|
if (msg.includes('query is too old') && msg.includes('query id is invalid')) {
|
|
29
|
-
return
|
|
24
|
+
return 'QueryIdInvalid'
|
|
30
25
|
}
|
|
31
26
|
if (msg.includes('can\'t use the media of the specified type in the album')) {
|
|
32
|
-
return
|
|
27
|
+
return 'MediaGroupedInvalid'
|
|
33
28
|
}
|
|
34
29
|
}
|
|
35
|
-
return
|
|
30
|
+
return 'Unknown'
|
|
36
31
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type * as HttpClient from '@effect/platform/HttpClient'
|
|
2
|
+
import type * as BotApiTransport from '../BotApiTransport.ts'
|
|
2
3
|
import type * as BotApiUrl from '../BotApiUrl.ts'
|
|
3
4
|
import * as HttpBody from '@effect/platform/HttpBody'
|
|
4
5
|
import * as Chunk from 'effect/Chunk'
|
|
5
6
|
import * as Effect from 'effect/Effect'
|
|
6
7
|
import * as Stream from 'effect/Stream'
|
|
7
|
-
import * as
|
|
8
|
+
import * as BotApiError from '../BotApiError.ts'
|
|
8
9
|
import * as File from '../File.ts'
|
|
9
10
|
|
|
10
11
|
interface ExtractedFile {
|
|
@@ -13,13 +14,16 @@ interface ExtractedFile {
|
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
|
-
* Recursively checks whether a value contains
|
|
17
|
+
* Recursively checks whether a value contains
|
|
18
|
+
* {@linkcode File.InputFile InputFile} instances.
|
|
17
19
|
*/
|
|
18
20
|
const hasInputFile = (value: unknown): boolean => {
|
|
19
|
-
if (value instanceof File.InputFile)
|
|
21
|
+
if (value instanceof File.InputFile) {
|
|
20
22
|
return true
|
|
21
|
-
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(value)) {
|
|
22
25
|
return value.some(hasInputFile)
|
|
26
|
+
}
|
|
23
27
|
if (typeof value === 'object' && value !== null) {
|
|
24
28
|
return Object.values(value).some(hasInputFile)
|
|
25
29
|
}
|
|
@@ -49,8 +53,9 @@ const cloneAndExtract = (
|
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
/**
|
|
52
|
-
*
|
|
53
|
-
* `attach://{id}` strings and collecting them into
|
|
56
|
+
* Clones parameters deeply, replacing {@linkcode File.InputFile InputFile}
|
|
57
|
+
* instances with `attach://{id}` strings and collecting them into
|
|
58
|
+
* the files array.
|
|
54
59
|
*/
|
|
55
60
|
const extractFiles = (params: unknown): {
|
|
56
61
|
params: Record<string, unknown>
|
|
@@ -81,8 +86,9 @@ const makeHttpBody = Effect.fnUntraced(function* (params: unknown) {
|
|
|
81
86
|
const { params: processedParams, files } = extractFiles(params)
|
|
82
87
|
const formData = new FormData()
|
|
83
88
|
for (const [key, value] of Object.entries(processedParams)) {
|
|
84
|
-
if (value == null)
|
|
89
|
+
if (value == null) {
|
|
85
90
|
continue
|
|
91
|
+
}
|
|
86
92
|
const serialized = typeof value === 'string' ? value : JSON.stringify(value)
|
|
87
93
|
formData.append(key, serialized)
|
|
88
94
|
}
|
|
@@ -97,10 +103,13 @@ const makeHttpBody = Effect.fnUntraced(function* (params: unknown) {
|
|
|
97
103
|
return HttpBody.formData(formData)
|
|
98
104
|
})
|
|
99
105
|
|
|
100
|
-
export const make = (
|
|
101
|
-
httpClient
|
|
102
|
-
botApiUrl
|
|
103
|
-
|
|
106
|
+
export const make = ({
|
|
107
|
+
httpClient,
|
|
108
|
+
botApiUrl,
|
|
109
|
+
}: {
|
|
110
|
+
httpClient: HttpClient.HttpClient
|
|
111
|
+
botApiUrl: BotApiUrl.Service
|
|
112
|
+
}): BotApiTransport.Service => ({
|
|
104
113
|
sendRequest: (method, params) => (
|
|
105
114
|
Effect.gen(function* () {
|
|
106
115
|
const body = yield* makeHttpBody(params)
|
|
@@ -108,11 +117,10 @@ export const make = (
|
|
|
108
117
|
const responseJson = yield* response.json
|
|
109
118
|
// We trust Bot API and don't want to introduce overhead with validation.
|
|
110
119
|
return responseJson as BotApiTransport.BotApiResponse
|
|
111
|
-
})
|
|
112
|
-
.
|
|
113
|
-
Effect.
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
120
|
+
}).pipe(
|
|
121
|
+
Effect.catchAll(cause => (
|
|
122
|
+
Effect.fail(new BotApiError.TransportError({ cause }))
|
|
123
|
+
)),
|
|
124
|
+
)
|
|
117
125
|
),
|
|
118
126
|
})
|
package/src/internal/dialog.ts
CHANGED
|
@@ -1,33 +1,116 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import type * as BotApi from '../BotApi.ts'
|
|
2
|
+
import * as Option from 'effect/Option'
|
|
3
|
+
import * as Dialog from '../Dialog.ts'
|
|
4
|
+
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// Dialog ID <-> Peer ID
|
|
7
|
+
// =============================================================================
|
|
8
|
+
|
|
9
|
+
export const decodeDialogId = (dialogId: number): Option.Option<
|
|
10
|
+
| { peer: 'user', id: Dialog.UserId }
|
|
11
|
+
| { peer: 'group', id: Dialog.GroupId }
|
|
12
|
+
| { peer: 'supergroup', id: Dialog.SupergroupId }
|
|
13
|
+
| { peer: 'monoforum', id: number }
|
|
14
|
+
| { peer: 'secret-chat', id: number }
|
|
15
|
+
> => {
|
|
16
|
+
if (Number.isSafeInteger(dialogId)) {
|
|
17
|
+
if (1 <= dialogId && dialogId <= 0xFFFFFFFFFF) {
|
|
18
|
+
return Option.some({ peer: 'user', id: +dialogId as Dialog.UserId })
|
|
19
|
+
}
|
|
20
|
+
if (-999999999999 <= dialogId && dialogId <= -1) {
|
|
21
|
+
return Option.some({ peer: 'group', id: -dialogId as Dialog.GroupId })
|
|
22
|
+
}
|
|
23
|
+
if (-1997852516352 <= dialogId && dialogId <= -1000000000001) {
|
|
24
|
+
return Option.some({ peer: 'supergroup', id: -(dialogId + 1000000000000) as Dialog.SupergroupId })
|
|
25
|
+
}
|
|
26
|
+
if (-4000000000000 <= dialogId && dialogId <= -2002147483649) {
|
|
27
|
+
return Option.some({ peer: 'monoforum', id: -(dialogId + 1000000000000) })
|
|
28
|
+
}
|
|
29
|
+
if (-2002147483648 <= dialogId && dialogId <= -1997852516353) {
|
|
30
|
+
return Option.some({ peer: 'secret-chat', id: dialogId + 2000000000000 })
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return Option.none()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const decodePeerId: {
|
|
37
|
+
(peer: 'user', dialogId: number): Option.Option<Dialog.UserId>
|
|
38
|
+
(peer: 'group', dialogId: number): Option.Option<Dialog.GroupId>
|
|
39
|
+
(peer: 'supergroup', dialogId: number): Option.Option<Dialog.SupergroupId>
|
|
40
|
+
(peer: 'monoforum', dialogId: number): Option.Option<number>
|
|
41
|
+
(peer: 'secret-chat', dialogId: number): Option.Option<number>
|
|
42
|
+
} = (peer, dialogId) => {
|
|
43
|
+
const decoded = decodeDialogId(dialogId)
|
|
44
|
+
if (Option.isSome(decoded) && decoded.value.peer === peer) {
|
|
45
|
+
return Option.some(decoded.value.id as any)
|
|
46
|
+
}
|
|
47
|
+
return Option.none()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const encodePeerId = (
|
|
51
|
+
peer: 'user' | 'group' | 'supergroup' | 'monoforum' | 'secret-chat',
|
|
52
|
+
id: number,
|
|
53
|
+
): Option.Option<Dialog.DialogId> => {
|
|
54
|
+
if (Number.isSafeInteger(id)) {
|
|
55
|
+
if (peer === 'user' && 1 <= id && id <= 0xFFFFFFFFFF) {
|
|
56
|
+
return Option.some(id as Dialog.DialogId)
|
|
57
|
+
}
|
|
58
|
+
if (peer === 'group' && 1 <= id && id <= 999999999999) {
|
|
59
|
+
return Option.some(-id as Dialog.DialogId)
|
|
60
|
+
}
|
|
61
|
+
if (peer === 'supergroup' && 1 <= id && id <= 997852516352) {
|
|
62
|
+
return Option.some(-(id + 1000000000000) as Dialog.DialogId)
|
|
63
|
+
}
|
|
64
|
+
if (peer === 'monoforum' && 1002147483649 <= id && id <= 3000000000000) {
|
|
65
|
+
return Option.some(-(id + 1000000000000) as Dialog.DialogId)
|
|
66
|
+
}
|
|
67
|
+
if (peer === 'secret-chat' && -2147483648 <= id && id <= 2147483647) {
|
|
68
|
+
return Option.some((id - 2000000000000) as Dialog.DialogId)
|
|
69
|
+
}
|
|
14
70
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
71
|
+
return Option.none()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// =============================================================================
|
|
75
|
+
// Constructors
|
|
76
|
+
// =============================================================================
|
|
20
77
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
78
|
+
export const ofMessage: (
|
|
79
|
+
message: BotApi.Types.Message,
|
|
80
|
+
) => Dialog.Dialog = (m) => {
|
|
81
|
+
// TODO: Remove type assertion when bot-api-spec updates types.
|
|
82
|
+
switch (m.chat.type as 'private' | 'group' | 'supergroup' | 'channel') {
|
|
83
|
+
case 'private': {
|
|
84
|
+
const user = new Dialog.User({
|
|
85
|
+
id: Option.getOrThrow(decodePeerId('user', m.chat.id)),
|
|
86
|
+
})
|
|
87
|
+
if (m.message_thread_id != null) {
|
|
88
|
+
return user.topic(m.message_thread_id)
|
|
89
|
+
}
|
|
90
|
+
return user
|
|
91
|
+
}
|
|
92
|
+
case 'group': {
|
|
93
|
+
return new Dialog.Group({
|
|
94
|
+
id: Option.getOrThrow(decodePeerId('group', m.chat.id)),
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
case 'supergroup': {
|
|
98
|
+
const supergroup = new Dialog.Supergroup({
|
|
99
|
+
id: Option.getOrThrow(decodePeerId('supergroup', m.chat.id)),
|
|
100
|
+
})
|
|
101
|
+
if (m.message_thread_id != null) {
|
|
102
|
+
return supergroup.topic(m.message_thread_id)
|
|
24
103
|
}
|
|
25
|
-
|
|
26
|
-
|
|
104
|
+
return supergroup
|
|
105
|
+
}
|
|
106
|
+
case 'channel': {
|
|
107
|
+
const channel = new Dialog.Channel({
|
|
108
|
+
id: Option.getOrThrow(decodePeerId('supergroup', m.chat.id)),
|
|
109
|
+
})
|
|
110
|
+
if (m.direct_messages_topic != null) {
|
|
111
|
+
return channel.directMessages(m.direct_messages_topic.topic_id)
|
|
27
112
|
}
|
|
28
|
-
|
|
29
|
-
this.dialogId = toDialogId(id)
|
|
113
|
+
return channel
|
|
30
114
|
}
|
|
31
115
|
}
|
|
32
|
-
return Base
|
|
33
116
|
}
|
package/src/internal/file.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as Effect from 'effect/Effect'
|
|
|
4
4
|
import * as BotApi from '../BotApi.ts'
|
|
5
5
|
import * as BotApiUrl from '../BotApiUrl.ts'
|
|
6
6
|
|
|
7
|
-
export const
|
|
7
|
+
export const download = Effect.fnUntraced(
|
|
8
8
|
function* (fileId: FileId) {
|
|
9
9
|
const file = yield* BotApi.callMethod('getFile', { file_id: fileId })
|
|
10
10
|
if (file.file_path == null) {
|
package/src/internal/runner.ts
CHANGED
|
@@ -1,54 +1,50 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type
|
|
3
|
-
import type { Runner } from '../Runner.ts'
|
|
1
|
+
import type * as BotApiError from '../BotApiError.ts'
|
|
2
|
+
import type * as Runner from '../Runner.ts'
|
|
4
3
|
import * as Duration from 'effect/Duration'
|
|
5
4
|
import * as Effect from 'effect/Effect'
|
|
6
5
|
import * as Match from 'effect/Match'
|
|
7
6
|
import * as Schedule from 'effect/Schedule'
|
|
8
|
-
import
|
|
9
|
-
import
|
|
7
|
+
import * as Bot from '../Bot.ts'
|
|
8
|
+
import * as BotApi from '../BotApi.ts'
|
|
10
9
|
|
|
11
10
|
export const makeSimple = (options?: {
|
|
12
11
|
allowedUpdates?: string[]
|
|
13
|
-
}): Runner<BotApiError
|
|
12
|
+
}): Runner.Runner<BotApiError.BotApiError, BotApi.BotApi> => ({
|
|
14
13
|
run: Effect.fnUntraced(
|
|
15
14
|
function* (bot) {
|
|
16
15
|
const { allowedUpdates } = options ?? {}
|
|
17
|
-
const api = yield* BotApi
|
|
16
|
+
const api = yield* BotApi.BotApi
|
|
18
17
|
let lastUpdateId: undefined | number
|
|
19
18
|
while (true) {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Match.
|
|
32
|
-
|
|
33
|
-
error.code >= 500 || (
|
|
34
|
-
error.code !== 401
|
|
35
|
-
&& error.code !== 403
|
|
36
|
-
&& error.code !== 404
|
|
37
|
-
),
|
|
38
|
-
),
|
|
39
|
-
'@grom.js/effect-tg/BotApiTransport/BotApiTransportError': () => Effect.succeed(true),
|
|
40
|
-
}),
|
|
41
|
-
),
|
|
42
|
-
}),
|
|
43
|
-
)
|
|
44
|
-
if (update) {
|
|
45
|
-
yield* Effect
|
|
46
|
-
.provideService(bot, Update, update)
|
|
47
|
-
.pipe(
|
|
48
|
-
Effect.catchAll(error => (
|
|
49
|
-
Effect.logError('Error in bot:', error)
|
|
19
|
+
const updates = yield* api.getUpdates({
|
|
20
|
+
offset: lastUpdateId == null ? undefined : lastUpdateId + 1,
|
|
21
|
+
allowed_updates: allowedUpdates,
|
|
22
|
+
timeout: 30,
|
|
23
|
+
limit: 1,
|
|
24
|
+
}).pipe(
|
|
25
|
+
Effect.retry({
|
|
26
|
+
schedule: Schedule.exponential(Duration.millis(100)),
|
|
27
|
+
while: e => Match.value(e).pipe(
|
|
28
|
+
Match.tag('InternalServerError', () => true),
|
|
29
|
+
Match.tag('TransportError', e => Match.value(e.cause).pipe(
|
|
30
|
+
Match.tag('RequestError', e => e.reason === 'Transport'),
|
|
31
|
+
Match.orElse(() => false),
|
|
50
32
|
)),
|
|
51
|
-
|
|
33
|
+
Match.orElse(() => false),
|
|
34
|
+
),
|
|
35
|
+
}),
|
|
36
|
+
Effect.catchTag('RateLimited', e => Effect.gen(function* () {
|
|
37
|
+
yield* Effect.sleep(e.retryAfter)
|
|
38
|
+
return []
|
|
39
|
+
})),
|
|
40
|
+
)
|
|
41
|
+
if (updates.length > 0) {
|
|
42
|
+
const update = updates[0]!
|
|
43
|
+
yield* Effect.provideService(bot, Bot.Update, update).pipe(
|
|
44
|
+
Effect.catchAll(error => (
|
|
45
|
+
Effect.logError('Error in bot:', error)
|
|
46
|
+
)),
|
|
47
|
+
)
|
|
52
48
|
lastUpdateId = update.update_id
|
|
53
49
|
}
|
|
54
50
|
}
|