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 | Util = require 'util'
|
26 | moment = require 'moment'
|
27 |
|
28 | module.exports = (robot) ->
|
29 | FitbitApiClient = require 'fitbit-node'
|
30 | Fitbit = new FitbitApiClient(
|
31 | clientId: process.env.FITBIT_CLIENT_ID,
|
32 | clientSecret: process.env.FITBIT_CLIENT_SECRET,
|
33 | apiVersion: '1.2'
|
34 | )
|
35 | accessToken = process.env.FITBIT_OAUTH_TOKEN
|
36 |
|
37 |
|
38 | robot.respond /fitbit$/i, (msg) ->
|
39 | getLeaderboard(msg)
|
40 |
|
41 |
|
42 | robot.respond /fitbit (?:token|setup)$/i, (msg) ->
|
43 | url = "" +
|
44 | "https://www.fitbit.com/oauth2/authorize?response_type=token" +
|
45 | "&client_id=#{process.env.FITBIT_CLIENT_ID}" +
|
46 | "&redirect_uri=<YOUR REDIRECT URL>" +
|
47 | "&scope=profile%20social&expires_in=31536000"
|
48 | msg.send """
|
49 | 1) Go to: #{url}
|
50 | 2) Save the URL token in the bot's configuration
|
51 | 3) Restart Hubot to load configuration
|
52 | """
|
53 |
|
54 |
|
55 | robot.respond /fitbit (steps|leaderboard|leaders)/i, (msg) ->
|
56 | getLeaderboard(msg)
|
57 |
|
58 |
|
59 | robot.respond /fitbit friends/i, (msg) ->
|
60 | Fitbit.get('/friends.json', accessToken)
|
61 | .then (res) ->
|
62 | responseBody = getResponseBody(res)
|
63 | responseHeaders = getResponseHeaders(res)
|
64 | return displayErrors(responseBody, msg) if responseHeaders.statusCode != 200
|
65 | friends = responseBody.friends
|
66 | if friends.length > 0
|
67 | list = []
|
68 | for own key, friend of friends
|
69 | list.push "#{friend.user.displayName}"
|
70 | msg.send list.join(", ")
|
71 | else
|
72 | msg.send "You have no friends on Fitbit. :("
|
73 | .catch (error) ->
|
74 | displayErrors(error, msg)
|
75 |
|
76 |
|
77 | robot.respond /fitbit register/i, (msg) ->
|
78 | Fitbit.get('/profile.json', accessToken)
|
79 | .then (res) ->
|
80 | responseBody = getResponseBody(res)
|
81 | responseHeaders = getResponseHeaders(res)
|
82 | return displayErrors(responseBody, msg) if responseHeaders.statusCode != 200
|
83 | user = responseBody.user
|
84 | unless user.fullName
|
85 | user.fullName = 'the bot'
|
86 | userId = user.encodedId
|
87 | msg.send """
|
88 | 1) Add #{user.fullName} as a friend - http://fitbit.com/user/#{userId}
|
89 | 2) Type `#{robot.name} fitbit approve`
|
90 | """
|
91 | .catch (error) ->
|
92 | displayErrors(error, msg)
|
93 |
|
94 |
|
95 | robot.respond /fitbit approve/i, (msg) ->
|
96 | Fitbit.get('/friends/invitations.json', accessToken)
|
97 | .then (res) ->
|
98 | responseBody = getResponseBody(res)
|
99 | responseHeaders = getResponseHeaders(res)
|
100 | return displayErrors(responseBody, msg) if responseHeaders.statusCode != 200
|
101 | if responseBody.friends.length is 0
|
102 | msg.send "No pending requests."
|
103 | return
|
104 | for own key, friend of getResponseBody(res).friends
|
105 | params =
|
106 | accept: true
|
107 | Fitbit.post(
|
108 | "/friends/invitations/#{friend.user.encodedId}.json",
|
109 | accessToken,
|
110 | params
|
111 | )
|
112 | .then (res) ->
|
113 | robot.logger.debug getResponseBody(res)
|
114 | msg.send "Approve: #{friend.user.displayName}"
|
115 | .catch (error) ->
|
116 | displayErrors(error, msg)
|
117 | .catch (error) ->
|
118 | displayErrors(error, msg)
|
119 |
|
120 | getLeaderboard = (msg) ->
|
121 | try
|
122 | Fitbit.get('/friends/leaderboard.json', accessToken)
|
123 | .then (res) ->
|
124 | responseBody = getResponseBody(res)
|
125 | responseHeaders = getResponseHeaders(res)
|
126 | return displayErrors(responseBody, msg) if responseHeaders.statusCode != 200
|
127 | leaders = responseBody.friends
|
128 | finalLeaders = []
|
129 | for own key, leader of leaders
|
130 | robot.logger.debug leader
|
131 | if leader.summary.steps > 0
|
132 |
|
133 | if moment(leader.lastUpdateTime) < moment().subtract(1, 'days')
|
134 | lastSync = " (#{moment(leader.lastUpdateTime).fromNow()})"
|
135 | else
|
136 | lastSync = ''
|
137 | rank = leader.rank.steps * 1
|
138 | displayName = leader.user.displayName || 'Unknown'
|
139 | steps = formatThousands(leader.summary.steps) || 0
|
140 | finalLeaders[rank] = "##{rank} #{displayName} - #{steps}#{lastSync}"
|
141 | msg.send finalLeaders.join("\n")
|
142 | .catch (error) ->
|
143 | displayErrors(error, msg)
|
144 | catch err
|
145 | msg.send "Unable to retrieve leaderboard."
|
146 |
|
147 | getResponseBody = (res) ->
|
148 | return res[0]
|
149 |
|
150 | getResponseHeaders = (res) ->
|
151 | return res[1]
|
152 |
|
153 | displayErrors = (err, msg) ->
|
154 | for own key, error of err.errors
|
155 | if error.errorType == 'expired_token'
|
156 | msg.send "Your Fitbit token has expired! See `#{robot.name} fitbit token` to set up a new one."
|
157 | else
|
158 | robot.logger.error err
|
159 | msg.send error.message
|
160 |
|
161 | formatThousands = (num) ->
|
162 | return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|