From 155582f585af412c7733ac980557d33a832139b8 Mon Sep 17 00:00:00 2001 From: Davincible Date: Tue, 24 Aug 2021 15:15:53 +0200 Subject: [PATCH] Use goroutines for long sequences --- goinsta.go | 199 +++++++++++++++++++++++++++++--------------- profiles.go | 127 ++++++++++++++++++---------- request.go | 3 +- search.go | 13 ++- tests/login_test.go | 20 +++-- 5 files changed, 236 insertions(+), 126 deletions(-) diff --git a/goinsta.go b/goinsta.go index 387b4cf..11d3eb1 100644 --- a/goinsta.go +++ b/goinsta.go @@ -494,89 +494,152 @@ func (insta *Instagram) OpenApp() (err error) { return } - err = insta.getAccountFamily() - if err != nil { - insta.WarnHandler("Non fatal error while fetching account family:", err) - } - - err = insta.sync() - if err != nil { - return - } - - err = insta.getNdxSteps() - if err != nil { - insta.WarnHandler("Non fatal error while fetching ndx steps:", err) + if err := insta.sync(); err != nil { + return err } - if !insta.Timeline.Next() { - return errors.New("Failed to fetch timeline during login procedure: " + - insta.Timeline.err.Error()) - } + wg := &sync.WaitGroup{} + errChan := make(chan error, 15) - err = insta.callNotifBadge() - if err != nil { - insta.WarnHandler("Non fatal error while fetching notify badge", err) - } + wg.Add(1) + go func() { + defer wg.Done() + err := insta.getAccountFamily() + if err != nil { + insta.WarnHandler("Non fatal error while fetching account family:", err) + } + }() - err = insta.banyan() - if err != nil { - insta.WarnHandler("Non fatal error while fetching banyan", err) - } + wg.Add(1) + go func() { + defer wg.Done() + if err := insta.getNdxSteps(); err != nil { + insta.WarnHandler("Non fatal error while fetching ndx steps:", err) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + if !insta.Timeline.Next() { + errChan <- errors.New("Failed to fetch timeline: " + + insta.Timeline.err.Error()) + } + }() - err = insta.callMediaBlocked() - if err != nil { - insta.WarnHandler("Non fatal error while fetching blocked media", err) - } + wg.Add(1) + go func() { + defer wg.Done() + if err := insta.callNotifBadge(); err != nil { + insta.WarnHandler("Non fatal error while fetching notify badge", err) + } + }() - // no clue what theses values could be used for - _, err = insta.getCooldowns() - if err != nil { - insta.WarnHandler("Non fatal error while fetching cool downs", err) - } + wg.Add(1) + go func() { + defer wg.Done() + if err := insta.banyan(); err != nil { + insta.WarnHandler("Non fatal error while fetching banyan", err) + } + }() - if !insta.Discover.Next() { - insta.WarnHandler("Non fatal error while fetching explore page", - insta.Discover.Error()) - } + wg.Add(1) + go func() { + defer wg.Done() + if err = insta.callMediaBlocked(); err != nil { + insta.WarnHandler("Non fatal error while fetching blocked media", err) + } + }() - err = insta.getConfig() - if err != nil { - insta.WarnHandler("Non fatal error while fetching config", err) - } + wg.Add(1) + go func() { + defer wg.Done() + // no clue what theses values could be used for + _, err = insta.getCooldowns() + if err != nil { + insta.WarnHandler("Non fatal error while fetching cool downs", err) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + if !insta.Discover.Next() { + insta.WarnHandler("Non fatal error while fetching explore page", + insta.Discover.Error()) + } + }() - // no clue what theses values could be used for - _, err = insta.getScoresBootstrapUsers() - if err != nil { - insta.WarnHandler("Non fatal error while fetching bootstrap user scores", err) - } + wg.Add(1) + go func() { + defer wg.Done() + if err := insta.getConfig(); err != nil { + insta.WarnHandler("Non fatal error while fetching config", err) + } + }() - if !insta.Activity.Next() { - return errors.New("Failed to fetch recent activity: " + - insta.Activity.err.Error()) - } + wg.Add(1) + go func() { + defer wg.Done() + // no clue what theses values could be used for + _, err = insta.getScoresBootstrapUsers() + if err != nil { + insta.WarnHandler("Non fatal error while fetching bootstrap user scores", err) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + if !insta.Activity.Next() { + errChan <- errors.New("Failed to fetch recent activity: " + + insta.Activity.err.Error()) + } + }() - err = insta.sendAdID() - if err != nil { - insta.WarnHandler("Non fatal error while sending ad id", err) - } + wg.Add(1) + go func() { + defer wg.Done() + if err := insta.sendAdID(); err != nil { + insta.WarnHandler("Non fatal error while sending ad id", err) + } + }() - err = insta.callStClPushPerm() - if err != nil { - insta.WarnHandler("Non fatal error while calling store client push permissions", err) - } + wg.Add(1) + go func() { + defer wg.Done() + if err := insta.callStClPushPerm(); err != nil { + insta.WarnHandler("Non fatal error while calling store client push permissions", err) + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + if !insta.Inbox.InitialSnapshot() { + err := insta.Inbox.Error() + if err != ErrNoMore { + errChan <- errors.New("Failed to fetch initial messages inbox snapshot: " + + err.Error()) + } + } + }() - if !insta.Inbox.InitialSnapshot() { - return errors.New("Failed to fetch initial messages inbox snapshot: " + - insta.Inbox.err.Error()) - } + wg.Add(1) + go func() { + defer wg.Done() + if err := insta.callContPointSig(); err != nil { + insta.WarnHandler("Non fatal error while calling contact point signal:", err) + } + }() - err = insta.callContPointSig() - if err != nil { - insta.WarnHandler("Non fatal error while calling contact point signal:", err) + wg.Wait() + select { + case err := <-errChan: + return err + default: + return nil } - - return nil } func (insta *Instagram) login() error { diff --git a/profiles.go b/profiles.go index f2ecaa2..c144158 100644 --- a/profiles.go +++ b/profiles.go @@ -4,7 +4,7 @@ import ( "encoding/json" "errors" "fmt" - "time" + "sync" ) // Profiles allows user function interactions @@ -22,10 +22,10 @@ type Profile struct { User *User Friendship *Friendship - Feed FeedMedia - Stories StoryMedia + Feed *FeedMedia + Stories *StoryMedia Highlights []*Reel - IGTV IGTVChannel + IGTV *IGTVChannel } // VisitProfile will perform the same request sequence as if you visited a profile @@ -65,63 +65,98 @@ func (insta *Instagram) VisitProfile(handle string) (*Profile, error) { // which will perform a search, register the click, and call this method. // func (user *User) VisitProfile() (*Profile, error) { - p := Profile{User: user} + p := &Profile{User: user} + + wg := &sync.WaitGroup{} + info := &sync.WaitGroup{} + errChan := make(chan error, 10) + + // Fetch Profile Info + wg.Add(1) + go func(wg *sync.WaitGroup) { + info.Add(1) + defer wg.Done() + defer info.Done() + if err := user.Info("entry_point", "profile", "from_module", "blended_search"); err != nil { + errChan <- err + } + }(wg) // Fetch Friendship - fr, err := user.GetFriendship() - if err != nil { - return nil, err - } - p.Friendship = fr + 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 - // usually gets called 3 times on profile visit, if enough media available - feed := user.Feed() - for i := 0; i < 3; i++ { - s := feed.Next() - err := feed.Error() - if !s && err != ErrNoMore { - return nil, err + 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() } - time.Sleep(200 * time.Millisecond) - } - p.Feed = *feed + }(wg) // Fetch Stories - stories, err := user.Stories() - if err != nil { - return nil, err - } - p.Stories = *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 - h, err := user.Highlights() - if err != nil { - return nil, err - } - p.Highlights = h - - // Fetch Profile Info - err = user.Info("entry_point", "profile", "from_module", "blended_search") - if err != nil { - return nil, err - } + 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 - _, err = user.GetFeaturedAccounts() - if err != nil { - user.insta.WarnHandler(err) - } + wg.Add(1) + go func(wg *sync.WaitGroup) { + defer wg.Done() + _, err := user.GetFeaturedAccounts() + if err != nil { + user.insta.WarnHandler(err) + } + }(wg) // Fetch IGTV - if user.IGTVCount > 0 { - igtv, err := user.IGTV() - if err != nil { - return nil, err + wg.Add(1) + go func(wg *sync.WaitGroup) { + defer wg.Done() + info.Wait() + if user.IGTVCount > 0 { + igtv, err := user.IGTV() + if err != nil { + errChan <- err + } + p.IGTV = igtv } - p.IGTV = *igtv + }(wg) + + wg.Wait() + select { + case err := <-errChan: + return p, err + default: + return p, nil } - return &p, nil } func newProfiles(insta *Instagram) *Profiles { diff --git a/request.go b/request.go index ac3198e..5548ae6 100644 --- a/request.go +++ b/request.go @@ -251,7 +251,8 @@ func (insta *Instagram) sendRequest(o *reqOptions) (body []byte, h http.Header, } func (insta *Instagram) checkXmidExpiry() { - if insta.xmidExpiry != -1 && time.Now().Unix() > insta.xmidExpiry-10 { + t := time.Now().Unix() + if insta.xmidExpiry != -1 && t > insta.xmidExpiry-10 { insta.xmidExpiry = -1 insta.zrToken() } diff --git a/search.go b/search.go index eb7102f..85da74f 100644 --- a/search.go +++ b/search.go @@ -274,6 +274,7 @@ func (insta *Instagram) sendSearchRegisterRequest(query map[string]string) error func (sb *Search) search(query string, fn func(string) (*SearchResult, error)) (*SearchResult, error) { insta := sb.insta + result := &SearchResult{} if insta.Discover.NumResults == 0 { sb.insta.Discover.Next() @@ -283,11 +284,11 @@ func (sb *Search) search(query string, fn func(string) (*SearchResult, error)) ( sb.insta.WarnHandler("Non fatal error while fetcihng recent search results", err) } + result.History = *h if err := sb.NullState(); err != nil { sb.insta.WarnHandler("Non fatal error while setting search null state", err) } - var result *SearchResult var q string for _, char := range query { q += string(char) @@ -295,10 +296,18 @@ func (sb *Search) search(query string, fn func(string) (*SearchResult, error)) ( if err != nil { return nil, err } + // If the query is a username, and in the top 10, return + if len(result.Results) >= 10 { + for _, r := range result.Results[:10] { + if r.User != nil && r.User.Username == query { + return result, nil + } + } + } + s := random(150, 500) time.Sleep(time.Duration(s) * time.Millisecond) } - result.History = *h return result, nil } diff --git a/tests/login_test.go b/tests/login_test.go index c2bd854..951e6fd 100644 --- a/tests/login_test.go +++ b/tests/login_test.go @@ -7,6 +7,17 @@ import ( ) func TestImportAccount(t *testing.T) { + // Test Import + insta, err := goinsta.EnvRandAcc() + if err != nil { + t.Fatal(err) + } + insta.OpenApp() + t.Logf("logged into Instagram as user '%s'", insta.Account.Username) + logPosts(t, insta) +} + +func TestLogin(t *testing.T) { // Test Login user, pass, err := goinsta.EnvRandLogin() if err != nil { @@ -20,15 +31,6 @@ func TestImportAccount(t *testing.T) { } t.Logf("Logged in successfully as %s\n", user) logPosts(t, insta) - - // Test Import - insta, err = goinsta.EnvRandAcc() - if err != nil { - t.Fatal(err) - } - insta.OpenApp() - t.Logf("logged into Instagram as user '%s'", insta.Account.Username) - logPosts(t, insta) } func logPosts(t *testing.T, insta *goinsta.Instagram) {