Feature: ChatBot integration

This commit is contained in:
Joaquin
2026-07-13 15:18:05 +00:00
parent 6f6e3161ec
commit a1835f2f3e
4 changed files with 579 additions and 0 deletions
+149
View File
@@ -0,0 +1,149 @@
/**
* KiraBot proxy — forwards /api/kirabot/* to KiraStream backend
* authenticating on behalf of the current kiratv user.
*/
const express = require('express');
const router = express.Router();
const { requireAuth } = require('../auth');
const { sources } = require('../db');
const http = require('http');
const KIRASTREAM_BASE = process.env.KIRASTREAM_BASE_URL || 'http://10.10.10.234';
// Token cache: { userId: { token, expires } }
const _tokenCache = new Map();
async function _getKirastreamToken(user) {
const cached = _tokenCache.get(user.id);
if (cached && cached.expires > Date.now()) {
return cached.token;
}
// Find user's xtream source pointing to kirastream
let db;
try {
db = await require('../db').loadDb();
} catch (e) {
return null;
}
const src = db.sources.find(
(s) => s.ownerId === user.id && s.type === 'xtream' && s.username && s.password
);
if (!src) return null;
// POST form to /api/user/auth/token
return new Promise((resolve) => {
const body = `username=${encodeURIComponent(src.username)}&password=${encodeURIComponent(src.password)}`;
const url = new URL(`${KIRASTREAM_BASE}/api/user/auth/token`);
const options = {
hostname: url.hostname,
port: url.port || 80,
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(body),
},
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
try {
const json = JSON.parse(data);
if (json.access_token) {
_tokenCache.set(user.id, {
token: json.access_token,
expires: Date.now() + 25 * 60 * 1000, // 25 min
});
resolve(json.access_token);
} else {
resolve(null);
}
} catch (e) {
resolve(null);
}
});
});
req.on('error', () => resolve(null));
req.write(body);
req.end();
});
}
async function _proxyToKirastream(req, res, path, method, body) {
const token = await _getKirastreamToken(req.user);
if (!token) {
return res.status(401).json({ error: 'No se pudo autenticar con KiraStream' });
}
const url = new URL(`${KIRASTREAM_BASE}/api/user/kirabot${path}`);
const bodyStr = body ? JSON.stringify(body) : '';
const options = {
hostname: url.hostname,
port: url.port || 80,
path: url.pathname + (url.search || ''),
method: method,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(bodyStr),
},
};
return new Promise((resolve) => {
const proxyReq = http.request(options, (proxyRes) => {
let data = '';
proxyRes.on('data', (chunk) => { data += chunk; });
proxyRes.on('end', () => {
try {
const json = JSON.parse(data);
res.status(proxyRes.statusCode).json(json);
} catch (e) {
res.status(proxyRes.statusCode).send(data);
}
resolve();
});
});
proxyReq.on('error', (err) => {
res.status(502).json({ error: 'Error de conexión con KiraStream', detail: err.message });
resolve();
});
if (bodyStr) proxyReq.write(bodyStr);
proxyReq.end();
});
}
router.use(requireAuth);
router.post('/chat', async (req, res) => {
await _proxyToKirastream(req, res, '/chat', 'POST', req.body);
});
router.get('/chat/history', async (req, res) => {
await _proxyToKirastream(req, res, '/chat/history', 'GET', null);
});
router.delete('/chat/history', async (req, res) => {
await _proxyToKirastream(req, res, '/chat/history', 'DELETE', null);
});
router.get('/status', async (req, res) => {
await _proxyToKirastream(req, res, '/status', 'GET', null);
});
router.get('/recordings', async (req, res) => {
const qs = req.query.limit ? `?limit=${req.query.limit}` : '';
await _proxyToKirastream(req, res, `/recordings${qs}`, 'GET', null);
});
router.post('/recordings', async (req, res) => {
await _proxyToKirastream(req, res, '/recordings', 'POST', req.body);
});
router.delete('/recordings/:id', async (req, res) => {
await _proxyToKirastream(req, res, `/recordings/${req.params.id}`, 'DELETE', null);
});
module.exports = router;