UNPKG

5.41 kBtext/coffeescriptView Raw
1# Description:
2# Fitbit leaderboards
3#
4# Configuration:
5# FITBIT_CLIENT_ID
6# FITBIT_CLIENT_SECRET
7# FITBIT_OAUTH_TOKEN
8# FITBIT_REDIRECT_URL
9#
10# Commands:
11# hubot fitbit leaders - Show table of leaders
12# hubot fitbit register - Show how to friend the bot
13# hubot fitbit approve - Approve all pending requests
14#
15# Notes:
16# To obtain/set the FITBIT_OAUTH_TOKEN, you will need to visit the "Implicit"
17# authorization URL. This will grant you a non-refreshable token for a year.
18#
19# Authors:
20# stephenyeargin, hogepodge
21
22Util = require 'util'
23moment = require 'moment'
24
25module.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 # Default action
37 robot.respond /fitbit$/i, (msg) ->
38 getLeaderboard(msg)
39
40 # Set up or renew the token
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 # Show the top five users
54 robot.respond /fitbit (steps|leaderboard|leaders)/i, (msg) ->
55 getLeaderboard(msg)
56
57 # Who are my friends?
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 # See how to friend the bot
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 # Approve existing friend requests
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, ",")