Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion mtproto.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 {
Expand Down
74 changes: 52 additions & 22 deletions telegram/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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
}

Expand Down
4 changes: 3 additions & 1 deletion telegram/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)...)
Expand Down
8 changes: 6 additions & 2 deletions telegram/newmessage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down
11 changes: 9 additions & 2 deletions telegram/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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])
Expand Down
15 changes: 12 additions & 3 deletions telegram/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand Down