-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
169 lines (147 loc) · 5.48 KB
/
index.js
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env node
const Bot = require('keybase-bot')
const moment = require('moment')
const axios = require('axios').default
const secrets = require('./secrets.js')
const togglAPI = axios.create({
baseURL: 'https://toggl.com',
headers: {
'Authorization': `Basic ${Buffer.from(`${secrets.toggl.api_key}:api_token`).toString('base64')}`,
},
})
const bot = new Bot()
async function main() {
try {
await bot.init(secrets.keybase.username, secrets.keybase.paperkey)
const info = bot.myInfo()
console.log(`Bot initialized with username ${info.username}.`)
// await bot.chat.clearCommands()
await bot.chat.advertiseCommands({
advertisements: [
{
type: 'public',
commands: [
{
name: 'toggl',
description: 'Talks with the Toggl API',
usage: '(budget | help)',
},
],
},
],
})
console.log(`Listening for all messages...`)
await bot.chat.watchAllChannelsForNewMessages(
async msg => {
try {
// Ignore non-text messages
if (msg.content.type !== 'text') {
return
}
// Ignore replies
if (!msg.content.text.body.startsWith('!toggl ')) {
return
}
// At this point we're certain the user is trying to interact with the bot
const parts = msg.content.text.body.split(' ')
if (parts[1] === 'budget') {
const thisMonth = moment().format('MMMM')
const queriedMonth = moment(parts[2] || thisMonth, 'MMMM').format('MMMM')
// Requesting the budget!
try {
let agg = {}
let totalProcessed = 0
let totalCount = 0
let page = 1
while (true) {
const res = await togglAPI.get('/reports/api/v2/details', {
params: {
'user_agent': '[email protected]',
'workspace_id': secrets.toggl.workspace_id,
'project_ids': secrets.toggl.project_id,
'since': moment(queriedMonth, 'MMMM').startOf('month').format('YYYY-MM-DD'),
'until': moment(queriedMonth, 'MMMM').endOf('month').format('YYYY-MM-DD'),
'display_hours': 'decimal',
'page': page,
},
})
totalCount = res.data.total_count
agg = res.data.data.map(item => ({
uid: item.uid.toString(), // we operate on string ids in this script
dur: item.dur,
})).reduce((prev, cur) => {
if (prev[cur.uid] === undefined) {
prev[cur.uid] = 0
}
prev[cur.uid] += cur.dur
return prev
}, agg)
totalProcessed += res.data.data.length
page++
if (totalProcessed >= totalCount) {
break
}
}
// At this point we've completed the processing, calculate the sums
const table = []
const names = []
for (const [uid, value] of Object.entries(agg)) {
const name = secrets.names[uid]
names.push(name)
const rate = secrets.rates[uid]
const hours = value / 1000 / 60 / 60 // convert from ms into a float
table.push({
name,
rate,
hours,
amount: rate * hours,
})
}
// For now we'll simply aggregate the table and hide the breakdown
const joinedNames = [names.slice(0, -1).join(', '), names.slice(-1)[0]].join(names.length < 2 ? '' : ' and ')
const totalHours = table.map(row => row.hours).reduce((a, b) => a + b, 0).toFixed(3)
const totalAmount = table.map(row => row.amount).reduce((a, b) => a + b, 0).toFixed(2)
bot.chat.send(msg.conversationId, {
body: `${queriedMonth === thisMonth ? 'This month' : `In ${queriedMonth}`} ${joinedNames} worked ${totalHours}h for a total cost of $${totalAmount}`,
})
} catch(e) {
if (e.responseBody) {
console.error(e)
const body = await e.responseBody
console.error(`Body: ${body}`)
return
}
throw e
}
return
}
if (parts[1] === 'help') {
if (msg.channel.name === secrets.acl.team || msg.channel.name.startsWith(`${secrets.acl.team}.`)) {
bot.chat.send(msg.conversationId, {
body: `Usage: \`!toggl budget\` to get the amount for the current month.`,
})
return
}
bot.chat.send(msg.conversationId, {
body: `Hello! I'm @${info.username}, currently only configured to serve the internal chat ops at @${secrets.acl.team}. Message @pzduniak if you're interested in a Toggl - Keybase integration.`
})
return
}
console.log(msg)
} catch (err) {
console.error(err)
}
},
e => console.error(e)
)
} catch (error) {
console.error(error)
}
}
async function shutdown() {
await bot.deinit()
process.exit()
}
process.on('SIGINT', shutdown)
process.on('SIGTERM', shutdown)
main()