Skip to content

Commit 0f954a7

Browse files
committed
Add AI functionality and full English documentation
1 parent af9fe69 commit 0f954a7

File tree

12 files changed

+434
-124
lines changed

12 files changed

+434
-124
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
BOT_TOKEN=your_telegram_bot_token
22
OPENAI_API_KEY=your_openai_api_key
3+
GROQ_API_KEY=your_groq_api_key

.pylintrc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ ignore=venv,.venv
44

55
[MESSAGES CONTROL]
66
# Disable warnings about missing docstrings
7+
# and other project-specific disables
8+
# Existing disables:
79
disable=missing-module-docstring,
810
missing-function-docstring,
911
too-few-public-methods,
10-
missing-final-newline
12+
missing-final-newline,
13+
import-outside-toplevel,
14+
redefined-outer-name,
15+
broad-exception-caught,
16+
too-many-return-statements
1117

1218
[FORMAT]
1319
# Max line length

README.md

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,54 @@
66
77
## 📌 Features
88

9-
- 📚 Generate personalized study plans (LLM/OpenAI)
9+
- 📚 Generate personalized study plans (LLM/OpenAI, fallback to Groq)
1010
- 📝 Export study plans to PDF/TXT
1111
- ⏰ Send reminders as Telegram messages for each study step
1212
- 🗄️ Store data using TinyDB
1313
- 📊 Python 3.10–3.13 support
1414

15+
## 🆕 Groq Fallback Integration
16+
17+
If the OpenAI API is unavailable, out of quota, or not configured, the bot will automatically use [Groq](https://groq.com/) as a fallback LLM provider. Groq offers:
18+
19+
- **Fast and reliable generations**
20+
- **No strict quotas for most users**
21+
- **OpenAI-compatible API**
22+
- **Always available fallback**
23+
24+
If both OpenAI and Groq are unavailable, the bot falls back to a local plan generator (simple stub).
25+
26+
### How it works
27+
28+
1. **Primary:** OpenAI API (if `OPENAI_API_KEY` is set and quota is available)
29+
2. **Fallback:** [Groq](https://groq.com/) (if `GROQ_API_KEY` is set)
30+
3. **Last resort:** Local plan generator (simple stub)
31+
32+
### How to use Groq
33+
34+
1. Register and get your API key at [Groq](https://console.groq.com/keys).
35+
2. Add the following line to your `.env` file:
36+
```
37+
GROQ_API_KEY=your_groq_api_key
38+
```
39+
3. (Optional) Add to `.env.example` for documentation:
40+
```
41+
GROQ_API_KEY=your_groq_api_key
42+
```
43+
44+
No other changes are needed — the bot will automatically use Groq if OpenAI is not available.
45+
46+
## 🌐 Multilingual Support
47+
48+
You can now choose your preferred language for all bot interactions! Use the `/language` command to select from English, Russian, or Spanish. The bot will automatically translate all responses, study plans, and reminders to your chosen language using LLMs (OpenAI or Groq). If translation is not possible, the original English text will be sent.
49+
50+
**Supported languages:**
51+
- English (`en`)
52+
- Русский (`ru`)
53+
- Español (`es`)
54+
55+
Translations are performed in real time using the same LLMs that generate study plans, ensuring high-quality and context-aware results.
56+
1557
## 🚀 Quick Start
1658

1759
### 1. Clone the project
@@ -32,6 +74,7 @@ Create a `.env` file in the root directory or rename `.env.example` to `.env` an
3274
```bash
3375
BOT_TOKEN=your_telegram_bot_token
3476
OPENAI_API_KEY=your_openai_api_key
77+
GROQ_API_KEY=your_groq_api_key
3578
```
3679

3780
### 4. Run the bot
@@ -70,7 +113,7 @@ EduPlannerBotAI/
70113
│ ├── start.py # /start and greeting
71114
│ └── planner.py # Study plan generation flow
72115
├── services/ # Core logic and helper functions
73-
│ ├── llm.py # OpenAI integration
116+
│ ├── llm.py # OpenAI and Groq integration
74117
│ ├── pdf.py # PDF export
75118
│ ├── txt.py # TXT export
76119
│ ├── reminders.py # Reminder simulation
@@ -87,6 +130,7 @@ EduPlannerBotAI/
87130
| Python 3.10+ | Programming language |
88131
| aiogram 3.x | Telegram Bot Framework |
89132
| OpenAI API | LLM for text generation |
133+
| Groq API | Fallback LLM provider |
90134
| fpdf | PDF file generation |
91135
| TinyDB | Lightweight NoSQL database |
92136
| python-dotenv | Environment variable management |
@@ -98,14 +142,28 @@ EduPlannerBotAI/
98142
- Python version compatibility: 3.10, 3.11, 3.12, 3.13
99143
- Custom `.pylintrc` configuration
100144

101-
## 📝 Release 2.0.0 Highlights
145+
## 📝 Release 2.1.0 Highlights
102146

103147
- Full English codebase (comments, docstrings, messages)
104148
- PEP8 and pylint compliance (score 10/10)
105149
- Full test coverage for all services and handlers
106150
- Improved error handling and async file operations
151+
- Multilingual support with LLM-based translation
152+
- Telegram reminders for study plans
153+
- **Groq fallback LLM integration**
154+
- **Arli AI fully removed**
107155
- Ready for open source and team development
108156

157+
## 🆕 2025 Updates
158+
159+
- All messages and buttons always contain non-empty text, eliminating Telegram errors (Bad Request: text must be non-empty).
160+
- Keyboards (format selection, next actions) are always accompanied by a short message to ensure buttons are displayed reliably.
161+
- Language selection buttons are not translated, so the language filter works correctly.
162+
- The entire bot scenario is fully localized: all messages, buttons, and files are translated to the user's selected language.
163+
- The logic is maximally simplified, with no unnecessary conditions; all stages work reliably and predictably.
164+
- Fallback to Groq is supported for generation and translation if OpenAI is unavailable.
165+
- The project is ready for use and open for extension.
166+
109167
## ⚠️ Handling Frequent 429 Errors
110168

111169
If you're experiencing too many `429 Too Many Requests` errors, consider the following:

bot.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from aiogram.client.default import DefaultBotProperties
99

1010
from config import TOKEN
11-
from handlers import start, planner
11+
from handlers import start, planner, language
1212

1313
# Create necessary directories
1414
os.makedirs("plans", exist_ok=True)
@@ -40,6 +40,7 @@
4040
# Register handlers
4141
dp.include_router(start.router)
4242
dp.include_router(planner.router)
43+
dp.include_router(language.router)
4344

4445
if __name__ == "__main__":
4546

config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55

66
TOKEN = os.getenv("BOT_TOKEN")
77
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
8+
GROQ_API_KEY = os.getenv("GROQ_API_KEY")

handlers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from . import start, planner
2+
from . import language

handlers/language.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
from aiogram import Router, types, F
2+
from aiogram.filters import Command
3+
from aiogram.fsm.context import FSMContext
4+
from services.db import set_user_language, get_user_language
5+
from services.llm import translate_text
6+
from handlers.planner import send_translated
7+
8+
router = Router()
9+
10+
LANGUAGES = {
11+
"en": "English",
12+
"ru": "Русский",
13+
"es": "Español",
14+
}
15+
16+
@router.message(Command("language"))
17+
async def choose_language(message: types.Message, state: FSMContext):
18+
user_id = message.from_user.id if message.from_user else 0
19+
user_lang = get_user_language(user_id) or "en"
20+
# Do not translate language selection buttons so the filter works correctly
21+
keyboard = types.ReplyKeyboardMarkup(
22+
keyboard=[
23+
[types.KeyboardButton(text=lang_name)] for lang_name in LANGUAGES.values()
24+
],
25+
resize_keyboard=True,
26+
one_time_keyboard=True,
27+
)
28+
prompt = await translate_text("Choose your language / Выберите язык:", user_lang)
29+
await send_translated(message, prompt)
30+
await message.answer(prompt, reply_markup=keyboard)
31+
await state.set_state("waiting_for_language")
32+
33+
@router.message(F.text.in_(LANGUAGES.values()))
34+
async def set_language(message: types.Message, state: FSMContext):
35+
lang_code = [k for k, v in LANGUAGES.items() if v == message.text][0]
36+
user_id = message.from_user.id if message.from_user else 0
37+
set_user_language(user_id, lang_code)
38+
lang_set_msg = await translate_text(f"Language set to {LANGUAGES[lang_code]}", lang_code)
39+
await send_translated(message, lang_set_msg)
40+
await message.answer(lang_set_msg, reply_markup=types.ReplyKeyboardRemove())
41+
await state.clear()
42+
# Send greeting and next-step instruction in the chosen language
43+
greeting = (
44+
"👋 Hi! I am a bot for creating study plans.\n"
45+
"Use the /plan command to start creating a study plan."
46+
)
47+
if lang_code != "en":
48+
greeting = await translate_text(greeting, lang_code)
49+
await send_translated(message, greeting)

0 commit comments

Comments
 (0)