Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: update postponed and handled only with next update #1382

Open
sadfun opened this issue May 16, 2024 · 3 comments
Open

Bug: update postponed and handled only with next update #1382

sadfun opened this issue May 16, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@sadfun
Copy link

sadfun commented May 16, 2024

What version of gotd are you using?

github.com/gotd/td v0.99.2

Can this issue be reproduced with the latest version?

Yes

Steps to reproduce

1. Receive a message from another User
2. Read it
3. Wait until another user edited this message
4. You will receive update about message edit only when another pts-changing update arrive (for example, new message, but not online status)

As I understand from logs, it happens because of some attempt of the library to fix update sequence:

{"level":"debug","ts":1715867034.0408983,"logger":"pts","msg":"Gap detected","gap":[{"from":4416,"to":4417}]}
{"level":"debug","ts":1715867034.0409162,"logger":"pts","msg":"Out of gap range, postponed","upd_from":4418,"upd_to":4418,"gaps":[{"from":4416,"to":4417}]}

Here is the minimal example to reproduce it:

package main

import (
	"context"
	"fmt"
	boltstor "github.com/gotd/contrib/bbolt"
	"github.com/gotd/contrib/storage"
	"github.com/gotd/td/telegram/query/dialogs"
	bolt "go.etcd.io/bbolt"
	"os"
	"os/signal"

	"github.com/go-faster/errors"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"github.com/gotd/td/examples"
	"github.com/gotd/td/telegram"
	"github.com/gotd/td/telegram/auth"
	"github.com/gotd/td/telegram/updates"
	updhook "github.com/gotd/td/telegram/updates/hook"
	"github.com/gotd/td/tg"
)

func main() {
	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
	defer cancel()
	if err := run(ctx); err != nil {
		panic(err)
	}
}

func run(ctx context.Context) error {
	log, _ := zap.NewDevelopment(zap.IncreaseLevel(zapcore.InfoLevel), zap.AddStacktrace(zapcore.FatalLevel))
	defer func() { _ = log.Sync() }()

	peerDB, err := bolt.Open("peers.db", 0600, nil)
	if err != nil {
		return fmt.Errorf("failed to open peer db: %w", err)
	}

	d := tg.NewUpdateDispatcher()
	gaps := updates.New(updates.Config{
		Handler: d,
		Logger:  log.Named("gaps"),
	})
	peerStorage := boltstor.NewPeerStorage(peerDB, []byte(fmt.Sprintf("peers")))
	hook := storage.UpdateHook(gaps, peerStorage)

	// Authentication flow handles authentication process, like prompting for code and 2FA password.
	flow := auth.NewFlow(examples.Terminal{}, auth.SendCodeOptions{})

	// Initializing client from environment.
	// Available environment variables:
	// 	APP_ID:         app_id of Telegram app.
	// 	APP_HASH:       app_hash of Telegram app.
	// 	SESSION_FILE:   path to session file
	// 	SESSION_DIR:    path to session directory, if SESSION_FILE is not set
	client, err := telegram.ClientFromEnvironment(telegram.Options{
		Logger:        log,
		UpdateHandler: gaps,
		Middlewares: []telegram.Middleware{
			updhook.UpdateHook(hook.Handle),
		},
	})
	if err != nil {
		return err
	}

	//
	d.OnNewMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateNewMessage) error {
		log.Info("New", zap.Any("message", update.Message))

		msg, ok := update.Message.(*tg.Message)
		if !ok {
			return nil
		}

		peerUser, ok := msg.PeerID.(*tg.PeerUser)
		if !ok {
			return nil
		}

		ah := findAccessHash(peerUser.UserID, e, client.API(), peerStorage)

		_, err = client.API().MessagesReadHistory(ctx, &tg.MessagesReadHistoryRequest{
			Peer:  &tg.InputPeerUser{UserID: peerUser.GetUserID(), AccessHash: ah},
			MaxID: msg.ID,
		})

		return err
	})

	// Edit message handler.
	d.OnEditMessage(func(ctx context.Context, e tg.Entities, update *tg.UpdateEditMessage) error {
		log.Info("Edit", zap.Any("message", update.Message))
		return nil
	})

	return client.Run(ctx, func(ctx context.Context) error {
		// Perform auth if no session is available.
		if err := client.Auth().IfNecessary(ctx, flow); err != nil {
			return errors.Wrap(err, "auth")
		}

		// Fetch user info.
		user, err := client.Self(ctx)
		if err != nil {
			return errors.Wrap(err, "call self")
		}

		return gaps.Run(ctx, client.API(), user.ID, updates.AuthOptions{
			OnStart: func(ctx context.Context) {
				log.Info("Gaps started")
			},
		})
	})
}

func findAccessHash(userID int64, e tg.Entities, client *tg.Client, peerStorage storage.PeerStorage) (ah int64) {
	if e.Users[userID] != nil {
		return e.Users[userID].AccessHash
	}

	// Find access hash for user.
	peer, err := peerStorage.Find(context.Background(), storage.PeerKey{
		Kind: dialogs.User,
		ID:   userID,
	})
	if err == nil && peer.User != nil && peer.User.AccessHash != 0 {
		return peer.User.AccessHash
	}

	collector := storage.CollectPeers(peerStorage)

	err = collector.Dialogs(context.Background(), dialogs.NewIterator(dialogs.NewQueryBuilder(client).GetDialogs(), 10))
	if err != nil {
		return
	}

	peer, err = peerStorage.Find(context.Background(), storage.PeerKey{
		Kind: dialogs.User,
		ID:   userID,
	})

	if err == nil && peer.User != nil && peer.User.AccessHash != 0 {
		return peer.User.AccessHash
	}

	return
}

What did you expect to see?

Update passed to handler immediately after arriving

What did you see instead?

Update passed to handler only when another update arrives

What Go version and environment are you using?

go version go1.21.1 darwin/arm64

go env
GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/sadfun/Library/Caches/go-build'
GOENV='/Users/sadfun/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/sadfun/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/sadfun/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.21.1'
GCCGO='gccgo'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='1'
GOMOD='/Users/sadfun/Developer/octopus/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/4r/2ndv351j4yx4cw17y8dbgr6r0000gn/T/go-build1564177886=/tmp/go-build -gno-record-gcc-switches -fno-common'
@sadfun sadfun added the bug Something isn't working label May 16, 2024
@Splatjov
Copy link

I have the same issue, please fix it asap!!

@NikBuka
Copy link

NikBuka commented May 17, 2024

@ernado we have this bug too, fix this please

@NikBuka
Copy link

NikBuka commented May 27, 2024

@ernado heeelp, please (

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants