Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmdrz committed Mar 5, 2019
1 parent 90a1fc3 commit 1858681
Show file tree
Hide file tree
Showing 40 changed files with 5,444 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.git
Dockerfile
.gitignore
README.md
aidenzibaei.json
static/data.json
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

aidenzibaei.json
static/data.json
8 changes: 8 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM golang:1.10

RUN mkdir -p $GOPATH/src/github.com/ahmdrz/instagraph
COPY . $GOPATH/src/github.com/ahmdrz/instagraph
WORKDIR $GOPATH/src/github.com/ahmdrz/instagraph
RUN go build -i -o instagraph

CMD [ "./instagraph" ]
17 changes: 17 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Gopkg.toml example
#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
#
# [prune]
# non-go = false
# go-tests = true
# unused-packages = true


[[constraint]]
name = "github.com/ahmdrz/goinsta"
version = "2.4.0"

[prune]
go-tests = true
unused-packages = true
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
# instagraph
# Instagraph
> Social graph network of your instagram account.
<p align="center"><img width=100% src="https://raw.githubusercontent.com/ahmdrz/instagraph/master/resources/screenshot.png"></p>

### Docker

```
$ docker pull ahmdrz/instagraph:latest
$ docker run -e INSTA_USERNAME="" -e INSTA_PASSWORD="" -p 8080:8080 ahmdrz/instagraph:latest
```

### Build

```
$ go build -i -o instagraph
$ ./instagraph -username="" -password=""
```

---

Powered with :heart: by `sigma.js` and `ahmdrz/goinsta`.
127 changes: 127 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package main

import (
"flag"
"io/ioutil"
"log"
"math/rand"
"net/http"
"os"
"path/filepath"
"time"

"github.com/ahmdrz/instagraph/src/graph"
"github.com/ahmdrz/instagraph/src/instagram"
)

func main() {
var (
username string
password string

// TODO: get these values from parameters
delay = 1
limit = 300
usersLimit = 300
listenAddr = "localhost:8080"
)
username = os.Getenv("INSTA_USERNAME")
password = os.Getenv("INSTA_PASSWORD")
if username == "" && password == "" {
flag.StringVar(&username, "username", "", "Instagram username")
flag.StringVar(&password, "password", "", "Instagram password")
flag.Parse()
}
g := graph.New()

var instance *instagram.Instagram
if fileExists(username + ".json") {
var err error
log.Printf("Loading instagram as %s ...", username)
instance, err = instagram.Import(username + ".json")
if err != nil {
log.Fatal(err)
return
}
} else {
var err error
log.Printf("Connecting to instagram as %s ...", username)
instance, err = instagram.New(username, password)
if err != nil {
log.Fatal(err)
return
}
log.Printf("Connected !")

instance.Export(username + ".json")
}

log.Printf("Fetching followings ...")
followings := instance.Followings()
shuffle(followings)

if limit == -1 {
limit = len(followings)
}

// TODO: open a file and write instead of saving data on memory
for i, user := range followings {
if i >= limit {
log.Println("Reached to limit.")
break
}

log.Printf("Scaning (%04d/%04d) user %s ...", i, limit, user.Username)

users := user.Followings(instance)
if len(users) > usersLimit {
users = users[:usersLimit]
}
shuffle(users)

for _, target := range users {
g.AddConnection(user.Username, target.Username)
}

time.Sleep(time.Duration(delay) * time.Second)
}

ioutil.WriteFile("static/data.json", g.Marshall(), 0755)

handler := http.NewServeMux()
handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
bytes, err := ioutil.ReadFile("static/index.html")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(bytes)
})
handler.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

log.Printf("Listening to %s ...", listenAddr)
log.Fatal(http.ListenAndServe(listenAddr, handler))
}

func shuffle(vals []instagram.User) {
r := rand.New(rand.NewSource(time.Now().Unix()))
for len(vals) > 0 {
n := len(vals)
randIndex := r.Intn(n)
vals[n-1], vals[randIndex] = vals[randIndex], vals[n-1]
vals = vals[:n-1]
}
}

func getPWD() string {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
return ""
}
return dir
}

func fileExists(filePath string) bool {
_, err := os.Stat(filePath)
return !os.IsNotExist(err)
}
Binary file added resources/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions src/graph/edge.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package graph

type Edge struct {
ID string `json:"id"`
Source string `json:"source"`
Target string `json:"target"`
}
74 changes: 74 additions & 0 deletions src/graph/graph.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package graph

import (
"encoding/json"
"fmt"
"log"
"math/rand"
"sort"
)

type Graph struct {
nodes map[string]int
edges []Edge
edgeCache map[string]bool
}

func New() *Graph {
return &Graph{
edges: make([]Edge, 0),
nodes: make(map[string]int),
edgeCache: make(map[string]bool),
}
}

func generateID(personA, personB string) string {
slice := []string{personA, personB}
sort.Strings(slice)
personA, personB = slice[0], slice[1]
return fmt.Sprintf("%s%s", personA, personB)
}

func (g *Graph) AddConnection(personA, personB string) {
g.nodes[personA]++
g.nodes[personB]++

id := generateID(personA, personB)
if _, ok := g.edgeCache[id]; !ok {
g.edges = append(g.edges, Edge{
ID: id,
Source: personA,
Target: personB,
})
g.edgeCache[id] = true
}
}

func random(a int) int {
return rand.Intn(a) - a/2
}

func (g *Graph) Marshall() []byte {
nodes := make([]Node, 0)

iteration, offset := 1, 100
for user, size := range g.nodes {
nodes = append(nodes, Node{
ID: user,
Label: user,
Size: size * 2,
X: size + random(iteration+offset),
Y: size + random(iteration+offset),
})
iteration++
}

bytes, err := json.Marshal(map[string]interface{}{
"edges": g.edges,
"nodes": nodes,
})
if err != nil {
log.Println(err)
}
return bytes
}
9 changes: 9 additions & 0 deletions src/graph/node.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package graph

type Node struct {
ID string `json:"id"`
Label string `json:"label"`
X int `json:"x"`
Y int `json:"y"`
Size int `json:"size"`
}
49 changes: 49 additions & 0 deletions src/instagram/instagram.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package instagram

import (
"github.com/ahmdrz/goinsta"
)

type Instagram struct {
insta *goinsta.Instagram
}

func New(username, password string) (*Instagram, error) {
insta := goinsta.New(username, password)
err := insta.Login()
if err != nil {
return nil, err
}
return &Instagram{
insta: insta,
}, nil
}

func (i *Instagram) Export(filePath string) error {
return i.insta.Export(filePath)
}

func Import(filePath string) (*Instagram, error) {
i, err := goinsta.Import(filePath)
if err != nil {
return nil, err
}
return &Instagram{
insta: i,
}, nil
}

func (i *Instagram) Followings() []User {
output := make([]User, 0)
users := i.insta.Account.Following()
for users.Next() {
for _, user := range users.Users {
output = append(output, User{
ID: user.ID,
Username: user.Username,
ProfilePic: user.ProfilePicURL,
})
}
}
return output
}
Loading

0 comments on commit 1858681

Please sign in to comment.