User: const { gpt, gptweb } = require("./API/gpt");
const { dalle } = require("./API/dalle");
const { gpt4o } = require("./API/gpt4o");
const { gpt3 } = require("./API/gpt3.5");
const { prompts } = require("./API/gpt-prompts");
const { blackbox } = require("./API/blackbox");
const { qwen } = require("./API/qwen");
const { gemini } = require("./API/gemini");

const WebSocket = require('ws');

const Bottleneck = require('bottleneck');

const { RateLimiterMemory } = require('rate-limiter-flexible');

// Объект для хранения действий и состояния каждого клиента
const clientActions = {};
// Объект для хранения ограничителей по каждому клиенту
const limiters = {}; 
// Замените на ваш домен
const allowedOrigin = 'http://gpt.loc'; 

// Настройки для ограничений по IP
const rateLimiter = new RateLimiterMemory({
    points: 20, // разрешается 10 запросов
    duration: 1, // за 1 секунду
	blockDuration: 60 // время блокировки в секундах
});

const wss = new WebSocket.Server({ port: 3000 });

wss.on('connection', (ws, req) => {
	
	const urlParams = new URLSearchParams(req.url.split('?')[1]);
    const userId = urlParams.get('token'); // Получаем UUID из URL
    const userIP = req.socket.remoteAddress;
	const userHeaders = req.headers;

	// Проверяем Origin
    if (!checkOrigin(userHeaders.origin, ws)) {
        return; // Если отклонено, выходим из обработчика
    }
	
	console.log('Client connected with user ID:', userId, 'Домен - ', userHeaders.origin);
	
	// Инициализируем состояние клиента, если его еще нет
    if (!clientActions[userId]) {
      clientActions[userId] = {
        actions: [], // Массив для хранения действий
        connected: true, // Статус подключения
      };
	  
	  // Создаем экземпляр Bottleneck для каждого клиента (ограничение на количество запросов по Id)
      limiters[userId] = new Bottleneck({
          maxConcurrent: 1, // Максимальное количество одновременных задач
          minTime: 1000, // Минимальное время между запросами в миллисекундах
		  highWater: 0 // Не разрешать в очереди больше 0 запросов (игнорировать)
      });
    }

    ws.on('message', (message) => {
		// Проверяем, является ли сообщение экземпляром Buffer
		if (Buffer.isBuffer(message))
			message = message.toString(); 
		try {
			// Преобразуем строку JSON обратно в объект
			message = JSON.parse(message); 
			//Получаем историю чата из запроса
			messages = message.messages;
			
			rateLimiter.consume(userIP)
                .then(() => {
					// Используем Bottleneck для управления количеством запросов
					limiters[userId].schedule(() => {
						// Отправка запроса на сервер чата
						return call(messages, ws);
					}).catch(error => {
						//console.error('Rate limit exceeded, message ignored:', error);
						const response = { code: 2, message: "Превышен лимит запросов. Пожалуйста, попробуйте позже." };
						// Отправка ответа клиенту
						sendMessage(ws, response);
					});
				})
                .catch(() => {
                    const response = { code: 2, message: "Вы временно заблокированы за превышение лимита запросов." };
                    // Отправка ответа клиенту
					sendMessage(ws, response);
                });
			
		} catch (error) {
				console.error('Error parsing JSON:', error);
				const response = { code: 2, message: "Произошла ошибка. Попробуйте еще раз."};
				console.log(response);
				// Отправка ответа клиенту
				sendMessage(ws, response);
		}
		
    });

    ws.on('close', () => {
		clientActions[userId].connected = false; // Обновление статуса подключения
		delete clientActions[userId]; // Удаление состояния клиента
		delete limiters[userId]; // Удаление ограничителя для данного клиента
        console.log('Client ',userId, ' disconnected');
    });
});


async function call(userMessage, ws) {
    try {
        let fullResponse = ""; // Переменная для результата
		
		console.log('Запрос клиента:', userMessage); 
		//throw new Error("Намеренная ошибка для тестирования catch");
        gpt4o({
            messages: userMessage,
            markdown: false
        }, (result) => {
			try {
				if (!result.finish) {
					// Запоминаем только последний ответ чата (он будет полный, промежуточные ответы - это накопления)
					fullResponse = result.message || "";
					//console.log(result);
				} else {
					// Если завершено, выводим полный ответ
					let response = { code: 1, message: fullResponse};
					console.log('Ответ чата: ', response);
					// Отправка ответа клиенту
					sendMessage(ws, response);
				}
			}
			catch (callbackError) {
                console.error('Error processing callback result:', callbackError);
                let response = { code: 2, message: "Произогла ошибка. Попробуйте еще раз." };
                // Отправка ответа клиенту
				sendMessage(ws, response);
            }
        });
    } catch (error) {
        console.error('Error:', error);
		let response = { code: 2, message: "Произогла ошибка. Попробуйте еще раз."};
		// Отправка ответа клиенту
		sendMessage(ws, response);
    }
}

console.log('WebSocket сервер запущен через порт 3000');


function checkOrigin(origin, socket) {
    if (origin !== allowedOrigin) {
        console.log('Недопустимый домен. Соединение разорвано: ', origin);
        socket.close(); // Закрываем соединение, если домен не разрешен
        return false; // Возвращаем false для индикатора отклонения
    }
    return true; // Возвращаем true, если домен разрешен
}

// Отправляет ответ клиенту
function sendMessage(ws, message) {
    if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify(message));
    } else {
        console.warn(`Trying to send message to a closed WebSocket connection.`);
    }
}

Какие есть библиотеки для ведения статистики работы этого кода, например, сколько пришло запросов за день, сколько запросов отправил каждый клиент, сколько подключений было за день и т.д.
Assistant:  ведения статистики работы вашего WebSocket сервера можно использовать различные библиотеки и подходы. Выбор зависит от того, какие именно метрики вам нужны и как вы планируете их хранить и анализировать. Вот несколько рекомендаций для систем и библиотек, которые могут вам помочь:

### 1. **Prometheus**
Prometheus — это система мониторинга и оповещения, которая позволяет собирать и хранить метрики. Вы можете собрать метрики, такие как количество запросов, количество подключений и т.д. Для интеграции вам понадобится библиотека `prom-client` для Node.js.

```bash
npm install prom-client
```

Пример использования:
```javascript
const client = require('prom-client');
const http = require('http');

// Создание метрик
const httpRequestCount = new client.Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests'
});

// Регистрация метрик
client.collectDefaultMetrics();

// Создание HTTP сервера для сбора метрик
const server = http.createServer((req, res) => {
  if (req.url === '/metrics') {
    res.setHeader('Content-Type', client.register.contentType);
    res.end(client.register.metrics());
  } else {
    res.end('Hello World!');
  }
});

// Увеличение счетчика запросов
wss.on('connection', (ws) => {
  httpRequestCount.inc();
});

// Запуск HTTP сервера
server.listen(3001, () => {
  console.log('Metrics server running on port 3001');
});
```

### 2. **Winston**
Если вам нужно логировать данные, такие как количество подключений или запросов, можно использовать библиотеку Winston. Это позволяет создать лог-файлы или отправлять данные в различные системы логирования.

```bash
npm install winston
```

Пример использования:
```javascript
const winston = require('winston');

// Настройка логгера
const logger = winston.createLogger({
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'statistic.log' }),
  ],
});

// Ведение статистики по запросам
let connectionCount = 0;

wss.on('connection', (ws) => {
    connectionCount++;
    logger.info(`New connection, total connections: ${connectionCount}`);
});

// Добавьте логирование для отслеживания запросов
ws.on('message', (message) => {
    logger.info(`Received message: ${message}`);
});
```

### 3. **MongoDB / PostgreSQL**
Для хранения детализированной статистики можно использовать базы данных, такие как MongoDB или PostgreSQL. Вы можете сохранять данные о клиентах, запросах, времени подключения и т.д. 

Пример скрипта для MongoDB:
```bash
npm install mongoose
```

```javascript
const mongoose = require('mongoose');

mongoose.connect('mongodb://localhost:27017/websocket_stats', { useNewUrlParser: true, useUnifiedTopology: true });

const requestSchema = new mongoose.Schema({
  userId: String,
  message: String,
  timestamp: { type: Date, default: Date.now }
});

const Request = mongoose.model('Request', requestSchema);

// Затем сохраняйте данные
ws.on('message', (message) => {
  const requestEntry = new Request({ userId: userId, message: message });
  requestEntry.save();
});
```

### 4. **ElasticSearch / Kibana**
Для более сложного анализа и визуализации вы можете использовать ElasticSearch для хранения данных и Kibana для визуализации. Это отличный выбор, если ваша система требует более сложных запросов и визуализаций.

Каждое из этих решений имеет свои преимущества и может быть адаптировано под конкретные нужды вашего приложения. Вы можете комбинировать их в зависимости от требований вашей системы мониторинга и статистики.
User: ElasticSearch как с эти раюотать
Assistant: