Skip to content

Commit 950d6e9

Browse files
committed
feat(github): token handling
1 parent 81d445e commit 950d6e9

File tree

1 file changed

+132
-1
lines changed

1 file changed

+132
-1
lines changed

nobori/github.go

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,137 @@
11
package nobori
22

3-
import "github.com/terrapkg/gura/db"
3+
import (
4+
"log"
5+
"net/http"
6+
"os"
7+
"slices"
8+
"strconv"
9+
"strings"
10+
"time"
11+
12+
"github.com/terrapkg/gura/db"
13+
)
14+
15+
// ? https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits
16+
// no more than 100 parallel requests
17+
var pool = make(chan db.Stream, 100)
18+
var tokens []Token
19+
20+
type Token struct {
21+
key string
22+
quota int16
23+
reset time.Time
24+
}
425

526
func fetchGitHub(stream db.Stream) {
27+
pool <- stream
28+
}
29+
30+
func fillGitHubTokens() Token {
31+
token_chan := make(chan Token)
32+
num := 0
33+
for token := range strings.SplitSeq(os.Getenv("GURA_GITHUB_TOKENS"), ";") {
34+
num++
35+
go func(token string) {
36+
req, err := http.NewRequest(http.MethodGet, "https://api.github.com/rate_limit", nil)
37+
if err != nil {
38+
log.Fatalln("github: can't swim new req:", err)
39+
}
40+
req.Header.Add("Authorization", "Bearer "+token)
41+
resp, err := http.DefaultClient.Do(req)
42+
if err != nil {
43+
log.Printf("github: GET /rate_limit: %v\n"+
44+
"github: give up on token: %s", err, token)
45+
return
46+
}
47+
quota, err := strconv.ParseInt(resp.Header.Get("x-ratelimit-remaining"), 10, 16)
48+
if err != nil {
49+
log.Printf("github: strconv x-ratelimit-remaining: %v\n"+
50+
"github: give up on token: %s", err, token)
51+
return
52+
}
53+
reset, err := strconv.ParseInt(resp.Header.Get("x-ratelimit-reset"), 10, 64)
54+
if err != nil {
55+
log.Printf("github: strconv x-ratelimit-reset: %v\n"+
56+
"github: give up on token: %s", err, token)
57+
return
58+
}
59+
token_chan <- Token{
60+
key: token,
61+
quota: int16(quota),
62+
reset: time.Unix(reset, 0),
63+
}
64+
}(token)
65+
}
66+
for ; num != 0; num-- {
67+
tokens = append(tokens, <-token_chan)
68+
}
69+
slices.SortFunc(tokens, func(a, b Token) int {
70+
if a.quota == 0 && b.quota == 0 {
71+
return a.reset.Compare(b.reset)
72+
}
73+
if a.quota < b.quota {
74+
return -1
75+
}
76+
if a.quota == b.quota {
77+
return 0
78+
}
79+
return +1
80+
})
81+
if len(tokens) == 0 {
82+
log.Fatalln("github: fatal: no tokens")
83+
}
84+
return tokens[0]
85+
}
86+
87+
func noMoreFish(token Token) bool {
88+
return token.quota == 0
89+
}
90+
func thanksForAllTheFish(token *Token, token_idx *int) {
91+
*token_idx = (*token_idx + 1) % len(tokens)
92+
*token = tokens[*token_idx]
93+
}
94+
func waitForFish(token Token) {
95+
if token.quota == 0 {
96+
time.Sleep(time.Until(token.reset))
97+
}
98+
}
99+
100+
func updToken(token *Token, resp *http.Response) {
101+
quota, err := strconv.ParseInt(resp.Header.Get("x-ratelimit-remaining"), 10, 16)
102+
if err != nil {
103+
log.Fatalf("github: strconv x-ratelimit-remaining: %v", err, *token)
104+
return
105+
}
106+
reset, err := strconv.ParseInt(resp.Header.Get("x-ratelimit-reset"), 10, 64)
107+
if err != nil {
108+
log.Fatalf("github: strconv x-ratelimit-reset: %v", err, *token)
109+
return
110+
}
111+
token.quota = int16(quota)
112+
token.reset = time.Unix(reset, 0)
113+
}
114+
115+
// the shark shall initialise the funny
116+
func swimGitHub() {
117+
token_idx := 0
118+
for token := fillGitHubTokens(); noMoreFish(token); thanksForAllTheFish(&token, &token_idx) {
119+
waitForFish(token)
120+
for {
121+
stream := <-pool
122+
req, err := http.NewRequest(http.MethodGet, "https://api.github.com/repos/"+stream.Fetch+"/tags", nil)
123+
if err != nil {
124+
log.Printf("github: req [%s]: %v", stream.Fetch, err)
125+
continue
126+
}
127+
req.Header.Add("Authorization", "Bearer "+token.key)
128+
resp, err := http.DefaultClient.Do(req)
129+
if err != nil {
130+
log.Printf("github: resp [%s]: %v", stream.Fetch, err)
131+
continue
132+
}
133+
updToken(&token, resp)
134+
// TODO: update stream
135+
}
136+
}
6137
}

0 commit comments

Comments
 (0)