-
Notifications
You must be signed in to change notification settings - Fork 56
/
wrapper.go
126 lines (103 loc) · 3.07 KB
/
wrapper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package goinsta
import (
"errors"
"fmt"
"net/http"
"time"
)
const (
TooManyRequestsTimeout = 60 * time.Second * 8
)
type ReqWrapper interface {
GoInstaWrapper(*ReqWrapperArgs) (body []byte, h http.Header, err error)
}
type ReqWrapperArgs struct {
insta *Instagram
reqOptions *reqOptions
Body []byte
Headers http.Header
Error error
}
type Wrapper struct {
o *ReqWrapperArgs
}
func (w *ReqWrapperArgs) RetryRequest() (body []byte, h http.Header, err error) {
return w.insta.sendRequest(w.reqOptions)
}
func (w *ReqWrapperArgs) GetWrapperCount() int {
return w.reqOptions.WrapperCount
}
func (w *ReqWrapperArgs) GetInsta() *Instagram {
return w.insta
}
func (w *ReqWrapperArgs) GetEndpoint() string {
return w.reqOptions.Endpoint
}
func (w *ReqWrapperArgs) SetInsta(insta *Instagram) {
w.insta = insta
}
func (w *ReqWrapperArgs) Ignore429() bool {
return w.reqOptions.Ignore429
}
func DefaultWrapper() *Wrapper {
return &Wrapper{}
}
// GoInstaWrapper is a warpper function for goinsta
func (w *Wrapper) GoInstaWrapper(o *ReqWrapperArgs) ([]byte, http.Header, error) {
// If no errors occured, directly return
if o.Error == nil {
return o.Body, o.Headers, o.Error
}
// If wrapper called more than threshold, return
if o.GetWrapperCount() > 3 {
return o.Body, o.Headers, o.Error
}
w.o = o
insta := o.GetInsta()
switch true {
case errors.Is(o.Error, ErrTooManyRequests):
// Some endpoints often return 429, too many requests, and can be safely ignored.
if o.Ignore429() {
return o.Body, o.Headers, nil
}
insta.warnHandler("Too many requests, sleeping for ", TooManyRequestsTimeout)
time.Sleep(TooManyRequestsTimeout)
case errors.Is(o.Error, Err2FARequired):
// Attempt auto 2FA login with TOTP code generation
err := insta.TwoFactorInfo.Login2FA()
if err != nil && err != Err2FANoCode {
return o.Body, o.Headers, err
} else {
return o.Body, o.Headers, o.Error
}
case errors.Is(o.Error, ErrLoggedOut):
fallthrough
case errors.Is(o.Error, ErrLoginRequired):
return o.Body, o.Headers, o.Error
case errors.Is(o.Error, ErrCheckpointRequired):
// Attempt to accecpt cookies using headless browser
err := insta.Checkpoint.Process()
if err != nil {
return o.Body, o.Headers, fmt.Errorf(
"failed to automatically process status code 400 'checkpoint_required' with checkpoint url '%s', please report this on github. Error provided: %w",
insta.Checkpoint.URL,
err,
)
}
insta.infoHandler(
fmt.Sprintf("Auto solving of checkpoint with url '%s' seems to have gone successful. This is an experimental feature, please let me know if it works! :)\n",
insta.Checkpoint.URL,
))
case errors.Is(o.Error, ErrCheckpointPassed):
// continue without doing anything, retry request
case errors.Is(o.Error, ErrChallengeRequired):
if err := insta.Challenge.Process(); err != nil {
return o.Body, o.Headers, fmt.Errorf("failed to process challenge automatically with: %w", err)
}
default:
// Unhandeled errors should be passed on
return o.Body, o.Headers, o.Error
}
body, h, err := w.o.RetryRequest()
return body, h, err
}