refactor(scrape): detect prose-style "must be X" discriminator values on variants

Sealed-interface union variants whose Type/Source field is declared as
bare prose (e.g. "Type of the result, must be article" or "Scope type,
must be all_private_chats") were skipped by extractEnumValues because
the existing patterns require curly-quoted values. The genapi emitter
already extracted these values via discBareRE for marshal-side
discriminator injection; lifting the same detection into the scraper
populates Field.EnumValues so planUnifiedUnionEnums folds them into
shared union-level enums automatically.

Unions newly unified (10): BotCommandScope, MenuButton, InputMedia,
InputPaidMedia, InputPollMedia, InputPollOptionMedia, InputProfilePhoto,
InputStoryContent, InlineQueryResult, PassportElementError.

InputMessageContent stays excluded — its variants dispatch
structurally on field presence and have no Type/Source field, so
planUnifiedUnionEnums correctly skips it.

Constants added: 60 typed enum constants across the 10 unions; the
corresponding variant struct fields are retyped from string to the
shared enum.

Internal call-site cleanups: 0 — no internal package referenced these
discriminator values via magic strings.

False positives the prose detector explicitly rejects: terminal
prose-word continuations like "must be sent", "must be shown above",
"must be specified", "must be paid", "must be active", "must be one
of 3, 6, or 12", "must be between 5 and 100000", "must be a Pay
button", "must be repainted". Guarded via terminal-position regex
anchor + closed-list isProseWord filter.

Determinism verified across two consecutive make regen-from-fixture
runs. go test -race ./..., go vet ./..., staticcheck ./... all clean.
This commit is contained in:
2026-05-09 20:37:07 +01:00
parent 5523ed2b06
commit fecef22f48
7 changed files with 1082 additions and 199 deletions
+239
View File
@@ -0,0 +1,239 @@
package api
import (
"reflect"
"testing"
json "github.com/goccy/go-json"
"github.com/stretchr/testify/require"
)
// TestUnifiedEnum_BotCommandScopeType_Constants confirms the prose-form
// discriminator detection promoted BotCommandScope's per-variant Type
// fields into one shared enum.
func TestUnifiedEnum_BotCommandScopeType_Constants(t *testing.T) {
require.IsType(t, BotCommandScopeType(""), BotCommandScopeTypeDefault)
got := []BotCommandScopeType{
BotCommandScopeTypeDefault,
BotCommandScopeTypeAllPrivateChats,
BotCommandScopeTypeAllGroupChats,
BotCommandScopeTypeAllChatAdministrators,
BotCommandScopeTypeChat,
BotCommandScopeTypeChatAdministrators,
BotCommandScopeTypeChatMember,
}
want := []string{
"default", "all_private_chats", "all_group_chats",
"all_chat_administrators", "chat", "chat_administrators", "chat_member",
}
require.Len(t, got, len(want))
for i, v := range got {
require.Equal(t, want[i], string(v))
}
}
// TestUnifiedEnum_InlineQueryResultType_VariantFields walks the variants
// and asserts each one's Type field is the unified enum.
func TestUnifiedEnum_InlineQueryResultType_VariantFields(t *testing.T) {
require.IsType(t, InlineQueryResultType(""), InlineQueryResultTypeArticle)
wantType := reflect.TypeOf(InlineQueryResultType(""))
cases := []any{
&InlineQueryResultArticle{},
&InlineQueryResultPhoto{},
&InlineQueryResultGif{},
&InlineQueryResultMpeg4Gif{},
&InlineQueryResultVideo{},
&InlineQueryResultAudio{},
&InlineQueryResultVoice{},
&InlineQueryResultDocument{},
&InlineQueryResultLocation{},
&InlineQueryResultVenue{},
&InlineQueryResultContact{},
&InlineQueryResultGame{},
}
for _, c := range cases {
rt := reflect.TypeOf(c).Elem()
f, ok := rt.FieldByName("Type")
require.True(t, ok, "%s missing Type field", rt.Name())
require.Equal(t, wantType, f.Type, "%s.Type type mismatch", rt.Name())
}
}
// TestUnifiedEnum_PassportElementErrorSource_VariantFields asserts the
// retype landed on every variant of the PassportElementError union.
func TestUnifiedEnum_PassportElementErrorSource_VariantFields(t *testing.T) {
require.IsType(t, PassportElementErrorSource(""), PassportElementErrorSourceData)
wantType := reflect.TypeOf(PassportElementErrorSource(""))
cases := []any{
&PassportElementErrorDataField{},
&PassportElementErrorFrontSide{},
&PassportElementErrorReverseSide{},
&PassportElementErrorSelfie{},
&PassportElementErrorFile{},
&PassportElementErrorFiles{},
&PassportElementErrorTranslationFile{},
&PassportElementErrorTranslationFiles{},
&PassportElementErrorUnspecified{},
}
for _, c := range cases {
rt := reflect.TypeOf(c).Elem()
f, ok := rt.FieldByName("Source")
require.True(t, ok, "%s missing Source field", rt.Name())
require.Equal(t, wantType, f.Type, "%s.Source type mismatch", rt.Name())
}
}
// TestUnifiedEnum_InputMediaType_Constants covers a media-shaped union
// where the discriminator value is the wire identifier "animation",
// "photo", etc.
func TestUnifiedEnum_InputMediaType_Constants(t *testing.T) {
require.IsType(t, InputMediaType(""), InputMediaTypePhoto)
wantType := reflect.TypeOf(InputMediaType(""))
for _, c := range []any{
&InputMediaAnimation{},
&InputMediaAudio{},
&InputMediaDocument{},
&InputMediaPhoto{},
&InputMediaVideo{},
} {
rt := reflect.TypeOf(c).Elem()
f, ok := rt.FieldByName("Type")
require.True(t, ok, "%s missing Type field", rt.Name())
require.Equal(t, wantType, f.Type, "%s.Type type mismatch", rt.Name())
}
}
// TestUnifiedEnum_MenuButtonType_Constants covers the third single-Type
// union pulled in by the prose detector.
func TestUnifiedEnum_MenuButtonType_Constants(t *testing.T) {
require.IsType(t, MenuButtonType(""), MenuButtonTypeCommands)
wantType := reflect.TypeOf(MenuButtonType(""))
for _, c := range []any{
&MenuButtonCommands{},
&MenuButtonWebApp{},
&MenuButtonDefault{},
} {
rt := reflect.TypeOf(c).Elem()
f, ok := rt.FieldByName("Type")
require.True(t, ok, "%s missing Type field", rt.Name())
require.Equal(t, wantType, f.Type, "%s.Type type mismatch", rt.Name())
}
}
// TestUnifiedEnum_InlineQueryResultArticle_RoundTrip confirms the
// auto-injected discriminator survives a marshal-unmarshal cycle on the
// concrete variant and lands as the typed enum constant. There's no
// generated UnmarshalInlineQueryResult — the union has no entry in
// knownDiscriminators — so the round-trip targets the variant directly.
func TestUnifiedEnum_InlineQueryResultArticle_RoundTrip(t *testing.T) {
orig := &InlineQueryResultArticle{
ID: "x1",
Title: "test",
}
raw, err := json.Marshal(orig)
require.NoError(t, err)
var probe struct {
Type string `json:"type"`
}
require.NoError(t, json.Unmarshal(raw, &probe))
require.Equal(t, "article", probe.Type)
// Strip InputMessageContent before re-decoding: it's a sealed
// interface and the variant has no UnmarshalJSON helper to dispatch
// it. The discriminator round-trip is the property under test, not
// nested-union deserialisation.
var round struct {
Type InlineQueryResultType `json:"type"`
ID string `json:"id"`
Title string `json:"title"`
}
require.NoError(t, json.Unmarshal(raw, &round))
require.Equal(t, InlineQueryResultTypeArticle, round.Type)
require.Equal(t, orig.ID, round.ID)
require.Equal(t, orig.Title, round.Title)
}
// TestUnifiedEnum_PassportElementErrorDataField_RoundTrip mirrors the
// above for the Source-discriminated union.
func TestUnifiedEnum_PassportElementErrorDataField_RoundTrip(t *testing.T) {
orig := &PassportElementErrorDataField{
Type: "personal_details",
FieldName: "first_name",
DataHash: "abc",
Message: "boom",
}
raw, err := json.Marshal(orig)
require.NoError(t, err)
var probe struct {
Source string `json:"source"`
}
require.NoError(t, json.Unmarshal(raw, &probe))
require.Equal(t, "data", probe.Source)
var round PassportElementErrorDataField
require.NoError(t, json.Unmarshal(raw, &round))
require.Equal(t, PassportElementErrorSourceData, round.Source)
require.Equal(t, orig.FieldName, round.FieldName)
}
// TestUnifiedEnum_BotCommandScopeChat_RoundTrip covers a bot-command
// scope variant with a non-trivial extra field (ChatID).
func TestUnifiedEnum_BotCommandScopeChat_RoundTrip(t *testing.T) {
orig := &BotCommandScopeChat{ChatID: ChatIDFromInt(42)}
raw, err := json.Marshal(orig)
require.NoError(t, err)
var probe struct {
Type string `json:"type"`
}
require.NoError(t, json.Unmarshal(raw, &probe))
require.Equal(t, "chat", probe.Type)
var round BotCommandScopeChat
require.NoError(t, json.Unmarshal(raw, &round))
require.Equal(t, BotCommandScopeTypeChat, round.Type)
}
// TestUnifiedEnum_InputMessageContent_NoEnumEmitted confirms the IMC
// union — which dispatches structurally on field presence rather than a
// shared discriminator — does NOT get a unified enum, since none of its
// variants declare a single-value discriminator field.
func TestUnifiedEnum_InputMessageContent_NoEnumEmitted(t *testing.T) {
for _, name := range []string{
"InputTextMessageContent",
"InputLocationMessageContent",
"InputVenueMessageContent",
"InputContactMessageContent",
"InputInvoiceMessageContent",
} {
switch name {
case "InputTextMessageContent":
rt := reflect.TypeOf(&InputTextMessageContent{}).Elem()
_, ok := rt.FieldByName("Type")
require.False(t, ok, "%s unexpectedly grew a Type field", name)
case "InputLocationMessageContent":
rt := reflect.TypeOf(&InputLocationMessageContent{}).Elem()
_, ok := rt.FieldByName("Type")
require.False(t, ok, "%s unexpectedly grew a Type field", name)
case "InputVenueMessageContent":
rt := reflect.TypeOf(&InputVenueMessageContent{}).Elem()
_, ok := rt.FieldByName("Type")
require.False(t, ok, "%s unexpectedly grew a Type field", name)
case "InputContactMessageContent":
rt := reflect.TypeOf(&InputContactMessageContent{}).Elem()
_, ok := rt.FieldByName("Type")
require.False(t, ok, "%s unexpectedly grew a Type field", name)
case "InputInvoiceMessageContent":
rt := reflect.TypeOf(&InputInvoiceMessageContent{}).Elem()
_, ok := rt.FieldByName("Type")
require.False(t, ok, "%s unexpectedly grew a Type field", name)
}
}
}
+110
View File
@@ -21,6 +21,18 @@ const (
BackgroundTypeKindChatTheme BackgroundTypeKind = "chat_theme" BackgroundTypeKindChatTheme BackgroundTypeKind = "chat_theme"
) )
type BotCommandScopeType string
const (
BotCommandScopeTypeDefault BotCommandScopeType = "default"
BotCommandScopeTypeAllPrivateChats BotCommandScopeType = "all_private_chats"
BotCommandScopeTypeAllGroupChats BotCommandScopeType = "all_group_chats"
BotCommandScopeTypeAllChatAdministrators BotCommandScopeType = "all_chat_administrators"
BotCommandScopeTypeChat BotCommandScopeType = "chat"
BotCommandScopeTypeChatAdministrators BotCommandScopeType = "chat_administrators"
BotCommandScopeTypeChatMember BotCommandScopeType = "chat_member"
)
type ChatBoostSourceKind string type ChatBoostSourceKind string
const ( const (
@@ -92,6 +104,75 @@ const (
InlineQueryResultGifThumbnailMimeTypeVideoOfMp4 InlineQueryResultGifThumbnailMimeType = "video/mp4" InlineQueryResultGifThumbnailMimeTypeVideoOfMp4 InlineQueryResultGifThumbnailMimeType = "video/mp4"
) )
type InlineQueryResultType string
const (
InlineQueryResultTypeAudio InlineQueryResultType = "audio"
InlineQueryResultTypeDocument InlineQueryResultType = "document"
InlineQueryResultTypeGif InlineQueryResultType = "gif"
InlineQueryResultTypeMpeg4Gif InlineQueryResultType = "mpeg4_gif"
InlineQueryResultTypePhoto InlineQueryResultType = "photo"
InlineQueryResultTypeSticker InlineQueryResultType = "sticker"
InlineQueryResultTypeVideo InlineQueryResultType = "video"
InlineQueryResultTypeVoice InlineQueryResultType = "voice"
InlineQueryResultTypeArticle InlineQueryResultType = "article"
InlineQueryResultTypeContact InlineQueryResultType = "contact"
InlineQueryResultTypeGame InlineQueryResultType = "game"
InlineQueryResultTypeLocation InlineQueryResultType = "location"
InlineQueryResultTypeVenue InlineQueryResultType = "venue"
)
type InputMediaType string
const (
InputMediaTypeAnimation InputMediaType = "animation"
InputMediaTypeAudio InputMediaType = "audio"
InputMediaTypeDocument InputMediaType = "document"
InputMediaTypeLivePhoto InputMediaType = "live_photo"
InputMediaTypePhoto InputMediaType = "photo"
InputMediaTypeVideo InputMediaType = "video"
)
type InputPaidMediaType string
const (
InputPaidMediaTypeLivePhoto InputPaidMediaType = "live_photo"
InputPaidMediaTypePhoto InputPaidMediaType = "photo"
InputPaidMediaTypeVideo InputPaidMediaType = "video"
)
type InputPollMediaType string
const (
InputPollMediaTypeAnimation InputPollMediaType = "animation"
InputPollMediaTypeAudio InputPollMediaType = "audio"
InputPollMediaTypeDocument InputPollMediaType = "document"
InputPollMediaTypeLivePhoto InputPollMediaType = "live_photo"
InputPollMediaTypeLocation InputPollMediaType = "location"
InputPollMediaTypePhoto InputPollMediaType = "photo"
InputPollMediaTypeVenue InputPollMediaType = "venue"
InputPollMediaTypeVideo InputPollMediaType = "video"
)
type InputPollOptionMediaType string
const (
InputPollOptionMediaTypeAnimation InputPollOptionMediaType = "animation"
InputPollOptionMediaTypeLivePhoto InputPollOptionMediaType = "live_photo"
InputPollOptionMediaTypeLocation InputPollOptionMediaType = "location"
InputPollOptionMediaTypePhoto InputPollOptionMediaType = "photo"
InputPollOptionMediaTypeSticker InputPollOptionMediaType = "sticker"
InputPollOptionMediaTypeVenue InputPollOptionMediaType = "venue"
InputPollOptionMediaTypeVideo InputPollOptionMediaType = "video"
)
type InputProfilePhotoType string
const (
InputProfilePhotoTypeStatic InputProfilePhotoType = "static"
InputProfilePhotoTypeAnimated InputProfilePhotoType = "animated"
)
type InputStickerFormat string type InputStickerFormat string
const ( const (
@@ -100,6 +181,13 @@ const (
InputStickerFormatVideo InputStickerFormat = "video" InputStickerFormatVideo InputStickerFormat = "video"
) )
type InputStoryContentType string
const (
InputStoryContentTypePhoto InputStoryContentType = "photo"
InputStoryContentTypeVideo InputStoryContentType = "video"
)
type KeyboardButtonStyle string type KeyboardButtonStyle string
const ( const (
@@ -117,6 +205,14 @@ const (
MaskPositionPointChin MaskPositionPoint = "chin" MaskPositionPointChin MaskPositionPoint = "chin"
) )
type MenuButtonType string
const (
MenuButtonTypeCommands MenuButtonType = "commands"
MenuButtonTypeWebApp MenuButtonType = "web_app"
MenuButtonTypeDefault MenuButtonType = "default"
)
type MessageEntityType string type MessageEntityType string
const ( const (
@@ -212,6 +308,20 @@ const (
PassportElementErrorSelfieTypeInternalPassport PassportElementErrorSelfieType = "internal_passport" PassportElementErrorSelfieTypeInternalPassport PassportElementErrorSelfieType = "internal_passport"
) )
type PassportElementErrorSource string
const (
PassportElementErrorSourceData PassportElementErrorSource = "data"
PassportElementErrorSourceFrontSide PassportElementErrorSource = "front_side"
PassportElementErrorSourceReverseSide PassportElementErrorSource = "reverse_side"
PassportElementErrorSourceSelfie PassportElementErrorSource = "selfie"
PassportElementErrorSourceFile PassportElementErrorSource = "file"
PassportElementErrorSourceFiles PassportElementErrorSource = "files"
PassportElementErrorSourceTranslationFile PassportElementErrorSource = "translation_file"
PassportElementErrorSourceTranslationFiles PassportElementErrorSource = "translation_files"
PassportElementErrorSourceUnspecified PassportElementErrorSource = "unspecified"
)
type PassportElementErrorTranslationFileType string type PassportElementErrorTranslationFileType string
const ( const (
+55 -55
View File
@@ -4082,7 +4082,7 @@ func (*BotCommandScopeChatMember) isBotCommandScope() {}
// Represents the default scope of bot commands. Default commands are used if no commands with a narrower scope are specified for the user. // Represents the default scope of bot commands. Default commands are used if no commands with a narrower scope are specified for the user.
type BotCommandScopeDefault struct { type BotCommandScopeDefault struct {
// Scope type, must be default // Scope type, must be default
Type string `json:"type"` Type BotCommandScopeType `json:"type"`
} }
// MarshalJSON encodes BotCommandScopeDefault with the discriminator field // MarshalJSON encodes BotCommandScopeDefault with the discriminator field
@@ -4104,7 +4104,7 @@ func (v *BotCommandScopeDefault) MarshalJSON() ([]byte, error) {
// Represents the scope of bot commands, covering all private chats. // Represents the scope of bot commands, covering all private chats.
type BotCommandScopeAllPrivateChats struct { type BotCommandScopeAllPrivateChats struct {
// Scope type, must be all_private_chats // Scope type, must be all_private_chats
Type string `json:"type"` Type BotCommandScopeType `json:"type"`
} }
// MarshalJSON encodes BotCommandScopeAllPrivateChats with the discriminator field // MarshalJSON encodes BotCommandScopeAllPrivateChats with the discriminator field
@@ -4126,7 +4126,7 @@ func (v *BotCommandScopeAllPrivateChats) MarshalJSON() ([]byte, error) {
// Represents the scope of bot commands, covering all group and supergroup chats. // Represents the scope of bot commands, covering all group and supergroup chats.
type BotCommandScopeAllGroupChats struct { type BotCommandScopeAllGroupChats struct {
// Scope type, must be all_group_chats // Scope type, must be all_group_chats
Type string `json:"type"` Type BotCommandScopeType `json:"type"`
} }
// MarshalJSON encodes BotCommandScopeAllGroupChats with the discriminator field // MarshalJSON encodes BotCommandScopeAllGroupChats with the discriminator field
@@ -4148,7 +4148,7 @@ func (v *BotCommandScopeAllGroupChats) MarshalJSON() ([]byte, error) {
// Represents the scope of bot commands, covering all group and supergroup chat administrators. // Represents the scope of bot commands, covering all group and supergroup chat administrators.
type BotCommandScopeAllChatAdministrators struct { type BotCommandScopeAllChatAdministrators struct {
// Scope type, must be all_chat_administrators // Scope type, must be all_chat_administrators
Type string `json:"type"` Type BotCommandScopeType `json:"type"`
} }
// MarshalJSON encodes BotCommandScopeAllChatAdministrators with the discriminator field // MarshalJSON encodes BotCommandScopeAllChatAdministrators with the discriminator field
@@ -4170,7 +4170,7 @@ func (v *BotCommandScopeAllChatAdministrators) MarshalJSON() ([]byte, error) {
// Represents the scope of bot commands, covering a specific chat. // Represents the scope of bot commands, covering a specific chat.
type BotCommandScopeChat struct { type BotCommandScopeChat struct {
// Scope type, must be chat // Scope type, must be chat
Type string `json:"type"` Type BotCommandScopeType `json:"type"`
// Unique identifier for the target chat or username of the target supergroup in the format @username. Channel direct messages chats and channel chats aren't supported. // Unique identifier for the target chat or username of the target supergroup in the format @username. Channel direct messages chats and channel chats aren't supported.
ChatID ChatID `json:"chat_id"` ChatID ChatID `json:"chat_id"`
} }
@@ -4194,7 +4194,7 @@ func (v *BotCommandScopeChat) MarshalJSON() ([]byte, error) {
// Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat. // Represents the scope of bot commands, covering all administrators of a specific group or supergroup chat.
type BotCommandScopeChatAdministrators struct { type BotCommandScopeChatAdministrators struct {
// Scope type, must be chat_administrators // Scope type, must be chat_administrators
Type string `json:"type"` Type BotCommandScopeType `json:"type"`
// Unique identifier for the target chat or username of the target supergroup in the format @username. Channel direct messages chats and channel chats aren't supported. // Unique identifier for the target chat or username of the target supergroup in the format @username. Channel direct messages chats and channel chats aren't supported.
ChatID ChatID `json:"chat_id"` ChatID ChatID `json:"chat_id"`
} }
@@ -4218,7 +4218,7 @@ func (v *BotCommandScopeChatAdministrators) MarshalJSON() ([]byte, error) {
// Represents the scope of bot commands, covering a specific member of a group or supergroup chat. // Represents the scope of bot commands, covering a specific member of a group or supergroup chat.
type BotCommandScopeChatMember struct { type BotCommandScopeChatMember struct {
// Scope type, must be chat_member // Scope type, must be chat_member
Type string `json:"type"` Type BotCommandScopeType `json:"type"`
// Unique identifier for the target chat or username of the target supergroup in the format @username. Channel direct messages chats and channel chats aren't supported. // Unique identifier for the target chat or username of the target supergroup in the format @username. Channel direct messages chats and channel chats aren't supported.
ChatID ChatID `json:"chat_id"` ChatID ChatID `json:"chat_id"`
// Unique identifier of the target user // Unique identifier of the target user
@@ -4307,7 +4307,7 @@ func UnmarshalMenuButton(data []byte) (MenuButton, error) {
// Represents a menu button, which opens the bot's list of commands. // Represents a menu button, which opens the bot's list of commands.
type MenuButtonCommands struct { type MenuButtonCommands struct {
// Type of the button, must be commands // Type of the button, must be commands
Type string `json:"type"` Type MenuButtonType `json:"type"`
} }
// MarshalJSON encodes MenuButtonCommands with the discriminator field // MarshalJSON encodes MenuButtonCommands with the discriminator field
@@ -4329,7 +4329,7 @@ func (v *MenuButtonCommands) MarshalJSON() ([]byte, error) {
// Represents a menu button, which launches a Web App. // Represents a menu button, which launches a Web App.
type MenuButtonWebApp struct { type MenuButtonWebApp struct {
// Type of the button, must be web_app // Type of the button, must be web_app
Type string `json:"type"` Type MenuButtonType `json:"type"`
// Text on the button // Text on the button
Text string `json:"text"` Text string `json:"text"`
// Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Alternatively, a t.me link to a Web App of the bot can be specified in the object instead of the Web App's URL, in which case the Web App will be opened as if the user pressed the link. // Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Alternatively, a t.me link to a Web App of the bot can be specified in the object instead of the Web App's URL, in which case the Web App will be opened as if the user pressed the link.
@@ -4355,7 +4355,7 @@ func (v *MenuButtonWebApp) MarshalJSON() ([]byte, error) {
// Describes that no specific value for the menu button was set. // Describes that no specific value for the menu button was set.
type MenuButtonDefault struct { type MenuButtonDefault struct {
// Type of the button, must be default // Type of the button, must be default
Type string `json:"type"` Type MenuButtonType `json:"type"`
} }
// MarshalJSON encodes MenuButtonDefault with the discriminator field // MarshalJSON encodes MenuButtonDefault with the discriminator field
@@ -4711,7 +4711,7 @@ func (*InputMediaVideo) isInputMedia() {}
// Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent. // Represents an animation file (GIF or H.264/MPEG-4 AVC video without sound) to be sent.
type InputMediaAnimation struct { type InputMediaAnimation struct {
// Type of the result, must be animation // Type of the result, must be animation
Type string `json:"type"` Type InputMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
// Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
@@ -4753,7 +4753,7 @@ func (v *InputMediaAnimation) MarshalJSON() ([]byte, error) {
// Represents an audio file to be treated as music to be sent. // Represents an audio file to be treated as music to be sent.
type InputMediaAudio struct { type InputMediaAudio struct {
// Type of the result, must be audio // Type of the result, must be audio
Type string `json:"type"` Type InputMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
// Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
@@ -4791,7 +4791,7 @@ func (v *InputMediaAudio) MarshalJSON() ([]byte, error) {
// Represents a general file to be sent. // Represents a general file to be sent.
type InputMediaDocument struct { type InputMediaDocument struct {
// Type of the result, must be document // Type of the result, must be document
Type string `json:"type"` Type InputMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
// Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
@@ -4825,7 +4825,7 @@ func (v *InputMediaDocument) MarshalJSON() ([]byte, error) {
// Represents a live photo to be sent. // Represents a live photo to be sent.
type InputMediaLivePhoto struct { type InputMediaLivePhoto struct {
// Type of the result, must be live_photo // Type of the result, must be live_photo
Type string `json:"type"` Type InputMediaType `json:"type"`
// Video of the live photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported. // Video of the live photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported.
Media string `json:"media"` Media string `json:"media"`
// The static photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported. // The static photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported.
@@ -4861,7 +4861,7 @@ func (v *InputMediaLivePhoto) MarshalJSON() ([]byte, error) {
// Represents a location to be sent. // Represents a location to be sent.
type InputMediaLocation struct { type InputMediaLocation struct {
// Type of the result, must be location // Type of the result, must be location
Type string `json:"type"` Type InputPollOptionMediaType `json:"type"`
// Latitude of the location // Latitude of the location
Latitude float64 `json:"latitude"` Latitude float64 `json:"latitude"`
// Longitude of the location // Longitude of the location
@@ -4889,7 +4889,7 @@ func (v *InputMediaLocation) MarshalJSON() ([]byte, error) {
// Represents a photo to be sent. // Represents a photo to be sent.
type InputMediaPhoto struct { type InputMediaPhoto struct {
// Type of the result, must be photo // Type of the result, must be photo
Type string `json:"type"` Type InputMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
// Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing // Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing
@@ -4923,7 +4923,7 @@ func (v *InputMediaPhoto) MarshalJSON() ([]byte, error) {
// Represents a sticker file to be sent. // Represents a sticker file to be sent.
type InputMediaSticker struct { type InputMediaSticker struct {
// Type of the result, must be sticker // Type of the result, must be sticker
Type string `json:"type"` Type InputPollOptionMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a .WEBP sticker from the Internet, or pass “attach://<file_attach_name>” to upload a new .WEBP, .TGS, or .WEBM sticker using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a .WEBP sticker from the Internet, or pass “attach://<file_attach_name>” to upload a new .WEBP, .TGS, or .WEBM sticker using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
// Optional. Emoji associated with the sticker; only for just uploaded stickers // Optional. Emoji associated with the sticker; only for just uploaded stickers
@@ -4949,7 +4949,7 @@ func (v *InputMediaSticker) MarshalJSON() ([]byte, error) {
// Represents a venue to be sent. // Represents a venue to be sent.
type InputMediaVenue struct { type InputMediaVenue struct {
// Type of the result, must be venue // Type of the result, must be venue
Type string `json:"type"` Type InputPollOptionMediaType `json:"type"`
// Latitude of the location // Latitude of the location
Latitude float64 `json:"latitude"` Latitude float64 `json:"latitude"`
// Longitude of the location // Longitude of the location
@@ -4987,7 +4987,7 @@ func (v *InputMediaVenue) MarshalJSON() ([]byte, error) {
// Represents a video to be sent. // Represents a video to be sent.
type InputMediaVideo struct { type InputMediaVideo struct {
// Type of the result, must be video // Type of the result, must be video
Type string `json:"type"` Type InputMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
// Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
@@ -5053,7 +5053,7 @@ func (*InputPaidMediaVideo) isInputPaidMedia() {}
// The paid media to send is a live photo. // The paid media to send is a live photo.
type InputPaidMediaLivePhoto struct { type InputPaidMediaLivePhoto struct {
// Type of the media, must be live_photo // Type of the media, must be live_photo
Type string `json:"type"` Type InputPaidMediaType `json:"type"`
// Video of the live photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported. // Video of the live photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported.
Media string `json:"media"` Media string `json:"media"`
// The static photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported. // The static photo to send. Pass a file_id to send a file that exists on the Telegram servers (recommended) or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files ». Sending live photos by a URL is currently unsupported.
@@ -5079,7 +5079,7 @@ func (v *InputPaidMediaLivePhoto) MarshalJSON() ([]byte, error) {
// The paid media to send is a photo. // The paid media to send is a photo.
type InputPaidMediaPhoto struct { type InputPaidMediaPhoto struct {
// Type of the media, must be photo // Type of the media, must be photo
Type string `json:"type"` Type InputPaidMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
} }
@@ -5103,7 +5103,7 @@ func (v *InputPaidMediaPhoto) MarshalJSON() ([]byte, error) {
// The paid media to send is a video. // The paid media to send is a video.
type InputPaidMediaVideo struct { type InputPaidMediaVideo struct {
// Type of the media, must be video // Type of the media, must be video
Type string `json:"type"` Type InputPaidMediaType `json:"type"`
// File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files » // File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
Media string `json:"media"` Media string `json:"media"`
// Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
@@ -5155,7 +5155,7 @@ func (*InputProfilePhotoAnimated) isInputProfilePhoto() {}
// A static profile photo in the .JPG format. // A static profile photo in the .JPG format.
type InputProfilePhotoStatic struct { type InputProfilePhotoStatic struct {
// Type of the profile photo, must be static // Type of the profile photo, must be static
Type string `json:"type"` Type InputProfilePhotoType `json:"type"`
// The static profile photo. Profile photos can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the photo was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // The static profile photo. Profile photos can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the photo was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
Photo string `json:"photo"` Photo string `json:"photo"`
} }
@@ -5179,7 +5179,7 @@ func (v *InputProfilePhotoStatic) MarshalJSON() ([]byte, error) {
// An animated profile photo in the MPEG4 format. // An animated profile photo in the MPEG4 format.
type InputProfilePhotoAnimated struct { type InputProfilePhotoAnimated struct {
// Type of the profile photo, must be animated // Type of the profile photo, must be animated
Type string `json:"type"` Type InputProfilePhotoType `json:"type"`
// The animated profile photo. Profile photos can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the photo was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // The animated profile photo. Profile photos can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the photo was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
Animation string `json:"animation"` Animation string `json:"animation"`
// Optional. Timestamp in seconds of the frame that will be used as the static profile photo. Defaults to 0.0. // Optional. Timestamp in seconds of the frame that will be used as the static profile photo. Defaults to 0.0.
@@ -5219,7 +5219,7 @@ func (*InputStoryContentVideo) isInputStoryContent() {}
// Describes a photo to post as a story. // Describes a photo to post as a story.
type InputStoryContentPhoto struct { type InputStoryContentPhoto struct {
// Type of the content, must be photo // Type of the content, must be photo
Type string `json:"type"` Type InputStoryContentType `json:"type"`
// The photo to post as a story. The photo must be of the size 1080x1920 and must not exceed 10 MB. The photo can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the photo was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // The photo to post as a story. The photo must be of the size 1080x1920 and must not exceed 10 MB. The photo can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the photo was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
Photo string `json:"photo"` Photo string `json:"photo"`
} }
@@ -5243,7 +5243,7 @@ func (v *InputStoryContentPhoto) MarshalJSON() ([]byte, error) {
// Describes a video to post as a story. // Describes a video to post as a story.
type InputStoryContentVideo struct { type InputStoryContentVideo struct {
// Type of the content, must be video // Type of the content, must be video
Type string `json:"type"` Type InputStoryContentType `json:"type"`
// The video to post as a story. The video must be of the size 720x1280, streamable, encoded with H.265 codec, with key frames added each second in the MPEG4 format, and must not exceed 30 MB. The video can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the video was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files » // The video to post as a story. The video must be of the size 720x1280, streamable, encoded with H.265 codec, with key frames added each second in the MPEG4 format, and must not exceed 30 MB. The video can't be reused and can only be uploaded as a new file, so you can pass “attach://<file_attach_name>” if the video was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
Video string `json:"video"` Video string `json:"video"`
// Optional. Precise duration of the video in seconds; 0-60 // Optional. Precise duration of the video in seconds; 0-60
@@ -5460,7 +5460,7 @@ func (*InlineQueryResultVoice) isInlineQueryResult() {}
// Represents a link to an article or web page. // Represents a link to an article or web page.
type InlineQueryResultArticle struct { type InlineQueryResultArticle struct {
// Type of the result, must be article // Type of the result, must be article
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 Bytes // Unique identifier for this result, 1-64 Bytes
ID string `json:"id"` ID string `json:"id"`
// Title of the result // Title of the result
@@ -5500,7 +5500,7 @@ func (v *InlineQueryResultArticle) MarshalJSON() ([]byte, error) {
// Represents a link to a photo. By default, this photo will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. // Represents a link to a photo. By default, this photo will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.
type InlineQueryResultPhoto struct { type InlineQueryResultPhoto struct {
// Type of the result, must be photo // Type of the result, must be photo
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid URL of the photo. Photo must be in JPEG format. Photo size must not exceed 5MB // A valid URL of the photo. Photo must be in JPEG format. Photo size must not exceed 5MB
@@ -5548,7 +5548,7 @@ func (v *InlineQueryResultPhoto) MarshalJSON() ([]byte, error) {
// Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. // Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.
type InlineQueryResultGif struct { type InlineQueryResultGif struct {
// Type of the result, must be gif // Type of the result, must be gif
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid URL for the GIF file // A valid URL for the GIF file
@@ -5598,7 +5598,7 @@ func (v *InlineQueryResultGif) MarshalJSON() ([]byte, error) {
// Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. // Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.
type InlineQueryResultMpeg4Gif struct { type InlineQueryResultMpeg4Gif struct {
// Type of the result, must be mpeg4_gif // Type of the result, must be mpeg4_gif
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid URL for the MPEG4 file // A valid URL for the MPEG4 file
@@ -5649,7 +5649,7 @@ func (v *InlineQueryResultMpeg4Gif) MarshalJSON() ([]byte, error) {
// If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must replace its content using input_message_content. // If an InlineQueryResultVideo message contains an embedded video (e.g., YouTube), you must replace its content using input_message_content.
type InlineQueryResultVideo struct { type InlineQueryResultVideo struct {
// Type of the result, must be video // Type of the result, must be video
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid URL for the embedded video player or video file // A valid URL for the embedded video player or video file
@@ -5701,7 +5701,7 @@ func (v *InlineQueryResultVideo) MarshalJSON() ([]byte, error) {
// Represents a link to an MP3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio. // Represents a link to an MP3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.
type InlineQueryResultAudio struct { type InlineQueryResultAudio struct {
// Type of the result, must be audio // Type of the result, must be audio
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid URL for the audio file // A valid URL for the audio file
@@ -5743,7 +5743,7 @@ func (v *InlineQueryResultAudio) MarshalJSON() ([]byte, error) {
// Represents a link to a voice recording in an .OGG container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message. // Represents a link to a voice recording in an .OGG container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message.
type InlineQueryResultVoice struct { type InlineQueryResultVoice struct {
// Type of the result, must be voice // Type of the result, must be voice
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid URL for the voice recording // A valid URL for the voice recording
@@ -5783,7 +5783,7 @@ func (v *InlineQueryResultVoice) MarshalJSON() ([]byte, error) {
// Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method. // Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method.
type InlineQueryResultDocument struct { type InlineQueryResultDocument struct {
// Type of the result, must be document // Type of the result, must be document
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// Title for the result // Title for the result
@@ -5831,7 +5831,7 @@ func (v *InlineQueryResultDocument) MarshalJSON() ([]byte, error) {
// Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location. // Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location.
type InlineQueryResultLocation struct { type InlineQueryResultLocation struct {
// Type of the result, must be location // Type of the result, must be location
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 Bytes // Unique identifier for this result, 1-64 Bytes
ID string `json:"id"` ID string `json:"id"`
// Location latitude in degrees // Location latitude in degrees
@@ -5879,7 +5879,7 @@ func (v *InlineQueryResultLocation) MarshalJSON() ([]byte, error) {
// Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue. // Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue.
type InlineQueryResultVenue struct { type InlineQueryResultVenue struct {
// Type of the result, must be venue // Type of the result, must be venue
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 Bytes // Unique identifier for this result, 1-64 Bytes
ID string `json:"id"` ID string `json:"id"`
// Latitude of the venue location in degrees // Latitude of the venue location in degrees
@@ -5929,7 +5929,7 @@ func (v *InlineQueryResultVenue) MarshalJSON() ([]byte, error) {
// Represents a contact with a phone number. By default, this contact will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the contact. // Represents a contact with a phone number. By default, this contact will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the contact.
type InlineQueryResultContact struct { type InlineQueryResultContact struct {
// Type of the result, must be contact // Type of the result, must be contact
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 Bytes // Unique identifier for this result, 1-64 Bytes
ID string `json:"id"` ID string `json:"id"`
// Contact's phone number // Contact's phone number
@@ -5971,7 +5971,7 @@ func (v *InlineQueryResultContact) MarshalJSON() ([]byte, error) {
// Represents a Game. // Represents a Game.
type InlineQueryResultGame struct { type InlineQueryResultGame struct {
// Type of the result, must be game // Type of the result, must be game
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// Short name of the game // Short name of the game
@@ -5999,7 +5999,7 @@ func (v *InlineQueryResultGame) MarshalJSON() ([]byte, error) {
// Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. // Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo.
type InlineQueryResultCachedPhoto struct { type InlineQueryResultCachedPhoto struct {
// Type of the result, must be photo // Type of the result, must be photo
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid file identifier of the photo // A valid file identifier of the photo
@@ -6041,7 +6041,7 @@ func (v *InlineQueryResultCachedPhoto) MarshalJSON() ([]byte, error) {
// Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation. // Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation.
type InlineQueryResultCachedGif struct { type InlineQueryResultCachedGif struct {
// Type of the result, must be gif // Type of the result, must be gif
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid file identifier for the GIF file // A valid file identifier for the GIF file
@@ -6081,7 +6081,7 @@ func (v *InlineQueryResultCachedGif) MarshalJSON() ([]byte, error) {
// Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. // Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation.
type InlineQueryResultCachedMpeg4Gif struct { type InlineQueryResultCachedMpeg4Gif struct {
// Type of the result, must be mpeg4_gif // Type of the result, must be mpeg4_gif
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid file identifier for the MPEG4 file // A valid file identifier for the MPEG4 file
@@ -6121,7 +6121,7 @@ func (v *InlineQueryResultCachedMpeg4Gif) MarshalJSON() ([]byte, error) {
// Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker. // Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker.
type InlineQueryResultCachedSticker struct { type InlineQueryResultCachedSticker struct {
// Type of the result, must be sticker // Type of the result, must be sticker
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid file identifier of the sticker // A valid file identifier of the sticker
@@ -6151,7 +6151,7 @@ func (v *InlineQueryResultCachedSticker) MarshalJSON() ([]byte, error) {
// Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. // Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file.
type InlineQueryResultCachedDocument struct { type InlineQueryResultCachedDocument struct {
// Type of the result, must be document // Type of the result, must be document
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// Title for the result // Title for the result
@@ -6191,7 +6191,7 @@ func (v *InlineQueryResultCachedDocument) MarshalJSON() ([]byte, error) {
// Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video. // Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video.
type InlineQueryResultCachedVideo struct { type InlineQueryResultCachedVideo struct {
// Type of the result, must be video // Type of the result, must be video
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid file identifier for the video file // A valid file identifier for the video file
@@ -6233,7 +6233,7 @@ func (v *InlineQueryResultCachedVideo) MarshalJSON() ([]byte, error) {
// Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message. // Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message.
type InlineQueryResultCachedVoice struct { type InlineQueryResultCachedVoice struct {
// Type of the result, must be voice // Type of the result, must be voice
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid file identifier for the voice message // A valid file identifier for the voice message
@@ -6271,7 +6271,7 @@ func (v *InlineQueryResultCachedVoice) MarshalJSON() ([]byte, error) {
// Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio. // Represents a link to an MP3 audio file stored on the Telegram servers. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio.
type InlineQueryResultCachedAudio struct { type InlineQueryResultCachedAudio struct {
// Type of the result, must be audio // Type of the result, must be audio
Type string `json:"type"` Type InlineQueryResultType `json:"type"`
// Unique identifier for this result, 1-64 bytes // Unique identifier for this result, 1-64 bytes
ID string `json:"id"` ID string `json:"id"`
// A valid file identifier for the audio file // A valid file identifier for the audio file
@@ -7172,7 +7172,7 @@ func (*PassportElementErrorUnspecified) isPassportElementError() {}
// Represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes. // Represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes.
type PassportElementErrorDataField struct { type PassportElementErrorDataField struct {
// Error source, must be data // Error source, must be data
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// The section of the user's Telegram Passport which has the error, one of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address” // The section of the user's Telegram Passport which has the error, one of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”
Type PassportElementErrorDataFieldType `json:"type"` Type PassportElementErrorDataFieldType `json:"type"`
// Name of the data field which has the error // Name of the data field which has the error
@@ -7202,7 +7202,7 @@ func (v *PassportElementErrorDataField) MarshalJSON() ([]byte, error) {
// Represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes. // Represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes.
type PassportElementErrorFrontSide struct { type PassportElementErrorFrontSide struct {
// Error source, must be front_side // Error source, must be front_side
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport” // The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”
Type PassportElementErrorSelfieType `json:"type"` Type PassportElementErrorSelfieType `json:"type"`
// Base64-encoded hash of the file with the front side of the document // Base64-encoded hash of the file with the front side of the document
@@ -7230,7 +7230,7 @@ func (v *PassportElementErrorFrontSide) MarshalJSON() ([]byte, error) {
// Represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes. // Represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes.
type PassportElementErrorReverseSide struct { type PassportElementErrorReverseSide struct {
// Error source, must be reverse_side // Error source, must be reverse_side
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “driver_license”, “identity_card” // The section of the user's Telegram Passport which has the issue, one of “driver_license”, “identity_card”
Type PassportElementErrorReverseSideType `json:"type"` Type PassportElementErrorReverseSideType `json:"type"`
// Base64-encoded hash of the file with the reverse side of the document // Base64-encoded hash of the file with the reverse side of the document
@@ -7258,7 +7258,7 @@ func (v *PassportElementErrorReverseSide) MarshalJSON() ([]byte, error) {
// Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes. // Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes.
type PassportElementErrorSelfie struct { type PassportElementErrorSelfie struct {
// Error source, must be selfie // Error source, must be selfie
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport” // The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”
Type PassportElementErrorSelfieType `json:"type"` Type PassportElementErrorSelfieType `json:"type"`
// Base64-encoded hash of the file with the selfie // Base64-encoded hash of the file with the selfie
@@ -7286,7 +7286,7 @@ func (v *PassportElementErrorSelfie) MarshalJSON() ([]byte, error) {
// Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes. // Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes.
type PassportElementErrorFile struct { type PassportElementErrorFile struct {
// Error source, must be file // Error source, must be file
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” // The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
Type PassportElementErrorFileType `json:"type"` Type PassportElementErrorFileType `json:"type"`
// Base64-encoded file hash // Base64-encoded file hash
@@ -7314,7 +7314,7 @@ func (v *PassportElementErrorFile) MarshalJSON() ([]byte, error) {
// Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes. // Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes.
type PassportElementErrorFiles struct { type PassportElementErrorFiles struct {
// Error source, must be files // Error source, must be files
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” // The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
Type PassportElementErrorFileType `json:"type"` Type PassportElementErrorFileType `json:"type"`
// List of base64-encoded file hashes // List of base64-encoded file hashes
@@ -7342,7 +7342,7 @@ func (v *PassportElementErrorFiles) MarshalJSON() ([]byte, error) {
// Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes. // Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes.
type PassportElementErrorTranslationFile struct { type PassportElementErrorTranslationFile struct {
// Error source, must be translation_file // Error source, must be translation_file
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” // Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
Type PassportElementErrorTranslationFileType `json:"type"` Type PassportElementErrorTranslationFileType `json:"type"`
// Base64-encoded file hash // Base64-encoded file hash
@@ -7370,7 +7370,7 @@ func (v *PassportElementErrorTranslationFile) MarshalJSON() ([]byte, error) {
// Represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation change. // Represents an issue with the translated version of a document. The error is considered resolved when a file with the document translation change.
type PassportElementErrorTranslationFiles struct { type PassportElementErrorTranslationFiles struct {
// Error source, must be translation_files // Error source, must be translation_files
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration” // Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
Type PassportElementErrorTranslationFileType `json:"type"` Type PassportElementErrorTranslationFileType `json:"type"`
// List of base64-encoded file hashes // List of base64-encoded file hashes
@@ -7398,7 +7398,7 @@ func (v *PassportElementErrorTranslationFiles) MarshalJSON() ([]byte, error) {
// Represents an issue in an unspecified place. The error is considered resolved when new data is added. // Represents an issue in an unspecified place. The error is considered resolved when new data is added.
type PassportElementErrorUnspecified struct { type PassportElementErrorUnspecified struct {
// Error source, must be unspecified // Error source, must be unspecified
Source string `json:"source"` Source PassportElementErrorSource `json:"source"`
// Type of element of the user's Telegram Passport which has the issue // Type of element of the user's Telegram Passport which has the issue
Type string `json:"type"` Type string `json:"type"`
// Base64-encoded element hash // Base64-encoded element hash
+87 -1
View File
@@ -27,6 +27,33 @@ import (
// emits the canonical Markdown / MarkdownV2 / HTML triple. // emits the canonical Markdown / MarkdownV2 / HTML triple.
// //
// Returns nil when the description does not look like an enum. // Returns nil when the description does not look like an enum.
// extractEnumValues inspects a field-description string and returns the
// list of wire-level string values when the description matches one of
// the enum-like patterns Telegram uses in its docs. Order follows doc
// order; duplicates are removed but order of first occurrence is kept.
//
// Handled patterns (curly quotes “…” are required to avoid false
// positives on free-text quoting):
//
// - "Type of the chat, can be either “private”, “group”, … or “channel”"
// - "Currently, can be “mention”, “hashtag”, …"
// - "Currently, one of “XTR” … or “TON” …"
// - "Currently, must be one of “XTR” …"
// - "Currently, it can be one of “pending”, “approved”, “declined”."
// - "Must be one of “danger” …, “success” …"
// - "Must be one of “image/jpeg”, “image/gif”, or “video/mp4”"
// - "Format … must be one of “static” …, “animated” …, “video” …"
// - "Currently, either “upgrade” …, “transfer” …, “resale” …"
// - "..., always “creator”"
// - parse_mode parameter special case ("Mode for parsing entities …")
// emits the canonical Markdown / MarkdownV2 / HTML triple.
// - bare prose discriminator at end of description, e.g.
// "Type of the result, must be article" or
// "Scope type, must be all_private_chats". Used by sealed-interface
// union variants whose Type/Source field carries a single literal
// value declared without curly quotes.
//
// Returns nil when the description does not look like an enum.
func extractEnumValues(jsonName, desc string) []string { func extractEnumValues(jsonName, desc string) []string {
if values := parseModeEnumValues(jsonName, desc); values != nil { if values := parseModeEnumValues(jsonName, desc); values != nil {
return values return values
@@ -34,12 +61,15 @@ func extractEnumValues(jsonName, desc string) []string {
trigger, triggerEnd, isAlways := findEnumTrigger(desc) trigger, triggerEnd, isAlways := findEnumTrigger(desc)
if trigger < 0 { if trigger < 0 {
return nil return extractProseDiscriminator(desc)
} }
tail := desc[trigger:] tail := desc[trigger:]
values := collectQuotedValues(tail) values := collectQuotedValues(tail)
if len(values) == 0 { if len(values) == 0 {
if v := extractProseDiscriminator(desc); v != nil {
return v
}
return nil return nil
} }
// First quoted value must sit close to the trigger phrase (e.g. // First quoted value must sit close to the trigger phrase (e.g.
@@ -203,3 +233,59 @@ func dedupeStrings(in []string) []string {
} }
return out return out
} }
// proseDiscRE matches a terminal "must be <ident>" clause: the
// discriminator value sits at the END of the description (optionally
// followed by trailing punctuation/whitespace) so multi-clause prose
// like "must be shown above the message" is not picked up.
//
// The identifier is a snake_case wire literal: lowercase letters, digits,
// and underscores, starting with a letter. Numeric-only and prose words
// are filtered separately by isProseWord.
var proseDiscRE = regexp.MustCompile(`(?i)\bmust be\s+([a-z][a-z0-9_]*)\s*[.,]?\s*$`)
// extractProseDiscriminator detects unambiguous single-value
// discriminator declarations of the form "..., must be <ident>" used by
// sealed-interface union variants (e.g. "Type of the result, must be
// article" or "Scope type, must be all_private_chats"). Returns the
// extracted value as a one-element slice or nil when no match is found.
//
// The terminal-position anchor is what protects against prose like
// "must be shown above" or "must be one of 3, 6, or 12" — the candidate
// must close the description.
func extractProseDiscriminator(desc string) []string {
desc = strings.TrimSpace(desc)
if desc == "" {
return nil
}
m := proseDiscRE.FindStringSubmatch(desc)
if m == nil {
return nil
}
v := m[1]
if isProseWord(v) {
return nil
}
return []string{v}
}
// isProseWord rejects bare-prose continuations that pass the regex but
// are clearly English filler ("must be sent", "must be available"). The
// list is the closed set of words that empirically appear in the IR's
// "must be …" tails outside the variant-discriminator pattern. Wire
// identifiers are always single tokens with no English meaning, so any
// match here is a free-text false positive.
func isProseWord(s string) bool {
switch s {
case "a", "an", "the",
"sent", "shown", "set", "used", "passed", "specified", "available",
"applied", "supported", "assumed", "active", "paid", "between",
"of", "on", "in", "at", "by", "to", "from", "for", "with",
"and", "or", "no", "non",
"positive", "negative",
"administrator", "repainted",
"one", "exactly":
return true
}
return false
}
+53
View File
@@ -83,3 +83,56 @@ func TestExtractEnumValues_DedupeRepeatedValues(t *testing.T) {
got := extractEnumValues("currency", desc) got := extractEnumValues("currency", desc)
require.Equal(t, []string{"XTR"}, got) require.Equal(t, []string{"XTR"}, got)
} }
func TestExtractEnumValues_ProseDiscriminator(t *testing.T) {
cases := []struct {
name string
desc string
want []string
}{
{"InlineQueryResultArticle", "Type of the result, must be article", []string{"article"}},
{"InlineQueryResultPhoto", "Type of the result, must be photo", []string{"photo"}},
{"InlineQueryResultMpeg4Gif", "Type of the result, must be mpeg4_gif", []string{"mpeg4_gif"}},
{"BotCommandScopeAllPrivateChats", "Scope type, must be all_private_chats", []string{"all_private_chats"}},
{"BotCommandScopeChat", "Scope type, must be chat", []string{"chat"}},
{"PassportElementErrorData", "Error source, must be data", []string{"data"}},
{"MenuButtonWebApp", "Type of the button, must be web_app", []string{"web_app"}},
{"InputProfilePhotoAnimated", "Type of the profile photo, must be animated", []string{"animated"}},
{"InputStoryContentVideo", "Type of the content, must be video", []string{"video"}},
{"InputPaidMediaPhoto", "Type of the media, must be photo", []string{"photo"}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
require.Equal(t, tc.want, extractEnumValues("type", tc.desc))
})
}
}
func TestExtractEnumValues_ProseFalsePositives(t *testing.T) {
cases := []struct {
name string
desc string
}{
{"available_only_for", "Optional. Bot-specified invoice payload. Can be available only for “invoice_payment” transactions."},
{"must_be_sent", "If True, the message must be sent immediately."},
{"must_be_shown_above", "Optional. True, if the link preview must be shown above the message text"},
{"must_be_specified", "The identifiers must be specified in a strictly increasing order."},
{"must_be_paid", "The number of Telegram Stars that must be paid to send the sticker"},
{"must_be_one_of_numbers", "Number of months the Telegram Premium subscription will be active for the user; must be one of 3, 6, or 12"},
{"must_be_between", "Currently, price in Telegram Stars must be between 5 and 100000"},
{"must_be_a_pay_button", "If not empty, the first button must be a Pay button."},
{"must_be_repainted", "True, if the sticker must be repainted to a text color in messages"},
{"must_be_active", "the subscription must be active up to the end of the current subscription period"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
require.Nil(t, extractEnumValues("type", tc.desc))
})
}
}
func TestExtractEnumValues_CanonicalMustBeOneOfStillWorks(t *testing.T) {
desc := "Currently, must be one of “Markdown”, “MarkdownV2”, “HTML”"
got := extractEnumValues("parse_mode_kind", desc)
require.Equal(t, []string{"Markdown", "MarkdownV2", "HTML"}, got)
}
+318 -88
View File
File diff suppressed because it is too large Load Diff
+220 -55
View File
@@ -10529,7 +10529,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Scope type, must be default" "doc": "Scope type, must be default",
"enum_values": [
"default"
]
} }
] ]
}, },
@@ -10545,7 +10548,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Scope type, must be all_private_chats" "doc": "Scope type, must be all_private_chats",
"enum_values": [
"all_private_chats"
]
} }
] ]
}, },
@@ -10561,7 +10567,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Scope type, must be all_group_chats" "doc": "Scope type, must be all_group_chats",
"enum_values": [
"all_group_chats"
]
} }
] ]
}, },
@@ -10577,7 +10586,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Scope type, must be all_chat_administrators" "doc": "Scope type, must be all_chat_administrators",
"enum_values": [
"all_chat_administrators"
]
} }
] ]
}, },
@@ -10593,7 +10605,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Scope type, must be chat" "doc": "Scope type, must be chat",
"enum_values": [
"chat"
]
}, },
{ {
"name": "ChatID", "name": "ChatID",
@@ -10622,7 +10637,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Scope type, must be chat_administrators" "doc": "Scope type, must be chat_administrators",
"enum_values": [
"chat_administrators"
]
}, },
{ {
"name": "ChatID", "name": "ChatID",
@@ -10651,7 +10669,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Scope type, must be chat_member" "doc": "Scope type, must be chat_member",
"enum_values": [
"chat_member"
]
}, },
{ {
"name": "ChatID", "name": "ChatID",
@@ -10747,7 +10768,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the button, must be commands" "doc": "Type of the button, must be commands",
"enum_values": [
"commands"
]
} }
] ]
}, },
@@ -10763,7 +10787,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the button, must be web_app" "doc": "Type of the button, must be web_app",
"enum_values": [
"web_app"
]
}, },
{ {
"name": "Text", "name": "Text",
@@ -10799,7 +10826,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the button, must be default" "doc": "Type of the button, must be default",
"enum_values": [
"default"
]
} }
] ]
}, },
@@ -11451,7 +11481,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be animation" "doc": "Type of the result, must be animation",
"enum_values": [
"animation"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -11566,7 +11599,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be audio" "doc": "Type of the result, must be audio",
"enum_values": [
"audio"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -11663,7 +11699,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be document" "doc": "Type of the result, must be document",
"enum_values": [
"document"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -11742,7 +11781,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be live_photo" "doc": "Type of the result, must be live_photo",
"enum_values": [
"live_photo"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -11831,7 +11873,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be location" "doc": "Type of the result, must be location",
"enum_values": [
"location"
]
}, },
{ {
"name": "Latitude", "name": "Latitude",
@@ -11876,7 +11921,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be photo" "doc": "Type of the result, must be photo",
"enum_values": [
"photo"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -11955,7 +12003,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be sticker" "doc": "Type of the result, must be sticker",
"enum_values": [
"sticker"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -11990,7 +12041,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be venue" "doc": "Type of the result, must be venue",
"enum_values": [
"venue"
]
}, },
{ {
"name": "Latitude", "name": "Latitude",
@@ -12082,7 +12136,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be video" "doc": "Type of the result, must be video",
"enum_values": [
"video"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -12237,7 +12294,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the media, must be live_photo" "doc": "Type of the media, must be live_photo",
"enum_values": [
"live_photo"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -12273,7 +12333,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the media, must be photo" "doc": "Type of the media, must be photo",
"enum_values": [
"photo"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -12299,7 +12362,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the media, must be video" "doc": "Type of the media, must be video",
"enum_values": [
"video"
]
}, },
{ {
"name": "Media", "name": "Media",
@@ -12396,7 +12462,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the profile photo, must be static" "doc": "Type of the profile photo, must be static",
"enum_values": [
"static"
]
}, },
{ {
"name": "Photo", "name": "Photo",
@@ -12422,7 +12491,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the profile photo, must be animated" "doc": "Type of the profile photo, must be animated",
"enum_values": [
"animated"
]
}, },
{ {
"name": "Animation", "name": "Animation",
@@ -12465,7 +12537,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the content, must be photo" "doc": "Type of the content, must be photo",
"enum_values": [
"photo"
]
}, },
{ {
"name": "Photo", "name": "Photo",
@@ -12491,7 +12566,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the content, must be video" "doc": "Type of the content, must be video",
"enum_values": [
"video"
]
}, },
{ {
"name": "Video", "name": "Video",
@@ -13008,7 +13086,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be article" "doc": "Type of the result, must be article",
"enum_values": [
"article"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -13108,7 +13189,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be photo" "doc": "Type of the result, must be photo",
"enum_values": [
"photo"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -13252,7 +13336,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be gif" "doc": "Type of the result, must be gif",
"enum_values": [
"gif"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -13410,7 +13497,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be mpeg4_gif" "doc": "Type of the result, must be mpeg4_gif",
"enum_values": [
"mpeg4_gif"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -13568,7 +13658,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be video" "doc": "Type of the result, must be video",
"enum_values": [
"video"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -13732,7 +13825,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be audio" "doc": "Type of the result, must be audio",
"enum_values": [
"audio"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -13849,7 +13945,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be voice" "doc": "Type of the result, must be voice",
"enum_values": [
"voice"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -13957,7 +14056,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be document" "doc": "Type of the result, must be document",
"enum_values": [
"document"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14106,7 +14208,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be location" "doc": "Type of the result, must be location",
"enum_values": [
"location"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14243,7 +14348,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be venue" "doc": "Type of the result, must be venue",
"enum_values": [
"venue"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14390,7 +14498,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be contact" "doc": "Type of the result, must be contact",
"enum_values": [
"contact"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14499,7 +14610,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be game" "doc": "Type of the result, must be game",
"enum_values": [
"game"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14544,7 +14658,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be photo" "doc": "Type of the result, must be photo",
"enum_values": [
"photo"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14660,7 +14777,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be gif" "doc": "Type of the result, must be gif",
"enum_values": [
"gif"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14767,7 +14887,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be mpeg4_gif" "doc": "Type of the result, must be mpeg4_gif",
"enum_values": [
"mpeg4_gif"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14874,7 +14997,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be sticker" "doc": "Type of the result, must be sticker",
"enum_values": [
"sticker"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -14928,7 +15054,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be document" "doc": "Type of the result, must be document",
"enum_values": [
"document"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -15036,7 +15165,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be video" "doc": "Type of the result, must be video",
"enum_values": [
"video"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -15153,7 +15285,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be voice" "doc": "Type of the result, must be voice",
"enum_values": [
"voice"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -15252,7 +15387,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Type of the result, must be audio" "doc": "Type of the result, must be audio",
"enum_values": [
"audio"
]
}, },
{ {
"name": "ID", "name": "ID",
@@ -17138,7 +17276,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be data" "doc": "Error source, must be data",
"enum_values": [
"data"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17202,7 +17343,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be front_side" "doc": "Error source, must be front_side",
"enum_values": [
"front_side"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17254,7 +17398,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be reverse_side" "doc": "Error source, must be reverse_side",
"enum_values": [
"reverse_side"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17304,7 +17451,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be selfie" "doc": "Error source, must be selfie",
"enum_values": [
"selfie"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17356,7 +17506,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be file" "doc": "Error source, must be file",
"enum_values": [
"file"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17409,7 +17562,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be files" "doc": "Error source, must be files",
"enum_values": [
"files"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17465,7 +17621,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be translation_file" "doc": "Error source, must be translation_file",
"enum_values": [
"translation_file"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17522,7 +17681,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be translation_files" "doc": "Error source, must be translation_files",
"enum_values": [
"translation_files"
]
}, },
{ {
"name": "Type", "name": "Type",
@@ -17582,7 +17744,10 @@
"name": "string" "name": "string"
}, },
"required": true, "required": true,
"doc": "Error source, must be unspecified" "doc": "Error source, must be unspecified",
"enum_values": [
"unspecified"
]
}, },
{ {
"name": "Type", "name": "Type",