Skip to content

Commit aa6618d

Browse files
committed
wip
1 parent dda4bf0 commit aa6618d

1 file changed

Lines changed: 53 additions & 41 deletions

File tree

README.md

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ Bot API exposes multiple methods for sending a message, each corresponding to a
193193
To send a message, you need:
194194
195195
- **Content** — content of the message to be sent.
196-
- **Dialog** — target chat and thread where the message will be sent.
196+
- **Dialog** — target chat and topic where the message will be sent.
197197
- **Markup** — (optional) markup for replying to the message.
198198
- **Reply** — (optional) information about the message being replied to.
199199
- **Options** — (optional) additional options for sending the message.
@@ -203,35 +203,7 @@ To send a message, you need:
203203
**Example:** Sending messages using `Send.sendMessage`.
204204
205205
```ts
206-
import { Content, Dialog, File, Reply, Send, Text } from '@grom.js/effect-tg'
207-
import { Effect } from 'effect'
208-
209-
const program = Effect.gen(function* () {
210-
// Send a text message
211-
yield* Send.sendMessage({
212-
content: Content.text(Text.plain('Hello!')),
213-
dialog: Dialog.user(123456789),
214-
})
215-
216-
// Send a photo with caption to a forum topic
217-
yield* Send.sendMessage({
218-
content: Content.photo(File.External(new URL('https://example.com/image.jpg')), {
219-
caption: Text.plain('Check this out!'),
220-
}),
221-
dialog: Dialog.supergroup(987654321).topic(42),
222-
})
223-
224-
// Reply to a message
225-
const sent = yield* Send.sendMessage({
226-
content: Content.dice('🎲'),
227-
dialog: Dialog.user(123456789),
228-
})
229-
yield* Send.sendMessage({
230-
content: Content.text(Text.plain('Good luck!')),
231-
dialog: Dialog.user(123456789),
232-
reply: Reply.toMessage(sent),
233-
})
234-
})
206+
// TODO: variety of examples
235207
```
236208
237209
#### Content
@@ -257,21 +229,29 @@ const program = Effect.gen(function* () {
257229
258230
#### Dialog
259231
260-
`Dialog` module provides constructors for all target types:
232+
`Dialog` module provides constructors for all target chats:
261233
262234
- `Dialog.user(id)` — private chat with a user.
263235
- `Dialog.group(id)` — group chat.
264236
- `Dialog.supergroup(id)` — supergroup chat.
265237
- `Dialog.channel(id)` — channel.
266238
267-
To target a specific thread or topic, chain a method on the peer:
239+
To target a specific topic, chain a method on the peer:
268240
269241
- `Dialog.user(id).topic(topicId)` — topic in a private chat.
270242
- `Dialog.supergroup(id).topic(topicId)` — topic in a forum supergroup.
271243
- `Dialog.channel(id).directMessages(topicId)` — channel direct messages.
272244
273245
`Dialog.ofMessage` extracts the dialog from an incoming `Message` object.
274246
247+
**Dialog ID vs peer ID**
248+
249+
Bot API uses a single integer (`chat_id`) that [encodes both peer type and ID](https://core.telegram.org/api/bots/ids): user 1:1; group = `-id`; supergroup/channel = `-(id + 1000000000000)`. Some responses return dialog IDs, others peer IDs — wrong format causes errors.
250+
251+
**Branded types**
252+
253+
`UserId`, `GroupId`, `SupergroupId`, `ChannelId`, `DialogId` prevent mixing. `SupergroupId` and `ChannelId` are the same type (supergroups are a special kind of channel; both share the same ID space). Use `Dialog.decodePeerId`, `Dialog.encodePeerId`, `Dialog.decodeDialogId` to convert.
254+
275255
#### Reply
276256
277257
`Reply` module provides two ways to create a reply reference:
@@ -281,35 +261,67 @@ To target a specific thread or topic, chain a method on the peer:
281261
282262
Both accept an optional `optional` flag — when `true`, the message will be sent even if the referenced message is not found.
283263
264+
#### Markup
265+
266+
`Markup` module provides reply markup types and constructors:
267+
268+
- `inlineKeyboard(rows)` — buttons attached to the message (callback, URL, web app, etc.).
269+
- `replyKeyboard(rows, options?)` — custom keyboard replacing the default; options include `oneTime`, `resizable`, `selective`, `inputPlaceholder`.
270+
- `replyKeyboardRemove` — hide a reply keyboard.
271+
- `forceReply` — show a reply input field.
272+
273+
Use `InlineButton` and `ReplyButton` builders to create button rows. Example:
274+
275+
```ts
276+
import { InlineButton, inlineKeyboard, ReplyButton, replyKeyboard } from '@grom.js/effect-tg'
277+
278+
// Inline keyboard: URL and callback buttons
279+
const inline = inlineKeyboard([
280+
[InlineButton.url('Open', 'https://example.com'), InlineButton.callback('Tap me', 'action_1')],
281+
])
282+
283+
// Reply keyboard: simple buttons
284+
const reply = replyKeyboard([
285+
['Option A', 'Option B'],
286+
[ReplyButton.requestContact('Share phone')],
287+
], { oneTime: true })
288+
```
289+
284290
### Prepared messages
285291

286-
`Send.message` creates a reusable `MessageToSend` that bundles content, markup, reply, and options — everything except the target dialog. This lets you define a message template once and send it to different dialogs later.
292+
`Send.message` creates a `MessageToSend` — a reusable Effect that bundles content, markup, reply, and options. It does not send until you run it.
293+
294+
Flow:
287295

288-
`MessageToSend` is pipeable: you can chain modifiers on it and provide the target dialog with `Send.to`.
296+
1. `Send.message(content)` creates a `MessageToSend` (an Effect that requires `TargetDialog`).
297+
2. Chain modifiers (`Send.withMarkup`, `Send.withoutNotification`, etc.) to customize.
298+
3. `Send.to(dialog)` provides the target and returns a plain Effect; the message sends when that Effect runs (e.g. `yield*` in a generator, `Effect.runPromise`, or as part of a larger program).
289299

290300
**Example:** Creating and sending prepared messages.
291301

292302
```ts
293303
import { Content, Dialog, Send, Text } from '@grom.js/effect-tg'
294-
import { pipe } from 'effect'
304+
import { Effect, pipe } from 'effect'
295305

296-
// Define a reusable message
306+
// Reusable template
297307
const welcomeMessage = Send.message(
298308
Content.text(Text.html('<b>Welcome!</b> Thanks for joining.')),
299309
)
300310

301-
// Send to a specific user
302-
const program = welcomeMessage.pipe(
303-
Send.to(Dialog.user(123456789)),
304-
)
311+
// Send to different dialogs — runs the Effect to perform the API call
312+
const program = Effect.gen(function* () {
313+
yield* welcomeMessage.pipe(Send.to(Dialog.user(123456789)))
314+
yield* welcomeMessage.pipe(Send.to(Dialog.user(987654321)))
315+
})
305316

306-
// Send a silent, protected message
317+
// With modifiers: silent, protected
307318
const secretMessage = pipe(
308319
Send.message(Content.text(Text.plain('Secret message!'))),
309320
Send.withoutNotification,
310321
Send.withContentProtection,
311322
Send.to(Dialog.user(123456789)),
312323
)
324+
// Use yield* secretMessage or Effect.runPromise(secretMessage) to send
313325
```
314326

315327
### Composing options

0 commit comments

Comments
 (0)