1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# Based on bountysource.cr located at https://github.com/crystal-lang/crystal-website/blob/master/scripts/bountysource.cr
import httpclient, asyncdispatch, json, strutils, os, strtabs, sequtils, future,
algorithm, times
type
BountySource = ref object
client: AsyncHttpClient
team: string
Sponsor = object
name, url, logo: string
amount, allTime: float
since: TimeInfo
const
team = "nim"
apiUrl = "https://api.bountysource.com"
githubApiUrl = "https://api.github.com"
proc newBountySource(team, token: string): BountySource =
result = BountySource(
client: newAsyncHttpClient(userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36"),
team: team
)
# Set up headers
result.client.headers["Accept"] = "application/vnd.bountysource+json; version=2"
result.client.headers["Authorization"] = "token " & token
result.client.headers["Referer"] = "https://salt.bountysource.com/teams/nim/admin/supporters"
result.client.headers["Origin"] = "https://salt.bountysource.com/"
proc getSupporters(self: BountySource): Future[JsonNode] {.async.} =
let response = await self.client.get(apiUrl &
"/supporters?order=monthly&per_page=200&team_slug=" & self.team)
doAssert response.status.startsWith($Http200)
return parseJson(await response.body)
proc getGithubUser(username: string): Future[JsonNode] {.async.} =
let client = newAsyncHttpClient()
let response = await client.get(githubApiUrl & "/users/" & username)
if response.status.startsWith($Http200):
return parseJson(await response.body)
else:
echo("Could not get Github user: ", username, ". ", response.status)
return nil
proc processSupporters(supporters: JsonNode) =
var before = supporters.elems.len
supporters.elems.keepIf(
item => item["display_name"].getStr != "Anonymous"
)
echo("Discarded ", before - supporters.elems.len, " anonymous sponsors.")
echo("Found ", supporters.elems.len, " named sponsors.")
supporters.elems.sort(
(x, y) => cmp(y["alltime_amount"].getFNum, x["alltime_amount"].getFNum)
)
proc quote(text: string): string =
if {' ', ','} in text:
return "\"" & text & "\""
else:
return text
proc getLevel(amount: float): int =
result = 0
const levels = [250, 150, 75, 25, 10, 5, 1]
for i in levels:
if amount.int <= i:
result = i
proc writeCsv(sponsors: seq[Sponsor], filename="sponsors.new.csv") =
var csv = ""
csv.add "logo, name, url, this_month, all_time, since, level\n"
for sponsor in sponsors:
csv.add "$#,$#,$#,$#,$#,$#,$#\n" % [
sponsor.logo.quote, sponsor.name.quote,
sponsor.url.quote, $sponsor.amount.int,
$sponsor.allTime.int, sponsor.since.format("MMM d, yyyy").quote,
$sponsor.amount.getLevel
]
writeFile(filename, csv)
echo("Written csv file to ", filename)
when isMainModule:
if paramCount() == 0:
quit("You need to specify the BountySource access token on the command\n" &
"line, you can find it by going onto https://www.bountysource.com/people/25278-dom96\n" &
"and looking at your browser's network inspector tab to see the token being\n" &
"sent to api.bountysource.com")
let token = paramStr(1)
let bountysource = newBountySource(team, token)
echo("Getting sponsors...")
let supporters = waitFor bountysource.getSupporters()
processSupporters(supporters)
echo("Generating sponsors list... (please be patient)")
var activeSponsors: seq[Sponsor] = @[]
var inactiveSponsors: seq[Sponsor] = @[]
for supporter in supporters:
let name = supporter["display_name"].getStr
var url = ""
let ghUser = waitFor getGithubUser(name)
if not ghUser.isNil:
if ghUser["blog"].kind != JNull:
url = ghUser["blog"].getStr
else:
url = ghUser["html_url"].getStr
if url.len > 0 and not url.startsWith("http"):
url = "http://" & url
let amount = supporter["monthly_amount"].getFNum()
# Only show URL when user donated at least $5.
if amount < 5:
url = ""
#let supporter = getSupporter(supporters,
# supportLevel["owner"]["display_name"].getStr)
#if supporter.isNil: continue
var logo = ""
if amount >= 75:
discard # TODO
let sponsor = Sponsor(name: name, url: url, logo: logo, amount: amount,
allTime: supporter["alltime_amount"].getFNum(),
since: parse(supporter["created_at"].getStr, "yyyy-MM-dd'T'hh:mm:ss")
)
if supporter["monthly_amount"].getFNum > 0.0:
activeSponsors.add(sponsor)
else:
inactiveSponsors.add(sponsor)
echo("Generated ", activeSponsors.len, " active sponsors")
echo("Generated ", inactiveSponsors.len, " inactive sponsors")
writeCsv(activeSponsors)
writeCsv(inactiveSponsors, "inactive_sponsors.new.csv")
|