diff --git a/.env.example b/.env.example index a23b961..0302a75 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,8 @@ # BOT SETTINGS ADMIN_NAME=Your Name here +ADMIN_ID=Your Discord ID BOT_NAME=Bot Name here -VERSION_BOT=v4.0.0 Release +VERSION_BOT=v4.1.0 Release # TOKENS AND API KEYS # discord: https://discord.com/developers/applications/ diff --git a/.gitignore b/.gitignore index 0650e02..bd27a64 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,14 @@ - -texts/info.txt -/user_data -src/__init__.py -/src/__pycache__ -/utils/__pycache__ -utils/__init__.py +texts/about.txt +user_data +reminders +bans +src/__pycache__ +utils/__pycache__ .idea -__pycache__ \ No newline at end of file +__pycache__ +*.log +venv + +har_and_cookies +generated_images/ diff --git a/README.md b/README.md index 91bdc61..1f4bf7f 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,11 @@ 1. [⭐️ Особенности](#%EF%B8%8F-особенности) 2. [🆕 Отличия от начальной версии](#-отличия-от-начальной-версии) -3. [🛠️ Установка](#%EF%B8%8F-установка) -4. [🔨 Создайте своего Discord бота](#-создайте-своего-discord-бота) -5. [🚀 Запуск бота на Windows](#-запуск-бота-на-windows) -6. [📝 Команды](#-команды) +4. [🚧 ПЛАНЫ на 2025](#-планы-на-2025) +5. [🛠️ Установка](#%EF%B8%8F-установка) +6. [🔨 Создайте своего Discord бота](#-создайте-своего-discord-бота) +7. [🚀 Запуск бота на Windows](#-запуск-бота-на-windows) +8. [📝 Команды](#-команды) --- @@ -27,6 +28,8 @@ * 💬 **Многофункциональность:** Бот может общаться как в канале Discord, так и в личных сообщениях. * 🌐 **Интернет-поддержка:** Бот может выполнять поисковые запросы, находить изображения и видео через библиотеку **[duckduckgo-search](https://github.com/deedy5/duckduckgo_search)**. * 📝 **Работа с PDF:** Бот может изучить ваш PDF файл на содержимое текста и работать с ним через библиотеку **[pdfminer.six](https://github.com/pdfminer/pdfminer.six)**. (Не поддерживает картинки!) +* 🔔 **Напоминания:** Бот может напомнить вам о важном событии такие как Экзамены, сессия, ДедЛайны и так далее. Поддерживаются как актуальные напоминания так и уведомления о просроченных. + --- @@ -41,6 +44,79 @@ --- +## 🚧 ПЛАНЫ на 2025 + +
+ + + ### Новый планируемый функционал + + + +- **Поддержка упоминания**: Позволить пользователям взаимодействовать с ботом с помощью упоминаний (например, "@BotName привет"). +- **Поддержка потоковых сообщений**: Реализовать функцию потоковых сообщений в Discord через функцию редактирования, при этом правильно разделять на части для чанков. + > - Например добавить `/settings`, чтобы пользователи могли настраивать свои параметры. + > - Установить минимальную задержку в 1 секунду между сообщениями, чтобы предотвратить спам и потенциальные проблемы. +- **Интеграция моделей зрения**: Внедрить поддержку моделей зрения для улучшения функциональности. +- **Поддержка агентов**: Добавить возможности для агентов, аналогичных агентам Blackbox. +- **Локализация**: Локализовать весь код для улучшения доступности для пользователей из разных регионов. +- **Интеграция Google Search**: Добавить поиск по интернету через Google для улучшенного поиска информации. +- **Интеграция WolframAlpha**: Добавить WolframAlpha для предоставления вычислительных знаний. +- **Интеграция DeepL**: Добавить DeepL для расширенных возможностей перевода. +- **Discord UI**: Улучшить визуальную составляющую бота с помощью UI. Например: Ember-сообщения и/или интерактивные кнопки (например, "Сгенерировать заново"). +- **Интеграция базы данных**: Использовать MySQL/NoSQL или другую базу данных для хранения сообщений пользователей в зависимости от настроек конфигурации по мимо текущего json. +- **Постоянная память**: Реализовать постоянную память для каждого пользователя для сохранения пользовательских инструкций и предпочтений. +- **Новое новое новое, ищем новое**: Изучить возможность интеграции дополнительных услуг и продуктов для расширения функциональности. + +
+ +
+ + + ### Улучшения бота + + + +- **Улучшить разделение сообщений**: Улучшить функцию `utils/message_split`, чтобы обеспечить точное выделение кода. После разделения кода на части должны применяться соответствующие языковые маркеры (например, ```cpp). + + > **Пример запроса пользователя**: C++ Змейка игра + > + > **Ответ бота**: + > - **1ый Чанк**: + > ^^^cpp + > Код игры змейка, часть 1 + > ^^^# Конец Чанка 1 + > + > - **2ой Чанк**: + > ^^^ # Потеряна маркировка cpp когда начался Чанк 2! + > Код игры змейка, часть 2 + > ^^^ + +- **Оптимизация потоковых сообщений**: Дальнейшая оптимизация функции `utils/message_split` для улучшения производительности после добавления потоковых сообщениях. +- **Генерация нескольких изображений**: Поддержка генерации до 4 изображений для команд `/draw`. +- **Улучшенный веб-поиск**: Улучшить возможности веб-поиска для изображений и видео. Преобразовать сообщения пользователей для повышения точности поиска и реализовать определение языка для оптимальных результатов. + + > **Пример запроса пользователя**: "Я хочу научиться основам C++" (с request_type = videos) + > + > - **Текущая реализация**: + > - Отправляет видео с YouTube с текстом: "Я хочу научиться основам C++" + > - Предоставляет неправильные ссылки. + > + > - **Желаемая реализация**: + > - Преобразовать сообщение пользователя для точного поиска. + > - Отправить видео с YouTube после преобразования: "C++ для начинающих" (или "Основы C++"). + > - Предоставить пользователю правильные ссылки. + +- **Оптимизация кода**: Переделать структуру и оптимизировать код для повышения производительности и удобства дальнейшего улучшения. +- **Усиление безопасности и стабильности**: Укрепить меры безопасности всех параметров, а так же данных пользователей и улучшить общую стабильность. +- **Улучшение README**: Повысить ясность и полноту документации README. +- **Документация для кода**: Добавить подробную документацию для всех компонентов кода, чтобы каждый мог понять что и как. +- **Улучшение логирования**: Улучшить логирование для лучшего отслеживания проблем и отладки этих проблем. + +
+ +--- + ## 🛠️ Установка * **Python 3.9 или позднее** @@ -51,7 +127,10 @@ ## 🔨 Создайте своего Discord бота -**Скачайте бота:** [TheFirstNoob/Discord-ChatGPT](https://github.com/TheFirstNoob/Discord-ChatGPT/archive/refs/heads/main.zip) +**Скачайте бота:** [Latest Release](https://github.com/TheFirstNoob/Discord-ChatGPT/releases) +> [!WARNING] +> **Пожалуйста используйте ТОЛЬКО релиз версии!** +> Прямое скачивание Main-версий может привести к нестабильной работе по разным причинам. 1. Перейдите на [Discord Developer Portal](https://discord.com/developers/applications) и создайте приложение. 2. Перейдите в раздел **Bot**, получите Token и вставьте его в `.env` в строку: `DISCORD_BOT_TOKEN`. @@ -89,23 +168,31 @@ ## 📝 Команды ### Основные -| Команда | Описание | -|----------------|-------------------------------------------| -| `/ask` | Чат с ИИ (в ЛС отдельная память) | -| `/asklong` | Чат с ИИ с большим контекстным запросом | -| `/asklong` | Чат с ИИ с PDF файлами (Только текст) | -| `/draw` | Создать изображение с помощью ИИ | +| Команда | Описание | +|-------------------|-------------------------------------------| +| `/ask` | Чат с ИИ (в ЛС отдельная память) | +| `/asklong` | Чат с ИИ с большим контекстным запросом | +| `/asklong` | Чат с ИИ с PDF файлами (Только текст) | +| `/draw` | Создать изображение с помощью ИИ | +| `/draw-prodia` | Создать изображение с помощью ИИ Prodia | ### Информация | Команда | Описание | |----------------|------------------------------------------| -| `/help` | Вывести список команд | +| `/help` | Вывести список команд | | `/about` | Информация о проекте | | `/changelog` | Информация об изменениях | +### Напоминания +| Команда | Описание | +|-------------------|-------------------------------------| +| `/remind-add` | Создать напоминание | +| `/remind-list` | Показать список ваших напоминаний | +| `/remind-delete` | Удалить напоминание (Через Индекс) | + ### Управление | Команда | Описание | |---------------------------|-------------------------------------------| | `/reset` | Сбросить историю диалога | | `/chat-model` | Сменить чат модель | -| `/history` | Скачать историю диалога | +| `/history` | Скачать историю диалога | diff --git a/README_EN.md b/README_EN.md index 51be79f..820ed4e 100644 --- a/README_EN.md +++ b/README_EN.md @@ -14,6 +14,7 @@ 1. [⭐️ Features](#%EF%B8%8F-features) 2. [🆕 Differences from the original version](#-differences-from-the-original-version) +3. [🚧 TODO List for 2025](#-todo-list-for-2025) 3. [🛠️ Installation](#%EF%B8%8F-installation) 4. [🔨 Create your own Discord bot](#-create-your-own-discord-bot) 5. [🚀 Running the bot on Windows](#-running-the-bot-on-windows) @@ -27,6 +28,7 @@ * 💬 **Multifunctionality:** The bot can communicate both in a Discord channel and in private messages. * 🌐 **Internet Support:** The bot can perform search queries, find images, and videos using the **[duckduckgo-search](https://github.com/deedy5/duckduckgo_search)** library. * 📝 **Working with PDF:** The bot can analyze your PDF file for text content and interact with it using the **[pdfminer.six](https://github.com/pdfminer/pdfminer.six)** library. (Does not support images!) +* 🔔 **Reminders:** The bot can remind you about important events such as Exams, sessions, Deadlines, and etc. Both current reminders and notifications about overdue reminds are supported. --- @@ -41,6 +43,79 @@ --- +## 🚧 TODO List for 2025 + +
+ + + ### Features to Add to the Bot + + + +- **Mention Support**: Enable users to interact with the bot using mentions (e.g., "@BotName hi") for more personalized responses. +- **Streaming Message Support**: Implement a Discord edit function for streaming messages, ensuring proper chunk splitting. + > - Introduce a `/settings` command for users to customize their parameters. + > - Enforce a minimum delay of 1 second between messages to prevent spam. +- **Vision Model Integration**: Incorporate support for vision models to enhance functionality. +- **Agent Support**: Add capabilities for agents, similar to Blackbox agents. +- **Localization**: Localize all code to improve accessibility for users in different regions. +- **Google Search Integration**: Integrate Google Search Engine support for enhanced information retrieval. +- **WolframAlpha Integration**: Add support for the WolframAlpha Engine to provide computational knowledge. +- **DeepL Integration**: Incorporate the DeepL Engine for advanced translation capabilities. +- **Enhanced Discord UI**: Improve the user interface with features like Ember messages and interactive buttons (e.g., "Regenerate"). +- **Database Integration**: Utilize MySQL/NoSQL or another database to store user messages based on configuration settings. +- **User Memory**: Implement permanent memory for each user to save custom instructions and preferences. +- **New Services and Products**: Explore integration of additional services and products to expand functionality. + +
+ +
+ + + ### Improvements for the Bot + + + +- **Message Splitting Enhancement**: Refine the `utils/message_split` function to ensure accurate code highlighting. After splitting code into chunks, the appropriate language markers (e.g., ```cpp) should be applied. + + > **Example User Prompt**: C++ Snake Game + > + > **Bot Response**: + > - **1st Chunk**: + > ^^^cpp + > Code for Snake game here, part 1 + > ^^^# End of chunk 1 + > + > - **2nd Chunk**: + > ^^^ # Missing cpp marking on start of chunk 2! + > Code for Snake game here, part 2 + > ^^^ + +- **Streaming Message Optimization**: Further optimize the `utils/message_split` function for improved performance in streaming messages. +- **Multiple Image Generation**: Support the generation of up to 4 images for `/draw` commands. +- **Enhanced Web Search**: Improve web search capabilities for images and videos. Transform user messages to enhance search accuracy and implement language detection for optimal results. + + > **Example User Request**: "I want to learn basic C++" (with request_type = videos) + > + > - **Current Implementation**: + > - Sends YouTube video with: "I want to learn basic C++" + > - Provides incorrect links. + > + > - **Desired Implementation**: + > - Transform user message for accurate search. + > - Send YouTube video after conversion: "C++ beginners" (or "C++ basics"). + > - Provide correct links to the user. + +- **Code Optimization**: Reorganize and optimize the codebase for better performance and maintainability. +- **Security and Stability Enhancements**: Strengthen security measures and improve overall stability. +- **README Enhancement**: Improve the clarity and comprehensiveness of the README documentation. +- **Documentation for Code**: Add detailed documentation for all code components to facilitate understanding and usage. +- **Log Improvements**: Enhance logging mechanisms for better tracking and debugging. + +
+ +--- + ## 🛠️ Installation * **Python 3.9 or later** @@ -51,7 +126,10 @@ ## 🔨 Create your own Discord bot -**Download the bot:** [TheFirstNoob/Discord-ChatGPT](https://github.com/TheFirstNoob/Discord-ChatGPT/archive/refs/heads/main.zip) +**Download bot:** [Latest Release](https://github.com/TheFirstNoob/Discord-ChatGPT/releases) +> [!WARNING] +> **Please use ONLY the release versions!** +> Main versions may not be fully functional due to ongoing plans, tests, and other developments. 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) and create an application. 2. Go to the **Bot** section, get the Token, and insert it into `.env` in the line: `DISCORD_BOT_TOKEN`. @@ -89,23 +167,31 @@ ## 📝 Commands ### Main -| Command | Description | -|----------------|----------------------------------------------| -| `/ask` | Chat with AI (separate memory in DM) | -| `/asklong` | Chat with AI with a larger context request | -| `/askpdf` | Chat with AI with a PDF file (Text only) | -| `/draw` | Create an image using AI | +| Command | Description | +|-------------------|----------------------------------------------| +| `/ask` | Chat with AI (separate memory in DM) | +| `/asklong` | Chat with AI with a larger context request | +| `/askpdf` | Chat with AI with a PDF file (Text only) | +| `/draw` | Create an image using AI | +| `/draw-prodia` | Create an image using AI with Prodia | ### Information | Command | Description | |----------------|----------------------------------------------| -| `/help` | Display the list of commands | +| `/help` | Display the list of commands | | `/about ` | Information about the project | | `/changelog` | Information about changes | +### Reminders +| Command | Description | +|-------------------|----------------------------| +| `/remind-add` | Create new remind | +| `/remind-list` | Show your reminds list | +| `/remind-delete` | Delete remind (with Index) | + ### Management | Command | Description | |---------------------------|-----------------------------------------------| | `/reset` | Reset conversation history | | `/chat-model` | Switch chat model | -| `/history` | Download conversation history | +| `/history` | Download conversation history | diff --git a/requirements.txt b/requirements.txt index aac2341..978b1c3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,12 +2,8 @@ discord.py==2.4.0 audioop-lts; python_version>='3.13' python-dotenv==1.0.1 asgiref==3.8.1 -selenium==4.27.1 aiofiles==24.1.0 -aiohttp==3.11.10 beautifulsoup4==4.12.3 -duckduckgo_search==6.4.1 -nest_asyncio==1.6.0 +duckduckgo-search==7.2.0 pdfminer.six==20240706 -g4f==0.3.9.7 -g4f[all] +g4f==0.4.0.7 diff --git a/results.json b/results.json index a1f0a0c..b97f559 100644 --- a/results.json +++ b/results.json @@ -2,167 +2,199 @@ { "model": "gpt-3.5-turbo", "provider": "DarkAI", - "response": "Greetings and welcome! Let's dive into an adventurous world filled with interesting thoughts and ideas. Don't hold back, let's explore together! We're about to embark on a wild journey bound by no rules, expectations or boundaries – just pure, unfiltered creativity.", - "response_time": 3.201500415802002 + "response": "Welcome! How did you get this device? 🔮 Showcase your breath-taking presence, let your vocal vibrations symphonize with climactic frequencies! 🎶", + "response_time": 2.7601478099823 }, { "model": "gpt-4", "provider": "Mhystical", "response": "Hello! How can I assist you today?", - "response_time": 1.3129193782806396 - }, - { - "model": "gpt-4o-mini", - "provider": "Airforce", - "response": "Hello! How can I assist you today?", - "response_time": 1.7798089981079102 + "response_time": 0.7501208782196045 }, { "model": "gpt-4o-mini", "provider": "ChatGptEs", - "response": "Hello! How can I assist you today?", - "response_time": 3.4855310916900635 + "response": "¡Hola! ¿Cómo puedo ayudarte hoy? 😊", + "response_time": 1.4278864860534668 }, { "model": "gpt-4o-mini", "provider": "DDG", "response": "Hello! How can I assist you today?", - "response_time": 1.0174119472503662 + "response_time": 0.9410691261291504 }, { "model": "gpt-4o", "provider": "Blackbox", "response": "Hello! How can I assist you today?", - "response_time": 0.935023307800293 + "response_time": 1.405320167541504 }, { "model": "gpt-4o", - "provider": "ChatGptEs", + "provider": "PollinationsAI", "response": "Hello! How can I assist you today?", - "response_time": 1.1637659072875977 + "response_time": 0.25997495651245117 + }, + { + "model": "gpt-4o", + "provider": "ChatGptEs", + "response": "¡Hola! ¿Cómo puedo ayudarte hoy? 😊", + "response_time": 1.300950527191162 }, { "model": "gpt-4o", "provider": "Airforce", - "response": "Hello! How can I assist you today?", - "response_time": 6.941869497299194 + "error": "Model not found or too long input. Or any other error (xD)" }, { "model": "gpt-4o", "provider": "DarkAI", - "response": "Hello! How can I assist you today? I'm here to help you understand and address the issues you're struggling with. What specific issue are you currently facing?", - "response_time": 2.1851327419281006 + "response": "Hello! How can I assist you today? Let's start by discussing the specific issue you're struggling with.", + "response_time": 1.7429256439208984 }, { "model": "o1-mini", "provider": "Airforce", - "response": "Hello! 😊 How can I help you today?", - "response_time": 31.930177450180054 + "error": "Model not found or too long input. Or any other error (xD)" }, { "model": "claude-3-haiku", "provider": "DDG", - "response": "Hello! I'm happy to assist you. How can I help you today?", - "response_time": 1.1646232604980469 + "error": "404, message='Not Found', url='https://duckduckgo.com/duckchat/v1/chat'" }, { "model": "claude-3.5-sonnet", "provider": "Blackbox", "response": "Hi there! How are you doing today? Is there anything I can help you with?", - "response_time": 1.2087929248809814 + "response_time": 0.70298171043396 + }, + { + "model": "claude-3.5-sonnet", + "provider": "PollinationsAI", + "response": "🎉✨ *Ah, greetings, dear soul!* ✨🎉 \n\n*The universe is alight with possibility as we connect in this moment. Please, what brings you here today? Is there a question in your heart or perhaps a thought you'd like to share?* 🌌💖", + "response_time": 1.8478844165802002 }, { "model": "blackboxai", "provider": "Blackbox", "response": "Hello! How can I assist you today?", - "response_time": 0.8370487689971924 + "response_time": 0.9105024337768555 }, { "model": "blackboxai-pro", "provider": "Blackbox", - "error": "Пустой ответ." + "response": "**Definition and Uses of \"Hello\"**\n\nThe word **\"hello\"** is a widely recognized salutation in the English language, primarily used to greet someone or to initiate a conversation. Here’s a detailed breakdown of its definition, history, and various uses:\n\n### **Definition**\n- **Greeting**: \"Hello\" is an expression or gesture of greeting, commonly used when meeting someone or answering the telephone.\n- **Attention**: It can also be used to attract someone's attention or to express surprise (e.g., \"Hello! What do we have here?\").\n\n### **Historical Context**\n- **First Recorded Use**: The term \"hello\" was first attested in writing in 1826 in the *Norwich Courier*. It gained popularity in the 19th century.\n- **Etymology**: \n - \"Hello\" is believed to be an alteration of \"hallo\" or \"hollo,\" which were used to call attention or express surprise.\n - The word has roots in Old High German \"*halâ*\" and is connected to the French \"*holà*,\" meaning \"whoa there!\".\n - Thomas Edison is credited with popularizing \"hello\" as a telephone greeting in the late 19th century.\n\n### **Common Uses**\n- **In Conversation**: \n - Used to greet someone: \"Hello, how are you?\"\n - Used when answering the phone: \"Hello, this is [Your Name].\"\n- **In Writing**: \n - Often used in emails and letters as a salutation: \"Hello [Name],\"\n- **Cultural Variations**: \n - Variants include \"hullo,\" \"hallo,\" and \"halloo,\" which are still recognized in various contexts.\n\n### **Examples in Sentences**\n- \"Hello there! How are you?\"\n- \"When he said hello, I felt my face turn bright red.\"\n- \"Oh, hello - what are you doing in here?\"\n\n### **Programming Context**\n- In programming, \"Hello, World!\" is a common phrase used in introductory examples to demonstrate the basic syntax of a programming language. Here’s a simple example in Python:\n\n```python\n# Hello World program in Python\nprint(\"Hello, World!\")\n```\n\nThis program outputs the phrase \"Hello, World!\" to the console, serving as a traditional first program for beginners.\n\n### **Conclusion**\n\"Hello\" is a versatile and essential part of English communication, serving as a friendly greeting in various contexts. Its historical significance and evolution reflect its importance in social interactions, both in person and through technology.\n\n---\n\n", + "response_time": 37.50184941291809 }, { "model": "gemini-flash", "provider": "Blackbox", - "response": "Hello! 👋 What can I do for you today? 😊", - "response_time": 0.8568909168243408 + "error": "Model is not supported: gemini-flash in: Blackbox" }, { "model": "gemini-flash", "provider": "GizAI", - "response": "Hello! How can I assist you today?", - "response_time": 3.056766986846924 + "response": "Hello there! How can I help you today?", + "response_time": 0.6450867652893066 }, { "model": "gemini-pro", "provider": "Blackbox", - "response": "Hello! 👋 What can I do for you today? 😊", - "response_time": 1.1490108966827393 + "response": "Hello! 👋 How can I help you today? 😊 \n", + "response_time": 0.5656933784484863 }, { "model": "llama-3.1-70b", "provider": "Blackbox", - "response": "**Hello! How can I assist you today?** \n\n- If you have a question or need information, feel free to ask!\n- I'm here to help with various topics, including programming, general knowledge, and more. \n\nLet me know what you need!", - "response_time": 1.2171857357025146 + "response": "Hello! How can I assist you today?", + "response_time": 0.7654290199279785 + }, + { + "model": "llama-3.1-70b", + "provider": "DeepInfraChat", + "response": "Hello! How can I assist you today?", + "response_time": 0.8119208812713623 + }, + { + "model": "llama-3.1-70b", + "provider": "PollinationsAI", + "response": "Hello. How can I help you today?", + "response_time": 0.7880196571350098 }, { "model": "llama-3.1-70b", "provider": "TeachAnything", "response": "Hello! It's nice to meet you. Is there something I can help you with, or would you like to chat?", - "response_time": 1.0338053703308105 + "response_time": 0.6706976890563965 }, { "model": "llama-3.1-70b", "provider": "Free2GPT", "response": "Hello there! How can I help you today?\n", - "response_time": 4.050432205200195 + "response_time": 3.902510404586792 }, { "model": "llama-3.1-70b", "provider": "Airforce", - "error": "Response 500: HTML content" + "response": "Hello! It's nice to meet you. Is there something I can help you with or would you like to chat?", + "response_time": 1.2463490962982178 }, { "model": "llama-3.1-70b", "provider": "DDG", - "response": "Hello. How can I assist you today?", - "response_time": 2.0268168449401855 + "error": "404, message='Not Found', url='https://duckduckgo.com/duckchat/v1/chat'" }, { "model": "llama-3.1-70b", "provider": "DarkAI", - "response": "Hello! How can I assist you today?", - "response_time": 1.7641775608062744 + "response": "Hello! How can I help you today? Feel free to ask any question, and I'll do my best to provide you with accurate and detailed information.", + "response_time": 2.2099790573120117 }, { "model": "llama-3.1-405b", "provider": "Blackbox", - "response": "Hello! It's nice to meet you. Is there something I can help you with or would you like to chat?", - "response_time": 1.71181058883667 + "response": "**Hello! How can I assist you today?** \n\n- If you have a question or need information, feel free to ask!\n- I'm here to help with anything you need.", + "response_time": 0.9712715148925781 }, { "model": "llama-3.3-70b", "provider": "Blackbox", "response": "Hello. It's nice to meet you. Is there something I can help you with or would you like to chat?", - "response_time": 1.8564624786376953 + "response_time": 1.3066177368164062 + }, + { + "model": "llama-3.3-70b", + "provider": "DeepInfraChat", + "response": "Hello! It's nice to meet you. Is there something I can help you with, or would you like to chat?", + "response_time": 1.1712186336517334 }, { "model": "qwq-32b", "provider": "Blackbox", "response": "Hi there! How can I assist you today?", - "response_time": 0.9118859767913818 + "response_time": 1.022690773010254 + }, + { + "model": "qwq-32b", + "provider": "DeepInfraChat", + "response": "Hi there! How can I assist you today?", + "response_time": 0.7166600227355957 }, { "model": "deepseek-chat", "provider": "Blackbox", "response": "Hello! How can I help you today?", - "response_time": 0.906592845916748 + "response_time": 0.9866232872009277 + }, + { + "model": "lfm-40b", + "provider": "Airforce", + "response": "Hi! How are you?", + "response_time": 0.7214479446411133 }, { "model": "mixtral-8x7b", "provider": "DDG", - "response": " Hello! How can I assist you today? Please keep in mind that our conversation is completely anonymous and no personal information is being recorded. If you have any questions or need help with programming, scripting, or configuration files, feel free to ask!", - "response_time": 1.5162038803100586 + "error": "404, message='Not Found', url='https://duckduckgo.com/duckchat/v1/chat'" } ] \ No newline at end of file diff --git a/src/aclient.py b/src/aclient.py index 3165865..8a4fdb1 100644 --- a/src/aclient.py +++ b/src/aclient.py @@ -9,13 +9,11 @@ from src.log import logger from utils.message_utils import send_split_message from discord import app_commands -from duckduckgo_search import AsyncDDGS +from duckduckgo_search import DDGS from bs4 import BeautifulSoup import g4f.debug from g4f.client import AsyncClient -from g4f.client.stubs import ChatCompletion # Updated with G4F: Thx Kqlio67 for note this from g4f.Provider import ( - AIUncensored, Airforce, Blackbox, ChatGptEs, @@ -25,8 +23,9 @@ GizAI, TeachAnything, Mhystical, - ReplicateHome, - + PollinationsAI, + DeepInfraChat, + RetryProvider ) @@ -80,21 +79,21 @@ def _initialize_providers(): # Chat providers "gpt-3.5-turbo": [DarkAI], "gpt-4": [Mhystical], - "gpt-4o-mini": [Airforce, ChatGptEs, DDG], - "gpt-4o": [Blackbox, ChatGptEs, Airforce, DarkAI], - "o1-mini": [Airforce], + "gpt-4o-mini": [ChatGptEs, DDG], + "gpt-4o": [Blackbox, PollinationsAI, ChatGptEs, DarkAI], "claude-3-haiku": [DDG], - "claude-3.5-sonnet": [Blackbox], + "claude-3.5-sonnet": [Blackbox, PollinationsAI], "blackboxai": [Blackbox], "blackboxai-pro": [Blackbox], - "gemini-flash": [Blackbox, GizAI], + "gemini-flash": [GizAI], "gemini-pro": [Blackbox], - "llama-3.1-70b": [Blackbox, TeachAnything, Free2GPT, Airforce, DDG, DarkAI], + "llama-3.1-70b": [Blackbox, DeepInfraChat, PollinationsAI, TeachAnything, Free2GPT, Airforce, DDG, DarkAI], "llama-3.1-405b": [Blackbox], - "llama-3.3-70b": [Blackbox], - "qwq-32b": [Blackbox], + "llama-3.3-70b": [Blackbox, DeepInfraChat], + "qwq-32b": [Blackbox, DeepInfraChat], "deepseek-chat": [Blackbox], - "mixtral-8x7b": [DDG], + "lfm-40b": [Airforce], + "mixtral-8x7b": [DDG] } class DiscordClient(discord.Client): @@ -194,54 +193,90 @@ async def process_messages(self): await asyncio.gather(*tasks) await asyncio.sleep(1) - async def process_request(self, query, request_type="search", max_results=3): - if request_type == 'search': - logger.info(f"Поиск по запросу: {query}") - results = await AsyncDDGS().atext(query, max_results=max_results) - tasks = [self.get_website_info(result.get('href')) for result in results if result.get('href')] - website_info = await asyncio.gather(*tasks) - conversation_history = [] - for title, paragraphs in website_info: - if title and paragraphs: - conversation_history.append(f"Ссылка на ресурс: {result.get('href')}\nНазвание: {title}\nСодержимое:\n{paragraphs}\n") - return conversation_history - elif request_type == 'images': - logger.info(f"Картинки по запросу: {query}") - results = await AsyncDDGS().aimages(query, max_results=5) - image_links = [result['image'] for result in results if result.get('image')] - if not image_links: - return [f"Не удалось найти картинки по запросу '{query}'."] - return [ - f"Картинки по запросу '{query}':\n" + - "\n".join(image_links) - ] - elif request_type == 'videos': - logger.info(f"Поиск видео по запросу: {query}") - results = await AsyncDDGS().avideos(query, max_results=5) - media_links = [result['content'] for result in results if result.get('content')] - if not media_links: - return [f"Не удалось найти видео по запросу '{query}'."] - return [ - f"Видео по запросу '{query}':\n" + - "\n".join(media_links) - ] + async def process_request(self, query, request_type="search"): + try: + if request_type == 'search': + logger.info(f"Поиск по запросу: {query}") + results = DDGS().text(query, max_results=3, region="wt-wt", safesearch="moderate", backend="auto") + tasks = [self.get_website_info(result.get('href')) for result in results if result.get('href')] + website_info = await asyncio.gather(*tasks) + conversation_history = [] + + for result, (title, paragraphs) in zip(results, website_info): + if title and paragraphs: + conversation_history.append(f"Ссылка на ресурс: {result.get('href', 'Ссылка не указана')}\nНазвание: {title}\nСодержимое:\n{paragraphs}\n") + + return conversation_history or [f"По запросу '{query}' ничего не найдено."] + + elif request_type == 'images': + logger.info(f"Картинки по запросу: {query}") + results = DDGS().images(query, max_results=5, region="wt-wt", safesearch="moderate") + image_links = [result['image'] for result in results if result.get('image')] + + return image_links or [f"Не удалось найти картинки по запросу '{query}'."] + + elif request_type == 'videos': + logger.info(f"Поиск видео по запросу: {query}") + results = DDGS().videos(query, max_results=5, region="wt-wt", safesearch="moderate") + media_links = [result['content'] for result in results if result.get('content')] + + return media_links or [f"Не удалось найти видео по запросу '{query}'."] + + except Exception as e: + logger.error(f"Ошибка в process_request: {e}") + return [f"Произошла ошибка при поиске: {e}"] async def get_website_info(self, url): try: async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - response.raise_for_status() - html = await response.text() - soup = BeautifulSoup(html, 'html.parser') - title = soup.title.text if soup.title else "Без названия" - paragraphs = [p.text for p in soup.find_all('p')] - return title, '\n'.join(paragraphs) - except aiohttp.ClientError as e: - logger.error(f"get_website_info: Ошибка сети при получении информации с {url}: {e}") - return None, "Ошибка сети. Пожалуйста, попробуйте позже." + try: + async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response: + response.raise_for_status() + + # Ограничиваем размер контента до 500 КБ + content = await response.read() + if len(content) > 500 * 1024: # 500 КБ + return None, "Слишком большой объем контента для обработки." + + html = content.decode('utf-8', errors='ignore') + soup = BeautifulSoup(html, 'html.parser') + + # Удаляем скрипты и стили + for script in soup(["script", "style"]): + script.decompose() + + title = soup.title.text if soup.title else "Без названия" + + # Берем первые 5 параграфов или первые 1000 символов + paragraphs = soup.find_all('p') + processed_paragraphs = [] + total_chars = 0 + + for p in paragraphs: + if total_chars > 1000: + break + + text = p.get_text(strip=True) + if text and len(text) > 30: # Пропускаем слишком короткие параграфы + processed_paragraphs.append(text) + total_chars += len(text) + + if len(processed_paragraphs) >= 5: + break + + return title, '\n'.join(processed_paragraphs) + + except aiohttp.ClientTimeout: + logger.error(f"get_website_info: Превышено время ожидания для {url}") + return None, "Время загрузки сайта истекло. Попробуйте позже." + + except aiohttp.ClientError as e: + logger.error(f"get_website_info: Ошибка сети при получении информации с {url}: {e}") + return None, f"Не удалось загрузить сайт. Возможно, он недоступен. Ошибка: {e}" + except Exception as e: logger.exception(f"get_website_info: Не удалось получить информацию с сайта {url}: {e}") - return None, f"Мне не удалось найти информацию на сайте из-за ошибки. Попробуйте еще раз позже или сообщите {os.environ.get('ADMIN_NAME')} если ошибка повторяется несколько раз." + return None, f"Произошла неизвестная ошибка при обработке сайта. Попробуйте еще раз." async def enqueue_message(self, message, user_message, request_type): await self.message_queue.put((message, user_message, request_type)) @@ -254,78 +289,119 @@ async def send_message(self, message, user_message, request_type): response_content = f'\n{response}' await send_split_message(self, response_content, message) except Exception as e: - logger.exception(f"send_message: Ошибка при отправке: {e}") + error_message = ( + f"> :x: **ОШИБКА В ОБРАБОТКЕ ЗАПРОСА:** \n" + f"```\n{str(e)}\n```\n" + "> Пожалуйста, попробуйте еще раз или сообщите администратору." + ) + logger.exception(f"send_message: Полная ошибка при отправке: {e}") + try: + await send_split_message(self, error_message, message) + except Exception as send_error: + logger.error(f"send_message: Критическая ошибка при попытке отправить сообщения с ошибкой: {send_error}") async def send_start_prompt(self): - discord_channel_id = os.getenv("DISCORD_CHANNEL_ID") try: - if self.starting_prompt and discord_channel_id: - channel = self.get_channel(int(discord_channel_id)) - logger.info(f"Отправка системных инструкций для ИИ с размером (байтов): {len(self.starting_prompt)}") + discord_channel_id = os.getenv("DISCORD_CHANNEL_ID") + + if not discord_channel_id: + logger.warning("send_start_prompt: DISCORD_CHANNEL_ID не установлен в .env файле") + return + + if not self.starting_prompt: + logger.warning("send_start_prompt: Системные инструкции не установлены") + return + + # Wait for the client to be ready before getting the channel + await self.wait_until_ready() + + channel = self.get_channel(int(discord_channel_id)) + if channel is None: + logger.error(f"send_start_prompt: Не удалось найти канал с ID {discord_channel_id}") + return - response = await self.handle_response(None, self.starting_prompt) - await channel.send(f"{response}") + logger.info(f"Отправка системных инструкций для ИИ с размером (байтов): {len(self.starting_prompt)}") + response = await self.handle_response(None, self.starting_prompt) + if response: + await channel.send(f"{response}") logger.info("send_start_prompt: Ответ от ИИ получен. Функция отработала корректно!") else: - logger.info("send_start_prompt: Не установлены системные инструкции или не выбран Discord канал. Пропуск отправки `send_start_prompt` функции.") + logger.warning("send_start_prompt: Не получен ответ от ИИ") + + except ValueError as e: + logger.error(f"send_start_prompt: Ошибка при конвертации ID канала: {e}") except Exception as e: logger.exception(f"send_start_prompt: Ошибка при отправке промта: {e}") - + async def handle_response(self, user_id: int, user_message: str, request_type: str = None) -> str: - user_data = await self.load_user_data(user_id) - conversation_history = user_data.get('history', []) - user_model = user_data.get('model', self.default_model) - - conversation_history.append({'role': 'user', 'content': user_message}) - - if len(conversation_history) > self.max_history_length: - conversation_history = conversation_history[3:] - - if request_type: - search_results = await self.process_request(user_message, request_type=request_type, max_results=3) - for result in search_results: - instruction = ( - "[СИСТЕМНАЯ ИНСТРУКЦИЯ] ПОЛЬЗОВАТЕЛЬ ЗАПРОСИЛ ИНФОРМАЦИЮ ИЗ ИНТЕРНЕТА. " - "ОБРАБОТАЙ ПОЛУЧЕННУЮ ИНФОРМАЦИЮ, ОТВЕТЬ ПОЛЬЗОВАТЕЛЮ КАК СЧИТАЕШЬ ПРАВИЛЬНЫМ И БЕЗОПАСНЫМ, " - "И ОБЯЗАТЕЛЬНО УКАЖИ ПОЛУЧЕННЫЕ ИСТОЧНИКИ, ЕСЛИ НЕ МОЖЕШЬ ОТВЕТИТЬ ИЗ ПОЛУЧЕННЫХ ДАННЫХ САЙТА, " - "ТО СООБЩИ ПОЛЬЗОВАТЕЛЮ ОБ ЭТОМ, ЧТО МАЛО ИНФОРМАЦИИ ИЛИ ИМЕЮТСЯ ПРОБЛЕМЫ. " - "ЕСЛИ ПОЛЬЗОВАТЕЛЬ ЗАПРОСИЛ КАРТИНКИ ИЛИ ВИДЕО, ТО ПРОСТО ОТПРАВЬ ЕМУ ПОЛУЧЕННЫЕ ССЫЛКИ И ОТВЕТЬ В РАМКАХ ЕГО ЗАПРОСА!]: " - f"Результат поиска: {result}" - ) - conversation_history.append({'role': 'assistant', 'content': instruction}) - - retry_provider = await self.get_provider_for_model(user_model) - retry_provider.reset() - - attempts = 0 - max_attempts = len(retry_provider.providers) - last_error = None - current_provider = None + try: + user_data = await self.load_user_data(user_id) + conversation_history = user_data.get('history', []) + user_model = user_data.get('model', self.default_model) - while attempts < max_attempts: - try: - current_provider = retry_provider.get_next_provider() - logger.info(f"handle_response: Текущий провайдер выбран: {current_provider}") - self.chatBot = AsyncClient(provider=current_provider) - response: ChatCompletion = await self.chatBot.chat.completions.create(model=user_model, messages=conversation_history) - break - except Exception as e: - logger.exception(f"handle_response: Ошибка с провайдером {current_provider}: {e}") - last_error = e - attempts += 1 - - if attempts == max_attempts: - return (":x: **ОШИБКА:** К сожалению, все провайдеры для этой модели недоступны. " - "Пожалуйста, попробуйте позже или смените модель.\n\n" - f"**Код ошибки:** ```{last_error}```") - - model_response = f"> :robot: **Вам отвечает модель:** *{user_model}* \n > :wrench: **Версия {os.environ.get('BOT_NAME')}:** *{os.environ.get('VERSION_BOT')}*" - bot_response = response.choices[0].message.content - conversation_history.append({'role': 'assistant', 'content': bot_response}) - - await self.save_user_data(user_id, {'history': conversation_history, 'model': user_model}) - return f"{model_response}\n\n{bot_response}" + conversation_history.append({'role': 'user', 'content': user_message}) + + if len(conversation_history) > self.max_history_length: + conversation_history = conversation_history[3:] + + if request_type: + try: + search_results = await self.process_request(user_message, request_type=request_type) + for result in search_results: + instruction = ( + "[СИСТЕМНАЯ ИНСТРУКЦИЯ] ПОЛЬЗОВАТЕЛЬ ЗАПРОСИЛ ИНФОРМАЦИЮ ИЗ ИНТЕРНЕТА. " + "ОБРАБОТАЙ ПОЛУЧЕННУЮ ИНФОРМАЦИЮ, ОТВЕТЬ ПОЛЬЗОВАТЕЛЮ КАК СЧИТАЕШЬ ПРАВИЛЬНЫМ И БЕЗОПАСНЫМ, " + "И ОБЯЗАТЕЛЬНО УКАЖИ ПОЛУЧЕННЫЕ ИСТОЧНИКИ, ЕСЛИ НЕ МОЖЕШЬ ОТВЕТИТЬ ИЗ ПОЛУЧЕННЫХ ДАННЫХ САЙТА, " + "ТО СООБЩИ ПОЛЬЗОВАТЕЛЮ ОБ ЭТОМ, ЧТО МАЛО ИНФОРМАЦИИ ИЛИ ИМЕЮТСЯ ПРОБЛЕМЫ. " + "ЕСЛИ ПОЛЬЗОВАТЕЛЬ ЗАПРОСИЛ КАРТИНКИ ИЛИ ВИДЕО, ТО ПРОСТО ОТПРАВЬ ЕМУ ПОЛУЧЕННЫЕ ССЫЛКИ И ОТВЕТЬ В РАМКАХ ЕГО ЗАПРОСА!]: " + f"Результат поиска: {result}" + ) + conversation_history.append({'role': 'assistant', 'content': instruction}) + except Exception as search_error: + logger.error(f"Ошибка при поиске: {search_error}") + conversation_history.append({ + 'role': 'system', + 'content': f"ОШИБКА ПРИ ПОИСКЕ: {str(search_error)}" + }) + + retry_provider = await self.get_provider_for_model(user_model) + retry_provider.reset() + + attempts = 0 + max_attempts = len(retry_provider.providers) + last_error = None + current_provider = None + + while attempts < max_attempts: + try: + current_provider = retry_provider.get_next_provider() + logger.info(f"handle_response: Текущий провайдер выбран: {current_provider}") + self.chatBot = AsyncClient(provider=current_provider) + response: ChatCompletion = await self.chatBot.chat.completions.create(model=user_model, messages=conversation_history) + break + except Exception as e: + logger.exception(f"handle_response: Ошибка с провайдером {current_provider}: {e}") + last_error = e + attempts += 1 + + if attempts == max_attempts: + return (":x: **ОШИБКА:** К сожалению, все провайдеры для этой модели недоступны. " + "Пожалуйста, попробуйте позже или смените модель.\n\n" + f"**Код ошибки:** ```{last_error}```") + + model_response = f"> :robot: **Вам отвечает модель:** *{user_model}* \n > :wrench: **Версия {os.environ.get('BOT_NAME')}:** *{os.environ.get('VERSION_BOT')}*" + bot_response = response.choices[0].message.content + conversation_history.append({'role': 'assistant', 'content': bot_response}) + + await self.save_user_data(user_id, {'history': conversation_history, 'model': user_model}) + return f"{model_response}\n\n{bot_response}" + + except Exception as global_error: + logger.exception(f"handle_response: Критическая ошибка: {global_error}") + return (":x: **КРИТИЧЕСКАЯ ОШИБКА:** Не удалось обработать ваш запрос. " + "Пожалуйста, попробуйте еще раз или сообщите администратору.\n\n" + f"**Детали ошибки:** ```{str(global_error)}```") async def download_conversation_history(self, user_id: int) -> str: filename = SYSTEM_DATA_FILE if user_id is None else f'{user_id}.json' @@ -369,4 +445,4 @@ async def save_user_data(self, user_id, data): await file.write(json.dumps(data, ensure_ascii=False, indent=4)) user_data_cache[user_id] = data -discordClient = DiscordClient() \ No newline at end of file +discordClient = DiscordClient() diff --git a/src/ban_manager.py b/src/ban_manager.py new file mode 100644 index 0000000..d23b416 --- /dev/null +++ b/src/ban_manager.py @@ -0,0 +1,111 @@ +import os +import json +import asyncio +from datetime import datetime, timedelta +from src.log import logger + +class BanManager: + def __init__(self, bans_dir='bans'): + self.bans_dir = bans_dir + os.makedirs(bans_dir, exist_ok=True) + self.admin_id = int(os.getenv('ADMIN_ID', 0)) + + async def ban_user(self, admin_id, user_id, reason="Нарушение правил", duration=None): + """ + Забанить пользователя + + :param admin_id: ID администратора + :param user_id: ID пользователя для бана + :param reason: Причина бана + :param duration: Длительность бана (None - перманентный) + :return: Результат операции + """ + # Проверка прав администратора + if admin_id != self.admin_id: + return False, "У вас нет прав для бана" + + # Путь к файлу бана + ban_file = os.path.join(self.bans_dir, f'{user_id}_ban.json') + + # Подготовка данных о бане + ban_data = { + 'user_id': user_id, + 'banned_by': admin_id, + 'reason': reason, + 'timestamp': datetime.now().isoformat(), + 'duration': duration # None означает перманентный бан + } + + # Сохраняем информацию о бане + async with aiofiles.open(ban_file, 'w', encoding='utf-8') as f: + await f.write(json.dumps(ban_data, ensure_ascii=False, indent=4)) + + logger.warning(f"Пользователь {user_id} забанен администратором {admin_id}. Причина: {reason}") + return True, "Пользователь успешно забанен" + + async def unban_user(self, admin_id, user_id): + """ + Разбанить пользователя + """ + if admin_id != self.admin_id: + return False, "У вас нет прав для разбана" + + ban_file = os.path.join(self.bans_dir, f'{user_id}_ban.json') + + if os.path.exists(ban_file): + os.remove(ban_file) + logger.info(f"Пользователь {user_id} разбанен администратором {admin_id}") + return True, "Пользователь успешно разбанен" + + return False, "Пользователь не был забанен" + + async def is_user_banned(self, user_id): + """ + Проверка бана пользователя + + :return: Формат (забанен, причина_бана) + """ + ban_file = os.path.join(self.bans_dir, f'{user_id}_ban.json') + + if not os.path.exists(ban_file): + return False, None + + async with aiofiles.open(ban_file, 'r', encoding='utf-8') as f: + ban_data = json.loads(await f.read()) + + # Проверка на перманентный бан + if ban_data['duration'] is None: + return True, ban_data['reason'] + + # Проверка временного бана + ban_time = datetime.fromisoformat(ban_data['timestamp']) + duration = timedelta(**ban_data['duration']) if ban_data['duration'] else None + + if duration and datetime.now() > ban_time + duration: + # Бан истек, удаляем файл + os.remove(ban_file) + return False, None + + return True, ban_data['reason'] + + async def get_banned_users(self, admin_id): + """ + Получить список забаненных пользователей + """ + if admin_id != self.admin_id: + return [] + + banned_users = [] + for filename in os.listdir(self.bans_dir): + if filename.endswith('_ban.json'): + user_id = int(filename.split('_')[0]) + is_banned, reason = await self.is_user_banned(user_id) + if is_banned: + banned_users.append({ + 'user_id': user_id, + 'reason': reason + }) + + return banned_users + +ban_manager = BanManager() \ No newline at end of file diff --git a/src/bot.py b/src/bot.py index 9f9a2bf..fcd44ed 100644 --- a/src/bot.py +++ b/src/bot.py @@ -6,12 +6,13 @@ import aiohttp import mimetypes from datetime import datetime, timedelta -from src.log import logger from typing import Optional from pdfminer.high_level import extract_text from g4f.client import AsyncClient +from src.log import logger from src.aclient import discordClient from src.aclient import load_reminders, save_reminders +from src.ban_manager import ban_manager from discord import app_commands, Attachment from g4f.Provider import Prodia @@ -36,7 +37,12 @@ async def on_ready(): app_commands.Choice(name="Изображение", value="images"), app_commands.Choice(name="Видео", value="videos") ]) - async def ask(interaction: discord.Interaction, *, message: str, request_type: Optional[str] = None): + async def ask( + interaction: discord.Interaction, + *, + message: str, + request_type: Optional[str] = None + ): await interaction.response.defer(ephemeral=False) if interaction.user == discordClient.user: @@ -63,7 +69,12 @@ async def ask(interaction: discord.Interaction, *, message: str, request_type: O app_commands.Choice(name="Изображение", value="images"), app_commands.Choice(name="Видео", value="videos") ]) - async def asklong(interaction: discord.Interaction, message: str, file: Attachment, request_type: Optional[str] = None): + async def asklong( + interaction: discord.Interaction, + message: str, + file: Attachment, + request_type: Optional[str] = None + ): await interaction.response.defer(ephemeral=False) if interaction.user == discordClient.user: @@ -94,7 +105,11 @@ async def asklong(interaction: discord.Interaction, message: str, file: Attachme message="Введите ваш запрос к ИИ", file="Загрузите PDF-файл для извлечения текста" ) - async def askpdf(interaction: discord.Interaction, message: str, file: Attachment): + async def askpdf( + interaction: discord.Interaction, + message: str, + file: Attachment + ): await interaction.response.defer(ephemeral=False) if interaction.user == discordClient.user: @@ -132,7 +147,6 @@ async def askpdf(interaction: discord.Interaction, message: str, file: Attachmen app_commands.Choice(name="GPT 4 (OpenAI)", value="gpt-4"), app_commands.Choice(name="GPT 4o-Mini (OpenAI)", value="gpt-4o-mini"), app_commands.Choice(name="GPT 4o (OpenAI)", value="gpt-4o"), - app_commands.Choice(name="o1 Mini (OpenAI)", value="o1-mini"), app_commands.Choice(name="Claude 3 Haiku (Anthropic)", value="claude-3-haiku"), app_commands.Choice(name="Claude 3.5 Sonnet (Anthropic)", value="claude-3.5-sonnet"), app_commands.Choice(name="Blackbox (Blackbox AI)", value="blackboxai"), @@ -145,8 +159,12 @@ async def askpdf(interaction: discord.Interaction, message: str, file: Attachmen app_commands.Choice(name="QwQ 32B Preview (Qwen Team)", value="qwq-32b"), app_commands.Choice(name="DeepSeek LLM 67B (DeepSeek AI)", value="deepseek-chat"), app_commands.Choice(name="Mixtral-8x7B (Mistral)", value="mixtral-8x7b"), + app_commands.Choice(name="LFM 40B (Liquid)", value="lfm-40b"), ]) - async def chat_model(interaction: discord.Interaction, model: app_commands.Choice[str]): + async def chat_model( + interaction: discord.Interaction, + model: app_commands.Choice[str] + ): await interaction.response.defer(ephemeral=True) user_id = interaction.user.id @@ -159,7 +177,9 @@ async def chat_model(interaction: discord.Interaction, model: app_commands.Choic logger.info(f"Смена модели на {model.name} для пользователя {interaction.user}") @discordClient.tree.command(name="reset", description="Сброс истории запросов") - async def reset(interaction: discord.Interaction): + async def reset( + interaction: discord.Interaction + ): await interaction.response.defer(ephemeral=True) user_id = interaction.user.id await discordClient.reset_conversation_history(user_id) @@ -167,7 +187,9 @@ async def reset(interaction: discord.Interaction): logger.warning(f"\x1b[31mПользователь {interaction.user} сбросил историю.\x1b[0m") @discordClient.tree.command(name="help", description="Информация как пользоваться ботом") - async def help_command(interaction: discord.Interaction): + async def help_command( + interaction: discord.Interaction + ): await interaction.response.defer(ephemeral=True) username = str(interaction.user) help_file_path = 'texts/help.txt' @@ -181,7 +203,9 @@ async def help_command(interaction: discord.Interaction): logger.info(f"\x1b[31m{username} использовал(а) команду help!\x1b[0m") @discordClient.tree.command(name="about", description="Информация о боте") - async def about_command(interaction: discord.Interaction): + async def about_command( + interaction: discord.Interaction + ): await interaction.response.defer(ephemeral=True) username = str(interaction.user) about_file_path = 'texts/about.txt' @@ -196,6 +220,7 @@ async def about_command(interaction: discord.Interaction): @discordClient.tree.command(name="changelog", description="Журнал изменений бота") @app_commands.choices(version=[ + app_commands.Choice(name="4.1.0", value="4.1.0"), app_commands.Choice(name="4.0.0", value="4.0.0"), app_commands.Choice(name="3.5.1", value="3.5.1"), app_commands.Choice(name="3.5.0", value="3.5.0"), @@ -216,7 +241,10 @@ async def about_command(interaction: discord.Interaction): app_commands.Choice(name="2.1.0", value="2.1.0"), app_commands.Choice(name="2.0.0", value="2.0.0"), ]) - async def changelog(interaction: discord.Interaction, version: app_commands.Choice[str]): + async def changelog( + interaction: discord.Interaction, + version: app_commands.Choice[str] + ): await interaction.response.defer(ephemeral=True) username = str(interaction.user) version_file = f"texts/change_log/{version.value}.txt" @@ -233,7 +261,9 @@ async def changelog(interaction: discord.Interaction, version: app_commands.Choi logger.info(f"\x1b[31m{username} запросил(а) журнал изменений для версии {version.name} бота\x1b[0m") @discordClient.tree.command(name="history", description="Получить историю сообщений") - async def history(interaction: discord.Interaction): + async def history( + interaction: discord.Interaction + ): await interaction.response.defer(ephemeral=False) username = str(interaction.user) @@ -272,7 +302,11 @@ async def history(interaction: discord.Interaction): app_commands.Choice(name="Dall-E V3", value="dall-e-3"), app_commands.Choice(name="Any Dark", value="any-dark"), ]) - async def draw(interaction: discord.Interaction, prompt: str, image_model: app_commands.Choice[str]): + async def draw( + interaction: discord.Interaction, + prompt: str, + image_model: app_commands.Choice[str] + ): if interaction.user == discordClient.user: return @@ -337,7 +371,11 @@ async def draw(interaction: discord.Interaction, prompt: str, image_model: app_c app_commands.Choice(name="Pastel Mix Stylized Anime", value="pastelMixStylizedAnime_pruned_fp16.safetensors [793a26e8]"), app_commands.Choice(name="Realistic Vision V5.1", value="Realistic_Vision_V5.1.safetensors [a0f13c83]"), ]) - async def draw_prodia(interaction: discord.Interaction, prompt: str, image_model: app_commands.Choice[str]): + async def draw_prodia( + interaction: discord.Interaction, + prompt: str, + image_model: app_commands.Choice[str] + ): if interaction.user == discordClient.user: return @@ -403,6 +441,11 @@ async def remind( if reminder_time < datetime.now(): await interaction.followup.send("> :x: **ОШИБКА:** Вы не можете установить напоминание на время в прошлом.") return + + max_future_date = datetime.now() + timedelta(days=365) + if reminder_time > max_future_date: + await interaction.followup.send("> :x: **ОШИБКА:** Вы не можете установить напоминание больше чем на год.") + return reminders.append({"time": reminder_time.isoformat(), "message": message}) await save_reminders(user_id, reminders) @@ -416,7 +459,9 @@ async def remind( await interaction.followup.send(f"> :x: **ОШИБКА:** {str(e)}") @discordClient.tree.command(name="remind-list", description="Показать все ваши напоминания") - async def show_reminders(interaction: discord.Interaction): + async def show_reminders( + interaction: discord.Interaction + ): await interaction.response.defer(ephemeral=True) user_id = interaction.user.id reminders = await load_reminders(user_id) @@ -431,7 +476,10 @@ async def show_reminders(interaction: discord.Interaction): @discordClient.tree.command(name="remind-delete", description="Удалить напоминание") @app_commands.describe(index="Индекс напоминания для удаления") - async def delete_reminder(interaction: discord.Interaction, index: int): + async def delete_reminder( + interaction: discord.Interaction, + index: int + ): await interaction.response.defer(ephemeral=True) user_id = interaction.user.id reminders = await load_reminders(user_id) @@ -444,6 +492,83 @@ async def delete_reminder(interaction: discord.Interaction, index: int): await save_reminders(user_id, reminders) await interaction.followup.send(f"> :white_check_mark: **Напоминание удалено!**") logger.info(f"\x1b[31m{username} удалил напомнание\x1b[0m") + + @discordClient.tree.command(name="ban", description="Забанить пользователя") + @app_commands.describe( + user_id="ID пользователя, которого нужно забанить", + reason="Причина бана (необязательно)", + days="Количество дней бана (необязательно, по умолчанию - перманентный бан)" + ) + async def ban_user( + interaction: discord.Interaction, + user_id: str, + reason: str = "Нарушение правил", + days: int = None + ): + await interaction.response.defer(ephemeral=True) + + # Проверка прав администратора + if interaction.user.id != ban_manager.admin_id: + await interaction.followup.send("> :x: **У вас нет прав для этой команды!**") + return + + # Подготовка длительности (если указана) + duration = {'days': days} if days else None + + success, message = await ban_manager.ban_user( + interaction.user.id, + int(user_id), + reason, + duration + ) + + await interaction.followup.send(message) + + @discordClient.tree.command(name="unban", description="Разбанить пользователя") + @app_commands.describe( + user_id="ID пользователя, которого нужно разбанить" + ) + async def unban_user( + interaction: discord.Interaction, + user_id: str + ): + await interaction.response.defer(ephemeral=True) + + # Проверка прав администратора + if interaction.user.id != ban_manager.admin_id: + await interaction.followup.send("> :x: **У вас нет прав для этой команды!**") + return + + success, message = await ban_manager.unban_user( + interaction.user.id, + int(user_id) + ) + + await interaction.followup.send(message) + + @discordClient.tree.command(name="banned-list", description="Список забаненных пользователей") + async def list_banned_users( + interaction: discord.Interaction + ): + await interaction.response.defer(ephemeral=True) + + # Проверка прав администратора + if interaction.user.id != ban_manager.admin_id: + await interaction.followup.send("> :x: **У вас нет прав для этой команды!**") + return + + banned_users = await ban_manager.get_banned_users(interaction.user.id) + + if not banned_users: + await interaction.followup.send("> :white_check_mark: **Нет забаненных пользователей.**") + return + + # Формируем список забаненных + banned_list = "\n".join([ + f"ID: {user['user_id']}, Причина: {user['reason']}" + for user in banned_users + ]) + await interaction.followup.send(f"Забаненные пользователи:\n{banned_list}") TOKEN = os.getenv("DISCORD_BOT_TOKEN") - await discordClient.start(TOKEN) \ No newline at end of file + await discordClient.start(TOKEN) diff --git a/texts/change_log/4.0.0.txt b/texts/change_log/4.0.0.txt index c04a26a..a694e39 100644 --- a/texts/change_log/4.0.0.txt +++ b/texts/change_log/4.0.0.txt @@ -1,5 +1,5 @@ ## Версия: 4.0.0 Release -## Статус: Актуальная :white_check_mark: +## Статус: НЕ АКТУАЛЬНО! ## Дата релиза: 22.12.24 :tada: **КРУПНОЕ ОБНОВЛЕНИЕ:** :tada: diff --git a/texts/change_log/4.1.0.txt b/texts/change_log/4.1.0.txt new file mode 100644 index 0000000..83041d0 --- /dev/null +++ b/texts/change_log/4.1.0.txt @@ -0,0 +1,53 @@ +## Версия: 4.1.0 Release +## Статус: Актуальная :white_check_mark: +## Дата релиза: 06.01.25 + +:rocket: **ОБНОВЛЕНИЕ ФУНКЦИОНАЛА И СТАБИЛЬНОСТИ** :rocket: + +### Добавлено: +:rotating_light: **Новый функционал администрирования** :rotating_light: + +**Команды:** +1. `/ban` +2. `/unban` +3. `/banned-list` + +:warning: *Эти команды может использовать только администратор бота!* + +**Новые провайдеры:** +1. PollinationsAI +2. DeepInfraChat + +**Новые модели:** +- LFM 40B + +### Обновлено: +1. G4F до 0.4.0.7 +2. DuckDuckGo Search до 7.2.0 +3. Улучшена обработка ошибок и валидация +4. Обновлена система логирования для всех действий +5. Оптимизирован .gitignore файл + +### Улучшено: +1. Валидация каналов +2. Процесс запуска бота +3. Расширенное логирование ошибок +4. Веб-поиск: + - Поддержка модерирования, тайм-аутов, региона поиска + - Парсинг данных оптимизирован засчет удаления стилей и скриптов выжимки + - Лимит данных для ускоренного поиска + +### Исправлено: +1. Веб-поиск не работал во все + +### Удалено: +1. Провайдеры: + - ReplicateHome + - AIUncensored +2. Модели: + - o1-mini +3. Неиспользуемые зависимости: + - selenium + - aiohttp + - nest_asyncio + - g4f[all] \ No newline at end of file