Repository navigation
Expand file tree
/
Copy pathindex.js
More file actions
123 lines (103 loc) · 4.17 KB
/
Copy pathindex.js
File metadata and controls
123 lines (103 loc) · 4.17 KB
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
import { RTMSManager } from '../../library/javascript/rtmsManager/RTMSManager.js';
import {
buildUrlValidationResponse,
captureRawBody,
verifyZoomWebhookRequest
} from '../../library/javascript/webhookManager/zoomWebhookSignature.js';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
import express from 'express';
import { closeHttpServer, installGracefulShutdown } from '../../library/javascript/commonHelpers/gracefulShutdown.js';
import http from 'http';
import { chatWithClaude, claudeConfig, clearClaudeStream } from './chatWithClaude.js';
import { sanitizeProviderError } from '../../library/javascript/commonHelpers/providerRequestControls.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
dotenv.config({ path: path.join(__dirname, '.env'), quiet: true });
const { MEDIA_PARAMS } = RTMSManager;
const appConfig = {
port: process.env.PORT || 3000,
webhookPath: process.env.WEBHOOK_PATH || '/webhook'
};
const rtmsConfig = {
logging: {
enabled: true,
logDir: path.join(__dirname, 'logs'),
console: true
},
mediaTypes: RTMSManager.MEDIA.TRANSCRIPT,
credentials: {
meeting: {
clientId: process.env.ZOOM_CLIENT_ID,
clientSecret: process.env.ZOOM_CLIENT_SECRET,
zoomSecretToken: process.env.ZOOM_SECRET_TOKEN,
}
},
mediaParams: {
transcript: {
contentType: MEDIA_PARAMS.MEDIA_CONTENT_TYPE_TEXT,
language: MEDIA_PARAMS.LANGUAGE_ID_ENGLISH,
}
}
};
console.log('[send_to_claude] App Configuration:', appConfig);
console.log('[send_to_claude] Provider Configuration:', { ...claudeConfig, apiKey: '[REDACTED]' });
console.log('[send_to_claude] RTMS Configuration:', RTMSManager.redactSecrets(rtmsConfig));
const app = express();
const server = http.createServer(app);
await RTMSManager.init(rtmsConfig);
app.use(appConfig.webhookPath, express.json({ verify: captureRawBody, limit: '1mb' }));
app.post(appConfig.webhookPath, (req, res) => {
const { event, payload } = req.body || {};
if (event === 'endpoint.url_validation' && payload?.plainToken) {
if (!process.env.ZOOM_SECRET_TOKEN) return res.status(500).json({ error: 'webhook_secret_not_configured' });
return res.json(buildUrlValidationResponse(payload.plainToken, process.env.ZOOM_SECRET_TOKEN));
}
const verification = verifyZoomWebhookRequest(req, process.env.ZOOM_SECRET_TOKEN, {
toleranceSeconds: process.env.WEBHOOK_TIMESTAMP_TOLERANCE_SECONDS
});
if (!verification.ok) {
const status = verification.reason === 'missing_webhook_secret_token' ? 500 : 401;
return res.status(status).json({ error: 'invalid_zoom_webhook' });
}
if (typeof event !== 'string' || !payload || typeof payload !== 'object') {
return res.status(400).json({ error: 'invalid_webhook_payload' });
}
res.once('finish', () => {
setImmediate(() => {
try {
console.log('[send_to_claude] Verified webhook event:', event);
RTMSManager.handleEvent(event, payload);
} catch (error) {
console.error('[send_to_claude] Deferred webhook processing failed:', error.message);
}
});
});
res.status(200).end();
});
RTMSManager.on('transcript', async ({ text, userName, streamId }) => {
console.log(`[TRANSCRIPT] ${userName}: ${text}`);
try {
const response = await chatWithClaude(text, streamId);
console.log('[Claude Response]:', response);
} catch (error) {
console.error('[Claude Error]', sanitizeProviderError('Claude', error));
}
});
RTMSManager.on('meeting.rtms_started', (payload) => {
console.log('[send_to_claude] RTMS Started:', payload.meeting_uuid);
});
RTMSManager.on('meeting.rtms_stopped', (payload) => {
console.log('[send_to_claude] RTMS Stopped:', payload.meeting_uuid);
clearClaudeStream(payload.rtms_stream_id);
});
await RTMSManager.start();
server.listen(appConfig.port, () => {
console.log(`[send_to_claude] Server listening on port ${appConfig.port}`);
console.log(`[send_to_claude] Webhook endpoint: http://localhost:${appConfig.port}${appConfig.webhookPath}`);
});
installGracefulShutdown({ name: 'send_to_claude', cleanup: async () => {
await closeHttpServer(server);
await RTMSManager.stop();
} });