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