summary refs log tree commit diff stats
path: root/web/bountysource.nim
blob: 5dfdb44975848f6dd3bb5bb296f57e6422168122 (plain) (blame)
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")