From 62bb884a16e928ec2f1c7e2760bc16511a205097 Mon Sep 17 00:00:00 2001 From: xuwanchao Date: Wed, 3 Jun 2026 15:32:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E6=9C=80=E5=A4=A7?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E6=AC=A1=E6=95=B0=E9=99=90=E5=88=B6=E9=98=B2?= =?UTF-8?q?=E6=AD=A2=E6=A0=88=E6=BA=A2=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mtproto.go | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/mtproto.go b/mtproto.go index 6485c8f8..e2b11b6d 100755 --- a/mtproto.go +++ b/mtproto.go @@ -741,6 +741,14 @@ func (m *MTProto) makeRequest(data tl.Object, expectedTypes ...reflect.Type) (an return m.makeRequestCtx(ctx, data, expectedTypes...) } +// 1. 定义一个控制递归层数的 key 类型(防止与其他 context key 冲突) +type retryKeyType struct{} + +var retryKey retryKeyType + +// 2. 定义最大递归/重试次数常量 +const maxRetries = 3 + func (m *MTProto) makeRequestCtx(ctx context.Context, data tl.Object, expectedTypes ...reflect.Type) (any, error) { if err := m.tcpState.WaitForActive(ctx); err != nil { return nil, fmt.Errorf("waiting for active tcp state: %w", err) @@ -755,7 +763,22 @@ func (m *MTProto) makeRequestCtx(ctx context.Context, data tl.Object, expectedTy if reconnErr := m.Reconnect(false); reconnErr != nil { return nil, fmt.Errorf("reconnecting: %w", reconnErr) } - return m.makeRequestCtx(ctx, data, expectedTypes...) + + // ====== 3. 核心修改:拦截无限递归 ====== + retries := 0 + if val := ctx.Value(retryKey); val != nil { + retries = val.(int) + } + + if retries >= maxRetries { + m.Logger.Errorf("[FATAL LOOP] Aborting request %T to avoid stack overflow. Reached max retries: %d", data, maxRetries) + return nil, fmt.Errorf("request aborted: exceeded maximum transport error retries (%d)", maxRetries) + } + + // 将重试次数 +1 并注入到新的 context 中传给下一层 + retryCtx := context.WithValue(ctx, retryKey, retries+1) + return m.makeRequestCtx(retryCtx, data, expectedTypes...) + // ====================================== } select { From 4af55fe62643d3becea11006c5a09f274948e421 Mon Sep 17 00:00:00 2001 From: xuwanchao Date: Wed, 8 Jul 2026 16:39:14 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=A4=84=E7=90=86=E6=96=AD=E8=A8=80?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- telegram/helpers.go | 74 +++++++++++++++++++++++++++++------------- telegram/messages.go | 4 ++- telegram/newmessage.go | 8 +++-- telegram/users.go | 11 +++++-- telegram/utils.go | 15 +++++++-- 5 files changed, 82 insertions(+), 30 deletions(-) diff --git a/telegram/helpers.go b/telegram/helpers.go index 41873f5c..c8d4b2ce 100644 --- a/telegram/helpers.go +++ b/telegram/helpers.go @@ -224,17 +224,23 @@ func calculateSha256Hash(localFile string) (string, error) { func processUpdates(updates Updates) []Message { var messages []Message processMessage := func(upd Update) { + var msg Message switch update := upd.(type) { case *UpdateNewMessage: - messages = append(messages, update.Message.(*MessageObj)) + msg = update.Message case *UpdateNewChannelMessage: - messages = append(messages, update.Message.(*MessageObj)) + msg = update.Message case *UpdateEditMessage: - messages = append(messages, update.Message.(*MessageObj)) + msg = update.Message case *UpdateEditChannelMessage: - messages = append(messages, update.Message.(*MessageObj)) + msg = update.Message case *UpdateBotEditBusinessMessage: - messages = append(messages, update.Message.(*MessageObj)) + msg = update.Message + } + if msg != nil { + if m, ok := msg.(*MessageObj); ok { + messages = append(messages, m) + } } } @@ -305,15 +311,25 @@ updateTypeSwitch: } switch upd := upd.(type) { case *UpdateNewMessage: - return upd.Message.(*MessageObj) + if m, ok := upd.Message.(*MessageObj); ok { + return m + } case *UpdateNewChannelMessage: - return upd.Message.(*MessageObj) + if m, ok := upd.Message.(*MessageObj); ok { + return m + } case *UpdateEditMessage: - return upd.Message.(*MessageObj) + if m, ok := upd.Message.(*MessageObj); ok { + return m + } case *UpdateEditChannelMessage: - return upd.Message.(*MessageObj) + if m, ok := upd.Message.(*MessageObj); ok { + return m + } case *UpdateBotEditBusinessMessage: - return upd.Message.(*MessageObj) + if m, ok := upd.Message.(*MessageObj); ok { + return m + } case *UpdateMessageID: return &MessageObj{ ID: upd.ID, @@ -328,7 +344,10 @@ updateTypeSwitch: if err != nil { chat = &PeerChat{} } - return &MessageObj{Out: update.Out, ID: update.ID, PeerID: chat.(*PeerChat), Date: update.Date, Entities: update.Entities, TtlPeriod: update.TtlPeriod} + if peerChat, ok := chat.(*PeerChat); ok { + return &MessageObj{Out: update.Out, ID: update.ID, PeerID: peerChat, Date: update.Date, Entities: update.Entities, TtlPeriod: update.TtlPeriod} + } + return &MessageObj{Out: update.Out, ID: update.ID, PeerID: &PeerChat{}, Date: update.Date, Entities: update.Entities, TtlPeriod: update.TtlPeriod} case *UpdateShort: upd = &UpdatesObj{Updates: []Update{update.Update}} goto updateTypeSwitch @@ -653,12 +672,20 @@ mediaTypeSwitch: case MessageMedia: switch media := media.(type) { case *MessageMediaPhoto: - Photo := media.Photo.(*PhotoObj) - return &InputMediaPhoto{ID: &InputPhotoObj{ID: Photo.ID, AccessHash: Photo.AccessHash, FileReference: Photo.FileReference}, TtlSeconds: getValue(attr.TTL, 0), Spoiler: getValue(attr.Spoiler, false)}, nil + if Photo, ok := media.Photo.(*PhotoObj); ok { + return &InputMediaPhoto{ID: &InputPhotoObj{ID: Photo.ID, AccessHash: Photo.AccessHash, FileReference: Photo.FileReference}, TtlSeconds: getValue(attr.TTL, 0), Spoiler: getValue(attr.Spoiler, false)}, nil + } + return &InputMediaPhoto{ID: &InputPhotoEmpty{}, TtlSeconds: getValue(attr.TTL, 0), Spoiler: getValue(attr.Spoiler, false)}, nil case *MessageMediaDocument: - return &InputMediaDocument{ID: &InputDocumentObj{ID: media.Document.(*DocumentObj).ID, AccessHash: media.Document.(*DocumentObj).AccessHash, FileReference: media.Document.(*DocumentObj).FileReference}, TtlSeconds: getValue(attr.TTL, 0), Spoiler: getValue(attr.Spoiler, false)}, nil + if doc, ok := media.Document.(*DocumentObj); ok { + return &InputMediaDocument{ID: &InputDocumentObj{ID: doc.ID, AccessHash: doc.AccessHash, FileReference: doc.FileReference}, TtlSeconds: getValue(attr.TTL, 0), Spoiler: getValue(attr.Spoiler, false)}, nil + } + return &InputMediaDocument{ID: &InputDocumentEmpty{}, TtlSeconds: getValue(attr.TTL, 0), Spoiler: getValue(attr.Spoiler, false)}, nil case *MessageMediaGeo: - return &InputMediaGeoPoint{GeoPoint: &InputGeoPointObj{Lat: media.Geo.(*GeoPointObj).Lat, Long: media.Geo.(*GeoPointObj).Long}}, nil + if geo, ok := media.Geo.(*GeoPointObj); ok { + return &InputMediaGeoPoint{GeoPoint: &InputGeoPointObj{Lat: geo.Lat, Long: geo.Long}}, nil + } + return &InputMediaGeoPoint{GeoPoint: &InputGeoPointEmpty{}}, nil case *MessageMediaGame: return &InputMediaGame{ID: &InputGameID{ID: media.Game.ID, AccessHash: media.Game.AccessHash}}, nil case *MessageMediaContact: @@ -668,14 +695,15 @@ mediaTypeSwitch: case *MessageMediaPoll: return convertPoll(media), nil case *MessageMediaVenue: - return &InputMediaVenue{GeoPoint: &InputGeoPointObj{Lat: media.Geo.(*GeoPointObj).Lat, Long: media.Geo.(*GeoPointObj).Long}, Title: media.Title, Address: media.Address, Provider: media.Provider, VenueID: media.VenueID, VenueType: media.VenueType}, nil + if geo, ok := media.Geo.(*GeoPointObj); ok { + return &InputMediaVenue{GeoPoint: &InputGeoPointObj{Lat: geo.Lat, Long: geo.Long}, Title: media.Title, Address: media.Address, Provider: media.Provider, VenueID: media.VenueID, VenueType: media.VenueType}, nil + } + return &InputMediaVenue{GeoPoint: &InputGeoPointEmpty{}, Title: media.Title, Address: media.Address, Provider: media.Provider, VenueID: media.VenueID, VenueType: media.VenueType}, nil case *MessageMediaWebPage: - switch media.Webpage.(type) { - case *WebPageObj: - return &InputMediaWebPage{URL: media.Webpage.(*WebPageObj).URL, ForceLargeMedia: media.ForceLargeMedia, ForceSmallMedia: media.ForceSmallMedia}, nil - case *WebPageEmpty: - return &InputMediaWebPage{URL: "", ForceLargeMedia: media.ForceLargeMedia, ForceSmallMedia: media.ForceSmallMedia}, nil + if wp, ok := media.Webpage.(*WebPageObj); ok { + return &InputMediaWebPage{URL: wp.URL, ForceLargeMedia: media.ForceLargeMedia, ForceSmallMedia: media.ForceSmallMedia}, nil } + return &InputMediaWebPage{URL: "", ForceLargeMedia: media.ForceLargeMedia, ForceSmallMedia: media.ForceSmallMedia}, nil case *MessageMediaToDo: return &InputMediaTodo{Todo: media.Todo}, nil case *MessageMediaStory: @@ -1411,7 +1439,9 @@ func packStoryToMessage(c *Client, story *StoryItemObj) *NewMessage { } m.Client = c - m.Message = m.OriginalUpdate.(*MessageObj) + if msgObj, ok := m.OriginalUpdate.(*MessageObj); ok { + m.Message = msgObj + } return m } diff --git a/telegram/messages.go b/telegram/messages.go index d37c28b3..2ef02536 100644 --- a/telegram/messages.go +++ b/telegram/messages.go @@ -702,7 +702,9 @@ func (c *Client) sendAlbum(Peer InputPeer, Album []*InputSingleMedia, sendAs Inp if result != nil { updates := processUpdates(result) for _, update := range updates { - update.(*MessageObj).PeerID = c.getPeer(Peer) + if msgObj, ok := update.(*MessageObj); ok { + msgObj.PeerID = c.getPeer(Peer) + } } results = append(results, PackMessages(c, updates)...) diff --git a/telegram/newmessage.go b/telegram/newmessage.go index 56eb7b35..87c23e82 100755 --- a/telegram/newmessage.go +++ b/telegram/newmessage.go @@ -39,7 +39,9 @@ func (m *NewMessage) MessageText() string { func (m *NewMessage) ReplyToMsgID() int32 { if m.Message.ReplyTo != nil { - return m.Message.ReplyTo.(*MessageReplyHeaderObj).ReplyToMsgID + if reply, ok := m.Message.ReplyTo.(*MessageReplyHeaderObj); ok { + return reply.ReplyToMsgID + } } return 0 } @@ -66,7 +68,9 @@ func (m *NewMessage) TopicID() (int32, bool) { func (m *NewMessage) ReplySenderID() int64 { if m.Message.ReplyTo != nil { - return m.Client.GetPeerID(m.Message.ReplyTo.(*MessageReplyHeaderObj).ReplyToPeerID) + if reply, ok := m.Message.ReplyTo.(*MessageReplyHeaderObj); ok { + return m.Client.GetPeerID(reply.ReplyToPeerID) + } } return 0 } diff --git a/telegram/users.go b/telegram/users.go index 55e6ed8c..d509c195 100644 --- a/telegram/users.go +++ b/telegram/users.go @@ -41,7 +41,10 @@ func (p *UserPhoto) FileID() string { func (p *UserPhoto) FileSize() int64 { if p, ok := p.Photo.(*PhotoObj); ok { if p.VideoSizes != nil { - return int64(p.VideoSizes[len(p.VideoSizes)-1].(*VideoSizeObj).Size) + if vs, ok := p.VideoSizes[len(p.VideoSizes)-1].(*VideoSizeObj); ok { + return int64(vs.Size) + } + return 0 } size, _ := getPhotoSize(p.Sizes[len(p.Sizes)-1]) return size @@ -59,11 +62,15 @@ func (p *UserPhoto) DcID() int32 { func (p *UserPhoto) InputLocation() (*InputPhotoFileLocation, error) { if photo, ok := p.Photo.(*PhotoObj); ok { if photo.VideoSizes != nil { + thumbSize := "" + if vs, ok := photo.VideoSizes[0].(*VideoSizeObj); ok { + thumbSize = vs.Type + } return &InputPhotoFileLocation{ ID: photo.ID, AccessHash: photo.AccessHash, FileReference: photo.FileReference, - ThumbSize: photo.VideoSizes[0].(*VideoSizeObj).Type, + ThumbSize: thumbSize, }, nil } _, thumbSize := getPhotoSize(photo.Sizes[len(photo.Sizes)-1]) diff --git a/telegram/utils.go b/telegram/utils.go index fe10fb4c..6a274884 100755 --- a/telegram/utils.go +++ b/telegram/utils.go @@ -689,7 +689,10 @@ func GetFileName(f any, video ...bool) string { switch f := f.(type) { case *MessageMediaDocument: - return getDocName(f.Document.(*DocumentObj)) + if doc, ok := f.Document.(*DocumentObj); ok { + return getDocName(doc) + } + return fmt.Sprintf("file_%s_%d", time.Now().Format("2006-01-02_15-04-05"), cryptoRandIntn(1000)) case *MessageMediaPhoto: if isVid { return fmt.Sprintf("video_%s_%d.mp4", time.Now().Format("2006-01-02_15-04-05"), cryptoRandIntn(1000)) @@ -720,7 +723,10 @@ func GetFileName(f any, video ...bool) string { func getFileSize(f any) int64 { switch f := f.(type) { case *MessageMediaDocument: - return f.Document.(*DocumentObj).Size + if doc, ok := f.Document.(*DocumentObj); ok { + return doc.Size + } + return 0 case *MessageMediaPhoto: if photo, p := f.Photo.(*PhotoObj); p { if len(photo.Sizes) == 0 { @@ -745,7 +751,10 @@ func getFileSize(f any) int64 { func getFileExt(f any) string { switch f := f.(type) { case *MessageMediaDocument: - doc := f.Document.(*DocumentObj) + doc, ok := f.Document.(*DocumentObj) + if !ok { + return "" + } if e := MimeTypes.Ext(doc.MimeType); e != "" { return e }