Skip to content

Commit

Permalink
feat: add proxy support and auth for telegram.
Browse files Browse the repository at this point in the history
  • Loading branch information
syhily committed Nov 15, 2022
1 parent 88709dd commit 0525e91
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 56 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ This is totally rewrite fork compare to its [original version](https://github.co

## Feature

### Login Aliyundrive to get the `refreshToken`

We would show a QR code at the first time. And cache the `refreshToken` after successfully login.

```shell
bookhunter aliyun
```

### Download books from Talebook

1. Download from previous progress.
Expand Down
1 change: 1 addition & 0 deletions cmd/flags/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var (
// Telegram configurations.

ChannelID = ""
Mobile = ""
ReLogin = false
AppID = int64(0)
AppHash = ""
Expand Down
3 changes: 3 additions & 0 deletions cmd/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var telegramCmd = &cobra.Command{
Row("Proxy", flags.Proxy).
Row("UserAgent", flags.UserAgent).
Row("Channel ID", flags.ChannelID).
Row("Mobile", flags.HideSensitive(flags.Mobile)).
Row("AppID", flags.HideSensitive(strconv.FormatInt(flags.AppID, 10))).
Row("AppHash", flags.HideSensitive(flags.AppHash)).
Row("Formats", flags.Formats).
Expand All @@ -42,6 +43,7 @@ var telegramCmd = &cobra.Command{
// Create the fetcher.
f, err := flags.NewFetcher(fetcher.Telegram, map[string]string{
"channelID": flags.ChannelID,
"mobile": flags.Mobile,
"reLogin": strconv.FormatBool(flags.ReLogin),
"appID": strconv.FormatInt(flags.AppID, 10),
"appHash": flags.AppHash,
Expand All @@ -62,6 +64,7 @@ func init() {

// Telegram download arguments.
f.StringVarP(&flags.ChannelID, "channelID", "k", flags.ChannelID, "The channelId for telegram.")
f.StringVarP(&flags.Mobile, "mobile", "b", flags.Mobile, "The mobile number, default (+86).")
f.BoolVar(&flags.ReLogin, "refresh", flags.ReLogin, "Refresh the login session.")
f.Int64Var(&flags.AppID, "appID", flags.AppID,
"The appID for telegram. Refer https://core.telegram.org/api/obtaining_api_id to create your own appID")
Expand Down
7 changes: 3 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/chyroc/go-aliyundrive v0.9.0
github.com/chyroc/persistent-cookiejar v0.1.0
github.com/go-resty/resty/v2 v2.7.0
github.com/gotd/contrib v0.13.0
github.com/gotd/td v0.71.0
github.com/jedib0t/go-pretty/v6 v6.4.2
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213
Expand All @@ -17,7 +18,9 @@ require (
github.com/stretchr/testify v1.8.1
github.com/yi-ge/unzip v1.0.2
go.uber.org/ratelimit v0.2.0
golang.org/x/net v0.2.0
golang.org/x/term v0.2.0
golang.org/x/time v0.2.0
)

require (
Expand All @@ -29,16 +32,13 @@ require (
github.com/go-faster/errors v0.6.1 // indirect
github.com/go-faster/jx v0.40.0 // indirect
github.com/go-faster/xor v0.3.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gotd/ige v0.2.2 // indirect
github.com/gotd/neo v0.1.5 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mdp/qrterminal v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/segmentio/asm v1.2.0 // indirect
Expand All @@ -51,7 +51,6 @@ require (
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.2.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.2.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
12 changes: 4 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,14 @@ github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gotd/contrib v0.13.0 h1:6Qq4jFAhq8bc8DyCM3sNiVj4TUClDLEEmUZR5Lr6L5M=
github.com/gotd/contrib v0.13.0/go.mod h1:Y1vtBgGhfnLKXQTCE6HzU9+quvacOAC1zxXA1/I5t9w=
github.com/gotd/ige v0.2.2 h1:XQ9dJZwBfDnOGSTxKXBGP4gMud3Qku2ekScRjDWWfEk=
github.com/gotd/ige v0.2.2/go.mod h1:tuCRb+Y5Y3eNTo3ypIfNpQ4MFjrnONiL2jN2AKZXmb0=
github.com/gotd/neo v0.1.5 h1:oj0iQfMbGClP8xI59x7fE/uHoTJD7NZH9oV1WNuPukQ=
Expand All @@ -68,7 +67,6 @@ github.com/jedib0t/go-pretty/v6 v6.4.2 h1:DcJNSNIb1E17Tvy9w9S7z+sExvWvvjNbFdyr6C
github.com/jedib0t/go-pretty/v6 v6.4.2/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
Expand All @@ -90,10 +88,8 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand Down Expand Up @@ -174,11 +170,11 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
3 changes: 1 addition & 2 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,9 @@ func New(c *Config) (*Client, error) {
client.SetCookieJar(cookieJar)

// Setting the proxy for the resty client.
// The proxy environment is also supported.
if c.Proxy != "" {
client.SetProxy(c.Proxy)
} else {
client.RemoveProxy()
}

return &Client{Client: client, Config: c}, nil
Expand Down
87 changes: 45 additions & 42 deletions internal/telegram/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,64 @@ import (
"github.com/gotd/td/telegram/auth"
"github.com/gotd/td/tg"
"golang.org/x/term"

"github.com/bookstairs/bookhunter/internal/log"
)

// TerminalAuth implements authentication via terminal.
type TerminalAuth struct {
mobile string
// login is used first time you execute the command line.
func (t *Telegram) login() error {
return t.execute(func(_ context.Context, _ *telegram.Client) error {
return nil
})
}

// authentication is used for log into the telegram with a session support.
// Every telegram execution will require this method.
func (t *Telegram) authentication(ctx context.Context) error {
// Setting up authentication flow helper based on terminal auth.
flow := auth.NewFlow(&terminalAuth{mobile: t.mobile}, auth.SendCodeOptions{})
if err := t.client.Auth().IfNecessary(ctx, flow); err != nil {
return err
}

status, _ := t.client.Auth().Status(ctx)
if !status.Authorized {
return errors.New("failed to login, please check you login info or refresh the session by --refresh")
}

return nil
}

func NewAuth(mobile string) auth.UserAuthenticator {
return &TerminalAuth{mobile: mobile}
// terminalAuth implements authentication via terminal.
type terminalAuth struct {
mobile string
}

func (t *TerminalAuth) Phone(_ context.Context) (string, error) {
func (t *terminalAuth) Phone(_ context.Context) (string, error) {
// Make the mobile number has the country code as the prefix.
addCountryCode := func(mobile string) string {
if strings.HasPrefix(mobile, "+") {
return mobile
} else if strings.HasPrefix(mobile, "86") {
return "+" + mobile
} else if mobile != "" {
return "+86" + mobile
} else {
return ""
}
}

if t.mobile == "" {
fmt.Print("Enter Phone Number (+86): ")
phone, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", err
}
t.mobile = AddCountryCode(phone)
t.mobile = addCountryCode(phone)
}

return t.mobile, nil
}

func (t *TerminalAuth) Password(_ context.Context) (string, error) {
func (t *terminalAuth) Password(_ context.Context) (string, error) {
fmt.Print("Enter 2FA password: ")
bytePwd, err := term.ReadPassword(0)
if err != nil {
Expand All @@ -46,48 +78,19 @@ func (t *TerminalAuth) Password(_ context.Context) (string, error) {
return strings.TrimSpace(string(bytePwd)), nil
}

func (t *TerminalAuth) AcceptTermsOfService(_ context.Context, tos tg.HelpTermsOfService) error {
func (t *terminalAuth) AcceptTermsOfService(_ context.Context, tos tg.HelpTermsOfService) error {
return &auth.SignUpRequired{TermsOfService: tos}
}

func (t *TerminalAuth) SignUp(_ context.Context) (auth.UserInfo, error) {
func (t *terminalAuth) SignUp(_ context.Context) (auth.UserInfo, error) {
return auth.UserInfo{}, errors.New("signup call is not expected")
}

func (t *TerminalAuth) Code(_ context.Context, _ *tg.AuthSentCode) (string, error) {
func (t *terminalAuth) Code(_ context.Context, _ *tg.AuthSentCode) (string, error) {
fmt.Print("Enter code: ")
code, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil {
return "", err
}
return strings.TrimSpace(code), nil
}

// AddCountryCode will return the mobile number with country code as the prefix.
func AddCountryCode(mobile string) string {
if strings.HasPrefix(mobile, "+") {
return mobile
} else if strings.HasPrefix(mobile, "86") {
return "+" + mobile
} else if mobile != "" {
return "+86" + mobile
} else {
return ""
}
}

// Login is used for log into the telegram with a session support.
func Login(ctx context.Context, mobile string, client *telegram.Client) error {
// Setting up authentication flow helper based on terminal auth.
flow := auth.NewFlow(NewAuth(mobile), auth.SendCodeOptions{})
if err := client.Auth().IfNecessary(ctx, flow); err != nil {
log.Fatal(err)
}

status, _ := client.Auth().Status(ctx)
if !status.Authorized {
return fmt.Errorf("failed to login, please check you login info or refresh the session by --refresh")
}

return nil
}
91 changes: 91 additions & 0 deletions internal/telegram/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package telegram

import (
"context"
"os"
"path/filepath"
"strconv"
"time"

"github.com/gotd/contrib/middleware/floodwait"
"github.com/gotd/contrib/middleware/ratelimit"
"github.com/gotd/td/session"
"github.com/gotd/td/telegram"
"github.com/gotd/td/telegram/dcs"
"golang.org/x/time/rate"

"github.com/bookstairs/bookhunter/internal/fetcher"
)

type Telegram struct {
channelID string
mobile string
appID int64
appHash string
client *telegram.Client
}

// New will create a telegram client.
func New(config *fetcher.Config) (*Telegram, error) {
// Create the session file.
path, err := config.ConfigPath()
if err != nil {
return nil, err
}
sessionPath := filepath.Join(path, "session.json")
if refresh, _ := strconv.ParseBool(config.Property("reLogin")); refresh {
_ = os.Remove(sessionPath)
}

channelID := config.Property("channelID")
mobile := config.Property("mobile")
appID, _ := strconv.ParseInt(config.Property("appID"), 10, 64)
appHash := config.Property("appHash")

// Create the http proxy dial.
dialFunc, err := createProxy(config.Proxy)
if err != nil {
return nil, err
}

// Create the backend telegram client.
client := telegram.NewClient(
int(appID),
appHash,
telegram.Options{
Resolver: dcs.Plain(dcs.PlainOptions{Dial: dialFunc}),
SessionStorage: &session.FileStorage{Path: sessionPath},
Middlewares: []telegram.Middleware{
floodwait.NewSimpleWaiter().WithMaxRetries(uint(3)),
ratelimit.New(rate.Every(100*time.Millisecond), 5),
},
},
)

t := &Telegram{
channelID: channelID,
mobile: mobile,
appID: appID,
appHash: appHash,
client: client,
}

// Try to sign in with the mobile.
if err := t.login(); err != nil {
return nil, err
}

return t, nil
}

// Every telegram execution should be wrapped in a client Run session.
// We have to expose this method for internal usage.
func (t *Telegram) execute(f func(context.Context, *telegram.Client) error) error {
return t.client.Run(context.Background(), func(ctx context.Context) error {
if err := t.authentication(ctx); err != nil {
return err
}

return f(ctx, t.client)
})
}

0 comments on commit 0525e91

Please sign in to comment.