├── go.mod ├── README.MD ├── .gitignore ├── main.go ├── go.sum ├── base └── base.go ├── githupapi └── userinfo.go └── models └── models.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/gittool 2 | 3 | go 1.17 4 | 5 | require github.com/go-resty/resty/v2 v2.7.0 6 | 7 | require golang.org/x/net v0.0.0-20211029224645-99673261e6eb // indirect 8 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Golang 调用Github API 2 | - 创建token.json 文件,里面添加github的token,内容如下: 3 | ```json 4 | { 5 | "token": "xxxxxxxxxxxx" 6 | } 7 | ``` 8 | 9 | 10 | 11 | ```golang 12 | // 获取用户基本信息 13 | go run main.go user rockyzsu // rocky 为你打算获取的用户 14 | 15 | // 获取所有粉丝,把粉丝按照关注数排序,默认取前10个 16 | go run main.go fans rockyzsu 17 | 18 | // 获取所有仓库数据 19 | go run main.go repo rockyzsu 20 | ``` 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | *.ini 8 | .idea/ 9 | *.json 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Dependency directories (remove the comment below to include it) 17 | # vendor/ 18 | *.swp 19 | .vscode/ 20 | .ipynb_checkpoints/ 21 | *.gz 22 | conf/ 23 | *.error 24 | config.json 25 | ubuntu-fs 26 | 27 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gittool/base" 6 | "github.com/gittool/githupapi" 7 | "os" 8 | "time" 9 | ) 10 | 11 | func main() { 12 | start := time.Now() 13 | if len(os.Args) < 2 { 14 | fmt.Println("Usage: ./main help") 15 | base.Help() 16 | } 17 | user := os.Args[2] 18 | switch os.Args[1] { 19 | case "user": 20 | githupapi.GetUserBaseInfos(user) 21 | case "fans": 22 | githupapi.GetHotFans(user, 10) 23 | case "repo": 24 | githupapi.GetUserRepos(user) 25 | default: 26 | base.Help() 27 | } 28 | used := time.Since(start) 29 | fmt.Println("Time used ", used) 30 | } 31 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= 2 | github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= 3 | golang.org/x/net v0.0.0-20211029224645-99673261e6eb h1:pirldcYWx7rx7kE5r+9WsOXPXK0+WH5+uZ7uPmJ44uM= 4 | golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 5 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 6 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 7 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 8 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 9 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 10 | -------------------------------------------------------------------------------- /base/base.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/gittool/models" 7 | "io/ioutil" 8 | "log" 9 | "os" 10 | ) 11 | 12 | func Must(err error) { 13 | if err != nil { 14 | panic(err) 15 | //return nil 16 | } 17 | } 18 | func JsonParse(filename string) (models.GithubToken, error) { 19 | v := models.GithubToken{} 20 | data, err := ioutil.ReadFile(filename) 21 | if err != nil { 22 | log.Println(err) 23 | return v, err 24 | } 25 | err = json.Unmarshal(data, &v) // 使用这个库得要把 json的定义结构体 的首字母大写 26 | if err != nil { 27 | log.Println(err) 28 | return v, err 29 | } 30 | return v, nil 31 | } 32 | 33 | func Token() string { 34 | filename := "token.json" 35 | token, err := JsonParse(filename) 36 | if err != nil { 37 | panic(err) 38 | } 39 | return token.Token 40 | 41 | } 42 | 43 | func RemoveDuplicated(originText []string) []string { 44 | target := make([]string, 0) 45 | tmpDict := make(map[string]interface{}, 0) 46 | for _, value := range originText { 47 | if _, ok := tmpDict[value]; !ok { 48 | target = append(target, value) 49 | tmpDict[value] = nil 50 | } 51 | } 52 | return target 53 | } 54 | 55 | func GetMapValue(m map[string]int) int { 56 | //fmt.Println(m) 57 | if len(m) < 1 { 58 | panic("length error") 59 | } 60 | for _, v := range m { 61 | return v 62 | } 63 | 64 | return 0 65 | 66 | } 67 | func GetMapKey(m map[string]int) string { 68 | //fmt.Println(m) 69 | if len(m) < 1 { 70 | panic("length error") 71 | } 72 | for k, _ := range m { 73 | return k 74 | } 75 | 76 | return "" 77 | } 78 | 79 | func Sort(origin map[string]int) []map[string]int { 80 | length := len(origin) 81 | //fmt.Println("now length is ", length) 82 | target := make([]map[string]int, 0) 83 | 84 | for k, v := range origin { 85 | tmpMap := make(map[string]int) 86 | tmpMap[k] = v 87 | target = append(target, tmpMap) 88 | } 89 | 90 | //fmt.Println(target) 91 | 92 | for i := 0; i < length; i++ { 93 | for j := i; j < length; j++ { 94 | if GetMapValue(target[i]) < GetMapValue(target[j]) { 95 | // swap 96 | tmpMap := make(map[string]int) 97 | tmpMap = target[i] 98 | target[i] = target[j] 99 | target[j] = tmpMap 100 | } 101 | } 102 | } 103 | return target 104 | } 105 | 106 | func Help() { 107 | fmt.Println(` 108 | ./main user rockyzsu 109 | ./main fans rockyzsu 110 | ./main repo rockyzsu 111 | `) 112 | os.Exit(0) 113 | } 114 | -------------------------------------------------------------------------------- /githupapi/userinfo.go: -------------------------------------------------------------------------------- 1 | package githupapi 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gittool/base" 6 | "github.com/gittool/models" 7 | "github.com/go-resty/resty/v2" 8 | "regexp" 9 | "strconv" 10 | "sync" 11 | ) 12 | 13 | const DEBUG = true 14 | 15 | func GetUserInfo(username string, ch chan *models.Userinfo) { 16 | token := base.Token() 17 | url := fmt.Sprintf("https://api.github.com/users/%s", username) 18 | 19 | client := resty.New() 20 | 21 | var result *models.Userinfo 22 | // 最简单的方法 23 | resp, err := client.R(). 24 | SetAuthToken(token). 25 | SetHeader("Accept", "application/vnd.github.v3+json"). 26 | SetQueryParams(map[string]string{ 27 | "per_page": "30", 28 | "page": "1", 29 | "sort": "forks", 30 | "direction": "des", 31 | }). 32 | SetResult(&result). 33 | Get(url) 34 | 35 | if err != nil { 36 | ch <- result 37 | } else { 38 | _ = resp 39 | ch <- result 40 | } 41 | } 42 | 43 | func GetUserBaseInfos(username string) *models.Userinfo { 44 | 45 | ch := make(chan *models.Userinfo) 46 | go GetUserInfo(username, ch) 47 | result := <-ch 48 | if DEBUG { 49 | fmt.Printf("%+v", result) 50 | } 51 | return result 52 | } 53 | 54 | func GetUserRepos(username string) []*models.Repo { 55 | userinfo := GetUserBaseInfos(username) 56 | //fmt.Printf("%+v", userinfo) 57 | totalPage := userinfo.PublicRepos/100 + 1 58 | repo_list := make([]*models.Repo, 0) 59 | for i := 1; i <= totalPage; i++ { 60 | repo_list = append(repo_list, GetUserRepo(username, totalPage)...) 61 | } 62 | //fmt.Println(repo_list) 63 | return repo_list 64 | } 65 | func GetUserRepo(username string, page int) []*models.Repo { 66 | token := base.Token() 67 | url := fmt.Sprintf("https://api.github.com/users/%s/repos?per_page=500", username) 68 | 69 | client := resty.New() 70 | 71 | var result []*models.Repo 72 | // 最简单的方法 73 | resp, err := client.R(). 74 | SetAuthToken(token). 75 | SetHeader("Accept", "application/vnd.github.v3+json"). 76 | SetQueryParams(map[string]string{ 77 | "per_page": "30", 78 | "page": strconv.Itoa(page), 79 | "sort": "forks", 80 | "direction": "des", 81 | }). 82 | SetResult(&result). 83 | Get(url) 84 | 85 | base.Must(err) 86 | //fmt.Println(resp) 87 | _ = resp 88 | repo_list := make([]*models.Repo, 0) 89 | 90 | for _, repo := range result { 91 | if repo.Fork == false { 92 | //fmt.Println(repo.Name) 93 | repo_list = append(repo_list, repo) 94 | fmt.Println(repo.Name) 95 | fmt.Println(repo.Description) 96 | fmt.Println() 97 | } 98 | } 99 | //fmt.Println(len(repo_list)) 100 | return repo_list 101 | } 102 | 103 | func getFollower(username string, count int) []string { 104 | var followers []*models.Follower 105 | var followers_list []string 106 | 107 | var total_page int 108 | total_page = count/100 + 1 109 | url := fmt.Sprintf("https://api.github.com/users/%s/followers?per_page=500", username) 110 | 111 | client := resty.New() 112 | 113 | for i := 1; i <= total_page; i++ { 114 | // 最简单的方法 115 | resp, err := client.R(). 116 | SetHeader("Accept", "application/vnd.github.v3+json"). 117 | SetResult(&followers). 118 | SetQueryParams(map[string]string{ 119 | "per_page": "100", 120 | "page": strconv.Itoa(i), 121 | "sort": "forks", 122 | "direction": "des", 123 | }). 124 | Get(url) 125 | 126 | base.Must(err) 127 | _ = resp 128 | //fmt.Println(resp) 129 | //fmt.Printf("%+v\n", result) 130 | //fmt.Println(result) 131 | link_re := regexp.MustCompile(`https://api.github.com/users/(.*)$`) 132 | 133 | for _, repo := range followers { 134 | fans_username := link_re.FindStringSubmatch(repo.Url) 135 | if len(fans_username) > 0 { 136 | followers_list = append(followers_list, fans_username[1]) 137 | } 138 | } 139 | } 140 | followers_list = base.RemoveDuplicated(followers_list) 141 | return followers_list 142 | } 143 | 144 | func GetFollowers(username string) { 145 | ch := make(chan *models.Userinfo) 146 | go GetUserInfo(username, ch) 147 | //fmt.Printf("共有%d个粉丝\n", userinfo.Followers) 148 | userinfo := <-ch 149 | followers_list := getFollower(username, userinfo.Followers) 150 | fmt.Println(followers_list) 151 | fmt.Println(len(followers_list)) 152 | } 153 | 154 | func GetHotFans(username string, topN int) map[string]int { 155 | //获取关注者中的大v 156 | base_ch := make(chan *models.Userinfo) 157 | go GetUserInfo(username, base_ch) 158 | userinfo := <-base_ch 159 | 160 | fmt.Printf("共有%d个粉丝\n", userinfo.Followers) 161 | 162 | followers_list := getFollower(username, userinfo.Followers) 163 | follower_map := make(map[string]int) 164 | wg := &sync.WaitGroup{} 165 | ch := make(chan *models.Userinfo, 20) 166 | for _, user := range followers_list { 167 | //follower := GetUserInfo(user) 168 | wg.Add(1) 169 | go func(username string) { 170 | GetUserInfo(username, ch) 171 | defer wg.Done() 172 | }(user) 173 | 174 | } 175 | 176 | go func(ch chan *models.Userinfo) { 177 | wg.Wait() 178 | close(ch) 179 | }(ch) 180 | 181 | for follower := range ch { 182 | //fmt.Println(follower) 183 | if follower == nil { 184 | continue 185 | } 186 | if follower.HtmlUrl == "" { 187 | continue 188 | } 189 | follower_map[follower.HtmlUrl] = follower.Followers 190 | } 191 | follower_map_list := base.Sort(follower_map) 192 | 193 | result := make(map[string]int) 194 | for i := 0; i < topN; i++ { 195 | name := base.GetMapKey(follower_map_list[i]) 196 | value := base.GetMapValue(follower_map_list[i]) 197 | result[name] = value 198 | fmt.Printf("User: %s, its fans: %d\n", name, value) 199 | } 200 | 201 | return result 202 | 203 | } 204 | -------------------------------------------------------------------------------- /models/models.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type Userinfo struct { 6 | Login string `json:"login"` 7 | Id int `json:"id"` 8 | NodeId string `json:"node_id"` 9 | AvatarUrl string `json:"avatar_url"` 10 | GravatarId string `json:"gravatar_id"` 11 | Url string `json:"url"` 12 | HtmlUrl string `json:"html_url"` 13 | FollowersUrl string `json:"followers_url"` 14 | FollowingUrl string `json:"following_url"` 15 | GistsUrl string `json:"gists_url"` 16 | StarredUrl string `json:"starred_url"` 17 | SubscriptionsUrl string `json:"subscriptions_url"` 18 | OrganizationsUrl string `json:"organizations_url"` 19 | ReposUrl string `json:"repos_url"` 20 | EventsUrl string `json:"events_url"` 21 | ReceivedEventsUrl string `json:"received_events_url"` 22 | Type string `json:"type"` 23 | SiteAdmin bool `json:"site_admin"` 24 | Name string `json:"name"` 25 | Company interface{} `json:"company"` 26 | Blog string `json:"blog"` 27 | Location string `json:"location"` 28 | Email interface{} `json:"email"` 29 | Hireable bool `json:"hireable"` 30 | Bio string `json:"bio"` 31 | TwitterUsername interface{} `json:"twitter_username"` 32 | PublicRepos int `json:"public_repos"` 33 | PublicGists int `json:"public_gists"` 34 | Followers int `json:"followers"` 35 | Following int `json:"following"` 36 | CreatedAt time.Time `json:"created_at"` 37 | UpdatedAt time.Time `json:"updated_at"` 38 | } 39 | 40 | type Repo struct { 41 | Id int `json:"id"` 42 | NodeId string `json:"node_id"` 43 | Name string `json:"name"` 44 | FullName string `json:"full_name"` 45 | Private bool `json:"private"` 46 | Owner struct { 47 | Login string `json:"login"` 48 | Id int `json:"id"` 49 | NodeId string `json:"node_id"` 50 | AvatarUrl string `json:"avatar_url"` 51 | GravatarId string `json:"gravatar_id"` 52 | Url string `json:"url"` 53 | HtmlUrl string `json:"html_url"` 54 | FollowersUrl string `json:"followers_url"` 55 | FollowingUrl string `json:"following_url"` 56 | GistsUrl string `json:"gists_url"` 57 | StarredUrl string `json:"starred_url"` 58 | SubscriptionsUrl string `json:"subscriptions_url"` 59 | OrganizationsUrl string `json:"organizations_url"` 60 | ReposUrl string `json:"repos_url"` 61 | EventsUrl string `json:"events_url"` 62 | ReceivedEventsUrl string `json:"received_events_url"` 63 | Type string `json:"type"` 64 | SiteAdmin bool `json:"site_admin"` 65 | } `json:"owner"` 66 | HtmlUrl string `json:"html_url"` 67 | Description string `json:"description"` 68 | Fork bool `json:"fork"` 69 | Url string `json:"url"` 70 | ForksUrl string `json:"forks_url"` 71 | KeysUrl string `json:"keys_url"` 72 | CollaboratorsUrl string `json:"collaborators_url"` 73 | TeamsUrl string `json:"teams_url"` 74 | HooksUrl string `json:"hooks_url"` 75 | IssueEventsUrl string `json:"issue_events_url"` 76 | EventsUrl string `json:"events_url"` 77 | AssigneesUrl string `json:"assignees_url"` 78 | BranchesUrl string `json:"branches_url"` 79 | TagsUrl string `json:"tags_url"` 80 | BlobsUrl string `json:"blobs_url"` 81 | GitTagsUrl string `json:"git_tags_url"` 82 | GitRefsUrl string `json:"git_refs_url"` 83 | TreesUrl string `json:"trees_url"` 84 | StatusesUrl string `json:"statuses_url"` 85 | LanguagesUrl string `json:"languages_url"` 86 | StargazersUrl string `json:"stargazers_url"` 87 | ContributorsUrl string `json:"contributors_url"` 88 | SubscribersUrl string `json:"subscribers_url"` 89 | SubscriptionUrl string `json:"subscription_url"` 90 | CommitsUrl string `json:"commits_url"` 91 | GitCommitsUrl string `json:"git_commits_url"` 92 | CommentsUrl string `json:"comments_url"` 93 | IssueCommentUrl string `json:"issue_comment_url"` 94 | ContentsUrl string `json:"contents_url"` 95 | CompareUrl string `json:"compare_url"` 96 | MergesUrl string `json:"merges_url"` 97 | ArchiveUrl string `json:"archive_url"` 98 | DownloadsUrl string `json:"downloads_url"` 99 | IssuesUrl string `json:"issues_url"` 100 | PullsUrl string `json:"pulls_url"` 101 | MilestonesUrl string `json:"milestones_url"` 102 | NotificationsUrl string `json:"notifications_url"` 103 | LabelsUrl string `json:"labels_url"` 104 | ReleasesUrl string `json:"releases_url"` 105 | DeploymentsUrl string `json:"deployments_url"` 106 | CreatedAt time.Time `json:"created_at"` 107 | UpdatedAt time.Time `json:"updated_at"` 108 | PushedAt time.Time `json:"pushed_at"` 109 | GitUrl string `json:"git_url"` 110 | SshUrl string `json:"ssh_url"` 111 | CloneUrl string `json:"clone_url"` 112 | SvnUrl string `json:"svn_url"` 113 | Homepage *string `json:"homepage"` 114 | Size int `json:"size"` 115 | StargazersCount int `json:"stargazers_count"` 116 | WatchersCount int `json:"watchers_count"` 117 | Language *string `json:"language"` 118 | HasIssues bool `json:"has_issues"` 119 | HasProjects bool `json:"has_projects"` 120 | HasDownloads bool `json:"has_downloads"` 121 | HasWiki bool `json:"has_wiki"` 122 | HasPages bool `json:"has_pages"` 123 | ForksCount int `json:"forks_count"` 124 | MirrorUrl interface{} `json:"mirror_url"` 125 | Archived bool `json:"archived"` 126 | Disabled bool `json:"disabled"` 127 | OpenIssuesCount int `json:"open_issues_count"` 128 | License *struct { 129 | Key string `json:"key"` 130 | Name string `json:"name"` 131 | SpdxId string `json:"spdx_id"` 132 | Url *string `json:"url"` 133 | NodeId string `json:"node_id"` 134 | } `json:"license"` 135 | AllowForking bool `json:"allow_forking"` 136 | IsTemplate bool `json:"is_template"` 137 | Topics []string `json:"topics"` 138 | Visibility string `json:"visibility"` 139 | Forks int `json:"forks"` 140 | OpenIssues int `json:"open_issues"` 141 | Watchers int `json:"watchers"` 142 | DefaultBranch string `json:"default_branch"` 143 | } 144 | 145 | type GithubToken struct { 146 | Token string `json:"token"` 147 | } 148 | 149 | type Repository struct { 150 | ID int `json:"id"` 151 | NodeID string `json:"node_id"` 152 | Name string `json:"name"` 153 | FullName string `json:"full_name"` 154 | Owner *Developer `json:"owner"` 155 | Private bool `json:"private"` 156 | Description string `json:"description"` 157 | Fork bool `json:"fork"` 158 | Language string `json:"language"` 159 | ForksCount int `json:"forks_count"` 160 | StargazersCount int `json:"stargazers_count"` 161 | WatchersCount int `json:"watchers_count"` 162 | OpenIssuesCount int `json:"open_issues_count"` 163 | } 164 | 165 | type Developer struct { 166 | Login string `json:"login"` 167 | ID int `json:"id"` 168 | NodeID string `json:"node_id"` 169 | AvatarURL string `json:"avatar_url"` 170 | GravatarID string `json:"gravatar_id"` 171 | Type string `json:"type"` 172 | SiteAdmin bool `json:"site_admin"` 173 | } 174 | 175 | type Follower struct { 176 | Login string `json:"login"` 177 | Id int `json:"id"` 178 | NodeId string `json:"node_id"` 179 | AvatarUrl string `json:"avatar_url"` 180 | GravatarId string `json:"gravatar_id"` 181 | Url string `json:"url"` 182 | HtmlUrl string `json:"html_url"` 183 | FollowersUrl string `json:"followers_url"` 184 | FollowingUrl string `json:"following_url"` 185 | GistsUrl string `json:"gists_url"` 186 | StarredUrl string `json:"starred_url"` 187 | SubscriptionsUrl string `json:"subscriptions_url"` 188 | OrganizationsUrl string `json:"organizations_url"` 189 | ReposUrl string `json:"repos_url"` 190 | EventsUrl string `json:"events_url"` 191 | ReceivedEventsUrl string `json:"received_events_url"` 192 | Type string `json:"type"` 193 | SiteAdmin bool `json:"site_admin"` 194 | } 195 | --------------------------------------------------------------------------------