feat(api): typed enums for all string-enum fields

The Telegram docs describe many string fields and parameters with
phrases like "can be ..., or ...", "must be one of ...", or "always X",
yet the generated Go API surface used raw `string` for every one of
them. Callers had to write magic strings or `string(api.ChatTypePrivate)`
to satisfy the field type. This change makes those fields typed Go
string enums emitted from the IR, so the IDE autocompletes valid values
and breaking-value drift surfaces at compile time.

Pipeline changes:

- internal/spec/ir.go: Field gains EnumValues []string. Empty for non-
  enum fields; otherwise the wire-level values in doc order, deduped.

- cmd/scrape/enums.go: extractEnumValues recognises the curly-quoted
  patterns Telegram uses ("can be either", "currently can be", "one
  of", "must be", "always X") and rejects free-text quoted refs (e.g.
  "Can be available only for X") via a tight gap check between the
  trigger phrase and the first quoted value. parse_mode parameters
  get the canonical Markdown / MarkdownV2 / HTML triple injected
  because Telegram links to a separate formatting-options section
  instead of listing values inline.

- cmd/genapi/enums.go: planEnums groups fields by sorted value-tuple,
  picks a canonical Go enum name (most-common candidate, parent-
  prefixed beats plain, shortest beats longer, alphabetical for
  determinism), resolves cross-group name collisions by parent prefix.

- cmd/genapi/emitter.go + templates: goField rewrites the field type
  to the planned enum name; multipartFieldEntry casts typed enum
  values back to string when composing the wire map; enums.tmpl now
  iterates the planned enums instead of hardcoding four hand-curated
  ones; sentinelForField produces typed-constant test fixtures.

- api/enums.gen.go: regenerated from the live IR. 66 enum types, 155
  constants. ParseMode, ChatType, MessageEntityType, ChatMember /
  MessageOrigin / PaidMedia / Background / StoryAreaType / Reaction /
  TransactionPartner / PassportElement variant Status & Type fields
  are now typed.

- api/enums.go: hand-coded UpdateType (used by transport.LongPoller).
  The Telegram docs do not enumerate Update payload kinds inline, so
  the codegen pipeline cannot synthesise this enum.

- api/types.gen.go, api/methods.gen.go, api/methods_gen_test.go: 137
  field declarations rewritten string -> typed enum.

- dispatch/, examples/: dropped every string(api.<Const>) cast. The
  HasEntity filter now takes api.MessageEntityType; ChatType filter
  compares typed values directly. ChatMember discriminator filter
  casts variant.Status (typed per variant) to string for comparison.

- internal/spec/api.json, testdata/golden/*: regenerated and
  refreshed. make regen-from-fixture is byte-deterministic across
  runs.

Renames (no compat shims; v1 pre-public):
- EntityX  -> MessageEntityTypeX  (e.g. EntityBotCommand -> MessageEntityTypeBotCommand)
- EntityStrike -> MessageEntityTypeStrikethrough (full wire name)
This commit is contained in:
2026-05-09 17:55:34 +01:00
parent 1da759ba8a
commit 3c04d7b0b1
32 changed files with 3487 additions and 668 deletions
+463 -33
View File
@@ -4,16 +4,102 @@
package api
// ParseMode controls how Telegram interprets formatting in message text.
type ParseMode string
type BackgroundFillFreeformGradientType string
const (
ParseModeMarkdown ParseMode = "Markdown" // legacy
ParseModeMarkdownV2 ParseMode = "MarkdownV2"
ParseModeHTML ParseMode = "HTML"
BackgroundFillFreeformGradientTypeFreeformGradient BackgroundFillFreeformGradientType = "freeform_gradient"
)
type BackgroundFillGradientType string
const (
BackgroundFillGradientTypeGradient BackgroundFillGradientType = "gradient"
)
type BackgroundFillSolidType string
const (
BackgroundFillSolidTypeSolid BackgroundFillSolidType = "solid"
)
type BackgroundTypeChatThemeType string
const (
BackgroundTypeChatThemeTypeChatTheme BackgroundTypeChatThemeType = "chat_theme"
)
type BackgroundTypeFillType string
const (
BackgroundTypeFillTypeFill BackgroundTypeFillType = "fill"
)
type BackgroundTypePatternType string
const (
BackgroundTypePatternTypePattern BackgroundTypePatternType = "pattern"
)
type BackgroundTypeWallpaperType string
const (
BackgroundTypeWallpaperTypeWallpaper BackgroundTypeWallpaperType = "wallpaper"
)
type ChatBoostSourceGiftCodeSource string
const (
ChatBoostSourceGiftCodeSourceGiftCode ChatBoostSourceGiftCodeSource = "gift_code"
)
type ChatBoostSourceGiveawaySource string
const (
ChatBoostSourceGiveawaySourceGiveaway ChatBoostSourceGiveawaySource = "giveaway"
)
type ChatBoostSourcePremiumSource string
const (
ChatBoostSourcePremiumSourcePremium ChatBoostSourcePremiumSource = "premium"
)
type ChatMemberAdministratorStatus string
const (
ChatMemberAdministratorStatusAdministrator ChatMemberAdministratorStatus = "administrator"
)
type ChatMemberBannedStatus string
const (
ChatMemberBannedStatusKicked ChatMemberBannedStatus = "kicked"
)
type ChatMemberLeftStatus string
const (
ChatMemberLeftStatusLeft ChatMemberLeftStatus = "left"
)
type ChatMemberMemberStatus string
const (
ChatMemberMemberStatusMember ChatMemberMemberStatus = "member"
)
type ChatMemberOwnerStatus string
const (
ChatMemberOwnerStatusCreator ChatMemberOwnerStatus = "creator"
)
type ChatMemberRestrictedStatus string
const (
ChatMemberRestrictedStatusRestricted ChatMemberRestrictedStatus = "restricted"
)
// ChatType is the type of a Telegram chat.
type ChatType string
const (
@@ -23,38 +109,382 @@ const (
ChatTypeChannel ChatType = "channel"
)
// UpdateType identifies an Update payload variant. Used by allowed_updates
// in getUpdates / setWebhook.
type UpdateType string
type EncryptedPassportElementType string
const (
UpdateMessage UpdateType = "message"
UpdateEditedMessage UpdateType = "edited_message"
UpdateChannelPost UpdateType = "channel_post"
UpdateEditedChannelPost UpdateType = "edited_channel_post"
UpdateCallbackQuery UpdateType = "callback_query"
UpdateInlineQuery UpdateType = "inline_query"
EncryptedPassportElementTypePersonalDetails EncryptedPassportElementType = "personal_details"
EncryptedPassportElementTypePassport EncryptedPassportElementType = "passport"
EncryptedPassportElementTypeDriverLicense EncryptedPassportElementType = "driver_license"
EncryptedPassportElementTypeIdentityCard EncryptedPassportElementType = "identity_card"
EncryptedPassportElementTypeInternalPassport EncryptedPassportElementType = "internal_passport"
EncryptedPassportElementTypeAddress EncryptedPassportElementType = "address"
EncryptedPassportElementTypeUtilityBill EncryptedPassportElementType = "utility_bill"
EncryptedPassportElementTypeBankStatement EncryptedPassportElementType = "bank_statement"
EncryptedPassportElementTypeRentalAgreement EncryptedPassportElementType = "rental_agreement"
EncryptedPassportElementTypePassportRegistration EncryptedPassportElementType = "passport_registration"
EncryptedPassportElementTypeTemporaryRegistration EncryptedPassportElementType = "temporary_registration"
EncryptedPassportElementTypePhoneNumber EncryptedPassportElementType = "phone_number"
EncryptedPassportElementTypeEmail EncryptedPassportElementType = "email"
)
type InlineQueryChatType string
const (
InlineQueryChatTypeSender InlineQueryChatType = "sender"
InlineQueryChatTypePrivate InlineQueryChatType = "private"
InlineQueryChatTypeGroup InlineQueryChatType = "group"
InlineQueryChatTypeSupergroup InlineQueryChatType = "supergroup"
InlineQueryChatTypeChannel InlineQueryChatType = "channel"
)
type InlineQueryResultDocumentMimeType string
const (
InlineQueryResultDocumentMimeTypeApplicationOfPdf InlineQueryResultDocumentMimeType = "application/pdf"
InlineQueryResultDocumentMimeTypeApplicationOfZip InlineQueryResultDocumentMimeType = "application/zip"
)
type InlineQueryResultGifThumbnailMimeType string
const (
InlineQueryResultGifThumbnailMimeTypeImageOfJpeg InlineQueryResultGifThumbnailMimeType = "image/jpeg"
InlineQueryResultGifThumbnailMimeTypeImageOfGif InlineQueryResultGifThumbnailMimeType = "image/gif"
InlineQueryResultGifThumbnailMimeTypeVideoOfMp4 InlineQueryResultGifThumbnailMimeType = "video/mp4"
)
type InputStickerFormat string
const (
InputStickerFormatStatic InputStickerFormat = "static"
InputStickerFormatAnimated InputStickerFormat = "animated"
InputStickerFormatVideo InputStickerFormat = "video"
)
type KeyboardButtonStyle string
const (
KeyboardButtonStyleDanger KeyboardButtonStyle = "danger"
KeyboardButtonStyleSuccess KeyboardButtonStyle = "success"
KeyboardButtonStylePrimary KeyboardButtonStyle = "primary"
)
type MaskPositionPoint string
const (
MaskPositionPointForehead MaskPositionPoint = "forehead"
MaskPositionPointEyes MaskPositionPoint = "eyes"
MaskPositionPointMouth MaskPositionPoint = "mouth"
MaskPositionPointChin MaskPositionPoint = "chin"
)
// MessageEntityType is the kind of an entity (mention, hashtag, command, ...).
type MessageEntityType string
const (
EntityMention MessageEntityType = "mention"
EntityHashtag MessageEntityType = "hashtag"
EntityCashtag MessageEntityType = "cashtag"
EntityBotCommand MessageEntityType = "bot_command"
EntityURL MessageEntityType = "url"
EntityEmail MessageEntityType = "email"
EntityPhoneNumber MessageEntityType = "phone_number"
EntityBold MessageEntityType = "bold"
EntityItalic MessageEntityType = "italic"
EntityUnderline MessageEntityType = "underline"
EntityStrike MessageEntityType = "strikethrough"
EntitySpoiler MessageEntityType = "spoiler"
EntityCode MessageEntityType = "code"
EntityPre MessageEntityType = "pre"
EntityTextLink MessageEntityType = "text_link"
EntityTextMention MessageEntityType = "text_mention"
EntityCustomEmoji MessageEntityType = "custom_emoji"
MessageEntityTypeMention MessageEntityType = "mention"
MessageEntityTypeHashtag MessageEntityType = "hashtag"
MessageEntityTypeCashtag MessageEntityType = "cashtag"
MessageEntityTypeBotCommand MessageEntityType = "bot_command"
MessageEntityTypeURL MessageEntityType = "url"
MessageEntityTypeEmail MessageEntityType = "email"
MessageEntityTypePhoneNumber MessageEntityType = "phone_number"
MessageEntityTypeBold MessageEntityType = "bold"
MessageEntityTypeItalic MessageEntityType = "italic"
MessageEntityTypeUnderline MessageEntityType = "underline"
MessageEntityTypeStrikethrough MessageEntityType = "strikethrough"
MessageEntityTypeSpoiler MessageEntityType = "spoiler"
MessageEntityTypeBlockquote MessageEntityType = "blockquote"
MessageEntityTypeExpandableBlockquote MessageEntityType = "expandable_blockquote"
MessageEntityTypeCode MessageEntityType = "code"
MessageEntityTypePre MessageEntityType = "pre"
MessageEntityTypeTextLink MessageEntityType = "text_link"
MessageEntityTypeTextMention MessageEntityType = "text_mention"
MessageEntityTypeCustomEmoji MessageEntityType = "custom_emoji"
MessageEntityTypeDateTime MessageEntityType = "date_time"
)
type MessageOriginChannelType string
const (
MessageOriginChannelTypeChannel MessageOriginChannelType = "channel"
)
type MessageOriginChatType string
const (
MessageOriginChatTypeChat MessageOriginChatType = "chat"
)
type MessageOriginHiddenUserType string
const (
MessageOriginHiddenUserTypeHiddenUser MessageOriginHiddenUserType = "hidden_user"
)
type MessageOriginUserType string
const (
MessageOriginUserTypeUser MessageOriginUserType = "user"
)
type OwnedGiftRegularType string
const (
OwnedGiftRegularTypeRegular OwnedGiftRegularType = "regular"
)
type OwnedGiftUniqueType string
const (
OwnedGiftUniqueTypeUnique OwnedGiftUniqueType = "unique"
)
type PaidMediaLivePhotoType string
const (
PaidMediaLivePhotoTypeLivePhoto PaidMediaLivePhotoType = "live_photo"
)
type PaidMediaPhotoType string
const (
PaidMediaPhotoTypePhoto PaidMediaPhotoType = "photo"
)
type PaidMediaPreviewType string
const (
PaidMediaPreviewTypePreview PaidMediaPreviewType = "preview"
)
type PaidMediaVideoType string
const (
PaidMediaVideoTypeVideo PaidMediaVideoType = "video"
)
type ParseMode string
const (
ParseModeMarkdown ParseMode = "Markdown"
ParseModeMarkdownV2 ParseMode = "MarkdownV2"
ParseModeHTML ParseMode = "HTML"
)
type PassportElementErrorDataFieldType string
const (
PassportElementErrorDataFieldTypePersonalDetails PassportElementErrorDataFieldType = "personal_details"
PassportElementErrorDataFieldTypePassport PassportElementErrorDataFieldType = "passport"
PassportElementErrorDataFieldTypeDriverLicense PassportElementErrorDataFieldType = "driver_license"
PassportElementErrorDataFieldTypeIdentityCard PassportElementErrorDataFieldType = "identity_card"
PassportElementErrorDataFieldTypeInternalPassport PassportElementErrorDataFieldType = "internal_passport"
PassportElementErrorDataFieldTypeAddress PassportElementErrorDataFieldType = "address"
)
type PassportElementErrorFileType string
const (
PassportElementErrorFileTypeUtilityBill PassportElementErrorFileType = "utility_bill"
PassportElementErrorFileTypeBankStatement PassportElementErrorFileType = "bank_statement"
PassportElementErrorFileTypeRentalAgreement PassportElementErrorFileType = "rental_agreement"
PassportElementErrorFileTypePassportRegistration PassportElementErrorFileType = "passport_registration"
PassportElementErrorFileTypeTemporaryRegistration PassportElementErrorFileType = "temporary_registration"
)
type PassportElementErrorReverseSideType string
const (
PassportElementErrorReverseSideTypeDriverLicense PassportElementErrorReverseSideType = "driver_license"
PassportElementErrorReverseSideTypeIdentityCard PassportElementErrorReverseSideType = "identity_card"
)
type PassportElementErrorSelfieType string
const (
PassportElementErrorSelfieTypePassport PassportElementErrorSelfieType = "passport"
PassportElementErrorSelfieTypeDriverLicense PassportElementErrorSelfieType = "driver_license"
PassportElementErrorSelfieTypeIdentityCard PassportElementErrorSelfieType = "identity_card"
PassportElementErrorSelfieTypeInternalPassport PassportElementErrorSelfieType = "internal_passport"
)
type PassportElementErrorTranslationFileType string
const (
PassportElementErrorTranslationFileTypePassport PassportElementErrorTranslationFileType = "passport"
PassportElementErrorTranslationFileTypeDriverLicense PassportElementErrorTranslationFileType = "driver_license"
PassportElementErrorTranslationFileTypeIdentityCard PassportElementErrorTranslationFileType = "identity_card"
PassportElementErrorTranslationFileTypeInternalPassport PassportElementErrorTranslationFileType = "internal_passport"
PassportElementErrorTranslationFileTypeUtilityBill PassportElementErrorTranslationFileType = "utility_bill"
PassportElementErrorTranslationFileTypeBankStatement PassportElementErrorTranslationFileType = "bank_statement"
PassportElementErrorTranslationFileTypeRentalAgreement PassportElementErrorTranslationFileType = "rental_agreement"
PassportElementErrorTranslationFileTypePassportRegistration PassportElementErrorTranslationFileType = "passport_registration"
PassportElementErrorTranslationFileTypeTemporaryRegistration PassportElementErrorTranslationFileType = "temporary_registration"
)
type PollType string
const (
PollTypeRegular PollType = "regular"
PollTypeQuiz PollType = "quiz"
)
type ReactionTypeCustomEmojiType string
const (
ReactionTypeCustomEmojiTypeCustomEmoji ReactionTypeCustomEmojiType = "custom_emoji"
)
type ReactionTypeEmojiType string
const (
ReactionTypeEmojiTypeEmoji ReactionTypeEmojiType = "emoji"
)
type ReactionTypePaidType string
const (
ReactionTypePaidTypePaid ReactionTypePaidType = "paid"
)
type RefundedPaymentCurrency string
const (
RefundedPaymentCurrencyXTR RefundedPaymentCurrency = "XTR"
)
type RevenueWithdrawalStateFailedType string
const (
RevenueWithdrawalStateFailedTypeFailed RevenueWithdrawalStateFailedType = "failed"
)
type RevenueWithdrawalStatePendingType string
const (
RevenueWithdrawalStatePendingTypePending RevenueWithdrawalStatePendingType = "pending"
)
type RevenueWithdrawalStateSucceededType string
const (
RevenueWithdrawalStateSucceededTypeSucceeded RevenueWithdrawalStateSucceededType = "succeeded"
)
type StickerType string
const (
StickerTypeRegular StickerType = "regular"
StickerTypeMask StickerType = "mask"
StickerTypeCustomEmoji StickerType = "custom_emoji"
)
type StoryAreaTypeLinkType string
const (
StoryAreaTypeLinkTypeLink StoryAreaTypeLinkType = "link"
)
type StoryAreaTypeLocationType string
const (
StoryAreaTypeLocationTypeLocation StoryAreaTypeLocationType = "location"
)
type StoryAreaTypeSuggestedReactionType string
const (
StoryAreaTypeSuggestedReactionTypeSuggestedReaction StoryAreaTypeSuggestedReactionType = "suggested_reaction"
)
type StoryAreaTypeUniqueGiftType string
const (
StoryAreaTypeUniqueGiftTypeUniqueGift StoryAreaTypeUniqueGiftType = "unique_gift"
)
type StoryAreaTypeWeatherType string
const (
StoryAreaTypeWeatherTypeWeather StoryAreaTypeWeatherType = "weather"
)
type SuggestedPostInfoState string
const (
SuggestedPostInfoStatePending SuggestedPostInfoState = "pending"
SuggestedPostInfoStateApproved SuggestedPostInfoState = "approved"
SuggestedPostInfoStateDeclined SuggestedPostInfoState = "declined"
)
type SuggestedPostPaidCurrency string
const (
SuggestedPostPaidCurrencyXTR SuggestedPostPaidCurrency = "XTR"
SuggestedPostPaidCurrencyTON SuggestedPostPaidCurrency = "TON"
)
type SuggestedPostRefundedReason string
const (
SuggestedPostRefundedReasonPostDeleted SuggestedPostRefundedReason = "post_deleted"
SuggestedPostRefundedReasonPaymentRefunded SuggestedPostRefundedReason = "payment_refunded"
)
type TransactionPartnerAffiliateProgramType string
const (
TransactionPartnerAffiliateProgramTypeAffiliateProgram TransactionPartnerAffiliateProgramType = "affiliate_program"
)
type TransactionPartnerFragmentType string
const (
TransactionPartnerFragmentTypeFragment TransactionPartnerFragmentType = "fragment"
)
type TransactionPartnerOtherType string
const (
TransactionPartnerOtherTypeOther TransactionPartnerOtherType = "other"
)
type TransactionPartnerTelegramAdsType string
const (
TransactionPartnerTelegramAdsTypeTelegramAds TransactionPartnerTelegramAdsType = "telegram_ads"
)
type TransactionPartnerTelegramApiType string
const (
TransactionPartnerTelegramApiTypeTelegramApi TransactionPartnerTelegramApiType = "telegram_api"
)
type TransactionPartnerUserTransactionType string
const (
TransactionPartnerUserTransactionTypeInvoicePayment TransactionPartnerUserTransactionType = "invoice_payment"
TransactionPartnerUserTransactionTypePaidMediaPayment TransactionPartnerUserTransactionType = "paid_media_payment"
TransactionPartnerUserTransactionTypeGiftPurchase TransactionPartnerUserTransactionType = "gift_purchase"
TransactionPartnerUserTransactionTypePremiumPurchase TransactionPartnerUserTransactionType = "premium_purchase"
TransactionPartnerUserTransactionTypeBusinessAccountTransfer TransactionPartnerUserTransactionType = "business_account_transfer"
)
type UniqueGiftInfoOrigin string
const (
UniqueGiftInfoOriginUpgrade UniqueGiftInfoOrigin = "upgrade"
UniqueGiftInfoOriginTransfer UniqueGiftInfoOrigin = "transfer"
UniqueGiftInfoOriginResale UniqueGiftInfoOrigin = "resale"
UniqueGiftInfoOriginGiftedUpgrade UniqueGiftInfoOrigin = "gifted_upgrade"
UniqueGiftInfoOriginOffer UniqueGiftInfoOrigin = "offer"
)
type UniqueGiftModelRarity string
const (
UniqueGiftModelRarityUncommon UniqueGiftModelRarity = "uncommon"
UniqueGiftModelRarityRare UniqueGiftModelRarity = "rare"
UniqueGiftModelRarityEpic UniqueGiftModelRarity = "epic"
UniqueGiftModelRarityLegendary UniqueGiftModelRarity = "legendary"
)
+34
View File
@@ -0,0 +1,34 @@
package api
// UpdateType identifies an Update payload variant. Used by allowed_updates
// in getUpdates / setWebhook. The Telegram docs do not enumerate these
// values inline (they are derived from the optional fields of Update),
// so the codegen pipeline cannot synthesise this enum and it lives here
// as a hand-curated companion to the generated enums.gen.go.
type UpdateType string
const (
UpdateMessage UpdateType = "message"
UpdateEditedMessage UpdateType = "edited_message"
UpdateChannelPost UpdateType = "channel_post"
UpdateEditedChannelPost UpdateType = "edited_channel_post"
UpdateBusinessConnection UpdateType = "business_connection"
UpdateBusinessMessage UpdateType = "business_message"
UpdateEditedBusinessMessage UpdateType = "edited_business_message"
UpdateDeletedBusinessMessages UpdateType = "deleted_business_messages"
UpdateMessageReaction UpdateType = "message_reaction"
UpdateMessageReactionCount UpdateType = "message_reaction_count"
UpdateInlineQuery UpdateType = "inline_query"
UpdateChosenInlineResult UpdateType = "chosen_inline_result"
UpdateCallbackQuery UpdateType = "callback_query"
UpdateShippingQuery UpdateType = "shipping_query"
UpdatePreCheckoutQuery UpdateType = "pre_checkout_query"
UpdatePurchasedPaidMedia UpdateType = "purchased_paid_media"
UpdatePoll UpdateType = "poll"
UpdatePollAnswer UpdateType = "poll_answer"
UpdateMyChatMember UpdateType = "my_chat_member"
UpdateChatMember UpdateType = "chat_member"
UpdateChatJoinRequest UpdateType = "chat_join_request"
UpdateChatBoost UpdateType = "chat_boost"
UpdateRemovedChatBoost UpdateType = "removed_chat_boost"
)
+32 -32
View File
@@ -198,7 +198,7 @@ type SendMessageParams struct {
// Text of the message to be sent, 1-4096 characters after entities parsing
Text string `json:"text"`
// Mode for parsing entities in the message text. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in message text, which can be specified instead of parse_mode
Entities []MessageEntity `json:"entities,omitempty"`
// Link preview generation options for the message
@@ -305,7 +305,7 @@ type CopyMessageParams struct {
// New caption for media, 0-1024 characters after entities parsing. If not specified, the original caption is kept
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the new caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the new caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Pass True, if the caption must be shown above the message media. Ignored if a new caption isn't specified.
@@ -379,7 +379,7 @@ type SendPhotoParams struct {
// Photo caption (may also be used when resending photos by file_id), 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the photo caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Pass True, if the caption must be shown above the message media
@@ -427,7 +427,7 @@ func (p *SendPhotoParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -509,7 +509,7 @@ type SendLivePhotoParams struct {
// Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the video caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Pass True, if the caption must be shown above the message media
@@ -560,7 +560,7 @@ func (p *SendLivePhotoParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -648,7 +648,7 @@ type SendAudioParams struct {
// Audio caption, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the audio caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Duration of the audio in seconds
@@ -703,7 +703,7 @@ func (p *SendAudioParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -796,7 +796,7 @@ type SendDocumentParams struct {
// Document caption (may also be used when resending documents by file_id), 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the document caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Disables automatic server-side content type detection for files uploaded using multipart/form-data
@@ -845,7 +845,7 @@ func (p *SendDocumentParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -941,7 +941,7 @@ type SendVideoParams struct {
// Video caption (may also be used when resending videos by file_id), 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the video caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Pass True, if the caption must be shown above the message media
@@ -1009,7 +1009,7 @@ func (p *SendVideoParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -1114,7 +1114,7 @@ type SendAnimationParams struct {
// Animation caption (may also be used when resending animation by file_id), 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the animation caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Pass True, if the caption must be shown above the message media
@@ -1174,7 +1174,7 @@ func (p *SendAnimationParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -1261,7 +1261,7 @@ type SendVoiceParams struct {
// Voice message caption, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the voice message caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Duration of the voice message in seconds
@@ -1307,7 +1307,7 @@ func (p *SendVoiceParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -1511,7 +1511,7 @@ type SendPaidMediaParams struct {
// Media caption, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the media caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Pass True, if the caption must be shown above the message media
@@ -1559,7 +1559,7 @@ func (p *SendPaidMediaParams) MultipartFields() map[string]string {
out["caption"] = p.Caption
}
if p.ParseMode != "" {
out["parse_mode"] = p.ParseMode
out["parse_mode"] = string(p.ParseMode)
}
if p.CaptionEntities != nil {
if b, _ := json.Marshal(p.CaptionEntities); len(b) > 0 {
@@ -1843,7 +1843,7 @@ type SendPollParams struct {
// Poll question, 1-300 characters
Question string `json:"question"`
// Mode for parsing entities in the question. See formatting options for more details. Currently, only custom emoji entities are allowed
QuestionParseMode string `json:"question_parse_mode,omitempty"`
QuestionParseMode ParseMode `json:"question_parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the poll question. It can be specified instead of question_parse_mode
QuestionEntities []MessageEntity `json:"question_entities,omitempty"`
// A JSON-serialized list of 1-12 answer options
@@ -1871,7 +1871,7 @@ type SendPollParams struct {
// Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing
Explanation string `json:"explanation,omitempty"`
// Mode for parsing entities in the explanation. See formatting options for more details.
ExplanationParseMode string `json:"explanation_parse_mode,omitempty"`
ExplanationParseMode ParseMode `json:"explanation_parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the poll explanation. It can be specified instead of explanation_parse_mode
ExplanationEntities []MessageEntity `json:"explanation_entities,omitempty"`
// Media added to the quiz explanation
@@ -1885,7 +1885,7 @@ type SendPollParams struct {
// Description of the poll to be sent, 0-1024 characters after entities parsing
Description string `json:"description,omitempty"`
// Mode for parsing entities in the poll description. See formatting options for more details.
DescriptionParseMode string `json:"description_parse_mode,omitempty"`
DescriptionParseMode ParseMode `json:"description_parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the poll description, which can be specified instead of description_parse_mode
DescriptionEntities []MessageEntity `json:"description_entities,omitempty"`
// Media added to the poll description
@@ -1990,7 +1990,7 @@ type SendMessageDraftParams struct {
// Text of the message to be sent, 0-4096 characters after entities parsing. Pass an empty text to show a “Thinking…” placeholder.
Text string `json:"text,omitempty"`
// Mode for parsing entities in the message text. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in message text, which can be specified instead of parse_mode
Entities []MessageEntity `json:"entities,omitempty"`
}
@@ -3390,7 +3390,7 @@ type SendGiftParams struct {
// Text that will be shown along with the gift; 0-128 characters
Text string `json:"text,omitempty"`
// Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.
TextParseMode string `json:"text_parse_mode,omitempty"`
TextParseMode ParseMode `json:"text_parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.
TextEntities []MessageEntity `json:"text_entities,omitempty"`
}
@@ -3415,7 +3415,7 @@ type GiftPremiumSubscriptionParams struct {
// Text that will be shown along with the service message about the subscription; 0-128 characters
Text string `json:"text,omitempty"`
// Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.
TextParseMode string `json:"text_parse_mode,omitempty"`
TextParseMode ParseMode `json:"text_parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, “custom_emoji”, and “date_time” are ignored.
TextEntities []MessageEntity `json:"text_entities,omitempty"`
}
@@ -3840,7 +3840,7 @@ type PostStoryParams struct {
// Caption of the story, 0-2048 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the story caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// A JSON-serialized list of clickable areas to be shown on the story
@@ -3896,7 +3896,7 @@ type EditStoryParams struct {
// Caption of the story, 0-2048 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the story caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// A JSON-serialized list of clickable areas to be shown on the story
@@ -4001,7 +4001,7 @@ type EditMessageTextParams struct {
// New text of the message, 1-4096 characters after entities parsing
Text string `json:"text"`
// Mode for parsing entities in the message text. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in message text, which can be specified instead of parse_mode
Entities []MessageEntity `json:"entities,omitempty"`
// Link preview generation options for the message
@@ -4032,7 +4032,7 @@ type EditMessageCaptionParams struct {
// New caption of the message, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Mode for parsing entities in the message caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// A JSON-serialized list of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages.
@@ -4492,7 +4492,7 @@ type UploadStickerFileParams struct {
// A file with the sticker in .WEBP, .PNG, .TGS, or .WEBM format. See https://core.telegram.org/stickers for technical requirements. More information on Sending Files »
Sticker *InputFile `json:"sticker"`
// Format of the sticker, must be one of “static”, “animated”, “video”
StickerFormat string `json:"sticker_format"`
StickerFormat InputStickerFormat `json:"sticker_format"`
}
// HasFile reports whether a multipart upload is required.
@@ -4507,7 +4507,7 @@ func (p *UploadStickerFileParams) HasFile() bool {
func (p *UploadStickerFileParams) MultipartFields() map[string]string {
out := map[string]string{}
out["user_id"] = strconv.FormatInt(p.UserID, 10)
out["sticker_format"] = p.StickerFormat
out["sticker_format"] = string(p.StickerFormat)
return out
}
@@ -4707,7 +4707,7 @@ type SetStickerSetThumbnailParams struct {
// A .WEBP or .PNG image with the thumbnail, must be up to 128 kilobytes in size and have a width and height of exactly 100px, or a .TGS animation with a thumbnail up to 32 kilobytes in size (see https://core.telegram.org/stickers#animation-requirements for animated sticker technical requirements), or a .WEBM video with the thumbnail up to 32 kilobytes in size; see https://core.telegram.org/stickers#video-requirements for video sticker technical requirements. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or upload a new one using multipart/form-data. More information on Sending Files ». Animated and video sticker set thumbnails can't be uploaded via HTTP URL. If omitted, then the thumbnail is dropped and the first sticker is used as the thumbnail.
Thumbnail *InputFile `json:"thumbnail,omitempty"`
// Format of the thumbnail, must be one of “static” for a .WEBP or .PNG image, “animated” for a .TGS animation, or “video” for a .WEBM video
Format string `json:"format"`
Format InputStickerFormat `json:"format"`
}
// HasFile reports whether a multipart upload is required.
@@ -4723,7 +4723,7 @@ func (p *SetStickerSetThumbnailParams) MultipartFields() map[string]string {
out := map[string]string{}
out["name"] = p.Name
out["user_id"] = strconv.FormatInt(p.UserID, 10)
out["format"] = p.Format
out["format"] = string(p.Format)
return out
}
+14 -14
View File
@@ -20954,7 +20954,7 @@ func Test_UploadStickerFile_Success(t *testing.T) {
params := &UploadStickerFileParams{
UserID: 42,
Sticker: &InputFile{PathOrID: "file_id_test"},
StickerFormat: "test_value",
StickerFormat: InputStickerFormatStatic,
}
_, err := UploadStickerFile(context.Background(), bot, params)
require.NoError(t, err)
@@ -20969,7 +20969,7 @@ func Test_UploadStickerFile_APIError(t *testing.T) {
params := &UploadStickerFileParams{
UserID: 42,
Sticker: &InputFile{PathOrID: "file_id_test"},
StickerFormat: "test_value",
StickerFormat: InputStickerFormatStatic,
}
_, err := UploadStickerFile(context.Background(), bot, params)
require.Error(t, err)
@@ -20987,7 +20987,7 @@ func Test_UploadStickerFile_NetworkError(t *testing.T) {
params := &UploadStickerFileParams{
UserID: 42,
Sticker: &InputFile{PathOrID: "file_id_test"},
StickerFormat: "test_value",
StickerFormat: InputStickerFormatStatic,
}
_, err := UploadStickerFile(context.Background(), bot, params)
require.Error(t, err)
@@ -21003,7 +21003,7 @@ func Test_UploadStickerFile_ParseError(t *testing.T) {
params := &UploadStickerFileParams{
UserID: 42,
Sticker: &InputFile{PathOrID: "file_id_test"},
StickerFormat: "test_value",
StickerFormat: InputStickerFormatStatic,
}
_, err := UploadStickerFile(context.Background(), bot, params)
require.Error(t, err)
@@ -21022,7 +21022,7 @@ func Test_UploadStickerFile_ContextCanceled(t *testing.T) {
params := &UploadStickerFileParams{
UserID: 42,
Sticker: &InputFile{PathOrID: "file_id_test"},
StickerFormat: "test_value",
StickerFormat: InputStickerFormatStatic,
}
_, err := UploadStickerFile(ctx, bot, params)
require.Error(t, err)
@@ -21063,7 +21063,7 @@ func Test_UploadStickerFile_Forbidden(t *testing.T) {
params := &UploadStickerFileParams{
UserID: 42,
Sticker: &InputFile{PathOrID: "file_id_test"},
StickerFormat: "test_value",
StickerFormat: InputStickerFormatStatic,
}
_, err := UploadStickerFile(context.Background(), bot, params)
require.Error(t, err)
@@ -21085,7 +21085,7 @@ func Test_UploadStickerFile_ServerError(t *testing.T) {
params := &UploadStickerFileParams{
UserID: 42,
Sticker: &InputFile{PathOrID: "file_id_test"},
StickerFormat: "test_value",
StickerFormat: InputStickerFormatStatic,
}
_, err := UploadStickerFile(context.Background(), bot, params)
require.Error(t, err)
@@ -22415,7 +22415,7 @@ func Test_SetStickerSetThumbnail_Success(t *testing.T) {
params := &SetStickerSetThumbnailParams{
Name: "test_value",
UserID: 42,
Format: "test_value",
Format: InputStickerFormatStatic,
}
_, err := SetStickerSetThumbnail(context.Background(), bot, params)
require.NoError(t, err)
@@ -22430,7 +22430,7 @@ func Test_SetStickerSetThumbnail_APIError(t *testing.T) {
params := &SetStickerSetThumbnailParams{
Name: "test_value",
UserID: 42,
Format: "test_value",
Format: InputStickerFormatStatic,
}
_, err := SetStickerSetThumbnail(context.Background(), bot, params)
require.Error(t, err)
@@ -22448,7 +22448,7 @@ func Test_SetStickerSetThumbnail_NetworkError(t *testing.T) {
params := &SetStickerSetThumbnailParams{
Name: "test_value",
UserID: 42,
Format: "test_value",
Format: InputStickerFormatStatic,
}
_, err := SetStickerSetThumbnail(context.Background(), bot, params)
require.Error(t, err)
@@ -22464,7 +22464,7 @@ func Test_SetStickerSetThumbnail_ParseError(t *testing.T) {
params := &SetStickerSetThumbnailParams{
Name: "test_value",
UserID: 42,
Format: "test_value",
Format: InputStickerFormatStatic,
}
_, err := SetStickerSetThumbnail(context.Background(), bot, params)
require.Error(t, err)
@@ -22483,7 +22483,7 @@ func Test_SetStickerSetThumbnail_ContextCanceled(t *testing.T) {
params := &SetStickerSetThumbnailParams{
Name: "test_value",
UserID: 42,
Format: "test_value",
Format: InputStickerFormatStatic,
}
_, err := SetStickerSetThumbnail(ctx, bot, params)
require.Error(t, err)
@@ -22524,7 +22524,7 @@ func Test_SetStickerSetThumbnail_Forbidden(t *testing.T) {
params := &SetStickerSetThumbnailParams{
Name: "test_value",
UserID: 42,
Format: "test_value",
Format: InputStickerFormatStatic,
}
_, err := SetStickerSetThumbnail(context.Background(), bot, params)
require.Error(t, err)
@@ -22546,7 +22546,7 @@ func Test_SetStickerSetThumbnail_ServerError(t *testing.T) {
params := &SetStickerSetThumbnailParams{
Name: "test_value",
UserID: 42,
Format: "test_value",
Format: InputStickerFormatStatic,
}
_, err := SetStickerSetThumbnail(context.Background(), bot, params)
require.Error(t, err)
+101 -101
View File
@@ -137,7 +137,7 @@ type Chat struct {
// Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier.
ID int64 `json:"id"`
// Type of the chat, can be either “private”, “group”, “supergroup” or “channel”
Type string `json:"type"`
Type ChatType `json:"type"`
// Optional. Title, for supergroups, channels and group chats
Title string `json:"title,omitempty"`
// Optional. Username, for private chats, supergroups and channels if available
@@ -157,7 +157,7 @@ type ChatFullInfo struct {
// Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier.
ID int64 `json:"id"`
// Type of the chat, can be either “private”, “group”, “supergroup” or “channel”
Type string `json:"type"`
Type ChatType `json:"type"`
// Optional. Title, for supergroups, channels and group chats
Title string `json:"title,omitempty"`
// Optional. Username, for private chats, supergroups and channels if available
@@ -614,7 +614,7 @@ func UnmarshalMaybeInaccessibleMessage(data []byte) (MaybeInaccessibleMessage, e
// This object represents one special entity in a text message. For example, hashtags, usernames, URLs, etc.
type MessageEntity struct {
// Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag or #hashtag@chatusername), “cashtag” ($USD or $USD@chatusername), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers), or “date_time” (for formatted date and time)
Type string `json:"type"`
Type MessageEntityType `json:"type"`
// Offset in UTF-16 code units to the start of the entity
Offset int64 `json:"offset"`
// Length of the entity in UTF-16 code units
@@ -736,7 +736,7 @@ type ReplyParameters struct {
// Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities. The message will fail to send if the quote isn't found in the original message.
Quote string `json:"quote,omitempty"`
// Optional. Mode for parsing entities in the quote. See formatting options for more details.
QuoteParseMode string `json:"quote_parse_mode,omitempty"`
QuoteParseMode ParseMode `json:"quote_parse_mode,omitempty"`
// Optional. A JSON-serialized list of special entities that appear in the quote. It can be specified instead of quote_parse_mode.
QuoteEntities []MessageEntity `json:"quote_entities,omitempty"`
// Optional. Position of the quote in the original message in UTF-16 code units
@@ -800,7 +800,7 @@ func UnmarshalMessageOrigin(data []byte) (MessageOrigin, error) {
// The message was originally sent by a known user.
type MessageOriginUser struct {
// Type of the message origin, always “user”
Type string `json:"type"`
Type MessageOriginUserType `json:"type"`
// Date the message was sent originally in Unix time
Date int64 `json:"date"`
// User that sent the message originally
@@ -810,7 +810,7 @@ type MessageOriginUser struct {
// The message was originally sent by an unknown user.
type MessageOriginHiddenUser struct {
// Type of the message origin, always “hidden_user”
Type string `json:"type"`
Type MessageOriginHiddenUserType `json:"type"`
// Date the message was sent originally in Unix time
Date int64 `json:"date"`
// Name of the user that sent the message originally
@@ -820,7 +820,7 @@ type MessageOriginHiddenUser struct {
// The message was originally sent on behalf of a chat to a group chat.
type MessageOriginChat struct {
// Type of the message origin, always “chat”
Type string `json:"type"`
Type MessageOriginChatType `json:"type"`
// Date the message was sent originally in Unix time
Date int64 `json:"date"`
// Chat that sent the message originally
@@ -832,7 +832,7 @@ type MessageOriginChat struct {
// The message was originally sent to a channel chat.
type MessageOriginChannel struct {
// Type of the message origin, always “channel”
Type string `json:"type"`
Type MessageOriginChannelType `json:"type"`
// Date the message was sent originally in Unix time
Date int64 `json:"date"`
// Channel chat to which the message was originally sent
@@ -1110,7 +1110,7 @@ func UnmarshalPaidMedia(data []byte) (PaidMedia, error) {
// The paid media is a live photo.
type PaidMediaLivePhoto struct {
// Type of the paid media, always “live_photo”
Type string `json:"type"`
Type PaidMediaLivePhotoType `json:"type"`
// The photo
LivePhoto LivePhoto `json:"live_photo"`
}
@@ -1118,7 +1118,7 @@ type PaidMediaLivePhoto struct {
// The paid media is a photo.
type PaidMediaPhoto struct {
// Type of the paid media, always “photo”
Type string `json:"type"`
Type PaidMediaPhotoType `json:"type"`
// The photo
Photo []PhotoSize `json:"photo"`
}
@@ -1126,7 +1126,7 @@ type PaidMediaPhoto struct {
// The paid media isn't available before the payment.
type PaidMediaPreview struct {
// Type of the paid media, always “preview”
Type string `json:"type"`
Type PaidMediaPreviewType `json:"type"`
// Optional. Media width as defined by the sender
Width *int64 `json:"width,omitempty"`
// Optional. Media height as defined by the sender
@@ -1138,7 +1138,7 @@ type PaidMediaPreview struct {
// The paid media is a video.
type PaidMediaVideo struct {
// Type of the paid media, always “video”
Type string `json:"type"`
Type PaidMediaVideoType `json:"type"`
// The video
Video Video `json:"video"`
}
@@ -1284,7 +1284,7 @@ type InputPollOption struct {
// Option text, 1-100 characters
Text string `json:"text"`
// Optional. Mode for parsing entities in the text. See formatting options for more details. Currently, only custom emoji entities are allowed
TextParseMode string `json:"text_parse_mode,omitempty"`
TextParseMode ParseMode `json:"text_parse_mode,omitempty"`
// Optional. A JSON-serialized list of special entities that appear in the poll option text. It can be specified instead of text_parse_mode
TextEntities []MessageEntity `json:"text_entities,omitempty"`
// Optional. Media added to the poll option
@@ -1322,7 +1322,7 @@ type Poll struct {
// True, if the poll is anonymous
IsAnonymous bool `json:"is_anonymous"`
// Poll type, currently can be “regular” or “quiz”
Type string `json:"type"`
Type PollType `json:"type"`
// True, if the poll allows multiple answers
AllowsMultipleAnswers bool `json:"allows_multiple_answers"`
// True, if the poll allows to change the chosen answer options
@@ -1388,7 +1388,7 @@ type InputChecklistTask struct {
// Text of the task; 1-100 characters after entities parsing
Text string `json:"text"`
// Optional. Mode for parsing entities in the text. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the text, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.
TextEntities []MessageEntity `json:"text_entities,omitempty"`
}
@@ -1398,7 +1398,7 @@ type InputChecklist struct {
// Title of the checklist; 1-255 characters after entities parsing
Title string `json:"title"`
// Optional. Mode for parsing entities in the title. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the title, which can be specified instead of parse_mode. Currently, only bold, italic, underline, strikethrough, spoiler, custom_emoji, and date_time entities are allowed.
TitleEntities []MessageEntity `json:"title_entities,omitempty"`
// List of 1-30 tasks in the checklist
@@ -1624,7 +1624,7 @@ func UnmarshalBackgroundFill(data []byte) (BackgroundFill, error) {
// The background is filled using the selected color.
type BackgroundFillSolid struct {
// Type of the background fill, always “solid”
Type string `json:"type"`
Type BackgroundFillSolidType `json:"type"`
// The color of the background fill in the RGB24 format
Color int64 `json:"color"`
}
@@ -1632,7 +1632,7 @@ type BackgroundFillSolid struct {
// The background is a gradient fill.
type BackgroundFillGradient struct {
// Type of the background fill, always “gradient”
Type string `json:"type"`
Type BackgroundFillGradientType `json:"type"`
// Top color of the gradient in the RGB24 format
TopColor int64 `json:"top_color"`
// Bottom color of the gradient in the RGB24 format
@@ -1644,7 +1644,7 @@ type BackgroundFillGradient struct {
// The background is a freeform gradient that rotates after every message in the chat.
type BackgroundFillFreeformGradient struct {
// Type of the background fill, always “freeform_gradient”
Type string `json:"type"`
Type BackgroundFillFreeformGradientType `json:"type"`
// A list of the 3 or 4 base colors that are used to generate the freeform gradient in the RGB24 format
Colors []int64 `json:"colors"`
}
@@ -1702,7 +1702,7 @@ func UnmarshalBackgroundType(data []byte) (BackgroundType, error) {
// The background is automatically filled based on the selected colors.
type BackgroundTypeFill struct {
// Type of the background, always “fill”
Type string `json:"type"`
Type BackgroundTypeFillType `json:"type"`
// The background fill
Fill BackgroundFill `json:"fill"`
// Dimming of the background in dark themes, as a percentage; 0-100
@@ -1736,7 +1736,7 @@ func (m *BackgroundTypeFill) UnmarshalJSON(data []byte) error {
// The background is a wallpaper in the JPEG format.
type BackgroundTypeWallpaper struct {
// Type of the background, always “wallpaper”
Type string `json:"type"`
Type BackgroundTypeWallpaperType `json:"type"`
// Document with the wallpaper
Document Document `json:"document"`
// Dimming of the background in dark themes, as a percentage; 0-100
@@ -1750,7 +1750,7 @@ type BackgroundTypeWallpaper struct {
// The background is a .PNG or .TGV (gzipped subset of SVG with MIME type “application/x-tgwallpattern”) pattern to be combined with the background fill chosen by the user.
type BackgroundTypePattern struct {
// Type of the background, always “pattern”
Type string `json:"type"`
Type BackgroundTypePatternType `json:"type"`
// Document with the pattern
Document Document `json:"document"`
// The background fill that is combined with the pattern
@@ -1790,7 +1790,7 @@ func (m *BackgroundTypePattern) UnmarshalJSON(data []byte) error {
// The background is taken directly from a built-in chat theme.
type BackgroundTypeChatTheme struct {
// Type of the background, always “chat_theme”
Type string `json:"type"`
Type BackgroundTypeChatThemeType `json:"type"`
// Name of the chat theme, which is usually an emoji
ThemeName string `json:"theme_name"`
}
@@ -1974,7 +1974,7 @@ type SuggestedPostPaid struct {
// Optional. Message containing the suggested post. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.
SuggestedPostMessage *Message `json:"suggested_post_message,omitempty"`
// Currency in which the payment was made. Currently, one of “XTR” for Telegram Stars or “TON” for toncoins
Currency string `json:"currency"`
Currency SuggestedPostPaidCurrency `json:"currency"`
// Optional. The amount of the currency that was received by the channel in nanotoncoins; for payments in toncoins only
Amount *int64 `json:"amount,omitempty"`
// Optional. The amount of Telegram Stars that was received by the channel; for payments in Telegram Stars only
@@ -1986,7 +1986,7 @@ type SuggestedPostRefunded struct {
// Optional. Message containing the suggested post. Note that the Message object in this field will not contain the reply_to_message field even if it itself is a reply.
SuggestedPostMessage *Message `json:"suggested_post_message,omitempty"`
// Reason for the refund. Currently, one of “post_deleted” if the post was deleted within 24 hours of being posted or removed from scheduled messages without being posted, or “payment_refunded” if the payer refunded their payment.
Reason string `json:"reason"`
Reason SuggestedPostRefundedReason `json:"reason"`
}
// This object represents a service message about the creation of a scheduled giveaway.
@@ -2074,7 +2074,7 @@ type LinkPreviewOptions struct {
// Describes the price of a suggested post.
type SuggestedPostPrice struct {
// Currency in which the post will be paid. Currently, must be one of “XTR” for Telegram Stars or “TON” for toncoins
Currency string `json:"currency"`
Currency SuggestedPostPaidCurrency `json:"currency"`
// The amount of the currency that will be paid for the post in the smallest units of the currency, i.e. Telegram Stars or nanotoncoins. Currently, price in Telegram Stars must be between 5 and 100000, and price in nanotoncoins must be between 10000000 and 10000000000000.
Amount int64 `json:"amount"`
}
@@ -2082,7 +2082,7 @@ type SuggestedPostPrice struct {
// Contains information about a suggested post.
type SuggestedPostInfo struct {
// State of the suggested post. Currently, it can be one of “pending”, “approved”, “declined”.
State string `json:"state"`
State SuggestedPostInfoState `json:"state"`
// Optional. Proposed price of the post. If the field is omitted, then the post is unpaid.
Price *SuggestedPostPrice `json:"price,omitempty"`
// Optional. Proposed send date of the post. If the field is omitted, then the post can be published at any time within 30 days at the sole discretion of the user or administrator who approves it.
@@ -2163,7 +2163,7 @@ type KeyboardButton struct {
// Optional. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.
IconCustomEmojiID string `json:"icon_custom_emoji_id,omitempty"`
// Optional. Style of the button. Must be one of “danger” (red), “success” (green) or “primary” (blue). If omitted, then an app-specific style is used.
Style string `json:"style,omitempty"`
Style KeyboardButtonStyle `json:"style,omitempty"`
// Optional. If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a “users_shared” service message. Available in private chats only.
RequestUsers *KeyboardButtonRequestUsers `json:"request_users,omitempty"`
// Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a “chat_shared” service message. Available in private chats only.
@@ -2261,7 +2261,7 @@ type InlineKeyboardButton struct {
// Optional. Unique identifier of the custom emoji shown before the text of the button. Can only be used by bots that purchased additional usernames on Fragment or in the messages directly sent by the bot to private, group and supergroup chats if the owner of the bot has a Telegram Premium subscription.
IconCustomEmojiID string `json:"icon_custom_emoji_id,omitempty"`
// Optional. Style of the button. Must be one of “danger” (red), “success” (green) or “primary” (blue). If omitted, then an app-specific style is used.
Style string `json:"style,omitempty"`
Style KeyboardButtonStyle `json:"style,omitempty"`
// Optional. HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id=<user_id> can be used to mention a user by their identifier without using a username, if this is allowed by their privacy settings.
URL string `json:"url,omitempty"`
// Optional. Data to be sent in a callback query to the bot when the button is pressed, 1-64 bytes
@@ -2568,7 +2568,7 @@ func UnmarshalChatMember(data []byte) (ChatMember, error) {
// Represents a chat member that owns the chat and has all administrator privileges.
type ChatMemberOwner struct {
// The member's status in the chat, always “creator”
Status string `json:"status"`
Status ChatMemberOwnerStatus `json:"status"`
// Information about the user
User User `json:"user"`
// True, if the user's presence in the chat is hidden
@@ -2580,7 +2580,7 @@ type ChatMemberOwner struct {
// Represents a chat member that has some additional privileges.
type ChatMemberAdministrator struct {
// The member's status in the chat, always “administrator”
Status string `json:"status"`
Status ChatMemberAdministratorStatus `json:"status"`
// Information about the user
User User `json:"user"`
// True, if the bot is allowed to edit administrator privileges of that user
@@ -2626,7 +2626,7 @@ type ChatMemberAdministrator struct {
// Represents a chat member that has no additional privileges or restrictions.
type ChatMemberMember struct {
// The member's status in the chat, always “member”
Status string `json:"status"`
Status ChatMemberMemberStatus `json:"status"`
// Optional. Tag of the member
Tag string `json:"tag,omitempty"`
// Information about the user
@@ -2638,7 +2638,7 @@ type ChatMemberMember struct {
// Represents a chat member that is under certain restrictions in the chat. Supergroups only.
type ChatMemberRestricted struct {
// The member's status in the chat, always “restricted”
Status string `json:"status"`
Status ChatMemberRestrictedStatus `json:"status"`
// Optional. Tag of the member
Tag string `json:"tag,omitempty"`
// Information about the user
@@ -2684,7 +2684,7 @@ type ChatMemberRestricted struct {
// Represents a chat member that isn't currently a member of the chat, but may join it themselves.
type ChatMemberLeft struct {
// The member's status in the chat, always “left”
Status string `json:"status"`
Status ChatMemberLeftStatus `json:"status"`
// Information about the user
User User `json:"user"`
}
@@ -2692,7 +2692,7 @@ type ChatMemberLeft struct {
// Represents a chat member that was banned in the chat and can't return to the chat or view chat messages.
type ChatMemberBanned struct {
// The member's status in the chat, always “kicked”
Status string `json:"status"`
Status ChatMemberBannedStatus `json:"status"`
// Information about the user
User User `json:"user"`
// Date when restrictions will be lifted for this user; Unix time. If 0, then the user is banned forever
@@ -2894,7 +2894,7 @@ func UnmarshalStoryAreaType(data []byte) (StoryAreaType, error) {
// Describes a story area pointing to a location. Currently, a story can have up to 10 location areas.
type StoryAreaTypeLocation struct {
// Type of the area, always “location”
Type string `json:"type"`
Type StoryAreaTypeLocationType `json:"type"`
// Location latitude in degrees
Latitude float64 `json:"latitude"`
// Location longitude in degrees
@@ -2906,7 +2906,7 @@ type StoryAreaTypeLocation struct {
// Describes a story area pointing to a suggested reaction. Currently, a story can have up to 5 suggested reaction areas.
type StoryAreaTypeSuggestedReaction struct {
// Type of the area, always “suggested_reaction”
Type string `json:"type"`
Type StoryAreaTypeSuggestedReactionType `json:"type"`
// Type of the reaction
ReactionType ReactionType `json:"reaction_type"`
// Optional. Pass True if the reaction area has a dark background
@@ -2942,7 +2942,7 @@ func (m *StoryAreaTypeSuggestedReaction) UnmarshalJSON(data []byte) error {
// Describes a story area pointing to an HTTP or tg:// link. Currently, a story can have up to 3 link areas.
type StoryAreaTypeLink struct {
// Type of the area, always “link”
Type string `json:"type"`
Type StoryAreaTypeLinkType `json:"type"`
// HTTP or tg:// URL to be opened when the area is clicked
URL string `json:"url"`
}
@@ -2950,7 +2950,7 @@ type StoryAreaTypeLink struct {
// Describes a story area containing weather information. Currently, a story can have up to 3 weather areas.
type StoryAreaTypeWeather struct {
// Type of the area, always “weather”
Type string `json:"type"`
Type StoryAreaTypeWeatherType `json:"type"`
// Temperature, in degree Celsius
Temperature float64 `json:"temperature"`
// Emoji representing the weather
@@ -2962,7 +2962,7 @@ type StoryAreaTypeWeather struct {
// Describes a story area pointing to a unique gift. Currently, a story can have at most 1 unique gift area.
type StoryAreaTypeUniqueGift struct {
// Type of the area, always “unique_gift”
Type string `json:"type"`
Type StoryAreaTypeUniqueGiftType `json:"type"`
// Unique name of the gift
Name string `json:"name"`
}
@@ -3054,7 +3054,7 @@ func UnmarshalReactionType(data []byte) (ReactionType, error) {
// The reaction is based on an emoji.
type ReactionTypeEmoji struct {
// Type of the reaction, always “emoji”
Type string `json:"type"`
Type ReactionTypeEmojiType `json:"type"`
// Reaction emoji. Currently, it can be one of "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""
Emoji string `json:"emoji"`
}
@@ -3062,7 +3062,7 @@ type ReactionTypeEmoji struct {
// The reaction is based on a custom emoji.
type ReactionTypeCustomEmoji struct {
// Type of the reaction, always “custom_emoji”
Type string `json:"type"`
Type ReactionTypeCustomEmojiType `json:"type"`
// Custom emoji identifier
CustomEmojiID string `json:"custom_emoji_id"`
}
@@ -3070,7 +3070,7 @@ type ReactionTypeCustomEmoji struct {
// The reaction is paid.
type ReactionTypePaid struct {
// Type of the reaction, always “paid”
Type string `json:"type"`
Type ReactionTypePaidType `json:"type"`
}
// Represents a reaction added to a message along with the number of times it was added.
@@ -3254,7 +3254,7 @@ type UniqueGiftModel struct {
// The number of unique gifts that receive this model for every 1000 gift upgrades. Always 0 for crafted gifts.
RarityPerMille int64 `json:"rarity_per_mille"`
// Optional. Rarity of the model if it is a crafted model. Currently, can be “uncommon”, “rare”, “epic”, or “legendary”.
Rarity string `json:"rarity,omitempty"`
Rarity UniqueGiftModelRarity `json:"rarity,omitempty"`
}
// This object describes the symbol shown on the pattern of a unique gift.
@@ -3362,9 +3362,9 @@ type UniqueGiftInfo struct {
// Information about the gift
Gift UniqueGift `json:"gift"`
// Origin of the gift. Currently, either “upgrade” for gifts upgraded from regular gifts, “transfer” for gifts transferred from other users or channels, “resale” for gifts bought from other users, “gifted_upgrade” for upgrades purchased after the gift was sent, or “offer” for gifts bought or sold through gift purchase offers
Origin string `json:"origin"`
Origin UniqueGiftInfoOrigin `json:"origin"`
// Optional. For gifts bought from other users, the currency in which the payment for the gift was done. Currently, one of “XTR” for Telegram Stars or “TON” for toncoins.
LastResaleCurrency string `json:"last_resale_currency,omitempty"`
LastResaleCurrency SuggestedPostPaidCurrency `json:"last_resale_currency,omitempty"`
// Optional. For gifts bought from other users, the price paid for the gift in either Telegram Stars or nanotoncoins
LastResaleAmount *int64 `json:"last_resale_amount,omitempty"`
// Optional. Unique identifier of the received gift for the bot; only present for gifts received on behalf of business accounts
@@ -3416,7 +3416,7 @@ func UnmarshalOwnedGift(data []byte) (OwnedGift, error) {
// Describes a regular gift owned by a user or a chat.
type OwnedGiftRegular struct {
// Type of the gift, always “regular”
Type string `json:"type"`
Type OwnedGiftRegularType `json:"type"`
// Information about the regular gift
Gift Gift `json:"gift"`
// Optional. Unique identifier of the gift for the bot; for gifts received on behalf of business accounts only
@@ -3450,7 +3450,7 @@ type OwnedGiftRegular struct {
// Describes a unique gift received and owned by a user or a chat.
type OwnedGiftUnique struct {
// Type of the gift, always “unique”
Type string `json:"type"`
Type OwnedGiftUniqueType `json:"type"`
// Information about the unique gift
Gift UniqueGift `json:"gift"`
// Optional. Unique identifier of the received gift for the bot; for gifts received on behalf of business accounts only
@@ -3765,7 +3765,7 @@ func UnmarshalChatBoostSource(data []byte) (ChatBoostSource, error) {
// The boost was obtained by subscribing to Telegram Premium or by gifting a Telegram Premium subscription to another user.
type ChatBoostSourcePremium struct {
// Source of the boost, always “premium”
Source string `json:"source"`
Source ChatBoostSourcePremiumSource `json:"source"`
// User that boosted the chat
User User `json:"user"`
}
@@ -3773,7 +3773,7 @@ type ChatBoostSourcePremium struct {
// The boost was obtained by the creation of Telegram Premium gift codes to boost a chat. Each such code boosts the chat 4 times for the duration of the corresponding Telegram Premium subscription.
type ChatBoostSourceGiftCode struct {
// Source of the boost, always “gift_code”
Source string `json:"source"`
Source ChatBoostSourceGiftCodeSource `json:"source"`
// User for which the gift code was created
User User `json:"user"`
}
@@ -3781,7 +3781,7 @@ type ChatBoostSourceGiftCode struct {
// The boost was obtained by the creation of a Telegram Premium or a Telegram Star giveaway. This boosts the chat 4 times for the duration of the corresponding Telegram Premium subscription for Telegram Premium giveaways and prize_star_count / 500 times for one year for Telegram Star giveaways.
type ChatBoostSourceGiveaway struct {
// Source of the boost, always “giveaway”
Source string `json:"source"`
Source ChatBoostSourceGiveawaySource `json:"source"`
// Identifier of a message in the chat with the giveaway; the message could have been deleted already. May be 0 if the message isn't sent yet.
GiveawayMessageID int64 `json:"giveaway_message_id"`
// Optional. User that won the prize in the giveaway if any; for Telegram Premium giveaways only
@@ -4015,7 +4015,7 @@ type InputMediaAnimation struct {
// Optional. Caption of the animation to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the animation caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4041,7 +4041,7 @@ type InputMediaAudio struct {
// Optional. Caption of the audio to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the audio caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Duration of the audio in seconds
@@ -4063,7 +4063,7 @@ type InputMediaDocument struct {
// Optional. Caption of the document to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the document caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always True, if the document is sent as part of an album.
@@ -4081,7 +4081,7 @@ type InputMediaLivePhoto struct {
// Optional. Caption of the live photo to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the live photo caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4111,7 +4111,7 @@ type InputMediaPhoto struct {
// Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the photo caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4167,7 +4167,7 @@ type InputMediaVideo struct {
// Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the video caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4317,7 +4317,7 @@ type Sticker struct {
// Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
FileUniqueID string `json:"file_unique_id"`
// Type of the sticker, currently one of “regular”, “mask”, “custom_emoji”. The type of the sticker is independent from its format, which is determined by the fields is_animated and is_video.
Type string `json:"type"`
Type StickerType `json:"type"`
// Sticker width
Width int64 `json:"width"`
// Sticker height
@@ -4351,7 +4351,7 @@ type StickerSet struct {
// Sticker set title
Title string `json:"title"`
// Type of stickers in the set, currently one of “regular”, “mask”, “custom_emoji”
StickerType string `json:"sticker_type"`
StickerType StickerType `json:"sticker_type"`
// List of all set stickers
Stickers []Sticker `json:"stickers"`
// Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format
@@ -4361,7 +4361,7 @@ type StickerSet struct {
// This object describes the position on faces where a mask should be placed by default.
type MaskPosition struct {
// The part of the face relative to which the mask should be placed. One of “forehead”, “eyes”, “mouth”, or “chin”.
Point string `json:"point"`
Point MaskPositionPoint `json:"point"`
// Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position.
XShift float64 `json:"x_shift"`
// Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position.
@@ -4375,7 +4375,7 @@ type InputSticker struct {
// The added sticker. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new file using multipart/form-data under <file_attach_name> name. Animated and video stickers can't be uploaded via HTTP URL. More information on Sending Files »
Sticker string `json:"sticker"`
// Format of the added sticker, must be one of “static” for a .WEBP or .PNG image, “animated” for a .TGS animation, “video” for a .WEBM video
Format string `json:"format"`
Format InputStickerFormat `json:"format"`
// List of 1-20 emoji associated with the sticker
EmojiList []string `json:"emoji_list"`
// Optional. Position where the mask should be placed on faces. For “mask” stickers only.
@@ -4395,7 +4395,7 @@ type InlineQuery struct {
// Offset of the results to be returned, can be controlled by the bot
Offset string `json:"offset"`
// Optional. Type of the chat from which the inline query was sent. Can be either “sender” for a private chat with the inline query sender, “private”, “group”, “supergroup”, or “channel”. The chat type should be always known for requests sent from official clients and most third-party clients, unless the request was sent from a secret chat
ChatType string `json:"chat_type,omitempty"`
ChatType InlineQueryChatType `json:"chat_type,omitempty"`
// Optional. Sender location, only for bots that request user location
Location *Location `json:"location,omitempty"`
}
@@ -4542,7 +4542,7 @@ type InlineQueryResultPhoto struct {
// Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the photo caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4570,13 +4570,13 @@ type InlineQueryResultGif struct {
// URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result
ThumbnailURL string `json:"thumbnail_url"`
// Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
ThumbnailMimeType string `json:"thumbnail_mime_type,omitempty"`
ThumbnailMimeType InlineQueryResultGifThumbnailMimeType `json:"thumbnail_mime_type,omitempty"`
// Optional. Title for the result
Title string `json:"title,omitempty"`
// Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4604,13 +4604,13 @@ type InlineQueryResultMpeg4Gif struct {
// URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result
ThumbnailURL string `json:"thumbnail_url"`
// Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
ThumbnailMimeType string `json:"thumbnail_mime_type,omitempty"`
ThumbnailMimeType InlineQueryResultGifThumbnailMimeType `json:"thumbnail_mime_type,omitempty"`
// Optional. Title for the result
Title string `json:"title,omitempty"`
// Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4639,7 +4639,7 @@ type InlineQueryResultVideo struct {
// Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the video caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4671,7 +4671,7 @@ type InlineQueryResultAudio struct {
// Optional. Caption, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the audio caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Performer
@@ -4697,7 +4697,7 @@ type InlineQueryResultVoice struct {
// Optional. Caption, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the voice message caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Recording duration in seconds
@@ -4719,13 +4719,13 @@ type InlineQueryResultDocument struct {
// Optional. Caption of the document to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the document caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// A valid URL for the file
DocumentURL string `json:"document_url"`
// MIME type of the content of the file, either “application/pdf” or “application/zip”
MimeType string `json:"mime_type"`
MimeType InlineQueryResultDocumentMimeType `json:"mime_type"`
// Optional. Short description of the result
Description string `json:"description,omitempty"`
// Optional. Inline keyboard attached to the message
@@ -4859,7 +4859,7 @@ type InlineQueryResultCachedPhoto struct {
// Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the photo caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4883,7 +4883,7 @@ type InlineQueryResultCachedGif struct {
// Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4907,7 +4907,7 @@ type InlineQueryResultCachedMpeg4Gif struct {
// Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4947,7 +4947,7 @@ type InlineQueryResultCachedDocument struct {
// Optional. Caption of the document to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the document caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Inline keyboard attached to the message
@@ -4971,7 +4971,7 @@ type InlineQueryResultCachedVideo struct {
// Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the video caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Pass True, if the caption must be shown above the message media
@@ -4995,7 +4995,7 @@ type InlineQueryResultCachedVoice struct {
// Optional. Caption, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the voice message caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Inline keyboard attached to the message
@@ -5015,7 +5015,7 @@ type InlineQueryResultCachedAudio struct {
// Optional. Caption, 0-1024 characters after entities parsing
Caption string `json:"caption,omitempty"`
// Optional. Mode for parsing entities in the audio caption. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
CaptionEntities []MessageEntity `json:"caption_entities,omitempty"`
// Optional. Inline keyboard attached to the message
@@ -5055,7 +5055,7 @@ type InputTextMessageContent struct {
// Text of the message to be sent, 1-4096 characters
MessageText string `json:"message_text"`
// Optional. Mode for parsing entities in the message text. See formatting options for more details.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
// Optional. List of special entities that appear in message text, which can be specified instead of parse_mode
Entities []MessageEntity `json:"entities,omitempty"`
// Optional. Link preview generation options for the message
@@ -5256,7 +5256,7 @@ type SuccessfulPayment struct {
// This object contains basic information about a refunded payment.
type RefundedPayment struct {
// Three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars. Currently, always “XTR”
Currency string `json:"currency"`
Currency RefundedPaymentCurrency `json:"currency"`
// Total refunded price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45, total_amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
TotalAmount int64 `json:"total_amount"`
// Bot-specified invoice payload
@@ -5352,13 +5352,13 @@ func UnmarshalRevenueWithdrawalState(data []byte) (RevenueWithdrawalState, error
// The withdrawal is in progress.
type RevenueWithdrawalStatePending struct {
// Type of the state, always “pending”
Type string `json:"type"`
Type RevenueWithdrawalStatePendingType `json:"type"`
}
// The withdrawal succeeded.
type RevenueWithdrawalStateSucceeded struct {
// Type of the state, always “succeeded”
Type string `json:"type"`
Type RevenueWithdrawalStateSucceededType `json:"type"`
// Date the withdrawal was completed in Unix time
Date int64 `json:"date"`
// An HTTPS URL that can be used to see transaction details
@@ -5368,7 +5368,7 @@ type RevenueWithdrawalStateSucceeded struct {
// The withdrawal failed and the transaction was refunded.
type RevenueWithdrawalStateFailed struct {
// Type of the state, always “failed”
Type string `json:"type"`
Type RevenueWithdrawalStateFailedType `json:"type"`
}
// Contains information about the affiliate that received a commission via this transaction.
@@ -5452,9 +5452,9 @@ func UnmarshalTransactionPartner(data []byte) (TransactionPartner, error) {
// Describes a transaction with a user.
type TransactionPartnerUser struct {
// Type of the transaction partner, always “user”
Type string `json:"type"`
Type MessageOriginUserType `json:"type"`
// Type of the transaction, currently one of “invoice_payment” for payments via invoices, “paid_media_payment” for payments for paid media, “gift_purchase” for gifts sent by the bot, “premium_purchase” for Telegram Premium subscriptions gifted by the bot, “business_account_transfer” for direct transfers from managed business accounts
TransactionType string `json:"transaction_type"`
TransactionType TransactionPartnerUserTransactionType `json:"transaction_type"`
// Information about the user
User User `json:"user"`
// Optional. Information about the affiliate that received a commission via this transaction. Can be available only for “invoice_payment” and “paid_media_payment” transactions.
@@ -5508,7 +5508,7 @@ func (m *TransactionPartnerUser) UnmarshalJSON(data []byte) error {
// Describes a transaction with a chat.
type TransactionPartnerChat struct {
// Type of the transaction partner, always “chat”
Type string `json:"type"`
Type MessageOriginChatType `json:"type"`
// Information about the chat
Chat Chat `json:"chat"`
// Optional. The gift sent to the chat by the bot
@@ -5518,7 +5518,7 @@ type TransactionPartnerChat struct {
// Describes the affiliate program that issued the affiliate commission received via this transaction.
type TransactionPartnerAffiliateProgram struct {
// Type of the transaction partner, always “affiliate_program”
Type string `json:"type"`
Type TransactionPartnerAffiliateProgramType `json:"type"`
// Optional. Information about the bot that sponsored the affiliate program
SponsorUser *User `json:"sponsor_user,omitempty"`
// The number of Telegram Stars received by the bot for each 1000 Telegram Stars received by the affiliate program sponsor from referred users
@@ -5528,7 +5528,7 @@ type TransactionPartnerAffiliateProgram struct {
// Describes a withdrawal transaction with Fragment.
type TransactionPartnerFragment struct {
// Type of the transaction partner, always “fragment”
Type string `json:"type"`
Type TransactionPartnerFragmentType `json:"type"`
// Optional. State of the transaction if the transaction is outgoing
WithdrawalState RevenueWithdrawalState `json:"withdrawal_state,omitempty"`
}
@@ -5560,13 +5560,13 @@ func (m *TransactionPartnerFragment) UnmarshalJSON(data []byte) error {
// Describes a withdrawal transaction to the Telegram Ads platform.
type TransactionPartnerTelegramAds struct {
// Type of the transaction partner, always “telegram_ads”
Type string `json:"type"`
Type TransactionPartnerTelegramAdsType `json:"type"`
}
// Describes a transaction with payment for paid broadcasting.
type TransactionPartnerTelegramApi struct {
// Type of the transaction partner, always “telegram_api”
Type string `json:"type"`
Type TransactionPartnerTelegramApiType `json:"type"`
// The number of successful requests that exceeded regular limits and were therefore billed
RequestCount int64 `json:"request_count"`
}
@@ -5574,7 +5574,7 @@ type TransactionPartnerTelegramApi struct {
// Describes a transaction with an unknown source or recipient.
type TransactionPartnerOther struct {
// Type of the transaction partner, always “other”
Type string `json:"type"`
Type TransactionPartnerOtherType `json:"type"`
}
// Describes a Telegram Star transaction. Note that if the buyer initiates a chargeback with the payment provider from whom they acquired Stars (e.g., Apple, Google) following this transaction, the refunded Stars will be deducted from the bot's balance. This is outside of Telegram's control.
@@ -5656,7 +5656,7 @@ type PassportFile struct {
// Describes documents or other Telegram Passport elements shared with the bot by the user.
type EncryptedPassportElement struct {
// Element type. One of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”, “phone_number”, “email”.
Type string `json:"type"`
Type EncryptedPassportElementType `json:"type"`
// Optional. Base64-encoded encrypted Telegram Passport element data provided by the user; available only for “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport” and “address” types. Can be decrypted and verified using the accompanying EncryptedCredentials.
Data string `json:"data,omitempty"`
// Optional. User's verified phone number; available only for “phone_number” type
@@ -5734,7 +5734,7 @@ type PassportElementErrorDataField struct {
// Error source, must be data
Source string `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”
Type string `json:"type"`
Type PassportElementErrorDataFieldType `json:"type"`
// Name of the data field which has the error
FieldName string `json:"field_name"`
// Base64-encoded data hash
@@ -5748,7 +5748,7 @@ type PassportElementErrorFrontSide struct {
// Error source, must be front_side
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”
Type string `json:"type"`
Type PassportElementErrorSelfieType `json:"type"`
// Base64-encoded hash of the file with the front side of the document
FileHash string `json:"file_hash"`
// Error message
@@ -5760,7 +5760,7 @@ type PassportElementErrorReverseSide struct {
// Error source, must be reverse_side
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “driver_license”, “identity_card”
Type string `json:"type"`
Type PassportElementErrorReverseSideType `json:"type"`
// Base64-encoded hash of the file with the reverse side of the document
FileHash string `json:"file_hash"`
// Error message
@@ -5772,7 +5772,7 @@ type PassportElementErrorSelfie struct {
// Error source, must be selfie
Source string `json:"source"`
// The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”
Type string `json:"type"`
Type PassportElementErrorSelfieType `json:"type"`
// Base64-encoded hash of the file with the selfie
FileHash string `json:"file_hash"`
// Error message
@@ -5784,7 +5784,7 @@ type PassportElementErrorFile struct {
// Error source, must be file
Source string `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”
Type string `json:"type"`
Type PassportElementErrorFileType `json:"type"`
// Base64-encoded file hash
FileHash string `json:"file_hash"`
// Error message
@@ -5796,7 +5796,7 @@ type PassportElementErrorFiles struct {
// Error source, must be files
Source string `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”
Type string `json:"type"`
Type PassportElementErrorFileType `json:"type"`
// List of base64-encoded file hashes
FileHashes []string `json:"file_hashes"`
// Error message
@@ -5808,7 +5808,7 @@ type PassportElementErrorTranslationFile struct {
// Error source, must be translation_file
Source string `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 string `json:"type"`
Type PassportElementErrorTranslationFileType `json:"type"`
// Base64-encoded file hash
FileHash string `json:"file_hash"`
// Error message
@@ -5820,7 +5820,7 @@ type PassportElementErrorTranslationFiles struct {
// Error source, must be translation_files
Source string `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 string `json:"type"`
Type PassportElementErrorTranslationFileType `json:"type"`
// List of base64-encoded file hashes
FileHashes []string `json:"file_hashes"`
// Error message
+61 -26
View File
@@ -164,15 +164,16 @@ var knownDiscriminators = map[string]discriminatorSpec{
type emitter struct {
api *spec.API
outDir string
enums *enumPlan
}
func newEmitter(api *spec.API, outDir string) *emitter {
return &emitter{api: api, outDir: outDir}
return &emitter{api: api, outDir: outDir, enums: planEnums(api)}
}
// emitTypes renders types.gen.go.
func (e *emitter) emitTypes() error {
t, err := template.New("types").Funcs(funcs()).Parse(typesTmpl)
t, err := template.New("types").Funcs(funcs(e.enums)).Parse(typesTmpl)
if err != nil {
return fmt.Errorf("parse types.tmpl: %w", err)
}
@@ -208,20 +209,33 @@ func loadAPI(path string) (*spec.API, error) {
return &api, nil
}
// funcs is the FuncMap shared across templates.
func funcs() template.FuncMap {
// funcs is the FuncMap shared across templates. plan is the resolved
// enum plan; pass nil only in unit tests that don't exercise enums.
func funcs(plan *enumPlan) template.FuncMap {
return template.FuncMap{
"goType": goType,
"goField": goField,
"docComment": docComment,
"isOptional": func(f spec.Field) bool { return !f.Required },
"not": func(b bool) bool { return !b },
"title": title,
"isFileField": isFileField,
"fileCheck": fileCheck,
"multipartFieldEntry": multipartFieldEntry,
"multipartFileEntry": multipartFileEntry,
"returnGoType": returnGoType,
"goType": goType,
"goField": func(parent string, f spec.Field) string {
return goField(plan, parent, f)
},
"docComment": docComment,
"isOptional": func(f spec.Field) bool { return !f.Required },
"not": func(b bool) bool { return !b },
"title": title,
"isFileField": isFileField,
"fileCheck": fileCheck,
"multipartFieldEntry": func(parent string, f spec.Field) string {
return multipartFieldEntry(plan, parent, f)
},
"multipartFileEntry": multipartFileEntry,
"returnGoType": returnGoType,
// enum helpers
"enums": func() []enumDecl {
if plan == nil {
return nil
}
return plan.All()
},
"enumConstName": constName,
// discriminator helpers for types.tmpl
"hasDiscriminator": func(name string) bool { s, ok := knownDiscriminators[name]; return ok && len(s.Variants) > 0 },
"isSealedUnionReturn": func(tr spec.TypeRef) bool {
@@ -295,8 +309,10 @@ func multipartFileEntry(f spec.Field) string {
// multipartFieldEntry generates the line that adds f to the multipart map.
// Required scalar fields go in unconditionally; optional ones go in only
// when non-zero/non-empty.
func multipartFieldEntry(f spec.Field) string {
// when non-zero/non-empty. Typed-string enum fields are cast to string
// before assignment because the multipart map is map[string]string.
func multipartFieldEntry(plan *enumPlan, parent string, f spec.Field) string {
enumName := plan.FieldEnum(parent, f.Name)
switch f.Type.Kind {
case spec.KindPrimitive:
switch f.Type.Name {
@@ -306,6 +322,12 @@ func multipartFieldEntry(f spec.Field) string {
}
return fmt.Sprintf("\tif p.%s != nil { out[%q] = strconv.FormatInt(*p.%s, 10) }\n", f.Name, f.JSONName, f.Name)
case "string":
if enumName != "" {
if f.Required {
return fmt.Sprintf("\tout[%q] = string(p.%s)\n", f.JSONName, f.Name)
}
return fmt.Sprintf("\tif p.%s != \"\" { out[%q] = string(p.%s) }\n", f.Name, f.JSONName, f.Name)
}
if f.Required {
return fmt.Sprintf("\tout[%q] = p.%s\n", f.JSONName, f.Name)
}
@@ -392,7 +414,7 @@ func returnGoElem(tr spec.TypeRef) string {
// emitMethods renders methods.gen.go.
func (e *emitter) emitMethods() error {
t, err := template.New("methods").Funcs(funcs()).Parse(methodsTmpl)
t, err := template.New("methods").Funcs(funcs(e.enums)).Parse(methodsTmpl)
if err != nil {
return fmt.Errorf("parse methods.tmpl: %w", err)
}
@@ -409,7 +431,7 @@ func (e *emitter) emitMethods() error {
// emitEnums renders enums.gen.go.
func (e *emitter) emitEnums() error {
t, err := template.New("enums").Funcs(funcs()).Parse(enumsTmpl)
t, err := template.New("enums").Funcs(funcs(e.enums)).Parse(enumsTmpl)
if err != nil {
return fmt.Errorf("parse enums.tmpl: %w", err)
}
@@ -558,8 +580,16 @@ func matchesVariants(got []string, want ...string) bool {
}
// goField returns the Go struct-field declaration for a Field.
func goField(f spec.Field) string {
// When the field carries scraper-detected enum values and the emitter
// has a planned enum name for (parent, field), the field's Go type is
// the enum identifier. Typed-string enums use the zero string ""
// behaviour for omitempty, so we do not pointer-wrap optional enum
// fields. Parent is "" for method parameters.
func goField(plan *enumPlan, parent string, f spec.Field) string {
tag := fmt.Sprintf("`json:%q`", f.JSONName+omitempty(f))
if name := plan.FieldEnum(parent, f.Name); name != "" {
return fmt.Sprintf("%s %s %s", f.Name, name, tag)
}
return fmt.Sprintf("%s %s %s", f.Name, goType(f.Type, !f.Required), tag)
}
@@ -623,14 +653,19 @@ func buildUnionTypeSet(api *spec.API) map[string]bool {
// makeSentinelValue returns a sentinelValue func that uses the given union type set.
// It returns a minimal valid Go expression for a spec.Field's type,
// used in generated test param literals.
func makeSentinelValue(unionTypes map[string]bool) func(spec.Field) string {
// used in generated test param literals. plan supplies typed-enum names
// so a method-param sentinel for a ParseMode field becomes a typed
// constant rather than a magic string.
func makeSentinelValue(unionTypes map[string]bool, plan *enumPlan) func(spec.Field) string {
return func(f spec.Field) string {
return sentinelForField(f, unionTypes)
return sentinelForField(f, unionTypes, plan)
}
}
func sentinelForField(f spec.Field, unionTypes map[string]bool) string {
func sentinelForField(f spec.Field, unionTypes map[string]bool, plan *enumPlan) string {
if name := plan.FieldEnum("", f.Name); name != "" && len(f.EnumValues) > 0 {
return constName(name, f.EnumValues[0])
}
tr := f.Type
switch tr.Kind {
case spec.KindPrimitive:
@@ -729,8 +764,8 @@ func (e *emitter) emitTests() error {
unionTypes := buildUnionTypeSet(e.api)
// Add test-specific helpers to the shared func map.
fm := funcs()
fm["sentinelValue"] = makeSentinelValue(unionTypes)
fm := funcs(e.enums)
fm["sentinelValue"] = makeSentinelValue(unionTypes, e.enums)
fm["successResp"] = successResp
t, err := template.New("tests").Funcs(fm).Parse(testsTmpl)
+307
View File
@@ -0,0 +1,307 @@
package main
import (
"sort"
"strings"
"github.com/lukaszraczylo/go-telegram/internal/spec"
)
// enumDecl is one generated enum: a Go type alias of string plus a set
// of named constants. Values keep doc order; constant identifiers are
// derived from values via constName.
type enumDecl struct {
Name string
Values []string
}
// enumPlan is the deduplicated, name-resolved set of enums emitted from
// an API IR. Lookup returns the enum name for a given field reference;
// All returns the deterministic-ordered list of declarations to emit.
type enumPlan struct {
// fieldKey -> enum name. The fieldKey is a string built by enumKey.
byField map[string]string
// enum name -> declaration.
decls map[string]enumDecl
}
// enumKey identifies a single Field occurrence so the emitter can look
// up the enum name later. Parent is "" for method params (the method
// doesn't share a Go type with the field).
func enumKey(parent, fieldName string) string { return parent + "::" + fieldName }
// planEnums walks the IR, decides on enum names, deduplicates, and
// returns an enumPlan. All scraper-marked enum fields are covered.
func planEnums(api *spec.API) *enumPlan {
type ref struct {
parent string
fieldName string
jsonName string
values []string
valueKey string // canonical key for value-set dedup
}
var refs []ref
collect := func(parent string, fields []spec.Field) {
for _, f := range fields {
if len(f.EnumValues) == 0 {
continue
}
refs = append(refs, ref{
parent: parent,
fieldName: f.Name,
jsonName: f.JSONName,
values: f.EnumValues,
valueKey: valueKey(f.EnumValues),
})
}
}
for _, t := range api.Types {
collect(t.Name, t.Fields)
}
for _, m := range api.Methods {
// Method params have no shared Go parent type, so we pass "" as
// the parent. The default-name heuristic still produces the
// right answer for ParseMode-style enums.
collect("", m.Params)
}
// candidate name per ref (before collision resolution)
candidate := make([]string, len(refs))
for i, r := range refs {
candidate[i] = defaultEnumName(r.parent, r.jsonName, r.fieldName)
}
// Group by valueKey to coalesce identical value-sets across fields.
// Choose canonical name: prefer the most common candidate; tie-break
// by shortest name; final tie-break alphabetical.
type groupInfo struct {
values []string
name string
first int
}
groups := map[string]*groupInfo{}
for i, r := range refs {
g, ok := groups[r.valueKey]
if !ok {
groups[r.valueKey] = &groupInfo{values: r.values, first: i}
g = groups[r.valueKey]
}
_ = g
}
// Rank candidate names per group.
for vk := range groups {
counts := map[string]int{}
hasParent := map[string]bool{}
var names []string
for i, r := range refs {
if r.valueKey != vk {
continue
}
n := candidate[i]
if _, ok := counts[n]; !ok {
names = append(names, n)
}
counts[n]++
if r.parent != "" {
hasParent[n] = true
}
}
// Pick the canonical name for this group:
// 1. highest occurrence count wins;
// 2. names that originated from a parent type win over plain
// method-param candidates (avoids "Format"-style
// monosyllables);
// 3. shortest name wins;
// 4. alphabetical for full determinism.
sort.SliceStable(names, func(a, b int) bool {
if counts[names[a]] != counts[names[b]] {
return counts[names[a]] > counts[names[b]]
}
if hasParent[names[a]] != hasParent[names[b]] {
return hasParent[names[a]]
}
if len(names[a]) != len(names[b]) {
return len(names[a]) < len(names[b])
}
return names[a] < names[b]
})
groups[vk].name = names[0]
}
// Collision pass: two groups must not share the same enum name.
// When that happens, suffix the loser(s) with their parent type
// name so the result is unique. Iterate in deterministic order
// (groups sorted by valueKey).
used := map[string]string{} // name -> valueKey owner
var keys []string
for vk := range groups {
keys = append(keys, vk)
}
sort.Strings(keys)
for _, vk := range keys {
g := groups[vk]
if _, taken := used[g.name]; !taken {
used[g.name] = vk
continue
}
// Find a unique name by prepending a parent prefix from one of
// the contributing refs (the lowest-index ref in this group).
for i, r := range refs {
if r.valueKey != vk {
continue
}
if r.parent == "" {
continue
}
cand := r.parent + goNamePart(r.jsonName)
if _, taken := used[cand]; !taken {
g.name = cand
used[cand] = vk
goto next
}
_ = i
}
// Fallback: append a numeric disambiguator. Should not happen
// in practice for the Telegram docs but keeps the algorithm
// total.
for n := 2; ; n++ {
cand := groups[vk].name + itoa(n)
if _, taken := used[cand]; !taken {
g.name = cand
used[cand] = vk
break
}
}
next:
}
// Build the plan.
plan := &enumPlan{
byField: map[string]string{},
decls: map[string]enumDecl{},
}
for i, r := range refs {
name := groups[r.valueKey].name
plan.byField[enumKey(r.parent, r.fieldName)] = name
_ = i
}
for vk, g := range groups {
plan.decls[g.name] = enumDecl{Name: g.name, Values: g.values}
_ = vk
}
return plan
}
// All returns the enum declarations sorted by name for deterministic emit.
func (p *enumPlan) All() []enumDecl {
out := make([]enumDecl, 0, len(p.decls))
for _, d := range p.decls {
out = append(out, d)
}
sort.Slice(out, func(i, j int) bool { return out[i].Name < out[j].Name })
return out
}
// FieldEnum returns the enum name for a field on a given parent type
// (use parent="" for method parameters), or "" if the field is not an
// enum.
func (p *enumPlan) FieldEnum(parent, fieldName string) string {
if p == nil {
return ""
}
return p.byField[enumKey(parent, fieldName)]
}
// defaultEnumName picks an initial Go enum name for a field. parse_mode
// fields collapse to the canonical "ParseMode"; otherwise the name is
// parent + PascalCase(jsonName).
func defaultEnumName(parent, jsonName, fieldName string) string {
if strings.HasSuffix(jsonName, "parse_mode") {
return "ParseMode"
}
return parent + goNamePart(jsonName)
}
// constName builds a Go constant identifier "<EnumName><PascalValue>"
// from a wire value. Slashes (mime types) become "Of" so
// "image/jpeg" → "ImageOfJpeg".
func constName(enumName, value string) string {
return enumName + valuePascal(value)
}
func valuePascal(v string) string {
// "image/jpeg" → "ImageOfJpeg"
parts := strings.Split(v, "/")
for i, p := range parts {
parts[i] = goNamePart(p)
}
return strings.Join(parts, "Of")
}
// goNamePart converts a snake_case (or already-PascalCase) token to
// PascalCase, mirroring scrape.goName behaviour without the acronym
// special-cases (which apply to wire identifiers, not enum values).
func goNamePart(s string) string {
if s == "" {
return ""
}
parts := strings.Split(s, "_")
var b strings.Builder
for _, p := range parts {
if p == "" {
continue
}
// Acronyms used in Telegram wire names. Keeping in sync with
// scrape/table.go avoids divergent capitalisation between
// fieldName and constName.
switch p {
case "id":
b.WriteString("ID")
continue
case "url":
b.WriteString("URL")
continue
case "ip":
b.WriteString("IP")
continue
case "https":
b.WriteString("HTTPS")
continue
case "json":
b.WriteString("JSON")
continue
case "html":
b.WriteString("HTML")
continue
}
if c := p[0]; c >= 'a' && c <= 'z' {
b.WriteByte(c - 'a' + 'A')
b.WriteString(p[1:])
} else {
b.WriteString(p)
}
}
return b.String()
}
func valueKey(values []string) string {
cp := make([]string, len(values))
copy(cp, values)
sort.Strings(cp)
return strings.Join(cp, "\x00")
}
func itoa(n int) string {
if n == 0 {
return "0"
}
var buf [20]byte
i := len(buf)
for n > 0 {
i--
buf[i] = byte('0' + n%10)
n /= 10
}
return string(buf[i:])
}
+5 -52
View File
@@ -4,57 +4,10 @@
package api
// ParseMode controls how Telegram interprets formatting in message text.
type ParseMode string
{{range $e := enums}}
type {{$e.Name}} string
const (
ParseModeMarkdown ParseMode = "Markdown" // legacy
ParseModeMarkdownV2 ParseMode = "MarkdownV2"
ParseModeHTML ParseMode = "HTML"
)
// ChatType is the type of a Telegram chat.
type ChatType string
const (
ChatTypePrivate ChatType = "private"
ChatTypeGroup ChatType = "group"
ChatTypeSupergroup ChatType = "supergroup"
ChatTypeChannel ChatType = "channel"
)
// UpdateType identifies an Update payload variant. Used by allowed_updates
// in getUpdates / setWebhook.
type UpdateType string
const (
UpdateMessage UpdateType = "message"
UpdateEditedMessage UpdateType = "edited_message"
UpdateChannelPost UpdateType = "channel_post"
UpdateEditedChannelPost UpdateType = "edited_channel_post"
UpdateCallbackQuery UpdateType = "callback_query"
UpdateInlineQuery UpdateType = "inline_query"
)
// MessageEntityType is the kind of an entity (mention, hashtag, command, ...).
type MessageEntityType string
const (
EntityMention MessageEntityType = "mention"
EntityHashtag MessageEntityType = "hashtag"
EntityCashtag MessageEntityType = "cashtag"
EntityBotCommand MessageEntityType = "bot_command"
EntityURL MessageEntityType = "url"
EntityEmail MessageEntityType = "email"
EntityPhoneNumber MessageEntityType = "phone_number"
EntityBold MessageEntityType = "bold"
EntityItalic MessageEntityType = "italic"
EntityUnderline MessageEntityType = "underline"
EntityStrike MessageEntityType = "strikethrough"
EntitySpoiler MessageEntityType = "spoiler"
EntityCode MessageEntityType = "code"
EntityPre MessageEntityType = "pre"
EntityTextLink MessageEntityType = "text_link"
EntityTextMention MessageEntityType = "text_mention"
EntityCustomEmoji MessageEntityType = "custom_emoji"
)
{{range $v := $e.Values}} {{enumConstName $e.Name $v}} {{$e.Name}} = {{printf "%q" $v}}
{{end}})
{{end}}
+17 -17
View File
@@ -175,101 +175,101 @@ func makeFieldVariants(name, jname string, kind spec.Kind, variants []string, re
func TestMultipartFieldEntry_Int64Required(t *testing.T) {
f := makeField("ChatID", "chat_id", "int64", spec.KindPrimitive, true)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `FormatInt`)
require.NotContains(t, got, "if p.")
}
func TestMultipartFieldEntry_Int64Optional(t *testing.T) {
f := makeField("MessageThreadID", "message_thread_id", "int64", spec.KindPrimitive, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `FormatInt`)
require.Contains(t, got, "if p.")
}
func TestMultipartFieldEntry_StringRequired(t *testing.T) {
f := makeField("Text", "text", "string", spec.KindPrimitive, true)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `out["text"]`)
require.NotContains(t, got, "if p.Text")
}
func TestMultipartFieldEntry_StringOptional(t *testing.T) {
f := makeField("ParseMode", "parse_mode", "string", spec.KindPrimitive, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `if p.ParseMode`)
}
func TestMultipartFieldEntry_BoolRequired(t *testing.T) {
f := makeField("DisableNotification", "disable_notification", "bool", spec.KindPrimitive, true)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `FormatBool`)
require.NotContains(t, got, "if p.")
}
func TestMultipartFieldEntry_BoolOptional(t *testing.T) {
f := makeField("Protected", "protect_content", "bool", spec.KindPrimitive, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `FormatBool`)
require.Contains(t, got, "if p.")
}
func TestMultipartFieldEntry_Float64Required(t *testing.T) {
f := makeField("Latitude", "latitude", "float64", spec.KindPrimitive, true)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `FormatFloat`)
require.NotContains(t, got, "if p.")
}
func TestMultipartFieldEntry_Float64Optional(t *testing.T) {
f := makeField("Longitude", "longitude", "float64", spec.KindPrimitive, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `FormatFloat`)
require.Contains(t, got, "if p.")
}
func TestMultipartFieldEntry_OneOf_ChatIDRequired(t *testing.T) {
f := makeFieldVariants("ChatID", "chat_id", spec.KindOneOf, []string{"int64", "string"}, true)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `.String()`)
require.NotContains(t, got, "IsZero")
}
func TestMultipartFieldEntry_OneOf_ChatIDOptional(t *testing.T) {
f := makeFieldVariants("ChatID", "chat_id", spec.KindOneOf, []string{"int64", "string"}, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `IsZero`)
}
func TestMultipartFieldEntry_OneOf_InputFileOrString(t *testing.T) {
f := makeFieldVariants("Photo", "photo", spec.KindOneOf, []string{"InputFile", "string"}, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `PathOrID`)
}
func TestMultipartFieldEntry_OneOf_SealedRequired(t *testing.T) {
f := makeFieldVariants("Markup", "reply_markup", spec.KindOneOf, []string{"A", "B"}, true)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `json.Marshal`)
}
func TestMultipartFieldEntry_OneOf_SealedOptional(t *testing.T) {
f := makeFieldVariants("Markup", "reply_markup", spec.KindOneOf, []string{"A", "B"}, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `json.Marshal`)
require.Contains(t, got, "if p.Markup")
}
func TestMultipartFieldEntry_Named_Required(t *testing.T) {
f := makeField("Entities", "entities", "MessageEntity", spec.KindNamed, true)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `json.Marshal`)
require.NotContains(t, got, "if p.")
}
func TestMultipartFieldEntry_Named_Optional(t *testing.T) {
f := makeField("Entities", "entities", "MessageEntity", spec.KindNamed, false)
got := multipartFieldEntry(f)
got := multipartFieldEntry(nil, "", f)
require.Contains(t, got, `json.Marshal`)
require.Contains(t, got, "if p.")
}
@@ -574,7 +574,7 @@ func TestSentinelForField(t *testing.T) {
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := sentinelForField(c.field, unionTypes)
got := sentinelForField(c.field, unionTypes, nil)
require.Contains(t, got, c.contains, "sentinelForField for %q", c.name)
})
}
@@ -637,7 +637,7 @@ func TestUnionTypeFor_OneOfNoMatch(t *testing.T) {
// ---------------------------------------------------------------------------
func TestFuncs_HasExpectedKeys(t *testing.T) {
fm := funcs()
fm := funcs(nil)
require.NotNil(t, fm)
for _, key := range []string{"goType", "docComment", "returnGoType", "unionFields"} {
require.NotNil(t, fm[key], "funcs() missing key %q", key)
+2 -2
View File
@@ -20,7 +20,7 @@ var _ = json.Marshal // keep import for complex multipart fields
//
{{docComment .Doc -}}
type {{title .Name}}Params struct {
{{range .Params}}{{docComment .Doc}} {{goField .}}
{{range .Params}}{{docComment .Doc}} {{goField "" .}}
{{end}}}
{{if .HasFiles}}
// HasFile reports whether a multipart upload is required.
@@ -31,7 +31,7 @@ func (p *{{title .Name}}Params) HasFile() bool {
// MultipartFields returns the non-file fields used in the multipart body.
func (p *{{title .Name}}Params) MultipartFields() map[string]string {
out := map[string]string{}
{{range .Params}}{{if not (isFileField .)}}{{multipartFieldEntry .}}{{end}}{{end}} return out
{{range .Params}}{{if not (isFileField .)}}{{multipartFieldEntry "" .}}{{end}}{{end}} return out
}
// MultipartFiles returns the file parts.
+1 -1
View File
@@ -82,7 +82,7 @@ func UnmarshalMaybeInaccessibleMessage(data []byte) (MaybeInaccessibleMessage, e
{{else}}
{{docComment .Doc -}}
type {{.Name}} struct {
{{range .Fields}}{{docComment .Doc}}{{goField .}}
{{range .Fields}}{{docComment .Doc}}{{goField $td.Name .}}
{{end}}}
{{$unionFields := unionFields .}}{{if $unionFields}}
// UnmarshalJSON decodes {{.Name}} by dispatching union-typed fields
+205
View File
@@ -0,0 +1,205 @@
package main
import (
"regexp"
"strings"
)
// 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.
//
// Returns nil when the description does not look like an enum.
func extractEnumValues(jsonName, desc string) []string {
if values := parseModeEnumValues(jsonName, desc); values != nil {
return values
}
trigger, triggerEnd, isAlways := findEnumTrigger(desc)
if trigger < 0 {
return nil
}
tail := desc[trigger:]
values := collectQuotedValues(tail)
if len(values) == 0 {
return nil
}
// First quoted value must sit close to the trigger phrase (e.g.
// "can be “private”…"). Phrasings like "can be available only for
// “invoice_payment”…" introduce a referenced value, not an enum,
// and the gap between trigger end and first quote rules them out.
firstQuote := strings.Index(desc[triggerEnd:], "“")
if firstQuote < 0 {
return nil
}
gap := desc[triggerEnd : triggerEnd+firstQuote]
// Allow "always " as a permitted bridge (e.g. "Currently, always
// “XTR”") and promote the match to single-value form.
if strings.Contains(strings.ToLower(gap), "always ") {
isAlways = true
} else if firstQuote > 8 {
return nil
}
// Single-value matches are only credible after "always". Multi-
// value matches are credible after any trigger; the trigger phrase
// already constrained the context.
if !isAlways && len(values) < 2 {
return nil
}
for _, v := range values {
if !looksLikeEnumValue(v) {
return nil
}
}
return dedupeStrings(values)
}
// parseMode parameters do not list values inline — Telegram links to a
// separate "formatting options" section. We hardcode the canonical set
// here so callers get a typed ParseMode without writing magic strings.
func parseModeEnumValues(jsonName, desc string) []string {
if !strings.HasSuffix(jsonName, "parse_mode") {
return nil
}
if !strings.Contains(desc, "Mode for parsing entities") {
return nil
}
return []string{"Markdown", "MarkdownV2", "HTML"}
}
// enumTriggers are anchor phrases that introduce a list of valid wire
// values. Order matches longest-prefix priority; the matcher uses the
// earliest match in the description.
var enumTriggers = []string{
"can be either ",
"can be one of ",
"can be ",
"must be one of ",
"must be ",
"currently one of ",
"currently, one of ",
"currently, either ",
"currently, must be one of ",
"currently, can be ",
"currently, it can be one of ",
"currently, ",
"one of ",
"either ",
"always ",
}
// findEnumTrigger returns the byte offset where the first enum trigger
// phrase begins, the offset just past the phrase, and whether the
// trigger is the single-value "always" form. Returns (-1, -1, false)
// when no trigger matches. Matching is case-insensitive so "Currently"
// and "currently" both fire.
func findEnumTrigger(desc string) (int, int, bool) {
lower := strings.ToLower(desc)
bestStart := -1
bestEnd := -1
bestAlways := false
for _, t := range enumTriggers {
i := strings.Index(lower, t)
if i < 0 {
continue
}
if bestStart != -1 && i >= bestStart {
// Earlier-trigger wins outright; on a tie, the longer trigger
// (which we visit first) already populated bestEnd.
continue
}
bestStart = i
bestEnd = i + len(t)
bestAlways = t == "always "
}
return bestStart, bestEnd, bestAlways
}
// quotedRE matches a curly-quoted token: “value”.
var quotedRE = regexp.MustCompile(`“([^”]*)”`)
// collectQuotedValues returns the contents of every “…” pair in s in
// order. Multi-line is fine; the docs use single-paragraph cells.
func collectQuotedValues(s string) []string {
matches := quotedRE.FindAllStringSubmatch(s, -1)
out := make([]string, 0, len(matches))
for _, m := range matches {
out = append(out, m[1])
}
return out
}
// looksLikeEnumValue returns true for short identifiers that fit the
// shape of a Telegram wire enum. This rules out values like
// "attach://…", "h264", arbitrary URLs, and stylised punctuation.
//
// Permitted shapes:
//
// a-z0-9_ (e.g. "private", "bot_command")
// A-Z0-9_ (e.g. "XTR", "TON", "MarkdownV2")
// mixed case incl. "/" once (e.g. "image/jpeg", "video/mp4")
func looksLikeEnumValue(v string) bool {
if v == "" || len(v) > 64 {
return false
}
if strings.Contains(v, "://") || strings.Contains(v, " ") {
return false
}
// "image/jpeg"-style mime types: at most one slash, both halves alnum.
if i := strings.Index(v, "/"); i >= 0 {
if strings.Count(v, "/") > 1 {
return false
}
left, right := v[:i], v[i+1:]
return isIdent(left) && isIdent(right)
}
return isIdent(v)
}
func isIdent(s string) bool {
if s == "" {
return false
}
for _, r := range s {
switch {
case r >= 'a' && r <= 'z':
case r >= 'A' && r <= 'Z':
case r >= '0' && r <= '9':
case r == '_' || r == '-' || r == '.':
default:
return false
}
}
return true
}
func dedupeStrings(in []string) []string {
seen := make(map[string]struct{}, len(in))
out := make([]string, 0, len(in))
for _, s := range in {
if _, ok := seen[s]; ok {
continue
}
seen[s] = struct{}{}
out = append(out, s)
}
return out
}
+85
View File
@@ -0,0 +1,85 @@
package main
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestExtractEnumValues_CanBeEither(t *testing.T) {
desc := "Type of the chat, can be either “private”, “group”, “supergroup” or “channel”"
got := extractEnumValues("type", desc)
require.Equal(t, []string{"private", "group", "supergroup", "channel"}, got)
}
func TestExtractEnumValues_CurrentlyCanBe(t *testing.T) {
desc := "Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), or “code” (monowidth string)"
got := extractEnumValues("type", desc)
require.Equal(t, []string{"mention", "hashtag", "code"}, got)
}
func TestExtractEnumValues_AlwaysSingle(t *testing.T) {
desc := "Type of the message origin, always “user”"
got := extractEnumValues("type", desc)
require.Equal(t, []string{"user"}, got)
}
func TestExtractEnumValues_MustBeOneOfMime(t *testing.T) {
desc := "Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”"
got := extractEnumValues("thumbnail_mime_type", desc)
require.Equal(t, []string{"image/jpeg", "image/gif", "video/mp4"}, got)
}
func TestExtractEnumValues_ParseModeSpecial(t *testing.T) {
desc := "Optional. Mode for parsing entities in the message text. See formatting options for more details."
got := extractEnumValues("parse_mode", desc)
require.Equal(t, []string{"Markdown", "MarkdownV2", "HTML"}, got)
}
func TestExtractEnumValues_QuestionParseMode(t *testing.T) {
desc := "Mode for parsing entities in the question. See formatting options for more details."
got := extractEnumValues("question_parse_mode", desc)
require.Equal(t, []string{"Markdown", "MarkdownV2", "HTML"}, got)
}
func TestExtractEnumValues_FalsePositiveReferencedValue(t *testing.T) {
// "Can be available only for "X"" is NOT an enum: the quote is a
// reference to a transaction-type value, not an introduced list.
desc := "Optional. Bot-specified invoice payload. Can be available only for “invoice_payment” transactions."
got := extractEnumValues("invoice_payload", desc)
require.Nil(t, got)
}
func TestExtractEnumValues_FalsePositiveSingleQuotedNonEnum(t *testing.T) {
// "can be ignored" with a quoted reference value later — not an enum.
desc := "Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side."
got := extractEnumValues("thumbnail", desc)
require.Nil(t, got)
}
func TestExtractEnumValues_RefundedPaymentCurrentlyAlways(t *testing.T) {
desc := "Three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars. Currently, always “XTR”"
got := extractEnumValues("currency", desc)
require.Equal(t, []string{"XTR"}, got)
}
func TestExtractEnumValues_RejectURLValues(t *testing.T) {
// "attach://<file_attach_name>" must never be promoted to an enum value.
desc := "Pass “attach://<file_attach_name>” to upload a new one"
got := extractEnumValues("media", desc)
require.Nil(t, got)
}
func TestExtractEnumValues_StringTypeOnly(t *testing.T) {
// (Sanity — table.go gates on string type, but the function itself
// should still respond consistently.)
desc := "ABC, can be “a”, “b”"
got := extractEnumValues("x", desc)
require.Equal(t, []string{"a", "b"}, got)
}
func TestExtractEnumValues_DedupeRepeatedValues(t *testing.T) {
desc := "Currently, one of “XTR” for Telegram Stars or “XTR” again"
got := extractEnumValues("currency", desc)
require.Equal(t, []string{"XTR"}, got)
}
+22 -10
View File
@@ -30,12 +30,18 @@ func parseFieldsTable(t *html.Node) []spec.Field {
desc := strings.TrimSpace(textOf(cells[2]))
required := !strings.HasPrefix(desc, "Optional.")
tref := parseTypeRef(typeText)
var enumVals []string
if tref.Kind == spec.KindPrimitive && tref.Name == "string" {
enumVals = extractEnumValues(jname, desc)
}
fields = append(fields, spec.Field{
Name: goName(jname),
JSONName: jname,
Type: parseTypeRef(typeText),
Required: required,
Doc: desc,
Name: goName(jname),
JSONName: jname,
Type: tref,
Required: required,
Doc: desc,
EnumValues: enumVals,
})
}
return fields
@@ -59,12 +65,18 @@ func parseParamsTable(t *html.Node) []spec.Field {
req := strings.EqualFold(strings.TrimSpace(textOf(cells[2])), "Yes")
desc := strings.TrimSpace(textOf(cells[3]))
tref := parseTypeRef(typeText)
var enumVals []string
if tref.Kind == spec.KindPrimitive && tref.Name == "string" {
enumVals = extractEnumValues(jname, desc)
}
params = append(params, spec.Field{
Name: goName(jname),
JSONName: jname,
Type: parseTypeRef(typeText),
Required: req,
Doc: desc,
Name: goName(jname),
JSONName: jname,
Type: tref,
Required: req,
Doc: desc,
EnumValues: enumVals,
})
}
return params
+6 -6
View File
@@ -15,17 +15,17 @@ func NewStatus(s string) dispatch.Filter[*api.ChatMemberUpdated] {
}
switch m := u.NewChatMember.(type) {
case *api.ChatMemberOwner:
return m.Status == s
return string(m.Status) == s
case *api.ChatMemberAdministrator:
return m.Status == s
return string(m.Status) == s
case *api.ChatMemberMember:
return m.Status == s
return string(m.Status) == s
case *api.ChatMemberRestricted:
return m.Status == s
return string(m.Status) == s
case *api.ChatMemberLeft:
return m.Status == s
return string(m.Status) == s
case *api.ChatMemberBanned:
return m.Status == s
return string(m.Status) == s
default:
return false
}
@@ -12,15 +12,15 @@ func memberUpdate(status string, fromID int64) *api.ChatMemberUpdated {
var newMember api.ChatMember
switch status {
case "member":
newMember = &api.ChatMemberMember{Status: status}
newMember = &api.ChatMemberMember{Status: api.ChatMemberMemberStatusMember}
case "administrator":
newMember = &api.ChatMemberAdministrator{Status: status}
newMember = &api.ChatMemberAdministrator{Status: api.ChatMemberAdministratorStatusAdministrator}
case "kicked":
newMember = &api.ChatMemberBanned{Status: status}
newMember = &api.ChatMemberBanned{Status: api.ChatMemberBannedStatusKicked}
case "left":
newMember = &api.ChatMemberLeft{Status: status}
newMember = &api.ChatMemberLeft{Status: api.ChatMemberLeftStatusLeft}
default:
newMember = &api.ChatMemberMember{Status: status}
newMember = &api.ChatMemberMember{Status: api.ChatMemberMemberStatusMember}
}
return &api.ChatMemberUpdated{
From: api.User{ID: fromID},
@@ -70,7 +70,7 @@ func TestComposedFilters(t *testing.T) {
func TestNewStatus_Owner(t *testing.T) {
u := &api.ChatMemberUpdated{
From: api.User{ID: 1},
NewChatMember: &api.ChatMemberOwner{Status: "creator"},
NewChatMember: &api.ChatMemberOwner{Status: api.ChatMemberOwnerStatusCreator},
}
require.True(t, cmfilter.NewStatus("creator")(u))
require.False(t, cmfilter.NewStatus("member")(u))
@@ -79,7 +79,7 @@ func TestNewStatus_Owner(t *testing.T) {
func TestNewStatus_Restricted(t *testing.T) {
u := &api.ChatMemberUpdated{
From: api.User{ID: 1},
NewChatMember: &api.ChatMemberRestricted{Status: "restricted"},
NewChatMember: &api.ChatMemberRestricted{Status: api.ChatMemberRestrictedStatusRestricted},
}
require.True(t, cmfilter.NewStatus("restricted")(u))
require.False(t, cmfilter.NewStatus("member")(u))
+5 -5
View File
@@ -48,7 +48,7 @@ func Command(name string) dispatch.Filter[*api.Message] {
return false
}
first := m.Entities[0]
if first.Type != string(api.EntityBotCommand) || first.Offset != 0 {
if first.Type != api.MessageEntityTypeBotCommand || first.Offset != 0 {
return false
}
end := int(first.Length)
@@ -72,7 +72,7 @@ func AnyCommand() dispatch.Filter[*api.Message] {
return false
}
first := m.Entities[0]
return first.Type == string(api.EntityBotCommand) && first.Offset == 0
return first.Type == api.MessageEntityTypeBotCommand && first.Offset == 0
}
}
@@ -105,8 +105,8 @@ func HasDocument() dispatch.Filter[*api.Message] {
}
// HasEntity returns a Filter that matches messages whose Entities contain at
// least one entity of type t (e.g. string(api.EntityBotCommand)).
func HasEntity(t string) dispatch.Filter[*api.Message] {
// least one entity of type t (e.g. api.MessageEntityTypeBotCommand).
func HasEntity(t api.MessageEntityType) dispatch.Filter[*api.Message] {
return func(m *api.Message) bool {
if m == nil {
return false
@@ -123,7 +123,7 @@ func HasEntity(t string) dispatch.Filter[*api.Message] {
// ChatType returns a Filter that matches messages whose Chat.Type equals t.
func ChatType(t api.ChatType) dispatch.Filter[*api.Message] {
return func(m *api.Message) bool {
return m != nil && m.Chat.Type == string(t)
return m != nil && m.Chat.Type == t
}
}
+8 -8
View File
@@ -11,7 +11,7 @@ import (
func msg(text string) *api.Message {
return &api.Message{
MessageID: 1,
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: text,
}
}
@@ -20,10 +20,10 @@ func cmdMsg(cmd string) *api.Message {
text := cmd
return &api.Message{
MessageID: 1,
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: text,
Entities: []api.MessageEntity{
{Type: string(api.EntityBotCommand), Offset: 0, Length: int64(len([]rune(text)))},
{Type: api.MessageEntityTypeBotCommand, Offset: 0, Length: int64(len([]rune(text)))},
},
}
}
@@ -72,7 +72,7 @@ func TestCommand(t *testing.T) {
t.Run("strips BotName suffix", func(t *testing.T) {
m := &api.Message{
Text: "/start@MyBot",
Entities: []api.MessageEntity{{Type: string(api.EntityBotCommand), Offset: 0, Length: 12}},
Entities: []api.MessageEntity{{Type: api.MessageEntityTypeBotCommand, Offset: 0, Length: 12}},
}
f := msgfilter.Command("/start")
require.True(t, f(m))
@@ -134,9 +134,9 @@ func TestHasDocument(t *testing.T) {
}
func TestHasEntity(t *testing.T) {
f := msgfilter.HasEntity(string(api.EntityURL))
f := msgfilter.HasEntity(api.MessageEntityTypeURL)
m := msg("check https://example.com")
m.Entities = []api.MessageEntity{{Type: string(api.EntityURL), Offset: 6, Length: 19}}
m.Entities = []api.MessageEntity{{Type: api.MessageEntityTypeURL, Offset: 6, Length: 19}}
require.True(t, f(m))
require.False(t, f(msg("plain")))
require.False(t, f(nil))
@@ -148,7 +148,7 @@ func TestChatType(t *testing.T) {
require.True(t, f(private))
group := msg("hi")
group.Chat.Type = string(api.ChatTypeGroup)
group.Chat.Type = api.ChatTypeGroup
require.False(t, f(group))
require.False(t, f(nil))
}
@@ -183,6 +183,6 @@ func TestComposedMessageFilters(t *testing.T) {
require.True(t, f(m))
m2 := msg("say hello")
m2.Chat.Type = string(api.ChatTypeGroup)
m2.Chat.Type = api.ChatTypeGroup
require.False(t, f(m2))
}
+3 -3
View File
@@ -17,7 +17,7 @@ func msgUpdate(id int64, text string) api.Update {
UpdateID: id,
Message: &api.Message{
MessageID: id,
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: text,
},
}
@@ -29,10 +29,10 @@ func cmdUpdate(id int64, cmd string) api.Update {
UpdateID: id,
Message: &api.Message{
MessageID: id,
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: cmd,
Entities: []api.MessageEntity{
{Type: string(api.EntityBotCommand), Offset: 0, Length: int64(len(cmd))},
{Type: api.MessageEntityTypeBotCommand, Offset: 0, Length: int64(len(cmd))},
},
},
}
+1 -1
View File
@@ -516,7 +516,7 @@ func extractCommand(m *api.Message) (cmd, args string, ok bool) {
return "", "", false
}
first := m.Entities[0]
if first.Type != string(api.EntityBotCommand) || first.Offset != 0 {
if first.Type != api.MessageEntityTypeBotCommand || first.Offset != 0 {
return "", "", false
}
cmd, sliceOk := utf16Slice(m.Text, int(first.Offset), int(first.Length))
+15 -15
View File
@@ -31,9 +31,9 @@ func cmdMessage(text string) api.Update {
return api.Update{
UpdateID: 1,
Message: &api.Message{
MessageID: 1, Date: 0, Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
MessageID: 1, Date: 0, Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: text,
Entities: []api.MessageEntity{{Type: string(api.EntityBotCommand), Offset: 0, Length: int64(indexEnd(text))}},
Entities: []api.MessageEntity{{Type: api.MessageEntityTypeBotCommand, Offset: 0, Length: int64(indexEnd(text))}},
},
}
}
@@ -153,10 +153,10 @@ func TestRouter_NonASCIICommand(t *testing.T) {
UpdateID: 1,
Message: &api.Message{
MessageID: 1,
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: text,
Entities: []api.MessageEntity{
{Type: string(api.EntityBotCommand), Offset: 0, Length: cmdU16Len},
{Type: api.MessageEntityTypeBotCommand, Offset: 0, Length: cmdU16Len},
},
},
}
@@ -196,7 +196,7 @@ func TestRouter_CommandValuesNotLeakedOnNoMatch(t *testing.T) {
u := api.Update{UpdateID: 1, Message: &api.Message{
MessageID: 1, Chat: api.Chat{ID: 1, Type: "private"},
Text: "/unknown",
Entities: []api.MessageEntity{{Type: string(api.EntityBotCommand), Offset: 0, Length: 8}},
Entities: []api.MessageEntity{{Type: api.MessageEntityTypeBotCommand, Offset: 0, Length: 8}},
}}
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
@@ -247,7 +247,7 @@ func TestRouter_OnChannelPost(t *testing.T) {
})
u := api.Update{UpdateID: 1, ChannelPost: &api.Message{
MessageID: 99, Chat: api.Chat{ID: -100, Type: string(api.ChatTypeChannel)},
MessageID: 99, Chat: api.Chat{ID: -100, Type: api.ChatTypeChannel},
}}
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel()
@@ -393,7 +393,7 @@ func TestRouter_OnInlineQueryFilter_Matches(t *testing.T) {
func TestRouter_FilterChain_Composition(t *testing.T) {
// Filter: private chat AND text contains "hello"
privateChat := Filter[*api.Message](func(m *api.Message) bool {
return m != nil && m.Chat.Type == string(api.ChatTypePrivate)
return m != nil && m.Chat.Type == api.ChatTypePrivate
})
hasHello := Filter[*api.Message](func(m *api.Message) bool {
return m != nil && len(m.Text) > 0 && containsStr(m.Text, "hello")
@@ -405,10 +405,10 @@ func TestRouter_FilterChain_Composition(t *testing.T) {
r.OnMessageFilter(combined, func(c *Context, m *api.Message) error { hit <- m.Text; return nil })
match := api.Update{UpdateID: 1, Message: &api.Message{
MessageID: 1, Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)}, Text: "say hello",
MessageID: 1, Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate}, Text: "say hello",
}}
noMatch := api.Update{UpdateID: 2, Message: &api.Message{
MessageID: 2, Chat: api.Chat{ID: 2, Type: string(api.ChatTypeGroup)}, Text: "say hello",
MessageID: 2, Chat: api.Chat{ID: 2, Type: api.ChatTypeGroup}, Text: "say hello",
}}
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
@@ -462,7 +462,7 @@ func TestRouter_ConcurrentDispatch_AllHandlersFire(t *testing.T) {
for i := range ups {
ups[i] = api.Update{UpdateID: int64(i + 1), Message: &api.Message{
MessageID: int64(i + 1),
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: "hi",
}}
}
@@ -493,7 +493,7 @@ func TestRouter_ConcurrentDispatch_SemaphoreBoundsConcurrency(t *testing.T) {
for i := range ups {
ups[i] = api.Update{UpdateID: int64(i + 1), Message: &api.Message{
MessageID: int64(i + 1),
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: "hi",
}}
}
@@ -555,7 +555,7 @@ func TestRouter_ConcurrentDispatch_WaitsForInFlight(t *testing.T) {
)
u := api.Update{UpdateID: 1, Message: &api.Message{
MessageID: 1, Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)}, Text: "hi",
MessageID: 1, Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate}, Text: "hi",
}}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
@@ -594,7 +594,7 @@ func TestRouter_SerialMode_NoRace(t *testing.T) {
for i := range ups {
ups[i] = api.Update{UpdateID: int64(i + 1), Message: &api.Message{
MessageID: int64(i + 1),
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: "hi",
}}
}
@@ -908,7 +908,7 @@ func TestRouter_ContextCancel_UnblocksWaitingAcquire(t *testing.T) {
for i := range limit {
lu.Send(api.Update{UpdateID: int64(i + 1), Message: &api.Message{
MessageID: int64(i + 1),
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: "hi",
}})
}
@@ -919,7 +919,7 @@ func TestRouter_ContextCancel_UnblocksWaitingAcquire(t *testing.T) {
// Send one more update — Run will block trying to acquire the full semaphore.
lu.Send(api.Update{UpdateID: int64(limit + 1), Message: &api.Message{
MessageID: int64(limit + 1),
Chat: api.Chat{ID: 1, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 1, Type: api.ChatTypePrivate},
Text: "extra",
}})
+1371 -152
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -15,7 +15,7 @@ Package message provides Filter helpers for \*api.Message payloads.
- [func Command\(name string\) dispatch.Filter\[\*api.Message\]](<#Command>)
- [func FromUser\(userID int64\) dispatch.Filter\[\*api.Message\]](<#FromUser>)
- [func HasDocument\(\) dispatch.Filter\[\*api.Message\]](<#HasDocument>)
- [func HasEntity\(t string\) dispatch.Filter\[\*api.Message\]](<#HasEntity>)
- [func HasEntity\(t api.MessageEntityType\) dispatch.Filter\[\*api.Message\]](<#HasEntity>)
- [func HasPhoto\(\) dispatch.Filter\[\*api.Message\]](<#HasPhoto>)
- [func InChat\(chatID int64\) dispatch.Filter\[\*api.Message\]](<#InChat>)
- [func IsForward\(\) dispatch.Filter\[\*api.Message\]](<#IsForward>)
@@ -75,10 +75,10 @@ HasDocument returns a Filter that matches messages with a Document attachment.
## func HasEntity
```go
func HasEntity(t string) dispatch.Filter[*api.Message]
func HasEntity(t api.MessageEntityType) dispatch.Filter[*api.Message]
```
HasEntity returns a Filter that matches messages whose Entities contain at least one entity of type t \(e.g. string\(api.EntityBotCommand\)\).
HasEntity returns a Filter that matches messages whose Entities contain at least one entity of type t \(e.g. api.MessageEntityTypeBotCommand\).
<a name="HasPhoto"></a>
## func HasPhoto
+2 -2
View File
@@ -68,7 +68,7 @@ func TestHandleStart_SendsInitialKeyboard(t *testing.T) {
bot := client.New("test:token", client.WithHTTPClient(m))
msg := &api.Message{
MessageID: 1,
Chat: api.Chat{ID: 42, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 42, Type: api.ChatTypePrivate},
From: &api.User{ID: 7, FirstName: "Alice"},
Text: "/start",
}
@@ -88,7 +88,7 @@ func callbackCtx(bot *client.Bot, q *api.CallbackQuery, groups []string) *dispat
func callbackQuery(data string, msgID int64, chatID int64) *api.CallbackQuery {
msg := &api.Message{
MessageID: msgID,
Chat: api.Chat{ID: chatID, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: chatID, Type: api.ChatTypePrivate},
}
return &api.CallbackQuery{
ID: "cb1",
+2 -2
View File
@@ -46,7 +46,7 @@ func msgUpd(userID, chatID int64, text string) api.Update {
}
}
entities = append(entities, api.MessageEntity{
Type: string(api.EntityBotCommand),
Type: api.MessageEntityTypeBotCommand,
Offset: 0,
Length: int64(end),
})
@@ -56,7 +56,7 @@ func msgUpd(userID, chatID int64, text string) api.Update {
Message: &api.Message{
MessageID: 1,
From: &api.User{ID: userID},
Chat: api.Chat{ID: chatID, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: chatID, Type: api.ChatTypePrivate},
Text: text,
Entities: entities,
},
+2 -2
View File
@@ -55,7 +55,7 @@ func TestHandleStart_GreetsUser(t *testing.T) {
bot := client.New("test:token", client.WithHTTPClient(m))
msg := &api.Message{
MessageID: 1,
Chat: api.Chat{ID: 42, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 42, Type: api.ChatTypePrivate},
From: &api.User{ID: 7, FirstName: "Alice"},
Text: "/start",
}
@@ -82,7 +82,7 @@ func TestHandleEcho_RepliesWithSameText(t *testing.T) {
bot := client.New("test:token", client.WithHTTPClient(m))
msg := &api.Message{
MessageID: 5,
Chat: api.Chat{ID: 42, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 42, Type: api.ChatTypePrivate},
From: &api.User{ID: 7, FirstName: "Alice"},
Text: "hello echo",
}
+1 -1
View File
@@ -49,7 +49,7 @@ func TestHandlePing_RepliesWithPong(t *testing.T) {
bot := client.New("test:token", client.WithHTTPClient(m))
msg := &api.Message{
MessageID: 1,
Chat: api.Chat{ID: 42, Type: string(api.ChatTypePrivate)},
Chat: api.Chat{ID: 42, Type: api.ChatTypePrivate},
From: &api.User{ID: 7, FirstName: "Alice"},
Text: "/ping",
}
+699 -123
View File
File diff suppressed because it is too large Load Diff
+5
View File
@@ -45,6 +45,11 @@ type Field struct {
Type TypeRef `json:"type"`
Required bool `json:"required,omitempty"`
Doc string `json:"doc,omitempty"`
// EnumValues, when non-empty, lists the wire-level string values the
// scraper detected for an enum-like description ("can be A, B or C",
// "always X", parse_mode special-case). Order is doc order, deduped.
// Emitted as a typed Go enum that replaces the field's string type.
EnumValues []string `json:"enum_values,omitempty"`
}
// Kind enumerates TypeRef shapes.
+6 -1
View File
@@ -98,7 +98,12 @@
"kind": "primitive",
"name": "string"
},
"doc": "Mode for parsing entities in the message text."
"doc": "Mode for parsing entities in the message text.",
"enum_values": [
"Markdown",
"MarkdownV2",
"HTML"
]
}
],
"returns": {
+1 -48
View File
@@ -4,57 +4,10 @@
package api
// ParseMode controls how Telegram interprets formatting in message text.
type ParseMode string
const (
ParseModeMarkdown ParseMode = "Markdown" // legacy
ParseModeMarkdown ParseMode = "Markdown"
ParseModeMarkdownV2 ParseMode = "MarkdownV2"
ParseModeHTML ParseMode = "HTML"
)
// ChatType is the type of a Telegram chat.
type ChatType string
const (
ChatTypePrivate ChatType = "private"
ChatTypeGroup ChatType = "group"
ChatTypeSupergroup ChatType = "supergroup"
ChatTypeChannel ChatType = "channel"
)
// UpdateType identifies an Update payload variant. Used by allowed_updates
// in getUpdates / setWebhook.
type UpdateType string
const (
UpdateMessage UpdateType = "message"
UpdateEditedMessage UpdateType = "edited_message"
UpdateChannelPost UpdateType = "channel_post"
UpdateEditedChannelPost UpdateType = "edited_channel_post"
UpdateCallbackQuery UpdateType = "callback_query"
UpdateInlineQuery UpdateType = "inline_query"
)
// MessageEntityType is the kind of an entity (mention, hashtag, command, ...).
type MessageEntityType string
const (
EntityMention MessageEntityType = "mention"
EntityHashtag MessageEntityType = "hashtag"
EntityCashtag MessageEntityType = "cashtag"
EntityBotCommand MessageEntityType = "bot_command"
EntityURL MessageEntityType = "url"
EntityEmail MessageEntityType = "email"
EntityPhoneNumber MessageEntityType = "phone_number"
EntityBold MessageEntityType = "bold"
EntityItalic MessageEntityType = "italic"
EntityUnderline MessageEntityType = "underline"
EntityStrike MessageEntityType = "strikethrough"
EntitySpoiler MessageEntityType = "spoiler"
EntityCode MessageEntityType = "code"
EntityPre MessageEntityType = "pre"
EntityTextLink MessageEntityType = "text_link"
EntityTextMention MessageEntityType = "text_mention"
EntityCustomEmoji MessageEntityType = "custom_emoji"
)
+1 -1
View File
@@ -37,7 +37,7 @@ type SendMessageParams struct {
// Text of the message to be sent.
Text string `json:"text"`
// Mode for parsing entities in the message text.
ParseMode string `json:"parse_mode,omitempty"`
ParseMode ParseMode `json:"parse_mode,omitempty"`
}
// SendMessage calls the sendMessage Telegram Bot API method.