@grom.js/effect-tg 0.3.1 → 0.5.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/Bot.d.ts +13 -0
- package/dist/Bot.d.ts.map +1 -0
- package/dist/Bot.js +5 -0
- package/dist/Bot.js.map +1 -0
- package/dist/BotApi.d.ts +12 -3
- package/dist/BotApi.d.ts.map +1 -1
- package/dist/BotApi.js +7 -2
- package/dist/BotApi.js.map +1 -0
- package/dist/BotApiTransport.d.ts +11 -10
- package/dist/BotApiTransport.d.ts.map +1 -1
- package/dist/BotApiTransport.js +6 -8
- package/dist/BotApiTransport.js.map +1 -0
- package/dist/BotApiUrl.d.ts +14 -0
- package/dist/BotApiUrl.d.ts.map +1 -0
- package/dist/BotApiUrl.js +13 -0
- package/dist/BotApiUrl.js.map +1 -0
- package/dist/Content.d.ts +288 -16
- package/dist/Content.d.ts.map +1 -1
- package/dist/Content.js +189 -28
- package/dist/Content.js.map +1 -0
- package/dist/Dialog.d.ts +61 -0
- package/dist/Dialog.d.ts.map +1 -0
- package/dist/Dialog.js +35 -0
- package/dist/Dialog.js.map +1 -0
- package/dist/File.d.ts +23 -0
- package/dist/File.d.ts.map +1 -0
- package/dist/File.js +24 -0
- package/dist/File.js.map +1 -0
- package/dist/LinkPreview.d.ts +2 -2
- package/dist/LinkPreview.d.ts.map +1 -1
- package/dist/LinkPreview.js +17 -16
- package/dist/LinkPreview.js.map +1 -0
- package/dist/Markup.d.ts +41 -0
- package/dist/Markup.d.ts.map +1 -0
- package/dist/Markup.js +10 -0
- package/dist/Markup.js.map +1 -0
- package/dist/Runner.d.ts +18 -0
- package/dist/Runner.d.ts.map +1 -0
- package/dist/Runner.js +7 -0
- package/dist/Runner.js.map +1 -0
- package/dist/Send.d.ts +177 -13
- package/dist/Send.d.ts.map +1 -1
- package/dist/Send.js +187 -18
- package/dist/Send.js.map +1 -0
- package/dist/Text.d.ts +26 -10
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +11 -12
- package/dist/Text.js.map +1 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -0
- package/dist/internal/botApi.d.ts +3 -4
- package/dist/internal/botApi.d.ts.map +1 -1
- package/dist/internal/botApi.js +18 -22
- package/dist/internal/botApi.js.map +1 -0
- package/dist/internal/botApiMethods.gen.d.ts +19 -19
- package/dist/internal/botApiMethods.gen.d.ts.map +1 -1
- package/dist/internal/botApiMethods.gen.js +1 -0
- package/dist/internal/botApiMethods.gen.js.map +1 -0
- package/dist/internal/botApiShape.gen.d.ts +159 -159
- package/dist/internal/botApiShape.gen.d.ts.map +1 -1
- package/dist/internal/botApiShape.gen.js +1 -0
- package/dist/internal/botApiShape.gen.js.map +1 -0
- package/dist/internal/botApiTransport.d.ts +4 -6
- package/dist/internal/botApiTransport.d.ts.map +1 -1
- package/dist/internal/botApiTransport.js +89 -17
- package/dist/internal/botApiTransport.js.map +1 -0
- package/dist/internal/botApiTypes.gen.js +1 -0
- package/dist/internal/botApiTypes.gen.js.map +1 -0
- package/dist/internal/dialog.d.ts +12 -0
- package/dist/internal/dialog.d.ts.map +1 -0
- package/dist/internal/dialog.js +19 -0
- package/dist/internal/dialog.js.map +1 -0
- package/dist/internal/runner.d.ts +8 -0
- package/dist/internal/runner.d.ts.map +1 -0
- package/dist/internal/runner.js +38 -0
- package/dist/internal/runner.js.map +1 -0
- package/dist/internal/send.d.ts +13 -0
- package/dist/internal/send.d.ts.map +1 -0
- package/dist/internal/send.js +220 -0
- package/dist/internal/send.js.map +1 -0
- package/package.json +14 -10
- package/src/Bot.ts +16 -0
- package/src/BotApi.ts +68 -0
- package/src/BotApiTransport.ts +55 -0
- package/src/BotApiUrl.ts +28 -0
- package/src/Content.ts +410 -0
- package/src/Dialog.ts +54 -0
- package/src/File.ts +45 -0
- package/src/LinkPreview.ts +26 -0
- package/src/Markup.ts +33 -0
- package/src/Runner.ts +22 -0
- package/src/Send.ts +330 -0
- package/src/Text.ts +42 -0
- package/src/index.ts +12 -0
- package/src/internal/botApi.ts +31 -0
- package/src/internal/botApiMethods.gen.ts +2027 -0
- package/src/internal/botApiShape.gen.ts +398 -0
- package/src/internal/botApiTransport.ts +118 -0
- package/src/internal/botApiTypes.gen.ts +4178 -0
- package/src/internal/dialog.ts +33 -0
- package/src/internal/runner.ts +57 -0
- package/src/internal/send.ts +318 -0
- package/dist/BotApiWebhook.d.ts +0 -41
- package/dist/BotApiWebhook.d.ts.map +0 -1
- package/dist/BotApiWebhook.js +0 -43
- package/dist/Chat.d.ts +0 -96
- package/dist/Chat.d.ts.map +0 -1
- package/dist/Chat.js +0 -48
- package/dist/InputFile.d.ts +0 -4
- package/dist/InputFile.d.ts.map +0 -1
- package/dist/InputFile.js +0 -3
- package/dist/internal/botApiMethod.d.ts +0 -8
- package/dist/internal/botApiMethod.d.ts.map +0 -1
- package/dist/internal/botApiMethod.js +0 -1
- package/dist/internal/chat.d.ts +0 -14
- package/dist/internal/chat.d.ts.map +0 -1
- package/dist/internal/chat.js +0 -20
package/src/Bot.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type * as Effect from 'effect/Effect'
|
|
2
|
+
import type * as BotApi from './BotApi.ts'
|
|
3
|
+
import * as Context from 'effect/Context'
|
|
4
|
+
|
|
5
|
+
export type Bot<E = never, R = never> = Effect.Effect<void, E, R | Update>
|
|
6
|
+
|
|
7
|
+
export class Update extends Context.Tag('@grom.js/effect-tg/Bot/Update')<
|
|
8
|
+
Update,
|
|
9
|
+
BotApi.Types.Update
|
|
10
|
+
>() {}
|
|
11
|
+
|
|
12
|
+
export interface Middleware {
|
|
13
|
+
<E, R>(self: Bot<E, R>): Bot<any, any>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const middleware: <M extends Middleware>(middleware: M) => M = mw => mw
|
package/src/BotApi.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { MethodParams, MethodResults } from './internal/botApiMethods.gen.ts'
|
|
2
|
+
import type { BotApiShape } from './internal/botApiShape.gen.ts'
|
|
3
|
+
import type * as Types from './internal/botApiTypes.gen.ts'
|
|
4
|
+
import * as Context from 'effect/Context'
|
|
5
|
+
import * as Data from 'effect/Data'
|
|
6
|
+
import * as Effect from 'effect/Effect'
|
|
7
|
+
import * as Layer from 'effect/Layer'
|
|
8
|
+
import * as BotApiTransport from './BotApiTransport.ts'
|
|
9
|
+
import * as internal from './internal/botApi.ts'
|
|
10
|
+
|
|
11
|
+
export type { MethodParams, MethodResults, Types }
|
|
12
|
+
|
|
13
|
+
export class BotApi extends Context.Tag('@grom.js/effect-tg/BotApi')<
|
|
14
|
+
BotApi,
|
|
15
|
+
BotApi.Service
|
|
16
|
+
>() {}
|
|
17
|
+
|
|
18
|
+
export declare namespace BotApi {
|
|
19
|
+
export type Service = BotApiShape
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface Method<
|
|
23
|
+
M extends keyof MethodParams,
|
|
24
|
+
E = BotApiError | BotApiTransport.BotApiTransportError,
|
|
25
|
+
R = never,
|
|
26
|
+
> {
|
|
27
|
+
(params: MethodParams[M]): Effect.Effect<MethodResults[M], E, R>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Error returned from the Bot API server in case of unsuccessful method call.
|
|
32
|
+
*/
|
|
33
|
+
export class BotApiError extends Data.TaggedError('@grom.js/effect-tg/BotApi/BotApiError')<{
|
|
34
|
+
code: number
|
|
35
|
+
description: string
|
|
36
|
+
parameters?: Types.ResponseParameters
|
|
37
|
+
}> {
|
|
38
|
+
override get message() {
|
|
39
|
+
return `(${this.code}) ${this.description}`
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const make: (
|
|
44
|
+
transport: BotApiTransport.BotApiTransport.Service,
|
|
45
|
+
) => BotApiShape = internal.make
|
|
46
|
+
|
|
47
|
+
export const layer: Layer.Layer<
|
|
48
|
+
BotApi,
|
|
49
|
+
never,
|
|
50
|
+
BotApiTransport.BotApiTransport
|
|
51
|
+
> = Layer.effect(
|
|
52
|
+
BotApi,
|
|
53
|
+
Effect.andThen(BotApiTransport.BotApiTransport, internal.make),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
export const callMethod: <M extends keyof MethodParams>(
|
|
57
|
+
method: M,
|
|
58
|
+
params: MethodParams[M],
|
|
59
|
+
) => Effect.Effect<
|
|
60
|
+
MethodResults[M],
|
|
61
|
+
BotApiError | BotApiTransport.BotApiTransportError,
|
|
62
|
+
BotApi
|
|
63
|
+
> = (
|
|
64
|
+
method: string,
|
|
65
|
+
params: unknown = undefined,
|
|
66
|
+
) => BotApi.pipe(
|
|
67
|
+
Effect.flatMap((api: any) => api[method](params)),
|
|
68
|
+
) as any
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type * as Types from './internal/botApiTypes.gen.ts'
|
|
2
|
+
import * as HttpClient from '@effect/platform/HttpClient'
|
|
3
|
+
import * as Context from 'effect/Context'
|
|
4
|
+
import * as Data from 'effect/Data'
|
|
5
|
+
import * as Effect from 'effect/Effect'
|
|
6
|
+
import * as Layer from 'effect/Layer'
|
|
7
|
+
import * as BotApiUrl from './BotApiUrl.ts'
|
|
8
|
+
import * as internal from './internal/botApiTransport.ts'
|
|
9
|
+
|
|
10
|
+
export class BotApiTransport extends Context.Tag('@grom.js/effect-tg/BotApiTransport')<
|
|
11
|
+
BotApiTransport,
|
|
12
|
+
BotApiTransport.Service
|
|
13
|
+
>() {}
|
|
14
|
+
|
|
15
|
+
export declare namespace BotApiTransport {
|
|
16
|
+
export interface Service {
|
|
17
|
+
sendRequest: (
|
|
18
|
+
method: string,
|
|
19
|
+
params: unknown,
|
|
20
|
+
) => Effect.Effect<BotApiResponse, BotApiTransportError>
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @see https://core.telegram.org/bots/api#making-requests
|
|
26
|
+
*/
|
|
27
|
+
export type BotApiResponse
|
|
28
|
+
= {
|
|
29
|
+
ok: true
|
|
30
|
+
result: unknown
|
|
31
|
+
description?: string
|
|
32
|
+
} | {
|
|
33
|
+
ok: false
|
|
34
|
+
error_code: number
|
|
35
|
+
description: string
|
|
36
|
+
parameters?: Types.ResponseParameters
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Error caused by the transport when accessing Bot API.
|
|
41
|
+
*/
|
|
42
|
+
export class BotApiTransportError extends Data.TaggedError('@grom.js/effect-tg/BotApiTransport/BotApiTransportError')<{
|
|
43
|
+
cause: unknown
|
|
44
|
+
}> {}
|
|
45
|
+
|
|
46
|
+
export const layer: Layer.Layer<
|
|
47
|
+
BotApiTransport,
|
|
48
|
+
never,
|
|
49
|
+
HttpClient.HttpClient | BotApiUrl.BotApiUrl
|
|
50
|
+
> = Layer.effect(
|
|
51
|
+
BotApiTransport,
|
|
52
|
+
Effect.all([HttpClient.HttpClient, BotApiUrl.BotApiUrl]).pipe(
|
|
53
|
+
Effect.andThen(reqs => internal.make(...reqs)),
|
|
54
|
+
),
|
|
55
|
+
)
|
package/src/BotApiUrl.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as Context from 'effect/Context'
|
|
2
|
+
|
|
3
|
+
export class BotApiUrl extends Context.Tag('@grom.js/effect-tg/BotApiUrl')<
|
|
4
|
+
BotApiUrl,
|
|
5
|
+
BotApiUrl.Service
|
|
6
|
+
>() {}
|
|
7
|
+
|
|
8
|
+
export declare namespace BotApiUrl {
|
|
9
|
+
export interface Service {
|
|
10
|
+
toMethod: (method: string) => URL
|
|
11
|
+
toFile: (filePath: string) => URL
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const makeProd = (token: string): BotApiUrl.Service => (
|
|
16
|
+
{
|
|
17
|
+
toMethod: (method: string) => new URL(`https://api.telegram.org/bot${token}/${method}`),
|
|
18
|
+
toFile: (filePath: string) => new URL(`https://api.telegram.org/file/bot${token}/${filePath}`),
|
|
19
|
+
}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export const makeTest = (token: string): BotApiUrl.Service => (
|
|
23
|
+
{
|
|
24
|
+
toMethod: (method: string) => new URL(`https://api.telegram.org/bot${token}/test/${method}`),
|
|
25
|
+
// TODO: make sure this works in test environment
|
|
26
|
+
toFile: (filePath: string) => new URL(`https://api.telegram.org/file/bot${token}/${filePath}`),
|
|
27
|
+
}
|
|
28
|
+
)
|
package/src/Content.ts
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import type * as Duration from 'effect/Duration'
|
|
2
|
+
import type * as File from './File.ts'
|
|
3
|
+
import type * as LinkPreview from './LinkPreview.ts'
|
|
4
|
+
import type * as Text_ from './Text.ts'
|
|
5
|
+
import * as Data from 'effect/Data'
|
|
6
|
+
import * as Option from 'effect/Option'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Content of a message to be sent.
|
|
10
|
+
*
|
|
11
|
+
* @todo Paid media
|
|
12
|
+
* @todo Invoices (fiat & stars)
|
|
13
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1_input_message_content.html TDLib • td_api.InputMessageContent}
|
|
14
|
+
*/
|
|
15
|
+
export type Content
|
|
16
|
+
= | Text
|
|
17
|
+
| Photo
|
|
18
|
+
| Audio
|
|
19
|
+
| Document
|
|
20
|
+
| Video
|
|
21
|
+
| Animation
|
|
22
|
+
| Voice
|
|
23
|
+
| VideoNote
|
|
24
|
+
| Location
|
|
25
|
+
| Venue
|
|
26
|
+
| Contact
|
|
27
|
+
| Dice
|
|
28
|
+
| Sticker
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Content of a text message.
|
|
32
|
+
*
|
|
33
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_text.html TDLib • td_api.inputMessageText}
|
|
34
|
+
* @see {@link https://core.telegram.org/bots/api#sendmessage Bot API • sendMessage}
|
|
35
|
+
*/
|
|
36
|
+
export class Text extends Data.TaggedClass('Text')<{
|
|
37
|
+
text: Text_.Text
|
|
38
|
+
linkPreview: Option.Option<LinkPreview.LinkPreview>
|
|
39
|
+
}> {}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Content of a photo message.
|
|
43
|
+
*
|
|
44
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_photo.html TDLib • td_api.inputMessagePhoto}
|
|
45
|
+
* @see {@link https://core.telegram.org/bots/api#sendphoto Bot API • sendPhoto}
|
|
46
|
+
*/
|
|
47
|
+
export class Photo extends Data.TaggedClass('Photo')<{
|
|
48
|
+
file: File.FileId | File.External | File.InputFile
|
|
49
|
+
caption: Option.Option<Text_.Text>
|
|
50
|
+
layout: 'caption-above' | 'caption-below'
|
|
51
|
+
spoiler: boolean
|
|
52
|
+
}> {}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Content of an audio message.
|
|
56
|
+
*
|
|
57
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_audio.html TDLib • td_api.inputMessageAudio}
|
|
58
|
+
* @see {@link https://core.telegram.org/bots/api#sendaudio Bot API • sendAudio}
|
|
59
|
+
*/
|
|
60
|
+
export class Audio extends Data.TaggedClass('Audio')<{
|
|
61
|
+
file: File.FileId | File.External | File.InputFile
|
|
62
|
+
caption: Option.Option<Text_.Text>
|
|
63
|
+
duration: Option.Option<Duration.Duration>
|
|
64
|
+
performer: Option.Option<string>
|
|
65
|
+
title: Option.Option<string>
|
|
66
|
+
thumbnail: Option.Option<File.InputFile>
|
|
67
|
+
}> {}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Content of a document message.
|
|
71
|
+
*
|
|
72
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_document.html TDLib • td_api.inputMessageDocument}
|
|
73
|
+
* @see {@link https://core.telegram.org/bots/api#senddocument Bot API • sendDocument}
|
|
74
|
+
*/
|
|
75
|
+
export class Document extends Data.TaggedClass('Document')<{
|
|
76
|
+
file: File.FileId | File.External | File.InputFile
|
|
77
|
+
caption: Option.Option<Text_.Text>
|
|
78
|
+
thumbnail: Option.Option<File.InputFile>
|
|
79
|
+
contentTypeDetection: boolean
|
|
80
|
+
}> {}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Content of a video message.
|
|
84
|
+
*
|
|
85
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_video.html TDLib • td_api.inputMessageVideo}
|
|
86
|
+
* @see {@link https://core.telegram.org/bots/api#sendvideo Bot API • sendVideo}
|
|
87
|
+
*/
|
|
88
|
+
export class Video extends Data.TaggedClass('Video')<{
|
|
89
|
+
file: File.FileId | File.External | File.InputFile
|
|
90
|
+
caption: Option.Option<Text_.Text>
|
|
91
|
+
layout: 'caption-above' | 'caption-below'
|
|
92
|
+
spoiler: boolean
|
|
93
|
+
duration: Option.Option<Duration.Duration>
|
|
94
|
+
width: Option.Option<number>
|
|
95
|
+
height: Option.Option<number>
|
|
96
|
+
thumbnail: Option.Option<File.InputFile>
|
|
97
|
+
cover: Option.Option<File.FileId | File.External | File.InputFile>
|
|
98
|
+
startAt: Option.Option<Duration.Duration>
|
|
99
|
+
supportsStreaming: boolean
|
|
100
|
+
}> {}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Content of an animation message (GIF or video without sound).
|
|
104
|
+
*
|
|
105
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_animation.html TDLib • td_api.inputMessageAnimation}
|
|
106
|
+
* @see {@link https://core.telegram.org/bots/api#sendanimation Bot API • sendAnimation}
|
|
107
|
+
*/
|
|
108
|
+
export class Animation extends Data.TaggedClass('Animation')<{
|
|
109
|
+
file: File.FileId | File.External | File.InputFile
|
|
110
|
+
caption: Option.Option<Text_.Text>
|
|
111
|
+
layout: 'caption-above' | 'caption-below'
|
|
112
|
+
spoiler: boolean
|
|
113
|
+
duration: Option.Option<Duration.Duration>
|
|
114
|
+
width: Option.Option<number>
|
|
115
|
+
height: Option.Option<number>
|
|
116
|
+
thumbnail: Option.Option<File.InputFile>
|
|
117
|
+
}> {}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Content of a voice note message.
|
|
121
|
+
*
|
|
122
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_voice_note.html TDLib • td_api.inputMessageVoiceNote}
|
|
123
|
+
* @see {@link https://core.telegram.org/bots/api#sendvoice Bot API • sendVoice}
|
|
124
|
+
*/
|
|
125
|
+
export class Voice extends Data.TaggedClass('Voice')<{
|
|
126
|
+
file: File.FileId | File.External | File.InputFile
|
|
127
|
+
caption: Option.Option<Text_.Text>
|
|
128
|
+
duration: Option.Option<Duration.Duration>
|
|
129
|
+
}> {}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Content of a video note message (round video).
|
|
133
|
+
*
|
|
134
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_video_note.html TDLib • td_api.inputMessageVideoNote}
|
|
135
|
+
* @see {@link https://core.telegram.org/bots/api#sendvideonote Bot API • sendVideoNote}
|
|
136
|
+
*/
|
|
137
|
+
export class VideoNote extends Data.TaggedClass('VideoNote')<{
|
|
138
|
+
file: File.FileId | File.InputFile
|
|
139
|
+
duration: Option.Option<Duration.Duration>
|
|
140
|
+
diameter: Option.Option<number>
|
|
141
|
+
thumbnail: Option.Option<File.InputFile>
|
|
142
|
+
}> {}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Content of a location message.
|
|
146
|
+
*
|
|
147
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_location.html TDLib • td_api.inputMessageLocation}
|
|
148
|
+
* @see {@link https://core.telegram.org/bots/api#sendlocation Bot API • sendLocation}
|
|
149
|
+
*/
|
|
150
|
+
export class Location extends Data.TaggedClass('Location')<{
|
|
151
|
+
latitude: number
|
|
152
|
+
longitude: number
|
|
153
|
+
uncertaintyRadius: Option.Option<number>
|
|
154
|
+
livePeriod: Option.Option<Duration.Duration>
|
|
155
|
+
heading: Option.Option<number>
|
|
156
|
+
proximityAlertRadius: Option.Option<number>
|
|
157
|
+
}> {}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Content of a venue message.
|
|
161
|
+
*
|
|
162
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_venue.html TDLib • td_api.inputMessageVenue}
|
|
163
|
+
* @see {@link https://core.telegram.org/bots/api#sendvenue Bot API • sendVenue}
|
|
164
|
+
*/
|
|
165
|
+
export class Venue extends Data.TaggedClass('Venue')<{
|
|
166
|
+
latitude: number
|
|
167
|
+
longitude: number
|
|
168
|
+
title: string
|
|
169
|
+
address: string
|
|
170
|
+
foursquareId: Option.Option<string>
|
|
171
|
+
foursquareType: Option.Option<string>
|
|
172
|
+
googlePlaceId: Option.Option<string>
|
|
173
|
+
googlePlaceType: Option.Option<string>
|
|
174
|
+
}> {}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Content of a contact message.
|
|
178
|
+
*
|
|
179
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_contact.html TDLib • td_api.inputMessageContact}
|
|
180
|
+
* @see {@link https://core.telegram.org/bots/api#sendcontact Bot API • sendContact}
|
|
181
|
+
*/
|
|
182
|
+
export class Contact extends Data.TaggedClass('Contact')<{
|
|
183
|
+
phoneNumber: string
|
|
184
|
+
firstName: string
|
|
185
|
+
lastName: Option.Option<string>
|
|
186
|
+
vcard: Option.Option<string>
|
|
187
|
+
}> {}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Content of a dice message.
|
|
191
|
+
*
|
|
192
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_dice.html TDLib • td_api.inputMessageDice}
|
|
193
|
+
* @see {@link https://core.telegram.org/bots/api#senddice Bot API • sendDice}
|
|
194
|
+
*/
|
|
195
|
+
export class Dice extends Data.TaggedClass('Dice')<{
|
|
196
|
+
emoji: '🎲' | '🎯' | '🏀' | '⚽' | '🎳' | '🎰'
|
|
197
|
+
}> {}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Content of a sticker message.
|
|
201
|
+
*
|
|
202
|
+
* @see {@link https://core.telegram.org/tdlib/docs/classtd_1_1td__api_1_1input_message_sticker.html TDLib • td_api.inputMessageSticker}
|
|
203
|
+
* @see {@link https://core.telegram.org/bots/api#sendsticker Bot API • sendSticker}
|
|
204
|
+
*/
|
|
205
|
+
export class Sticker extends Data.TaggedClass('Sticker')<{
|
|
206
|
+
file: File.FileId | File.External | File.InputFile
|
|
207
|
+
emoji: Option.Option<string>
|
|
208
|
+
}> {}
|
|
209
|
+
|
|
210
|
+
// ——— Constructors ——————————————————————————————————————————————————————————
|
|
211
|
+
|
|
212
|
+
export const text = (
|
|
213
|
+
text: Text_.Text,
|
|
214
|
+
options?: {
|
|
215
|
+
linkPreview?: LinkPreview.LinkPreview
|
|
216
|
+
},
|
|
217
|
+
) => new Text({
|
|
218
|
+
text,
|
|
219
|
+
linkPreview: Option.fromNullable(options?.linkPreview),
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
export const photo = (
|
|
223
|
+
file: File.FileId | File.External | File.InputFile,
|
|
224
|
+
options: {
|
|
225
|
+
caption?: Text_.Text
|
|
226
|
+
layout?: 'caption-above' | 'caption-below'
|
|
227
|
+
spoiler?: boolean
|
|
228
|
+
},
|
|
229
|
+
): Photo => new Photo({
|
|
230
|
+
file,
|
|
231
|
+
caption: Option.fromNullable(options.caption),
|
|
232
|
+
layout: options.layout ?? 'caption-below',
|
|
233
|
+
spoiler: options.spoiler ?? false,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
export const audio = (
|
|
237
|
+
file: File.FileId | File.External | File.InputFile,
|
|
238
|
+
options: {
|
|
239
|
+
caption?: Text_.Text
|
|
240
|
+
duration?: Duration.Duration
|
|
241
|
+
performer?: string
|
|
242
|
+
title?: string
|
|
243
|
+
thumbnail?: File.InputFile
|
|
244
|
+
},
|
|
245
|
+
): Audio => new Audio({
|
|
246
|
+
file,
|
|
247
|
+
caption: Option.fromNullable(options.caption),
|
|
248
|
+
duration: Option.fromNullable(options.duration),
|
|
249
|
+
performer: Option.fromNullable(options.performer),
|
|
250
|
+
title: Option.fromNullable(options.title),
|
|
251
|
+
thumbnail: Option.fromNullable(options.thumbnail),
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
export const document = (
|
|
255
|
+
file: File.FileId | File.External | File.InputFile,
|
|
256
|
+
options: {
|
|
257
|
+
caption?: Text_.Text
|
|
258
|
+
thumbnail?: File.InputFile
|
|
259
|
+
contentTypeDetection?: boolean
|
|
260
|
+
},
|
|
261
|
+
): Document => new Document({
|
|
262
|
+
file,
|
|
263
|
+
caption: Option.fromNullable(options.caption),
|
|
264
|
+
thumbnail: Option.fromNullable(options.thumbnail),
|
|
265
|
+
contentTypeDetection: options.contentTypeDetection ?? false,
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
export const video = (
|
|
269
|
+
file: File.FileId | File.External | File.InputFile,
|
|
270
|
+
options: {
|
|
271
|
+
caption?: Text_.Text
|
|
272
|
+
layout?: 'caption-above' | 'caption-below'
|
|
273
|
+
spoiler?: boolean
|
|
274
|
+
duration?: Duration.Duration
|
|
275
|
+
width?: number
|
|
276
|
+
height?: number
|
|
277
|
+
thumbnail?: File.InputFile
|
|
278
|
+
cover?: File.FileId | File.External | File.InputFile
|
|
279
|
+
startAt?: Duration.Duration
|
|
280
|
+
supportsStreaming?: boolean
|
|
281
|
+
},
|
|
282
|
+
): Video => new Video({
|
|
283
|
+
file,
|
|
284
|
+
caption: Option.fromNullable(options.caption),
|
|
285
|
+
layout: options.layout ?? 'caption-below',
|
|
286
|
+
spoiler: options.spoiler ?? false,
|
|
287
|
+
duration: Option.fromNullable(options.duration),
|
|
288
|
+
width: Option.fromNullable(options.width),
|
|
289
|
+
height: Option.fromNullable(options.height),
|
|
290
|
+
thumbnail: Option.fromNullable(options.thumbnail),
|
|
291
|
+
cover: Option.fromNullable(options.cover),
|
|
292
|
+
startAt: Option.fromNullable(options.startAt),
|
|
293
|
+
supportsStreaming: options.supportsStreaming ?? false,
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
export const animation = (
|
|
297
|
+
file: File.FileId | File.External | File.InputFile,
|
|
298
|
+
options: {
|
|
299
|
+
caption?: Text_.Text
|
|
300
|
+
layout?: 'caption-above' | 'caption-below'
|
|
301
|
+
spoiler?: boolean
|
|
302
|
+
duration?: Duration.Duration
|
|
303
|
+
width?: number
|
|
304
|
+
height?: number
|
|
305
|
+
thumbnail?: File.InputFile
|
|
306
|
+
},
|
|
307
|
+
): Animation => new Animation({
|
|
308
|
+
file,
|
|
309
|
+
caption: Option.fromNullable(options.caption),
|
|
310
|
+
layout: options.layout ?? 'caption-below',
|
|
311
|
+
spoiler: options.spoiler ?? false,
|
|
312
|
+
duration: Option.fromNullable(options.duration),
|
|
313
|
+
width: Option.fromNullable(options.width),
|
|
314
|
+
height: Option.fromNullable(options.height),
|
|
315
|
+
thumbnail: Option.fromNullable(options.thumbnail),
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
export const voice = (
|
|
319
|
+
file: File.FileId | File.External | File.InputFile,
|
|
320
|
+
options: {
|
|
321
|
+
caption?: Text_.Text
|
|
322
|
+
duration?: Duration.Duration
|
|
323
|
+
},
|
|
324
|
+
): Voice => new Voice({
|
|
325
|
+
file,
|
|
326
|
+
caption: Option.fromNullable(options.caption),
|
|
327
|
+
duration: Option.fromNullable(options.duration),
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
export const videoNote = (
|
|
331
|
+
file: File.FileId | File.InputFile,
|
|
332
|
+
options: {
|
|
333
|
+
duration?: Duration.Duration
|
|
334
|
+
diameter?: number
|
|
335
|
+
thumbnail?: File.InputFile
|
|
336
|
+
},
|
|
337
|
+
): VideoNote => new VideoNote({
|
|
338
|
+
file,
|
|
339
|
+
duration: Option.fromNullable(options.duration),
|
|
340
|
+
diameter: Option.fromNullable(options.diameter),
|
|
341
|
+
thumbnail: Option.fromNullable(options.thumbnail),
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
export const location = (options: {
|
|
345
|
+
latitude: number
|
|
346
|
+
longitude: number
|
|
347
|
+
uncertaintyRadius?: number
|
|
348
|
+
}): Location => new Location({
|
|
349
|
+
latitude: options.latitude,
|
|
350
|
+
longitude: options.longitude,
|
|
351
|
+
uncertaintyRadius: Option.fromNullable(options.uncertaintyRadius),
|
|
352
|
+
livePeriod: Option.none(),
|
|
353
|
+
heading: Option.none(),
|
|
354
|
+
proximityAlertRadius: Option.none(),
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
export const liveLocation = (options: {
|
|
358
|
+
latitude: number
|
|
359
|
+
longitude: number
|
|
360
|
+
uncertaintyRadius?: number
|
|
361
|
+
livePeriod: Duration.Duration
|
|
362
|
+
heading?: number
|
|
363
|
+
proximityAlertRadius?: number
|
|
364
|
+
}): Location => new Location({
|
|
365
|
+
latitude: options.latitude,
|
|
366
|
+
longitude: options.longitude,
|
|
367
|
+
uncertaintyRadius: Option.fromNullable(options.uncertaintyRadius),
|
|
368
|
+
livePeriod: Option.some(options.livePeriod),
|
|
369
|
+
heading: Option.fromNullable(options.heading),
|
|
370
|
+
proximityAlertRadius: Option.fromNullable(options.proximityAlertRadius),
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
export const venue = (options: {
|
|
374
|
+
latitude: number
|
|
375
|
+
longitude: number
|
|
376
|
+
title: string
|
|
377
|
+
address: string
|
|
378
|
+
foursquareId?: string
|
|
379
|
+
foursquareType?: string
|
|
380
|
+
googlePlaceId?: string
|
|
381
|
+
googlePlaceType?: string
|
|
382
|
+
}): Venue => new Venue({
|
|
383
|
+
latitude: options.latitude,
|
|
384
|
+
longitude: options.longitude,
|
|
385
|
+
title: options.title,
|
|
386
|
+
address: options.address,
|
|
387
|
+
foursquareId: Option.fromNullable(options.foursquareId),
|
|
388
|
+
foursquareType: Option.fromNullable(options.foursquareType),
|
|
389
|
+
googlePlaceId: Option.fromNullable(options.googlePlaceId),
|
|
390
|
+
googlePlaceType: Option.fromNullable(options.googlePlaceType),
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
export const contact = (options: {
|
|
394
|
+
phoneNumber: string
|
|
395
|
+
firstName: string
|
|
396
|
+
lastName?: string
|
|
397
|
+
vcard?: string
|
|
398
|
+
}): Contact => new Contact({
|
|
399
|
+
phoneNumber: options.phoneNumber,
|
|
400
|
+
firstName: options.firstName,
|
|
401
|
+
lastName: Option.fromNullable(options.lastName),
|
|
402
|
+
vcard: Option.fromNullable(options.vcard),
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
export const dice = (emoji: Dice['emoji']): Dice => new Dice({ emoji })
|
|
406
|
+
|
|
407
|
+
export const sticker = (
|
|
408
|
+
file: File.FileId | File.External | File.InputFile,
|
|
409
|
+
emoji?: string,
|
|
410
|
+
): Sticker => new Sticker({ file, emoji: Option.fromNullable(emoji) })
|
package/src/Dialog.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as Data from 'effect/Data'
|
|
2
|
+
import * as internal from './internal/dialog.ts'
|
|
3
|
+
|
|
4
|
+
export type Dialog
|
|
5
|
+
= | UserId
|
|
6
|
+
| GroupId
|
|
7
|
+
| ChannelId
|
|
8
|
+
| SupergroupId
|
|
9
|
+
| PublicChannel
|
|
10
|
+
| PublicSupergroup
|
|
11
|
+
| ForumTopic
|
|
12
|
+
| ChannelDm
|
|
13
|
+
|
|
14
|
+
export class UserId extends internal.PeerId({
|
|
15
|
+
tag: 'UserId',
|
|
16
|
+
isValid: id => (id >= 1 && id <= 0xFFFFFFFFFF),
|
|
17
|
+
toDialogId: id => id,
|
|
18
|
+
}) {}
|
|
19
|
+
|
|
20
|
+
export class GroupId extends internal.PeerId({
|
|
21
|
+
tag: 'GroupId',
|
|
22
|
+
isValid: id => (id >= 1 && id <= 999999999999),
|
|
23
|
+
toDialogId: id => -id,
|
|
24
|
+
}) {}
|
|
25
|
+
|
|
26
|
+
export class ChannelId extends internal.PeerId({
|
|
27
|
+
tag: 'ChannelId',
|
|
28
|
+
isValid: id => (id >= 1 && id <= 997852516352),
|
|
29
|
+
toDialogId: id => -(1000000000000 + id),
|
|
30
|
+
}) {}
|
|
31
|
+
|
|
32
|
+
export class SupergroupId extends internal.PeerId({
|
|
33
|
+
tag: 'SupergroupId',
|
|
34
|
+
isValid: id => (id >= 1 && id <= 997852516352),
|
|
35
|
+
toDialogId: id => -(1000000000000 + id),
|
|
36
|
+
}) {}
|
|
37
|
+
|
|
38
|
+
export class PublicChannel extends Data.TaggedClass('PublicChannel')<{
|
|
39
|
+
username: string
|
|
40
|
+
}> {}
|
|
41
|
+
|
|
42
|
+
export class PublicSupergroup extends Data.TaggedClass('PublicSupergroup')<{
|
|
43
|
+
username: string
|
|
44
|
+
}> {}
|
|
45
|
+
|
|
46
|
+
export class ForumTopic extends Data.TaggedClass('ForumTopic')<{
|
|
47
|
+
forum: SupergroupId | PublicSupergroup
|
|
48
|
+
topicId: number
|
|
49
|
+
}> {}
|
|
50
|
+
|
|
51
|
+
export class ChannelDm extends Data.TaggedClass('ChannelDm')<{
|
|
52
|
+
channel: ChannelId | PublicChannel
|
|
53
|
+
userId: number
|
|
54
|
+
}> {}
|
package/src/File.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type * as Stream from 'effect/Stream'
|
|
2
|
+
import * as HttpClient from '@effect/platform/HttpClient'
|
|
3
|
+
import * as Brand from 'effect/Brand'
|
|
4
|
+
import * as Data from 'effect/Data'
|
|
5
|
+
import * as Effect from 'effect/Effect'
|
|
6
|
+
import * as BotApi from './BotApi.ts'
|
|
7
|
+
import * as BotApiUrl from './BotApiUrl.ts'
|
|
8
|
+
|
|
9
|
+
export type FileId = string & Brand.Brand<'FileId'>
|
|
10
|
+
export const FileId = Brand.nominal<FileId>()
|
|
11
|
+
|
|
12
|
+
export type External = URL & Brand.Brand<'External'>
|
|
13
|
+
export const External = Brand.nominal<External>()
|
|
14
|
+
|
|
15
|
+
export class InputFile extends Data.TaggedClass('InputFile')<{
|
|
16
|
+
stream: Stream.Stream<Uint8Array>
|
|
17
|
+
filename: string
|
|
18
|
+
mimeType?: string
|
|
19
|
+
}> {}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
const downloadRequest = Effect.fnUntraced(
|
|
25
|
+
function* (fileId: FileId) {
|
|
26
|
+
const file = yield* BotApi.callMethod('getFile', { file_id: fileId })
|
|
27
|
+
if (file.file_path == null) {
|
|
28
|
+
return yield* Effect.die(new Error(`Bot API returned no file path for file "${fileId}".`))
|
|
29
|
+
}
|
|
30
|
+
const url = yield* BotApiUrl.BotApiUrl
|
|
31
|
+
return yield* HttpClient.get(url.toFile(file.file_path))
|
|
32
|
+
},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
export const download = (fileId: FileId) => (
|
|
36
|
+
downloadRequest(fileId).pipe(
|
|
37
|
+
Effect.flatMap(request => request.arrayBuffer),
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export const downloadStream = (fileId: FileId) => (
|
|
42
|
+
downloadRequest(fileId).pipe(
|
|
43
|
+
Effect.andThen(request => request.stream),
|
|
44
|
+
)
|
|
45
|
+
)
|