Created
February 21, 2025 10:10
-
-
Save collymore/da6a6d2a4beaade5dda04c9b53c270c0 to your computer and use it in GitHub Desktop.
Toggle and Slack Client updates Napkin JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default async (request, response) => { | |
// Map Toggl client IDs to Slack channel IDs (private or public) | |
const clientToSlackChannel = { | |
61586288: 'C04D3USRW1Z', // Client 1 ID Toggl : Slack Channel | |
53983123: 'C01PCCUFXN0', // Client 2 | |
53983123: 'C01PCCUFXN0', // Client 3 | |
}; | |
// 1) Prepare date range for the current month | |
const now = new Date(); | |
const year = now.getFullYear(); | |
const month = String(now.getMonth() + 1).padStart(2, '0'); | |
const startDate = `${year}-${month}-01`; | |
// End date: first day of next month | |
const endDateObj = new Date(year, now.getMonth() + 1, 1); | |
const endDate = endDateObj.toISOString().split('T')[0]; | |
try { | |
// Process all clients in parallel (or you could do .forEach and await sequentially) | |
const results = await Promise.all( | |
Object.entries(clientToSlackChannel).map(async ([clientId, slackChannel]) => { | |
// Build Toggl URL for each client | |
const togglUrl = | |
'https://api.track.toggl.com/reports/api/v2/summary' | |
+ `?workspace_id=${process.env.TOGGL_WORKSPACE_ID}` | |
+ `&client_ids=${clientId}` | |
+ `&since=${startDate}` | |
+ `&until=${endDate}`; | |
// Prepare authorization | |
const togglAuth = Buffer.from( | |
process.env.TOGGL_API_TOKEN + ':api_token' | |
).toString('base64'); | |
// Fetch Toggl data | |
const togglResponse = await fetch(togglUrl, { | |
method: 'GET', | |
headers: { | |
Authorization: `Basic ${togglAuth}`, | |
'Content-Type': 'application/json', | |
}, | |
}); | |
if (!togglResponse.ok) { | |
const errorText = await togglResponse.text(); | |
// Return an error-like result for this client | |
return { | |
clientId, | |
slackChannel, | |
error: `Toggl API error: ${errorText}`, | |
}; | |
} | |
const togglData = await togglResponse.json(); | |
// If no data returned, post a Slack message for zero usage | |
if (!togglData.data || togglData.data.length === 0) { | |
await postToSlack( | |
slackChannel, | |
`No time logged this month for client ID: ${clientId}` | |
); | |
return { | |
clientId, | |
slackChannel, | |
clientName: `Client ID ${clientId}`, | |
totalHours: 0, | |
message: 'No usage data found', | |
}; | |
} | |
// Build a report for each project | |
let totalSecondsAllProjects = 0; | |
const projectLines = []; | |
togglData.data.forEach(item => { | |
const projectName = item.title?.project || 'Unnamed Project'; | |
const totalSeconds = item.time || item.total_grand || 0; | |
totalSecondsAllProjects += totalSeconds; | |
const hoursThisProject = (totalSeconds / 3600000).toFixed(2); | |
projectLines.push(`• *${projectName}*: ${hoursThisProject}h`); | |
}); | |
// Summaries (client name might be togglData.data[0].title.client) | |
const clientName = | |
togglData.data[0].title?.client || `Client ID ${clientId}`; | |
const totalHours = (totalSecondsAllProjects / 3600000).toFixed(2); | |
// Slack message | |
const messageText = [ | |
`*${clientName} usage this month:*`, | |
...projectLines, | |
`*Total:* ${totalHours}h` | |
].join('\n'); | |
// Post to Slack | |
await postToSlack(slackChannel, messageText); | |
return { | |
clientId, | |
slackChannel, | |
clientName, | |
totalHours, | |
message: `Successfully sent usage data for ${clientName} to channel ${slackChannel}`, | |
}; | |
}) | |
); | |
// 3) Return the combined results for all clients | |
response.json({ | |
message: 'All clients processed successfully', | |
results, | |
}); | |
} catch (err) { | |
response.status(500).send(`Unexpected error: ${err.message}`); | |
} | |
}; | |
/** | |
* Helper function: Post a message to Slack | |
*/ | |
async function postToSlack(channel, text) { | |
const slackResult = await fetch('https://slack.com/api/chat.postMessage', { | |
method: 'POST', | |
headers: { | |
Authorization: `Bearer ${process.env.SLACK_BOT_TOKEN}`, | |
'Content-Type': 'application/json; charset=utf-8', | |
}, | |
body: JSON.stringify({ channel, text }), | |
}); | |
const slackData = await slackResult.json(); | |
if (!slackData.ok) { | |
throw new Error(`Slack API error: ${slackData.error || 'unknown error'}`); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment