ahmdrz / goinsta

Unofficial Instagram API written in Golang
MIT License
895 stars 247 forks source link

Handling of inconsistent type for next_max_id field (fixes #305) #306

Closed buckket closed 4 years ago

buckket commented 4 years ago

As described in #305 the next_max_id field can either be a string or an int depending on the API endpoint. This change dynamically unmarshals the field to the right type.

falzm commented 4 years ago

Hi, following your suggestion in #305 I've tried your branch, but unfortunately it doesn't work any better. Running this snippet:

package main

import (
    "fmt"
    "os"

    "github.com/ahmdrz/goinsta/v2"
)

func main() {
    var (
        insta *goinsta.Instagram
        err   error
    )

    igLogin := os.Getenv("IG_LOGIN")
    igPassword := os.Getenv("IG_PASSWORD")

    if igLogin == "" || igPassword == "" {
        panic("IG_LOGIN or IG_PASSWORD environment variable unset")
    }

    insta = goinsta.New(igLogin, igPassword)
    if err = insta.Login(); err != nil {
        panic(err)
    }

    fmt.Printf("%#v\n", insta.Account.Following())
}

Gives me:

$ go run -mod vendor followings.go
&goinsta.Users{inst:(*goinsta.Instagram)(0xc00014c000), err:error(nil), endpoint:"friendships/7413559489/following/", Status:"", BigList:false, Users:[]goinsta.User(nil), PageSize:0, RawNextID:json.RawMessage(nil), NextID:""}

Here is my local module state:

diff --git a/go.mod b/go.mod
index 2c4b460..b4af744 100644
--- a/go.mod
+++ b/go.mod
@@ -9,3 +9,5 @@ require (
        github.com/kr/pretty v0.2.0 // indirect
        github.com/pkg/errors v0.8.1
 )
+
+replace github.com/ahmdrz/goinsta/v2 => github.com/buckket/goinsta/v2 v2.4.6-0.20200401235525-0daaded545d6
diff --git a/go.sum b/go.sum
index 488a5ec..2c28916 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,7 @@
 github.com/Masterminds/goutils v1.0.1 h1:upyB/JGR/aPHTE3f4O3mBIbJCr7aPup+/03IS4aXU6Y=
 github.com/Masterminds/goutils v1.0.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
-github.com/ahmdrz/goinsta/v2 v2.4.4 h1:AMqRsuduhXeTBbHMu5HZdnFwYIVylTFn/s9cwd6oJaY=
-github.com/ahmdrz/goinsta/v2 v2.4.4/go.mod h1:XUEELCWd3hVSqADejqxTa8Uc7Yto9cb6a6HrfkGwVf8=
-github.com/ahmdrz/goinsta/v2 v2.4.5 h1:yiNEpTNEx/VWlcCq93miWUx6d2uxdEyklwljsZaiD5I=
-github.com/ahmdrz/goinsta/v2 v2.4.5/go.mod h1:XUEELCWd3hVSqADejqxTa8Uc7Yto9cb6a6HrfkGwVf8=
+github.com/buckket/goinsta/v2 v2.4.6-0.20200401235525-0daaded545d6 h1:B6gyE0rIkYZc/AyH2Q1E0c+veXaArdCNl/vzpuqS9KE=
+github.com/buckket/goinsta/v2 v2.4.6-0.20200401235525-0daaded545d6/go.mod h1:XUEELCWd3hVSqADejqxTa8Uc7Yto9cb6a6HrfkGwVf8=
 github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY=
 github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA=
 github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
diff --git a/vendor/github.com/ahmdrz/goinsta/v2/users.go b/vendor/github.com/ahmdrz/goinsta/v2/users.go
index ccfd22c..9a02c04 100644
--- a/vendor/github.com/ahmdrz/goinsta/v2/users.go
+++ b/vendor/github.com/ahmdrz/goinsta/v2/users.go
@@ -4,6 +4,7 @@ import (
        "encoding/json"
        "errors"
        "fmt"
+       "strconv"
 )

 // Users is a struct that stores many user's returned by many different methods.
@@ -17,11 +18,12 @@ type Users struct {
        err      error
        endpoint string

-       Status   string `json:"status"`
-       BigList  bool   `json:"big_list"`
-       Users    []User `json:"users"`
-       PageSize int    `json:"page_size"`
-       NextID   string `json:"next_max_id"`
+       Status    string          `json:"status"`
+       BigList   bool            `json:"big_list"`
+       Users     []User          `json:"users"`
+       PageSize  int             `json:"page_size"`
+       RawNextID json.RawMessage `json:"next_max_id"`
+       NextID    string          `json:"-"`
 }

 func newUsers(inst *Instagram) *Users {
@@ -66,6 +68,19 @@ func (users *Users) Next() bool {
                usrs := Users{}
                err = json.Unmarshal(body, &usrs)
                if err == nil {
+                       if len(usrs.RawNextID) > 0 && usrs.RawNextID[0] == '"' && usrs.RawNextID[len(usrs.RawNextID)-1] == '"' {
+                               if err := json.Unmarshal(usrs.RawNextID, &usrs.NextID); err != nil {
+                                       users.err = err
+                                       return false
+                               }
+                       } else {
+                               var nextID int64
+                               if err := json.Unmarshal(usrs.RawNextID, &nextID); err != nil {
+                                       users.err = err
+                                       return false
+                               }
+                               usrs.NextID = strconv.FormatInt(nextID, 10)
+                       }
                        *users = usrs
                        if !usrs.BigList || usrs.NextID == "" {
                                users.err = ErrNoMore
diff --git a/vendor/modules.txt b/vendor/modules.txt
index d8f447b..b095bc6 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,6 +1,6 @@
 # github.com/Masterminds/goutils v1.0.1
 github.com/Masterminds/goutils
-# github.com/ahmdrz/goinsta/v2 v2.4.5
+# github.com/ahmdrz/goinsta/v2 v2.4.5 => github.com/buckket/goinsta/v2 v2.4.6-0.20200401235525-0daaded545d6
 github.com/ahmdrz/goinsta/v2
 # github.com/gorilla/feeds v1.1.1
 github.com/gorilla/feeds

Can you please try running the same snippet and tell me if you have better luck?

buckket commented 4 years ago

You would still have to call .Next() to actually execute the request.

Try:

followings := insta.Account.Following()
for followings.Next() {
    err := followers.Error()
    if err != nil && err != goinsta.ErrNoMore {
        # here you should handle an actual error
        fmt.Printf("%#v\n", err)
    }
    fmt.Printf("%#v\n", followings.Users)
}

Hope this helps.

falzm commented 4 years ago

@buckket indeed it helped, I didn't get how the API works. I don't really understand why this is implemented this way, but to actually have the list of followings this is the code that works:

    followings := insta.Account.Following()
    for followings.Next() {
        err := followings.Error()
        if err != nil && err != goinsta.ErrNoMore {
            // Handle error
        }

        for _, u := range followings.Users {
            fmt.Println(u.Username)
        }
    }

Thank you for putting me on the right tracks, hopefully your PR will be merged someday! 👍