-
Notifications
You must be signed in to change notification settings - Fork 58
/
profiles.go
226 lines (206 loc) · 5.24 KB
/
profiles.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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package goinsta
import (
"encoding/json"
"errors"
"fmt"
"sync"
)
// Profiles allows user function interactions
type Profiles struct {
insta *Instagram
}
// Profile represents an instagram user with their various properties, such as
// their account info, stored in Profile.User (a *User struct), feed, stories,
// Highlights, IGTV posts, and friendship status.
//
type Profile struct {
User *User
Friendship *Friendship
Feed *FeedMedia
Stories *StoryMedia
Highlights []*Reel
IGTV *IGTVChannel
}
// VisitProfile will perform the same request sequence as if you visited a profile
// in the app. It will first call Instagram.Search(user), then register the click,
// and lastly visit the profile with User.VisitProfile() and gather (some) posts
// from the user feed, stories, grab the friendship status, and if available IGTV posts.
//
// You can access the profile info from the profile struct by calling Profile.Feed,
// Profile.Stories, Profile.User etc. See the Profile struct for all properties.
//
func (insta *Instagram) VisitProfile(handle string) (*Profile, error) {
sr, err := insta.Search(handle)
if err != nil {
return nil, err
}
for _, r := range sr.Results {
if r.User != nil && r.User.Username == handle {
err = r.RegisterClick()
if err != nil {
return nil, err
}
return r.User.VisitProfile()
}
}
return nil, errors.New("Profile not found")
}
// VisitProfile will perform the same request sequence as if you visited a profile
// in the app. Thus it will gather (some) posts from the user feed, stories,
// grab the friendship status, and if available IGTV posts.
//
// You can access the profile info from the profile struct by calling Profile.Feed,
// Profile.Stories, Profile.User etc. See the Profile struct for all properties.
//
// This method will visit a profile directly from an already existing User instance.
// To visit a profile without the User struct, you can use Insta.VisitProfile(user),
// which will perform a search, register the click, and call this method.
//
func (user *User) VisitProfile() (*Profile, error) {
p := &Profile{User: user}
wg := &sync.WaitGroup{}
info := &sync.WaitGroup{}
errChan := make(chan error, 10)
// Fetch Profile Info
wg.Add(1)
info.Add(1)
go func(wg, info *sync.WaitGroup) {
defer wg.Done()
defer info.Done()
if err := user.Info("entry_point", "profile", "from_module", "blended_search"); err != nil {
errChan <- err
}
}(wg, info)
// Fetch Friendship
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
fr, err := user.GetFriendship()
if err != nil {
errChan <- err
}
p.Friendship = fr
}(wg)
// Fetch Feed
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
feed := user.Feed()
p.Feed = feed
if !feed.Next() && feed.Error() != ErrNoMore {
errChan <- feed.Error()
}
}(wg)
// Fetch Stories
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
stories, err := user.Stories()
p.Stories = stories
if err != nil {
errChan <- err
}
}(wg)
// Fetch Highlights
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
h, err := user.Highlights()
p.Highlights = h
if err != nil {
errChan <- err
}
}(wg)
// Fetch Featured Accounts
wg.Add(1)
go func(wg *sync.WaitGroup) {
defer wg.Done()
_, err := user.GetFeaturedAccounts()
if err != nil {
user.insta.warnHandler(err)
}
}(wg)
// Fetch IGTV
wg.Add(1)
go func(wg, info *sync.WaitGroup) {
defer wg.Done()
info.Wait()
if user.IGTVCount > 0 {
igtv, err := user.IGTV()
if err != nil {
errChan <- err
}
p.IGTV = igtv
}
}(wg, info)
wg.Wait()
select {
case err := <-errChan:
return p, err
default:
return p, nil
}
}
func newProfiles(insta *Instagram) *Profiles {
profiles := &Profiles{
insta: insta,
}
return profiles
}
// ByName return a *User structure parsed by username.
// This is not the preffered method to fetch a profile, as the app will
// not simply call this endpoint. It is better to use insta.Search(user),
// or insta.Searchbar.SearchUser(user).
//
func (prof *Profiles) ByName(name string) (*User, error) {
body, err := prof.insta.sendSimpleRequest(urlUserByName, name)
if err == nil {
resp := userResp{}
err = json.Unmarshal(body, &resp)
if err == nil {
user := &resp.User
user.insta = prof.insta
return user, err
}
}
return nil, err
}
// ByID returns a *User structure parsed by user id.
func (prof *Profiles) ByID(id_ interface{}) (*User, error) {
var id string
switch x := id_.(type) {
case int64:
id = fmt.Sprintf("%d", x)
case int:
id = fmt.Sprintf("%d", x)
case string:
id = x
default:
return nil, errors.New("invalid id, please provide a string or int(64)")
}
body, _, err := prof.insta.sendRequest(
&reqOptions{
Endpoint: fmt.Sprintf(urlUserByID, id),
},
)
if err == nil {
resp := userResp{}
err = json.Unmarshal(body, &resp)
if err == nil {
user := &resp.User
user.insta = prof.insta
return user, err
}
}
return nil, err
}
// Blocked returns a list of users you have blocked.
func (prof *Profiles) Blocked() ([]BlockedUser, error) {
body, err := prof.insta.sendSimpleRequest(urlBlockedList)
if err == nil {
resp := blockedListResp{}
err = json.Unmarshal(body, &resp)
return resp.BlockedList, err
}
return nil, err
}