Skip to content

Commit bf5e559

Browse files
committed
Merge branch 'main' of https://github.com/dmitryesin/diffy-bot into feature/get-applications-endpoints
2 parents 19391dd + f069c35 commit bf5e559

6 files changed

Lines changed: 171 additions & 135 deletions

File tree

README.md

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
1-
<h1 align="center"> Diffy Bot </h1>
1+
<h1 align="center"> Diffy Bot: Differential Equation Solver </h1>
22

33
<img src="docs/images/preview.png" class="center">
44

5-
This bot is designed to solve differential equations of various orders using numerical methods such as Euler's Method, Improved Euler's Method, Runge-Kutta Method, and Dormand-Prince Method. It provides a Telegram interface for users to input equations and receive solutions.
5+
<p align="center"> <em> A powerful Telegram bot designed for solving differential equations using advanced numerical methods. </em> </p>
66

77
## Features
88

9-
- Supports solving 1st and higher-order differential equations.
10-
- Multiple numerical methods available:
11-
- Euler's Method
12-
- Improved Euler's Method
13-
- Runge-Kutta Method
14-
- Dormand-Prince Method
15-
- Generates plots of solutions for better visualization.
16-
- Solution history tracking.
17-
- Multilingual support.
18-
- Configurable settings for numerical methods, rounding precision, and language.
9+
- **Equation Solving**
10+
Capable of solving first-order and higher-order ordinary differential equations (ODEs).
11+
The bot accepts user-defined equations and initial conditions, automatically parsing and preparing them for numerical analysis.
12+
13+
- **Numerical Methods**
14+
Implements a range of well-established numerical methods to approximate solutions with varying degrees of accuracy and computational efficiency:
15+
- Euler's Method;
16+
- Midpoint Method;
17+
- Heun's Method;
18+
- Runge-Kutta Method;
19+
- Dormand-Prince Method.
20+
21+
- **Visualization**
22+
Automatically generates clear and informative plots of the computed solutions.
23+
These visualizations help users interpret the behavior of differential systems over time.
24+
25+
- **History Tracking**
26+
Maintains a record of previously solved equations and their corresponding results, enabling users to revisit and review past computations.
27+
28+
- **Multilingual Support**
29+
Offers interaction in multiple languages, enhancing accessibility and usability for users from diverse linguistic backgrounds.
30+
31+
- **Customizable Settings**
32+
Provides a flexible interface for tailoring computational and interaction parameters to user preferences:
33+
- **Numerical Method** – Choose from a list of supported methods (e.g., Euler, Runge-Kutta, Dormand-Prince).
34+
- **Precision Control** – Set the number of decimal places to which numerical results are rounded.
35+
- **Language Selection** – Choose the interface language for all commands and responses.
36+
- **Hints Toggle** – Enable or disable contextual tips and usage hints to streamline the interface.
1937

2038
## License
2139

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ services:
4545
- solver-network
4646

4747
solver-db:
48-
image: postgres:17.5
48+
image: postgres:17.5-alpine
4949
container_name: diffy-bot-db-container
5050
environment:
5151
POSTGRES_USER: ${DB_USERNAME}
@@ -55,7 +55,7 @@ services:
5555
- "5433:5432"
5656
volumes:
5757
- pgdata:/var/lib/postgresql/data
58-
- ./database/schema.sql:/docker-entrypoint-initdb.d/init.sql
58+
- ./database/schema.sql:/docker-entrypoint-initdb.d/init.sql:ro
5959
networks:
6060
- solver-network
6161

solver-bot/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ FROM python:3.13.3-slim
22

33
WORKDIR /solver-client
44

5-
COPY solver-bot /solver-client
6-
COPY .env .env
5+
COPY solver-bot .
6+
COPY .env .
77

88
RUN pip install --no-cache-dir -r requirements.txt
99

solver-bot/src/main/python/shell.py

Lines changed: 119 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,18 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
8686
[
8787
InlineKeyboardButton(
8888
LANG_TEXTS[current_language]["solve"],
89-
callback_data="solve"),
89+
callback_data="solve"
90+
),
9091
InlineKeyboardButton(
9192
LANG_TEXTS[current_language]["settings"],
92-
callback_data="settings")
93+
callback_data="settings"
94+
)
9395
],
9496
[
9597
InlineKeyboardButton(
9698
LANG_TEXTS[current_language]["solve_history"],
97-
callback_data="solve_history")
99+
callback_data="solve_history"
100+
)
98101
]
99102
]
100103

@@ -140,26 +143,41 @@ async def settings(update: Update, context: ContextTypes.DEFAULT_TYPE):
140143
current_hints = context.user_data.get('hints', DEFAULT_HINTS)
141144

142145
keyboard = [
143-
[InlineKeyboardButton(
144-
LANG_TEXTS[current_language]["change_method"],
145-
callback_data="settings_method")],
146-
[InlineKeyboardButton(
147-
LANG_TEXTS[current_language]["change_rounding"],
148-
callback_data="settings_rounding")],
149-
[InlineKeyboardButton(
150-
LANG_TEXTS[current_language]["change_language"],
151-
callback_data="settings_language")],
152-
[InlineKeyboardButton(
153-
LANG_TEXTS[current_language]["hints_switch"] + " " +
154-
LANG_TEXTS[current_language]["hints_switch_on"],
155-
callback_data="true")
156-
if current_hints == "true" else InlineKeyboardButton(
146+
[
147+
InlineKeyboardButton(
148+
LANG_TEXTS[current_language]["change_method"],
149+
callback_data="settings_method"
150+
)
151+
],
152+
[
153+
InlineKeyboardButton(
154+
LANG_TEXTS[current_language]["change_rounding"],
155+
callback_data="settings_rounding"
156+
)
157+
],
158+
[
159+
InlineKeyboardButton(
160+
LANG_TEXTS[current_language]["change_language"],
161+
callback_data="settings_language"
162+
)
163+
],
164+
[
165+
InlineKeyboardButton(
166+
LANG_TEXTS[current_language]["hints_switch"] + " " +
167+
LANG_TEXTS[current_language]["hints_switch_on"],
168+
callback_data="true"
169+
) if current_hints == "true" else InlineKeyboardButton(
157170
LANG_TEXTS[current_language]["hints_switch"] + " " +
158171
LANG_TEXTS[current_language]["hints_switch_off"],
159-
callback_data="false")],
160-
[InlineKeyboardButton(
161-
LANG_TEXTS[current_language]["back"],
162-
callback_data="back")]
172+
callback_data="false"
173+
)
174+
],
175+
[
176+
InlineKeyboardButton(
177+
LANG_TEXTS[current_language]["back"],
178+
callback_data="back"
179+
)
180+
]
163181
]
164182

165183
new_text = LANG_TEXTS[current_language]["settings_menu"]
@@ -199,41 +217,23 @@ async def settings_method(update: Update, context: ContextTypes.DEFAULT_TYPE):
199217
current_language = context.user_data.get('language', DEFAULT_LANGUAGE)
200218
current_method = context.user_data.get('method', DEFAULT_METHOD)
201219

202-
keyboard = [
203-
[InlineKeyboardButton(
204-
f"→ {LANG_TEXTS[current_language]["numerical_methods"]["euler"]} ←",
205-
callback_data="euler")
206-
if current_method == "euler" else InlineKeyboardButton(
207-
LANG_TEXTS[current_language]["numerical_methods"]["euler"],
208-
callback_data="euler")],
209-
[InlineKeyboardButton(
210-
f"→ {LANG_TEXTS[current_language]["numerical_methods"]["midpoint"]} ←",
211-
callback_data="midpoint")
212-
if current_method == "midpoint" else InlineKeyboardButton(
213-
LANG_TEXTS[current_language]["numerical_methods"]["midpoint"],
214-
callback_data="midpoint")],
215-
[InlineKeyboardButton(
216-
f"→ {LANG_TEXTS[current_language]["numerical_methods"]["heun"]} ←",
217-
callback_data="heun")
218-
if current_method == "heun" else InlineKeyboardButton(
219-
LANG_TEXTS[current_language]["numerical_methods"]["heun"],
220-
callback_data="heun")],
221-
[InlineKeyboardButton(
222-
f"→ {LANG_TEXTS[current_language]["numerical_methods"]["runge_kutta"]} ←",
223-
callback_data="runge_kutta")
224-
if current_method == "runge_kutta" else InlineKeyboardButton(
225-
LANG_TEXTS[current_language]["numerical_methods"]["runge_kutta"],
226-
callback_data="runge_kutta")],
227-
[InlineKeyboardButton(
228-
f"→ {LANG_TEXTS[current_language]["numerical_methods"]["dormand_prince"]} ←",
229-
callback_data="dormand_prince")
230-
if current_method == "dormand_prince" else InlineKeyboardButton(
231-
LANG_TEXTS[current_language]["numerical_methods"]["dormand_prince"],
232-
callback_data="dormand_prince")],
233-
[InlineKeyboardButton(
220+
methods = ["euler", "midpoint", "heun", "runge_kutta", "dormand_prince"]
221+
222+
numerical_texts = LANG_TEXTS[current_language]["numerical_methods"]
223+
224+
keyboard = []
225+
226+
for method in methods:
227+
method_name = numerical_texts[method]
228+
text = f"→ {method_name} ←" if current_method == method else method_name
229+
keyboard.append([InlineKeyboardButton(text, callback_data=method)])
230+
231+
keyboard.append([
232+
InlineKeyboardButton(
234233
LANG_TEXTS[current_language]["back"],
235-
callback_data="settings_back")]
236-
]
234+
callback_data="settings_back"
235+
)
236+
])
237237

238238
new_text = LANG_TEXTS[current_language]["settings_menu"]
239239
new_reply_markup = InlineKeyboardMarkup(keyboard)
@@ -272,31 +272,26 @@ async def settings_rounding(update: Update, context: ContextTypes.DEFAULT_TYPE):
272272
current_language = context.user_data.get('language', DEFAULT_LANGUAGE)
273273
current_rounding = context.user_data.get('rounding', DEFAULT_ROUNDING)
274274

275-
keyboard = [
276-
[
277-
InlineKeyboardButton(
278-
"→ 4 ←", callback_data="4")
279-
if current_rounding == "4" else InlineKeyboardButton(
280-
"4", callback_data="4"),
281-
InlineKeyboardButton(
282-
"→ 6 ←", callback_data="6")
283-
if current_rounding == "6" else InlineKeyboardButton(
284-
"6", callback_data="6"),
285-
InlineKeyboardButton(
286-
"→ 8 ←", callback_data="8")
287-
if current_rounding == "8" else InlineKeyboardButton(
288-
"8", callback_data="8"),
289-
],
290-
[InlineKeyboardButton(
291-
f"→ {LANG_TEXTS[current_language]["without_rounding"]} ←",
292-
callback_data="16")
293-
if current_rounding == "16" else InlineKeyboardButton(
294-
LANG_TEXTS[current_language]["without_rounding"],
295-
callback_data="16")],
296-
[InlineKeyboardButton(
275+
roundings = ["4", "6", "8"]
276+
277+
keyboard = []
278+
279+
row = []
280+
for rounding in roundings:
281+
text = f"→ {rounding} ←" if current_rounding == rounding else rounding
282+
row.append(InlineKeyboardButton(text, callback_data=rounding))
283+
keyboard.append(row)
284+
285+
no_rounding_text = LANG_TEXTS[current_language]["without_rounding"]
286+
text = f"→ {no_rounding_text} ←" if current_rounding == "16" else no_rounding_text
287+
keyboard.append([InlineKeyboardButton(text, callback_data="16")])
288+
289+
keyboard.append([
290+
InlineKeyboardButton(
297291
LANG_TEXTS[current_language]["back"],
298-
callback_data="settings_back")]
299-
]
292+
callback_data="settings_back"
293+
)
294+
])
300295

301296
new_text = LANG_TEXTS[current_language]["settings_menu"]
302297
new_reply_markup = InlineKeyboardMarkup(keyboard)
@@ -334,24 +329,25 @@ async def settings_language(update: Update, context: ContextTypes.DEFAULT_TYPE):
334329

335330
current_language = context.user_data.get('language', DEFAULT_LANGUAGE)
336331

337-
keyboard = [
338-
[InlineKeyboardButton(
339-
"→ English ←", callback_data="en")
340-
if current_language == "en" else InlineKeyboardButton(
341-
"English", callback_data="en")],
342-
[InlineKeyboardButton(
343-
"→ Русский ←", callback_data="ru")
344-
if current_language == "ru" else InlineKeyboardButton(
345-
"Русский", callback_data="ru")],
346-
[InlineKeyboardButton(
347-
"→ 中文 ←", callback_data="zh")
348-
if current_language == "zh" else InlineKeyboardButton(
349-
"中文", callback_data="zh")],
350-
[InlineKeyboardButton(
351-
LANG_TEXTS[current_language]["back"],
352-
callback_data="settings_back")]
332+
languages = [
333+
("en", "English"),
334+
("ru", "Русский"),
335+
("zh", "中文")
353336
]
354337

338+
keyboard = []
339+
340+
for language_callback, language in languages:
341+
text = f"→ {language} ←" if current_language == language_callback else language
342+
keyboard.append([InlineKeyboardButton(text, callback_data=language_callback)])
343+
344+
keyboard.append([
345+
InlineKeyboardButton(
346+
LANG_TEXTS[current_language]["back"],
347+
callback_data="settings_back"
348+
)
349+
])
350+
355351
new_text = LANG_TEXTS[current_language]["settings_menu"]
356352
new_reply_markup = InlineKeyboardMarkup(keyboard)
357353

@@ -498,12 +494,12 @@ async def solve_history_details(update: Update, context: ContextTypes.DEFAULT_TY
498494
f"{print_solution(solution, order, current_rounding)}"
499495
)
500496

501-
keyboard = [
502-
[InlineKeyboardButton(
497+
keyboard = [[
498+
InlineKeyboardButton(
503499
LANG_TEXTS[current_language]["back"],
504500
callback_data="solve_history_back"
505-
)]
506-
]
501+
)
502+
]]
507503

508504
media = InputMediaPhoto(
509505
media=plot_graph,
@@ -789,12 +785,18 @@ async def solution(update: Update, context: ContextTypes.DEFAULT_TYPE):
789785
current_language = context.user_data.get('language', DEFAULT_LANGUAGE)
790786

791787
keyboard = [
792-
[InlineKeyboardButton(
793-
LANG_TEXTS[current_language]["solve_over"],
794-
callback_data="solve")],
795-
[InlineKeyboardButton(
796-
LANG_TEXTS[current_language]["menu"],
797-
callback_data="menu")]
788+
[
789+
InlineKeyboardButton(
790+
LANG_TEXTS[current_language]["solve_over"],
791+
callback_data="solve"
792+
)
793+
],
794+
[
795+
InlineKeyboardButton(
796+
LANG_TEXTS[current_language]["menu"],
797+
callback_data="menu"
798+
)
799+
]
798800
]
799801

800802
new_reply_markup = InlineKeyboardMarkup(keyboard)
@@ -911,12 +913,18 @@ async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE):
911913
current_language = context.user_data.get('language', DEFAULT_LANGUAGE)
912914

913915
keyboard = [
914-
[InlineKeyboardButton(
915-
LANG_TEXTS[current_language]["solve_over"],
916-
callback_data="solve")],
917-
[InlineKeyboardButton(
918-
LANG_TEXTS[current_language]["menu"],
919-
callback_data="menu")]
916+
[
917+
InlineKeyboardButton(
918+
LANG_TEXTS[current_language]["solve_over"],
919+
callback_data="solve"
920+
)
921+
],
922+
[
923+
InlineKeyboardButton(
924+
LANG_TEXTS[current_language]["menu"],
925+
callback_data="menu"
926+
)
927+
]
920928
]
921929

922930
new_reply_markup = InlineKeyboardMarkup(keyboard)

0 commit comments

Comments
 (0)