diff --git a/README.md b/README.md index ffd82da..cdc20bf 100644 --- a/README.md +++ b/README.md @@ -147,14 +147,6 @@ All groups in one crawl share one timestamp. The login session grants full account access, so restrict Redis access accordingly; deleting `telegram-export:session-gotd` forces a clean re-login. -### Upgrading from the Python version - -The config and export keys keep the same format, so existing crawls stay -readable by `compare` and `export`. The Python version kept a Telethon session -under `telegram-export:session`. On first run, the Go version converts it into -`telegram-export:session-gotd`, so you do not need a new login code. The old key -is only read, never changed, and can be deleted afterwards. - ## Development ```bash diff --git a/internal/store/app-config_test.go b/internal/store/app-config_test.go index ba2e218..64addee 100644 --- a/internal/store/app-config_test.go +++ b/internal/store/app-config_test.go @@ -24,7 +24,7 @@ func TestConfigRoundTrip(t *testing.T) { } } -func TestLoadConfigReadsPythonWrittenJSON(t *testing.T) { +func TestLoadConfigReadsStoredJSON(t *testing.T) { st, mr := newTestStore(t) mr.Set("telegram-export:config", `{"api_id": 123, "api_hash": "hash", "phone": "+1555", "group_ids": [-1001234567890]}`) diff --git a/internal/store/session-storage.go b/internal/store/session-storage.go index d2692de..8ba3534 100644 --- a/internal/store/session-storage.go +++ b/internal/store/session-storage.go @@ -11,12 +11,7 @@ import ( // The login session grants full account access; it lives only in Redis // (behind TLS/auth), never on local disk. Deleting it forces a clean re-login. -// -// The Python version stored a Telethon StringSession under "session". This -// version keeps its own format under a separate key and only reads the old -// one once, so an existing login carries over without a new code. -func sessionKey() string { return key("session-gotd") } -func telethonSessionKey() string { return key("session") } +func sessionKey() string { return key("session-gotd") } // SessionStorage implements gotd's session.Storage on Redis. type SessionStorage struct { @@ -28,33 +23,16 @@ func (s *Store) SessionStorage() *SessionStorage { return &SessionStorage{store: s} } -// LoadSession returns the stored session, importing a Telethon session when -// no gotd session exists yet. +// LoadSession returns the stored session, or session.ErrNotFound before the +// first login. func (ss *SessionStorage) LoadSession(ctx context.Context) ([]byte, error) { raw, err := ss.store.rdb.Get(ctx, sessionKey()).Bytes() - if err == nil { - return raw, nil + if errors.Is(err, redis.Nil) { + return nil, session.ErrNotFound } - if !errors.Is(err, redis.Nil) { + if err != nil { return nil, fmt.Errorf("read session: %w", err) } - - telethon, err := ss.store.rdb.Get(ctx, telethonSessionKey()).Result() - if errors.Is(err, redis.Nil) || telethon == "" { - return nil, session.ErrNotFound - } - if err != nil { - return nil, fmt.Errorf("read Telethon session: %w", err) - } - - raw, err = convertTelethonSession(ctx, telethon) - if err != nil { - // An unreadable old session should mean a fresh login, not a dead end. - return nil, session.ErrNotFound - } - if err := ss.StoreSession(ctx, raw); err != nil { - return nil, err - } return raw, nil } @@ -65,15 +43,3 @@ func (ss *SessionStorage) StoreSession(ctx context.Context, data []byte) error { } return nil } - -func convertTelethonSession(ctx context.Context, telethon string) ([]byte, error) { - data, err := session.TelethonSession(telethon) - if err != nil { - return nil, err - } - var mem session.StorageMemory - if err := (&session.Loader{Storage: &mem}).Save(ctx, data); err != nil { - return nil, err - } - return mem.LoadSession(ctx) -} diff --git a/internal/store/session-storage_test.go b/internal/store/session-storage_test.go index aea009e..48b0ee8 100644 --- a/internal/store/session-storage_test.go +++ b/internal/store/session-storage_test.go @@ -1,23 +1,13 @@ package store import ( - "bytes" "context" - "encoding/base64" "errors" "testing" "github.com/gotd/td/session" ) -// telethonString builds a Telethon StringSession: version "1" plus base64 -// of DC ID, IPv4, port, and a 256-byte auth key. -func telethonString(dc byte) string { - raw := []byte{dc, 149, 154, 167, 51, 0x01, 0xbb} - raw = append(raw, bytes.Repeat([]byte{7}, 256)...) - return "1" + base64.URLEncoding.EncodeToString(raw) -} - func TestLoadSessionNotFound(t *testing.T) { st, _ := newTestStore(t) @@ -39,37 +29,3 @@ func TestSessionRoundTrip(t *testing.T) { t.Fatalf("LoadSession = %q, %v", got, err) } } - -func TestLoadSessionImportsTelethonSession(t *testing.T) { - st, mr := newTestStore(t) - ctx := context.Background() - mr.Set("telegram-export:session", telethonString(2)) - - raw, err := st.SessionStorage().LoadSession(ctx) - if err != nil { - t.Fatalf("LoadSession: %v", err) - } - data, err := (&session.Loader{Storage: st.SessionStorage()}).Load(ctx) - if err != nil { - t.Fatalf("Loader.Load: %v", err) - } - if data.DC != 2 || data.Addr != "149.154.167.51:443" { - t.Fatalf("imported session DC=%d Addr=%q", data.DC, data.Addr) - } - if stored, _ := mr.Get("telegram-export:session-gotd"); stored != string(raw) { - t.Fatal("imported session was not saved under the gotd key") - } - if old, _ := mr.Get("telegram-export:session"); old != telethonString(2) { - t.Fatal("Telethon session key was modified") - } -} - -func TestLoadSessionIgnoresBrokenTelethonSession(t *testing.T) { - st, mr := newTestStore(t) - mr.Set("telegram-export:session", "not-a-session") - - _, err := st.SessionStorage().LoadSession(context.Background()) - if !errors.Is(err, session.ErrNotFound) { - t.Fatalf("LoadSession error = %v, want session.ErrNotFound", err) - } -} diff --git a/internal/telegram/group-id.go b/internal/telegram/group-id.go index abd2dfe..daed940 100644 --- a/internal/telegram/group-id.go +++ b/internal/telegram/group-id.go @@ -22,8 +22,8 @@ type groupRef struct { id int64 } -// parseGroupID splits a marked ID (as Telethon and the Python version store -// it) into a basic group or a channel/supergroup and its raw Telegram ID. +// parseGroupID splits a marked ID (as configured and stored) into a basic +// group or a channel/supergroup and its raw Telegram ID. func parseGroupID(markedID int64) (groupRef, error) { switch { case markedID <= -channelIDOffset: