I need assistance with my Slack AI chatbot, built on a Node.js Vercel function. The bot is experiencing an issue related to Slack event subscriptions. Specifically, the bot needs to respond to events within 3 seconds, but when handling large queries, it occasionally sends two responses. This happens because if the bot doesn’t respond within 3 seconds, Slack re-triggers the event.
import crypto from 'crypto';
import { sendGPTResponse } from './_chat';
// Set to store processed request timestamps temporarily
const processedEvents: Set<string> = new Set();
async function isValidSlackRequest(request: Request, body: any) {
const signingSecret = process.env.SLACK_SIGNING_SECRET!;
const timestamp = request.headers.get('X-Slack-Request-Timestamp')!;
const slackSignature = request.headers.get('X-Slack-Signature')!;
const base = `v0:${timestamp}:${JSON.stringify(body)}`;
const hmac = crypto.createHmac('sha256', signingSecret).update(base).digest('hex');
const computedSignature = `v0=${hmac}`;
console.log('isValidSlackRequest:', { computedSignature, slackSignature });
return computedSignature === slackSignature;
}
async function processEvent(body: any) {
const eventType = body.event.type;
if (eventType === 'app_mention') {
// Process the event asynchronously with a delay
// setTimeout(async () => {
// try {
await sendGPTResponse(body.event);
console.log(`OpenAI API call for event ${body.event_id} completed successfully.`);
// } catch (error) {
// console.error(`Error while processing event ${body.event_id} with OpenAI:`, error);
// // Optionally handle the error here, such as retrying or marking the event for manual intervention.
// }
// }, 0); // Execute asynchronously, allowing the main thread to respond promptly
}
}
export async function POST(request: Request) {
console.log('Received a POST request');
const rawBody = await request.text();
const body = JSON.parse(rawBody);
const requestType = body.type;
const eventId = body.event_id;
const response = new Response('OK', { status: 200 });
// Check if the event is already processed
if (processedEvents.has(eventId)) {
console.log(`Event ${eventId} already processed. Skipping.`);
return response;
}
// Mark the event as processed before actual processing to avoid race conditions
processedEvents.add(eventId);
console.log(`Event ${eventId} added to processedEvents set.`);
if (requestType === 'url_verification') {
console.log('Handling url_verification request');
return new Response(body.challenge, { status: 200 });
}
if (await isValidSlackRequest(request, body)) {
// Process the event asynchronously
try {
if (requestType === 'event_callback') {
const eventType = body.event.type;
// Filter events only for 'app_mention'
if (eventType === 'app_mention') {
await processEvent(body);
console.log(`Event ${eventId} processing initiated.`);
}
}
} catch (error) {
console.error(`Error processing event ${eventId}:`, error);
}
return response;
}
console.log('Invalid Slack request');
return new Response('Invalid request', { status: 400 });
}
// Clear processed events set every hour to prevent memory leaks
setInterval(() => {
processedEvents.clear();
console.log('Cleared processed events set');
}, 60 * 60 * 1000);
New contributor
suleman khan is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.