Skip to content

Commit f495fee

Browse files
author
Saveliy Yudin
committed
feat: add open_app button, 5 new update types, and GoDoc field descriptions
- NewOpenAppButton(text, webApp) constructor and Button.WebApp field - BotStoppedUpdate, DialogMutedUpdate, DialogUnmutedUpdate, DialogClearedUpdate, DialogRemovedUpdate with tests - GoDoc comments on User, Chat, Message, NewMessageBody, ChatMember, Update and other core structs from dev.max.ru documentation - Update types tables in guide.md, guide-ru.md - Fix README Quick Start (add package main, func main, indentation) - Add NewOpenAppButton to README, guide.md, guide-ru.md
1 parent cb61107 commit f495fee

7 files changed

Lines changed: 327 additions & 60 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [v0.2.2] - 2026-02-15
4+
5+
### Added
6+
- **Button type `open_app`**: `NewOpenAppButton(text, webAppURL)` constructor and `Button.WebApp` field for launching mini-apps inside the messenger
7+
- **5 new update types**: `BotStoppedUpdate`, `DialogMutedUpdate`, `DialogUnmutedUpdate`, `DialogClearedUpdate`, `DialogRemovedUpdate` — with constants, Go structs, and JSON round-trip tests
8+
39
## [v0.2.1] - 2026-02-15
410

511
### Fixed

README.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,28 @@ go get github.com/maxigo-bot/maxigo-client
3535
## Quick Start
3636

3737
```go
38+
package main
39+
3840
import (
39-
"context"
40-
"fmt"
41-
"log"
41+
"context"
42+
"fmt"
43+
"log"
4244

43-
maxigo "github.com/maxigo-bot/maxigo-client"
45+
maxigo "github.com/maxigo-bot/maxigo-client"
4446
)
4547

46-
client, err := maxigo.New("YOUR_BOT_TOKEN")
47-
if err != nil {
48-
log.Fatal(err)
49-
}
50-
51-
bot, err := client.GetBot(context.Background())
52-
if err != nil {
53-
log.Fatal(err)
48+
func main() {
49+
client, err := maxigo.New("YOUR_BOT_TOKEN")
50+
if err != nil {
51+
log.Fatal(err)
52+
}
53+
54+
bot, err := client.GetBot(context.Background())
55+
if err != nil {
56+
log.Fatal(err)
57+
}
58+
fmt.Printf("Bot: %s (ID: %d)\n", bot.FirstName, bot.UserID)
5459
}
55-
fmt.Printf("Bot: %s (ID: %d)\n", bot.FirstName, bot.UserID)
5660
```
5761

5862

@@ -80,6 +84,7 @@ maxigo.NewRequestContactButton("Share contact") // request c
8084
maxigo.NewRequestGeoLocationButton("Send location", true) // request geo (quick=true)
8185
maxigo.NewChatButton("Create chat", "Title") // create chat
8286
maxigo.NewMessageButton("Send") // message from user
87+
maxigo.NewOpenAppButton("Open WebApp", "bot_username") // open mini-app
8388
```
8489

8590
**Attachments:**

docs/guide-ru.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ maxigo.NewChatButton("Создать чат", "Название чата")
156156

157157
// Сообщение — при нажатии текст кнопки отправляется в чат от имени пользователя
158158
maxigo.NewMessageButton("Записаться на приём")
159+
160+
// Мини-приложение — открывает мини-приложение внутри мессенджера
161+
maxigo.NewOpenAppButton("Открыть WebApp", "bot_username")
159162
```
160163

161164
**Пример — кнопка запроса контакта в инлайн-клавиатуре:**
@@ -383,6 +386,11 @@ for {
383386
| `UpdateUserRemoved` | `UserRemovedUpdate` | Пользователь удалён из чата |
384387
| `UpdateChatTitleChanged` | `ChatTitleChangedUpdate` | Название чата изменено |
385388
| `UpdateMessageChatCreated` | `MessageChatCreatedUpdate` | Чат создан через кнопку |
389+
| `UpdateBotStopped` | `BotStoppedUpdate` | Пользователь остановил бота |
390+
| `UpdateDialogMuted` | `DialogMutedUpdate` | Диалог замьючен |
391+
| `UpdateDialogUnmuted` | `DialogUnmutedUpdate` | Диалог размьючен |
392+
| `UpdateDialogCleared` | `DialogClearedUpdate` | История диалога очищена |
393+
| `UpdateDialogRemoved` | `DialogRemovedUpdate` | Диалог удалён |
386394

387395
## Обработка ошибок
388396

docs/guide.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,9 @@ maxigo.NewChatButton("Create chat", "Chat Title")
183183

184184
// Message button — button text is sent as a message from the user in chat
185185
maxigo.NewMessageButton("Book appointment")
186+
187+
// Open app button — opens a mini-app inside the messenger
188+
maxigo.NewOpenAppButton("Open WebApp", "bot_username")
186189
```
187190

188191
**Example — request contact button in inline keyboard:**
@@ -462,6 +465,11 @@ for {
462465
| `UpdateUserRemoved` | `UserRemovedUpdate` | User removed from chat |
463466
| `UpdateChatTitleChanged` | `ChatTitleChangedUpdate` | Chat title changed |
464467
| `UpdateMessageChatCreated` | `MessageChatCreatedUpdate` | Chat created via button |
468+
| `UpdateBotStopped` | `BotStoppedUpdate` | User stopped the bot |
469+
| `UpdateDialogMuted` | `DialogMutedUpdate` | User muted dialog |
470+
| `UpdateDialogUnmuted` | `DialogUnmutedUpdate` | User unmuted dialog |
471+
| `UpdateDialogCleared` | `DialogClearedUpdate` | User cleared dialog |
472+
| `UpdateDialogRemoved` | `DialogRemovedUpdate` | User removed dialog |
465473

466474
## Error Handling
467475

types.go

Lines changed: 143 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,17 @@ const (
8181
UpdateMessageEdited UpdateType = "message_edited"
8282
UpdateMessageRemoved UpdateType = "message_removed"
8383
UpdateBotStarted UpdateType = "bot_started"
84+
UpdateBotStopped UpdateType = "bot_stopped"
8485
UpdateBotAdded UpdateType = "bot_added"
8586
UpdateBotRemoved UpdateType = "bot_removed"
8687
UpdateUserAdded UpdateType = "user_added"
8788
UpdateUserRemoved UpdateType = "user_removed"
8889
UpdateChatTitleChanged UpdateType = "chat_title_changed"
8990
UpdateMessageChatCreated UpdateType = "message_chat_created"
91+
UpdateDialogMuted UpdateType = "dialog_muted"
92+
UpdateDialogUnmuted UpdateType = "dialog_unmuted"
93+
UpdateDialogCleared UpdateType = "dialog_cleared"
94+
UpdateDialogRemoved UpdateType = "dialog_removed"
9095
)
9196

9297
// ChatAdminPermission represents a permission granted to a chat admin.
@@ -101,27 +106,38 @@ const (
101106
PermWrite ChatAdminPermission = "write"
102107
)
103108

104-
// User represents a Max user.
109+
// User represents a Max user or bot.
105110
type User struct {
106-
UserID int64 `json:"user_id"`
107-
FirstName string `json:"first_name"`
108-
LastName *string `json:"last_name,omitempty"`
109-
Username *string `json:"username,omitempty"`
110-
IsBot bool `json:"is_bot"`
111-
LastActivityTime int64 `json:"last_activity_time"`
111+
// Unique identifier of the user or bot.
112+
UserID int64 `json:"user_id"`
113+
// Display name of the user or bot.
114+
FirstName string `json:"first_name"`
115+
// Display last name. Not returned for bots.
116+
LastName *string `json:"last_name,omitempty"`
117+
// Bot username or unique public name. May be null for users.
118+
Username *string `json:"username,omitempty"`
119+
// True if this is a bot.
120+
IsBot bool `json:"is_bot"`
121+
// Last activity time in MAX (Unix time in milliseconds).
122+
// May be absent if the user disabled online status in settings.
123+
LastActivityTime int64 `json:"last_activity_time"`
112124
}
113125

114126
// UserWithPhoto extends User with avatar and description.
115127
type UserWithPhoto struct {
116128
User
117-
Description *string `json:"description,omitempty"`
118-
AvatarURL string `json:"avatar_url,omitempty"`
119-
FullAvatarURL string `json:"full_avatar_url,omitempty"`
129+
// User or bot description (up to 16000 characters).
130+
Description *string `json:"description,omitempty"`
131+
// Small avatar URL.
132+
AvatarURL string `json:"avatar_url,omitempty"`
133+
// Full-size avatar URL.
134+
FullAvatarURL string `json:"full_avatar_url,omitempty"`
120135
}
121136

122137
// BotInfo represents the bot's info returned by GET /me.
123138
type BotInfo struct {
124139
UserWithPhoto
140+
// Commands supported by the bot (up to 32).
125141
Commands []BotCommand `json:"commands,omitempty"`
126142
}
127143

@@ -148,22 +164,38 @@ type Image struct {
148164

149165
// Chat represents a Max chat.
150166
type Chat struct {
151-
ChatID int64 `json:"chat_id"`
152-
Type ChatType `json:"type"`
153-
Status ChatStatus `json:"status"`
154-
Title *string `json:"title"`
155-
Icon *Image `json:"icon"`
156-
LastEventTime int64 `json:"last_event_time"`
157-
ParticipantsCount int `json:"participants_count"`
158-
OwnerID *int64 `json:"owner_id,omitempty"`
159-
Participants map[string]int64 `json:"participants,omitempty"`
160-
IsPublic bool `json:"is_public"`
161-
Link *string `json:"link,omitempty"`
162-
Description *string `json:"description"`
163-
DialogWithUser *UserWithPhoto `json:"dialog_with_user,omitempty"`
164-
MessagesCount *int `json:"messages_count,omitempty"`
165-
ChatMessageID *string `json:"chat_message_id,omitempty"`
166-
PinnedMessage *Message `json:"pinned_message,omitempty"`
167+
// Chat identifier.
168+
ChatID int64 `json:"chat_id"`
169+
// Chat type: "chat" (group), "dialog" (direct), or "channel".
170+
Type ChatType `json:"type"`
171+
// Bot's status in the chat: "active", "removed", "left", "closed".
172+
Status ChatStatus `json:"status"`
173+
// Display title. May be null for dialogs.
174+
Title *string `json:"title"`
175+
// Chat icon.
176+
Icon *Image `json:"icon"`
177+
// Last event time in the chat (Unix time).
178+
LastEventTime int64 `json:"last_event_time"`
179+
// Number of participants. Always 2 for dialogs.
180+
ParticipantsCount int `json:"participants_count"`
181+
// Chat owner ID.
182+
OwnerID *int64 `json:"owner_id,omitempty"`
183+
// Participants with last activity time. May be null for chat lists.
184+
Participants map[string]int64 `json:"participants,omitempty"`
185+
// Whether the chat is publicly accessible (always false for dialogs).
186+
IsPublic bool `json:"is_public"`
187+
// Chat invite link.
188+
Link *string `json:"link,omitempty"`
189+
// Chat description.
190+
Description *string `json:"description"`
191+
// User info for dialog chats (type "dialog" only).
192+
DialogWithUser *UserWithPhoto `json:"dialog_with_user,omitempty"`
193+
// Message count. Only for group chats and channels, not dialogs.
194+
MessagesCount *int `json:"messages_count,omitempty"`
195+
// ID of the message containing the button that initiated this chat.
196+
ChatMessageID *string `json:"chat_message_id,omitempty"`
197+
// Pinned message. Only returned when requesting a specific chat.
198+
PinnedMessage *Message `json:"pinned_message,omitempty"`
167199
}
168200

169201
// ChatList represents a paginated list of chats.
@@ -183,12 +215,18 @@ type ChatPatch struct {
183215
// ChatMember represents a member of a chat.
184216
type ChatMember struct {
185217
UserWithPhoto
186-
LastAccessTime int64 `json:"last_access_time"`
187-
IsOwner bool `json:"is_owner"`
188-
IsAdmin bool `json:"is_admin"`
189-
JoinTime int64 `json:"join_time"`
190-
Permissions []ChatAdminPermission `json:"permissions"`
191-
Alias *string `json:"alias"`
218+
// Last activity time in the chat. May be stale for superchats.
219+
LastAccessTime int64 `json:"last_access_time"`
220+
// Whether the user is the chat owner.
221+
IsOwner bool `json:"is_owner"`
222+
// Whether the user is a chat administrator.
223+
IsAdmin bool `json:"is_admin"`
224+
// Time when the user joined the chat (Unix time).
225+
JoinTime int64 `json:"join_time"`
226+
// Admin permissions. Null if the member is not an admin.
227+
Permissions []ChatAdminPermission `json:"permissions"`
228+
// Custom admin title shown in chat.
229+
Alias *string `json:"alias"`
192230
}
193231

194232
// ChatMembersList represents a paginated list of chat members.
@@ -223,13 +261,20 @@ type MessageStat struct {
223261

224262
// Message represents a message in a chat.
225263
type Message struct {
226-
Sender *User `json:"sender,omitempty"`
227-
Recipient Recipient `json:"recipient"`
228-
Timestamp int64 `json:"timestamp"`
229-
Link *LinkedMessage `json:"link,omitempty"`
230-
Body MessageBody `json:"body"`
231-
Stat *MessageStat `json:"stat,omitempty"`
232-
URL *string `json:"url,omitempty"`
264+
// User who sent the message.
265+
Sender *User `json:"sender,omitempty"`
266+
// Recipient — can be a user or a chat.
267+
Recipient Recipient `json:"recipient"`
268+
// Message creation time (Unix time).
269+
Timestamp int64 `json:"timestamp"`
270+
// Forwarded or replied-to message.
271+
Link *LinkedMessage `json:"link,omitempty"`
272+
// Message content: text and attachments.
273+
Body MessageBody `json:"body"`
274+
// Message statistics.
275+
Stat *MessageStat `json:"stat,omitempty"`
276+
// Public link to a channel post. Absent for dialogs and group chats.
277+
URL *string `json:"url,omitempty"`
233278
}
234279

235280
// MessageBody represents the body of a message.
@@ -312,11 +357,16 @@ type MessageList struct {
312357
// Use [Some] to set optional fields. Unset fields are omitted from JSON,
313358
// which tells the server to keep the existing value when editing a message.
314359
type NewMessageBody struct {
315-
Text OptString `json:"text,omitzero"`
316-
Attachments []AttachmentRequest `json:"attachments,omitzero"`
317-
Link *NewMessageLink `json:"link,omitempty"`
318-
Notify OptBool `json:"notify,omitzero"`
319-
Format Optional[TextFormat] `json:"format,omitzero"`
360+
// Message text (up to 4000 characters).
361+
Text OptString `json:"text,omitzero"`
362+
// Message attachments. If empty, all existing attachments will be removed.
363+
Attachments []AttachmentRequest `json:"attachments,omitzero"`
364+
// Link to another message (for reply or forward).
365+
Link *NewMessageLink `json:"link,omitempty"`
366+
// If false, chat members will not be notified (default true).
367+
Notify OptBool `json:"notify,omitzero"`
368+
// Text formatting mode: "markdown" or "html".
369+
Format Optional[TextFormat] `json:"format,omitzero"`
320370

321371
// DisableLinkPreview prevents the server from generating link previews.
322372
// Sent as a query parameter, not in the JSON body.
@@ -476,7 +526,7 @@ type InlineKeyboardAttachment struct {
476526

477527
// Button represents a button in an inline keyboard.
478528
// Use the Type field to determine the button kind: "callback", "link",
479-
// "request_contact", "request_geo_location", "chat", "message".
529+
// "request_contact", "request_geo_location", "chat", "message", "open_app".
480530
type Button struct {
481531
Type string `json:"type"`
482532
Text string `json:"text"`
@@ -488,6 +538,7 @@ type Button struct {
488538
ChatDescription OptString `json:"chat_description,omitzero"`
489539
StartPayload OptString `json:"start_payload,omitzero"`
490540
UUID OptInt64 `json:"uuid,omitzero"`
541+
WebApp string `json:"web_app,omitempty"`
491542
}
492543

493544
// NewCallbackButton creates a callback button that sends payload to the bot.
@@ -527,6 +578,12 @@ func NewMessageButton(text string) Button {
527578
return Button{Type: "message", Text: text}
528579
}
529580

581+
// NewOpenAppButton creates a button that opens a mini app inside the messenger.
582+
// The webApp parameter is the bot username whose mini app to launch.
583+
func NewOpenAppButton(text, webApp string) Button {
584+
return Button{Type: "open_app", Text: text, WebApp: webApp}
585+
}
586+
530587
// ReplyButton represents a button in a reply keyboard.
531588
type ReplyButton struct {
532589
Type string `json:"type,omitempty"`
@@ -749,14 +806,18 @@ type VideoAttachmentDetails struct {
749806

750807
// Update is the base for all update events.
751808
type Update struct {
809+
// Discriminator that determines the update type.
752810
UpdateType UpdateType `json:"update_type"`
753-
Timestamp int64 `json:"timestamp"`
811+
// Unix time when the event occurred.
812+
Timestamp int64 `json:"timestamp"`
754813
}
755814

756815
// MessageCreatedUpdate is received when a new message is created.
757816
type MessageCreatedUpdate struct {
758817
Update
759-
Message Message `json:"message"`
818+
// The newly created message.
819+
Message Message `json:"message"`
820+
// User's current locale (IETF BCP 47). Only available in dialogs.
760821
UserLocale *string `json:"user_locale,omitempty"`
761822
}
762823

@@ -841,6 +902,41 @@ type MessageChatCreatedUpdate struct {
841902
StartPayload *string `json:"start_payload,omitempty"`
842903
}
843904

905+
// BotStoppedUpdate is received when a user stops the bot.
906+
type BotStoppedUpdate struct {
907+
Update
908+
ChatID int64 `json:"chat_id"`
909+
User User `json:"user"`
910+
}
911+
912+
// DialogMutedUpdate is received when a user mutes the dialog with the bot.
913+
type DialogMutedUpdate struct {
914+
Update
915+
ChatID int64 `json:"chat_id"`
916+
User User `json:"user"`
917+
}
918+
919+
// DialogUnmutedUpdate is received when a user unmutes the dialog with the bot.
920+
type DialogUnmutedUpdate struct {
921+
Update
922+
ChatID int64 `json:"chat_id"`
923+
User User `json:"user"`
924+
}
925+
926+
// DialogClearedUpdate is received when a user clears the dialog history.
927+
type DialogClearedUpdate struct {
928+
Update
929+
ChatID int64 `json:"chat_id"`
930+
User User `json:"user"`
931+
}
932+
933+
// DialogRemovedUpdate is received when a user removes the dialog with the bot.
934+
type DialogRemovedUpdate struct {
935+
Update
936+
ChatID int64 `json:"chat_id"`
937+
User User `json:"user"`
938+
}
939+
844940
// UpdateList is the response from GET /updates.
845941
type UpdateList struct {
846942
Updates []json.RawMessage `json:"updates"`

0 commit comments

Comments
 (0)