turns-00035.parquet:12467
1100d582666cb6cd4aebceb4degenerate_repetitionAbsentFinal dense release
Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.
1100d582666cb6cd4aebceb4Assistant:
User:
请根据以下原始数据执行以下任务:
检查句子中是否存在任何歧义或逻辑错误,并用一句话说明这些问题。
示例:
原始数据: "根据牛顿第一定律,我推算出本次世界百大物理学家排名,爱因斯坦只能屈居第二。"
这句话误将牛顿第一定律与物理学家的排名混淆,实际是物理定律,而不是排名。
现在,请对以下原始数据进行同样的分析和改进:
原始数据: "“爷是你爹”小明对他爸说"
Assistant:这句话存在逻辑错误,因为它本质上表达了一个自相矛盾的关系,小明称自己为父亲,这在逻辑上是不可能的。
da6ddd2c2ca69ed05cf71d56Assistant:
User:
请根据以下原始数据执行以下任务:
检查句子中是否存在任何歧义或逻辑错误,并用一句话说明这些问题。
示例:
原始数据: "根据牛顿第一定律,我推算出本次世界百大物理学家排名,爱因斯坦只能屈居第二。"
这句话误将牛顿第一定律与物理学家的排名混淆,实际是物理定律,而不是排名。
现在,请对以下原始数据进行同样的分析和改进:
原始数据: "好消息,由于大门忘记锁,本院病人于今早全部出院"
Assistant:这句话存在逻辑错误,因为“由于大门忘记锁”并不能自动成为患者全部出院的“好消息”,还可能导致安全问题。
d31e33d84cd7ad9a6f727471Hello! How can I assist you today?
fd253ec96d6728db946f6408Hi there!
Hi! How can I help you today?
f4a81e64db2d07657784765bMy bf started to walk a bit after injury and I want to tell him I wish im there for him and lean on me while he wants to walk be his walking stick
That's a sweet sentiment! You could say something like: "Hey love, I just wanted to let you know how proud I am of you for starting to walk again. I truly wish I could be there for you right now. Remember, I’m right here cheering you on! Lean on me as much as you need, and just know that I’m your walking stick in spirit. You’ve got this, and I believe in you!" Feel free to adjust it to make it sound more like you!
fa2ab61f044792615776a83aГарри и Рон прошли в основной тур олимпиады по искусственному интеллекту. Поскольку ни Гарри, ни Рон не готовились и ничего не знали, то они просто угадывали ответы на вопросы. Всего в отборочном туре было N вопросов. Гарри удалось угадать ответы на X вопросов, а Рону только на Y вопросов. Найдите среднее количество вопросов в которых у Гарри и Рона получился одинаковый результат (то есть оба угадали или не угадали ответ) N = 31 X = 19 Y = 12
Давайте разберёмся, как найти среднее количество вопросов, в которых результаты Гарри и Рона совпадают (то есть оба правильно или оба неправильно ответили).
**Дано:**
- Общее количество вопросов \( N = 31 \).
- Гарри правильно ответил на \( X = 19 \) вопросов, значит, неправильно на \( N - X = 12 \) вопросов.
- Рон правильно ответил на \( Y = 12 \) вопросов, значит, неправильно на \( N - Y = 19 \) вопросов.
**Нам нужно найти:**
Среднее количество вопросов, в которых:
1. **Оба правильно ответили**: \( |C_1 \cap C_2| \), где \( C_1 \) — множество вопросов, которые правильно ответил Гарри, а \( C_2 \) — множество вопросов, которые правильно ответил Рон.
2. **Оба неправильно ответили**: \( |C_1^c \cap C_2^c| \), где \( C_1^c \) и \( C_2^c \) — дополнения множеств \( C_1 \) и \( C_2 \) соответственно.
**Ожидаемое значение:**
\[
E[|C_1 \cap C_2|] = \frac{X \times Y}{N} = \frac{19 \times 12}{31} = \frac{228}{31}
\]
\[
E[|C_1^c \cap C_2^c|] = \frac{(N - X) \times (N - Y)}{N} = \frac{12 \times 19}{31} = \frac{228}{31}
\]
**Общая средняя численность совпадающих результатов:**
\[
E = E[|C_1 \cap C_2|] + E[|C_1^c \cap C_2^c|] = \frac{228}{31} + \frac{228}{31} = \frac{456}{31} \approx 14.71
\]
Таким образом, среднее количество вопросов, в которых у Гарри и Рона получился одинаковый результат, составляет примерно **14.71**.
**Ответ:** Приблизительно 14.71 вопроса оказываются с одинаковым результатом у Гарри и Рона.1bcef1ebe6b3852f5b7168e2Гарри и Рон прошли в основной тур олимпиады по искусственному интеллекту. Поскольку ни Гарри, ни Рон не готовились и ничего не знали, то они просто угадывали ответы на вопросы. Всего в отборочном туре было N вопросов. Гарри удалось угадать ответы на X вопросов, а Рону только на Y вопросов. Найдите среднее количество вопросов в которых у Гарри и Рона получился одинаковый результат (то есть оба угадали или не угадали ответ) N = 31 X = 19 Y = 12
Чтобы найти среднее количество вопросов, в которых Гарри и Рон получили одинаковые результаты (оба ответили правильно или оба ошиблись), воспользуемся вероятностным подходом.
**Дано:**
- Общее количество вопросов, \( N = 31 \)
- Гарри правильно ответил на \( X = 19 \) вопросов
- Рон правильно ответил на \( Y = 12 \) вопросов
**Шаги решения:**
1. **Определим вероятность правильного ответа:**
- Вероятность, что Гарри ответил правильно на вопрос: \( P_H = \frac{X}{N} = \frac{19}{31} \)
- Вероятность, что Рон ответил правильно на вопрос: \( P_R = \frac{Y}{N} = \frac{12}{31} \)
2. **Вероятность одинакового результата для одного вопроса:**
- Оба ответили правильно: \( P_H \times P_R = \frac{19}{31} \times \frac{12}{31} = \frac{228}{961} \)
- Оба ошиблись: \( (1 - P_H) \times (1 - P_R) = \frac{12}{31} \times \frac{19}{31} = \frac{228}{961} \)
- Итого вероятность одинакового результата: \( \frac{228}{961} + \frac{228}{961} = \frac{456}{961} \)
3. **Находим ожидаемое количество вопросов с одинаковым результатом:**
\[
\text{Ожидаемое количество} = N \times \left( \frac{456}{961} \right) = 31 \times \frac{456}{961} \approx 14.71
\]
**Ответ:**
В среднем Гарри и Рон получат одинаковый результат по примерно **14.71** вопросам из 31.1fcf1ca65c5464cda0a91631Гарри и Рон прошли в основной тур олимпиады по искусственному интеллекту. Поскольку ни Гарри, ни Рон не готовились и ничего не знали, то они просто угадывали ответы на вопросы. Всего в отборочном туре было N вопросов. Гарри удалось угадать ответы на X вопросов, а Рону только на Y вопросов. Найдите среднее количество вопросов в которых у Гарри и Рона получился одинаковый результат (то есть оба угадали или не угадали ответ) N = 31 X = 19 Y = 12
Чтобы найти среднее количество вопросов, в которых у Гарри и Рона одинаковый результат (оба правильно ответили или оба не правильно ответили), можно воспользоваться следующими шагами:
1. **Обозначения:**
- Общее количество вопросов: \( N = 31 \)
- Гарри ответил правильно на \( X = 19 \) вопросов.
- Рон ответил правильно на \( Y = 12 \) вопросов.
2. **Ожидание перекрытия (оба правильно):**
- Ожидаемое количество вопросов, на которые ответили оба правильно, рассчитывается как:
\[
E[C] = \frac{X \times Y}{N} = \frac{19 \times 12}{31} \approx 7.35
\]
3. **Ожидание обоих неправильных ответов:**
- Общее количество вопросов, на которые Гарри ответил неправильно: \( N - X = 12 \).
- Общее количество вопросов, на которые Рон ответил неправильно: \( N - Y = 19 \).
- Ожидаемое количество вопросов, на которые ответили оба неправильно:
\[
E[W] = \frac{(N - X) \times (N - Y)}{N} = \frac{12 \times 19}{31} \approx 7.35
\]
4. **Суммарное ожидание одинаковых результатов:**
\[
E[C + W] = E[C] + E[W] \approx 7.35 + 7.35 = 14.7
\]
Таким образом, **среднее количество вопросов, в которых у Гарри и Рона одинаковый результат, составляет примерно 14.7**.
**Ответ:** В среднем у Гарри и Рона одинаковый результат получается примерно 14.71 вопроса.e68d021d3ede753c4b002242нужно построить картинку в java, зная координаты M297 21C293 208 304 210 292 209 281 209 218 218 211 206 120 45 55 33 176 27
Конечно, я помогу вам создать изображение в Java, используя заданные координаты. Похоже, что вы предоставили команды пути в стиле SVG, где `M` означает "Move To" (переместиться в точку), а `C` — "Curve To" (кубическая криволинейная кривая Безье).
Вот пошаговый пример того, как можно реализовать это с использованием Java Swing и `Graphics2D`:
1. **Создайте основной класс приложения:**
```java
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Path2D;
public class PathDrawing extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawPath((Graphics2D) g);
}
private void drawPath(Graphics2D g2d) {
// Установка толщины линии и цвета
g2d.setStroke(new BasicStroke(2));
g2d.setColor(Color.BLUE);
// Создание объекта пути
Path2D.Double path = new Path2D.Double();
// Начальная точка (M297 21)
path.moveTo(297, 21);
// Первая кривая Безье (C293 208 304 210 292 209)
path.curveTo(293, 208, 304, 210, 292, 209);
// Вторая кривая Безье (C281 209 218 218 211 206)
path.curveTo(281, 209, 218, 218, 211, 206);
// Третья кривая Безье (C120 45 55 33 176 27)
path.curveTo(120, 45, 55, 33, 176, 27);
// Отрисовка пути
g2d.draw(path);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Рисование пути");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PathDrawing());
frame.setSize(400, 300);
frame.setLocationRelativeTo(null); // Центрирование окна
frame.setVisible(true);
}
}
```
2. **Пояснение к коду:**
- **Импорты:**
- `JFrame` и `JPanel` из Swing для создания окна и панели рисования.
- `Graphics`, `Graphics2D`, `BasicStroke`, `Color` для работы с графикой.
- `Path2D` для создания сложных геометрических фигур путем объединения различных сегментов.
- **Класс `PathDrawing`:**
- Наследуется от `JPanel` и переопределяет метод `paintComponent`, где выполняется отрисовка.
- Метод `drawPath` создает объект `Path2D.Double` и добавляет команды `moveTo` и `curveTo` согласно вашим координатам.
- Устанавливается цвет линии и ее толщина для лучшей видимости.
- **Метод `main`:**
- Создает и настраивает основное окно приложения.
- Добавляет экземпляр `PathDrawing` в окно и делает его видимым.
3. **Запуск приложения:**
Скомпилируйте и запустите этот код. Вы должны увидеть окно с отрисованным путем, сформированным по заданным координатам.
4. **Дополнительные настройки:**
- **Масштабирование и позиционирование:**
В зависимости от диапазона координат, вам может понадобиться масштабировать или сместить путь, чтобы он корректно отображался в окне. Это можно сделать с помощью преобразований `AffineTransform`.
- **Заливка формы:**
Если вы хотите не только контур, но и заливку, используйте методы `g2d.fill(path)` вместо или вместе с `g2d.draw(path)`.
- **Обработка событий:**
Для интерактивности можно добавить обработку событий мыши или клавиатуры.
5. **Полный рабочий пример:**
Ниже приведен полный код для вашего удобства:
```java
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Path2D;
public class PathDrawing extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
drawPath((Graphics2D) g);
}
private void drawPath(Graphics2D g2d) {
// Установка толщины линии и цвета
g2d.setStroke(new BasicStroke(2));
g2d.setColor(Color.BLUE);
// Создание объекта пути
Path2D.Double path = new Path2D.Double();
// Начальная точка (M297 21)
path.moveTo(297, 21);
// Первая кривая Безье (C293 208 304 210 292 209)
path.curveTo(293, 208, 304, 210, 292, 209);
// Вторая кривая Безье (C281 209 218 218 211 206)
path.curveTo(281, 209, 218, 218, 211, 206);
// Третья кривая Безье (C120 45 55 33 176 27)
path.curveTo(120, 45, 55, 33, 176, 27);
// Отрисовка пути
g2d.draw(path);
}
public static void main(String[] args) {
JFrame frame = new JFrame("Рисование пути");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new PathDrawing());
frame.setSize(400, 300);
frame.setLocationRelativeTo(null); // Центрирование окна
frame.setVisible(true);
}
}
```
6. **Компиляция и запуск:**
- Сохраните код в файл с именем `PathDrawing.java`.
- Откройте терминал или командную строку и перейдите в каталог с файлом.
- Скомпилируйте код:
```bash
javac PathDrawing.java
```
- Запустите программу:
```bash
java PathDrawing
```
Вы увидите окно с нарисованным путем согласно заданным координатам. Вы можете настроить размеры окна, цвет, толщину линии и другие параметры по своему усмотрению.
Если у вас возникнут дополнительные вопросы или потребуется расширение функциональности, не стесняйтесь обращаться!1b516f5c0bf130fdfcbda3e4Вот ошибка: Traceback (most recent call last):
File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 31, in <module> start(fakepyfile,mainpyfile)
File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 30, in start
exec(open(mainpyfile).read(), __main__.__dict__)
File "<string>", line 603
async def collect_mailing_message(update: Update, context: CallbackContext) -> None:
SyntaxError: expected 'except' or 'finally' block
[Program finished]. Вот мой код: import pytz
import sqlite3
from datetime import datetime, timedelta
from telegram import Update, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import (
ApplicationBuilder,
CommandHandler,
CallbackContext,
MessageHandler,
filters,
CallbackQueryHandler,
)
import configparser
import os
import logging
# Настройка логирования
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
# Чтение конфигурационного файла
config = configparser.ConfigParser()
config.read('config.ini')
# Получение токена из конфигурационного файла
TOKEN = config.get('Telegram', 'Token')
# ID главного администратора
MAIN_ADMIN_ID = 1108429314
def create_config_file():
config['Telegram'] = {'Token': 'your_bot_token_here'}
with open('config.ini', 'w') as configfile:
config.write(configfile)
def init_db():
conn = sqlite3.connect('university.db')
c = conn.cursor()
# Создание таблицы schedule
c.execute('''CREATE TABLE IF NOT EXISTS schedule
(course INTEGER, direction TEXT, week_type TEXT, day TEXT, slot INTEGER, time TEXT, details TEXT)''')
# Создание таблицы users с колонкой created_at
c.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT,
course INTEGER,
direction TEXT,
subscribed INTEGER DEFAULT 1,
notifications TEXT DEFAULT 'homework,changes,announcements,university',
blocked_until TEXT DEFAULT NULL,
created_at TEXT DEFAULT (datetime('now','localtime'))
)
''')
# Создание таблицы teachers
c.execute('''CREATE TABLE IF NOT EXISTS teachers
(id INTEGER PRIMARY KEY)''')
# Добавить главного администратора в таблицу преподавателей
c.execute("INSERT OR IGNORE INTO teachers (id) VALUES (?)", (MAIN_ADMIN_ID,))
# Проверка и добавление колонки created_at, если таблица уже существует без неё
c.execute("PRAGMA table_info(users)")
columns = [info[1] for info in c.fetchall()]
if 'created_at' not in columns:
try:
c.execute("ALTER TABLE users ADD COLUMN created_at TEXT DEFAULT (datetime('now','localtime'))")
conn.commit()
print("Колонка 'created_at' успешно добавлена в таблицу 'users'.")
except sqlite3.OperationalError as e:
print(f"Ошибка при добавлении колонки 'created_at': {e}")
# Инициализация расписания
c.execute("DELETE FROM schedule")
times = ["8:30 - 10:00", "10:10 - 11:40", "12:30 - 14:00", "14:10 - 15:40", "15:50 - 17:20", "17:30 - 19:00"]
directions = ['Ф', 'У', 'ФМ', 'ГМУ', 'БИ']
schedule_bi = {
"чётная": {
"Понедельник": ["Матем (л) (222)", "Матем (с) (222)", "Физ-ра", "Англ (209)", "", ""],
"Вторник": ["История (с) (108)", "История (л) (108)", "ИТПД (с) (217)", "", "", ""],
"Среда": ["", "", "", "ИТПД (с) (217)", "Phython (л) (111)", "Phython (л) (111)"],
"Четверг": ["", "История (с) (204)", "Менеджмент (л) (209)", "", "", ""],
"Пятница": ["", "", "", "БЖД (л) (304)", "Менеджмент (с) (108)", "Физ-ра"]
},
"нечётная": {
"Понедельник": ["Матем (л) (222)", "Матем (с) (222)", "Физ-ра", "Англ (209)", "", ""],
"Вторник": ["Англ (209)", "История (л) (108)", "ИТПД (с) (217)", "История ФУ (л) (211)", "", ""],
"Среда": ["", "", "", "ИТПД (с) (217)", "Введение в спец (л) (111)", "Phython (с) (111)"],
"Четверг": ["Введение в спец (с) (213)", "Матем (с) (204)", "История (с) (209)", "", "", ""],
"Пятница": ["", "", "", "БЖД (с) (304)", "Менеджмент (с) (108)", "Физ-ра"]
}
}
for course in range(1, 5):
for direction in directions:
schedule_data = {
"чётная": {day: [""] * 6 for day in ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"]},
"нечётная": {day: [""] * 6 for day in ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"]}
}
if direction == 'БИ' and course == 1:
schedule_data = schedule_bi
else:
for week_type, days in schedule_data.items():
for day in days:
days[day][0] = f"Курс {course}, Группа {direction}"
for week_type, days in schedule_data.items():
for day, slots in days.items():
for slot in range(6):
time = times[slot]
detail = slots[slot]
c.execute(
"INSERT INTO schedule (course, direction, week_type, day, slot, time, details) VALUES (?, ?, ?, ?, ?, ?, ?)",
(course, direction, week_type, day, slot + 1, time, detail)
)
conn.commit()
conn.close()
def get_day_name(index):
days_rus = [
"Понедельник",
"Вторник",
"Среда",
"Четверг",
"Пятница",
"Суббота",
"Воскресенье"
]
return days_rus[index]
def is_even_week(date):
week_number = date.isocalendar()[1]
return week_number % 2 == 0
def get_week_type(date):
return 'чётная' if is_even_week(date) else 'нечётная'
def get_week_type_short(date):
return 'чёт' if is_even_week(date) else 'нечёт'
def get_schedule_for_day(course, direction, day_name, date):
week_type = get_week_type(date)
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute(
"SELECT slot, time, details FROM schedule WHERE course = ? AND direction = ? AND week_type = ? AND day = ? ORDER BY slot",
(course, direction, week_type, day_name)
)
results = c.fetchall()
conn.close()
if not results:
return "❌ Нет занятий"
day_schedule = ""
for slot, time, details in results:
if details:
day_schedule += f"{slot}. {time}\n{details}\n\n"
return day_schedule.strip() if day_schedule else "❌ Нет занятий"
async def start(update: Update, context: CallbackContext) -> None:
user_id = update.effective_user.id
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute("SELECT blocked_until FROM users WHERE id=?", (user_id,))
blocked_until = c.fetchone()
if blocked_until and blocked_until[0]:
blocked_until_date = datetime.fromisoformat(blocked_until[0])
if blocked_until_date > datetime.now():
await update.message.reply_text(f"❌ Вы заблокированы! До разблокировки {blocked_until_date}.")
conn.close()
return
conn.close()
# Генерация меню
keyboard = [
["🗓 Выбрать дату расписания", "📅 Расписание на завтра", "📅 Расписание на сегодня"],
["📆 Понедельник", "📆 Вторник", "📆 Среда"],
["📆 Четверг", "📆 Пятница", "📆 Суббота"],
["👤 Личный кабинет"]
]
reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True, resize_keyboard=True)
# Приветственное сообщение с меню
await update.message.reply_text(
'👋 Привет! Поменяй группу и курс на соответствующие в своём личном кабинете.',
reply_markup=reply_markup
)
# Отображение личного кабинета
await personal_cabinet(update, context)
async def handle_message(update: Update, context: CallbackContext) -> None:
user_id = update.message.from_user.id
user_message = update.message.text.strip().lower()
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute("SELECT blocked_until FROM users WHERE id=?", (user_id,))
blocked_until = c.fetchone()
if blocked_until and blocked_until[0]:
blocked_until_date = datetime.fromisoformat(blocked_until[0])
if blocked_until_date > datetime.now():
await update.message.reply_text(f"❌ Вы заблокированы! До разблокировки {blocked_until_date}.")
conn.close()
return
# Обработка специальных действий
if context.user_data.get('action') == 'promote_admin':
try:
promote_id = int(user_message)
c.execute("SELECT id FROM users WHERE id = ?", (promote_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не найден.")
else:
c.execute("INSERT OR IGNORE INTO teachers (id) VALUES (?)", (promote_id,))
conn.commit()
await update.message.reply_text(f"✅ Пользователь с ID {promote_id} назначен администратором.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при назначении администратора: {e}")
await update.message.reply_text("❌ Произошла ошибка при назначении администратора.")
finally:
conn.close()
context.user_data['action'] = None
return
if context.user_data.get('action') == 'demote_admin':
try:
demote_id = int(user_message)
if demote_id == MAIN_ADMIN_ID:
await update.message.reply_text("❌ Невозможно удалить главного администратора.")
else:
c.execute("SELECT id FROM teachers WHERE id = ?", (demote_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не является администратором.")
else:
c.execute("DELETE FROM teachers WHERE id = ?", (demote_id,))
conn.commit()
await update.message.reply_text(f"✅ Пользователь с ID {demote_id} удален из администраторов.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при удалении администратора: {e}")
await update.message.reply_text("❌ Произошла ошибка при удалении администратора.")
finally:
conn.close()
context.user_data['action'] = None
return
if context.user_data.get('action') == 'block_user':
try:
block_id = int(user_message)
c.execute("SELECT id FROM users WHERE id = ?", (block_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не найден.")
else:
# Установим блокировку на 24 часа
block_until = datetime.now() + timedelta(hours=24)
c.execute("UPDATE users SET blocked_until = ? WHERE id = ?", (block_until.isoformat(), block_id))
conn.commit()
await update.message.reply_text(f"🔒 Пользователь с ID {block_id} заблокирован до {block_until}.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при блокировке пользователя: {e}")
await update.message.reply_text("❌ Произошла ошибка при блокировке пользователя.")
finally:
conn.close()
context.user_data['action'] = None
return
if context.user_data.get('action') == 'unblock_user':
try:
unblock_id = int(user_message)
if unblock_id == MAIN_ADMIN_ID:
await update.message.reply_text("❌ Невозможно разблокировать главного администратора.")
conn.close()
context.user_data['action'] = None
return
c.execute("SELECT id FROM users WHERE id = ? AND blocked_until IS NOT NULL", (unblock_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не заблокирован.")
else:
c.execute("UPDATE users SET blocked_until = NULL WHERE id = ?", (unblock_id,))
conn.commit()
await update.message.reply_text(f"🔓 Пользователь с ID {unblock_id} разблокирован.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при разблокировке пользователя: {e}")
await update.message.reply_text("❌ Произошла ошибка при разблокировке пользователя.")
finally:
conn.close()
context.user_data['action'] = None
return
conn.close()
# Основная логика обработки сообщений
logger.info(f"Received message: {user_message}")
if context.user_data.get('awaiting_date', False):
if user_message.isdigit():
day = int(user_message)
current_month = datetime.now(pytz.timezone('Asia/Omsk')).month
current_year = datetime.now(pytz.timezone('Asia/Omsk')).year
try:
date_obj = datetime(current_year, current_month, day, tzinfo=pytz.timezone('Asia/Omsk'))
if date_obj.month != current_month:
raise ValueError
await show_schedule_by_date(update, context, date_obj)
except ValueError:
await update.message.reply_text('❌ Неверный день. Пожалуйста, введи число от 1 до последнего дня месяца.')
finally:
context.user_data['awaiting_date'] = False
else:
await update.message.reply_text('❌ Пожалуйста, введи число для получения расписания.')
return
if user_message in ["расписание на сегодня", "расписание на завтра", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота"]:
await show_schedule(update, context, user_message)
elif user_message == "выбрать дату расписания":
logger.info("User chose to select a specific date for schedule")
context.user_data['awaiting_date'] = True
await update.message.reply_text("🗓 Введи необходимое число этого месяца:")
elif user_message == "личный кабинет":
logger.info("User accessing personal cabinet")
await personal_cabinet(update, context)
else:
logger.warning(f"Unrecognized menu option: {user_message}")
await update.message.reply_text('❓ Выбери опцию из меню.')
async def personal_cabinet(update: Update, context: CallbackContext):
user_id = update.effective_user.id
username = update.effective_user.username or ""
first_name = update.effective_user.first_name or ""
last_name = update.effective_user.last_name or ""
conn = sqlite3.connect('university.db')
c = conn.cursor()
# Проверка существования пользователя в базе данных
c.execute(
"SELECT username, course, direction, subscribed, notifications FROM users WHERE id = ?",
(user_id,)
)
user = c.fetchone()
# Добавление пользователя, если он ещё не существует
if not user:
current_time = datetime.now(pytz.timezone('Asia/Omsk')).isoformat()
c.execute(
"INSERT INTO users (id, username, course, direction, created_at) VALUES (?, ?, ?, ?, ?)",
(user_id, username, 1, "Ф", current_time)
)
conn.commit()
user = (username, 1, "Ф", 1, 'homework,changes,announcements,university')
# Проверка существования ID в таблице teachers, добавление, если его там нет
c.execute("SELECT 1 FROM teachers WHERE id = ?", (user_id,))
is_teacher = c.fetchone() is not None
if user_id == MAIN_ADMIN_ID and not is_teacher:
c.execute("INSERT INTO teachers (id) VALUES (?)", (user_id,))
conn.commit()
is_teacher = True
conn.close()
current_date = datetime.now(pytz.timezone('Asia/Omsk'))
week_number = current_date.isocalendar()[1]
week_type = get_week_type(current_date)
week_status = f"{week_number} {week_type}"
role = "Администратор" if is_teacher else "Студент"
message_text = (
f"👤 **Личный кабинет**\n\n"
f"**Имя:** {first_name} {last_name}\n"
f"**Ник:** @{user[0]}\n"
f"**ID:** `{user_id}`\n\n"
f"**Роль:** {role}\n"
f"**Группа:** {user[2]}\n"
f"**Курс:** {user[1]}\n\n"
f"**Неделя:** {week_status}\n"
f"**Дата:** {current_date.strftime('%d.%m.%Y')}\n"
f"**Время:** {current_date.strftime('%H:%M:%S')}\n"
)
buttons = [
[
InlineKeyboardButton("🔄 Изменить группу", callback_data='change_direction'),
InlineKeyboardButton("🔄 Изменить курс", callback_data='change_course')
],
[InlineKeyboardButton("🔔 Уведомления", callback_data='manage_notifications')]
]
if is_teacher:
buttons.append([InlineKeyboardButton("🔧 Для администраторов", callback_data='admin_panel')])
reply_markup = InlineKeyboardMarkup(buttons)
if update.callback_query:
await update.callback_query.edit_message_text(
text=message_text,
reply_markup=reply_markup,
parse_mode='Markdown'
)
else:
await update.message.reply_text(
text=message_text,
reply_markup=reply_markup,
parse_mode='Markdown'
)
async def admin_panel(update: Update, context: CallbackContext):
query = update.callback_query
user_id = query.from_user.id
logger.info(f"Accessing admin panel - User ID: {user_id}")
if not is_teacher_user(user_id):
await query.answer("❌ Нет доступа!", show_alert=True)
return
buttons = [
[InlineKeyboardButton("📈 Статистика", callback_data='show_statistics')],
[InlineKeyboardButton("📢 Рассылки", callback_data='announcements_menu')],
[InlineKeyboardButton("🔒 Блокировки", callback_data='blocking_menu')]
]
if user_id == MAIN_ADMIN_ID:
buttons.append([InlineKeyboardButton("➕ Назначить администратора", callback_data='promote_admin')])
buttons.append([InlineKeyboardButton("➖ Удалить администратора", callback_data='demote_admin')])
# Добавим кнопку "Назад" для возвращения в личный кабинет
buttons.append([InlineKeyboardButton("🔙 Назад", callback_data='personal_cabinet')])
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🔧 **Для администраторов:**", reply_markup=reply_markup, parse_mode='Markdown')
async def handle_callback(update: Update, context: CallbackContext):
query = update.callback_query
user_id = query.from_user.id
data = query.data
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
if data == 'change_course':
c.execute("SELECT course FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_course = result[0]
new_course = 1 if current_course == 4 else current_course + 1
c.execute("UPDATE users SET course = ? WHERE id = ?", (new_course, user_id))
conn.commit()
await query.answer(f"✅ Курс изменён на {new_course}")
await personal_cabinet(update, context)
else:
await query.answer("❌ Пользователь не найден.")
elif data == 'change_direction':
c.execute("SELECT direction FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_direction = result[0]
directions = ['Ф', 'У', 'ФМ', 'ГМУ', 'БИ']
try:
new_direction_index = (directions.index(current_direction) + 1) % len(directions)
new_direction = directions[new_direction_index]
c.execute("UPDATE users SET direction = ? WHERE id = ?", (new_direction, user_id))
conn.commit()
await query.answer(f"✅ Группа изменена на {new_direction}")
await personal_cabinet(update, context)
except ValueError:
await query.answer("❌ Некорректная текущая группа.")
else:
await query.answer("❌ Пользователь не найден.")
elif data.startswith("toggle_"):
notification_type = data.split("_")[1]
await toggle_notification(update, user_id, notification_type)
elif data == 'manage_notifications':
await manage_notifications(update, user_id)
elif data == 'admin_panel':
await admin_panel(update, context)
elif data == 'show_statistics' and is_teacher_user(user_id):
await show_statistics(update, context)
# Меню рассылок
elif data == 'announcements_menu' and is_teacher_user(user_id):
# Меню рассылок
buttons = [
[InlineKeyboardButton("📤 Отправить рассылку", callback_data='send_announcement')],
[InlineKeyboardButton("🗂 Глобальная рассылка", callback_data='send_global_announcement')],
[InlineKeyboardButton("🔙 Назад", callback_data='admin_panel')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="📢 **Меню рассылок:**", reply_markup=reply_markup, parse_mode='Markdown')
# Начало рассылки
elif data == 'send_announcement' and is_teacher_user(user_id):
# Выбор типа рассылки
buttons = [
[InlineKeyboardButton("📚 Домашнее задание", callback_data='mailing_homework')],
[InlineKeyboardButton("🔄 Изменения в расписании", callback_data='mailing_changes')],
[InlineKeyboardButton("📢 Оповещения группы", callback_data='mailing_announcements')],
[InlineKeyboardButton("🏫 Оповещения университета", callback_data='mailing_university')],
[InlineKeyboardButton("🔙 Назад", callback_data='announcements_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="📤 **Выберите тип рассылки:**", reply_markup=reply_markup, parse_mode='Markdown')
# Выбор типа рассылки для групповой отправки
elif data.startswith('mailing_') and not data.startswith('mailing_target'):
mailing_type = data.split('_')[1]
context.user_data['mailing_type'] = mailing_type
# Выбор целевой аудитории
buttons = [
[InlineKeyboardButton("📆 По курсу и группе", callback_data='mailing_target_group')],
[InlineKeyboardButton("🔙 Назад", callback_data='send_announcement')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text=f"📤 **Выбрано: {mailing_type.capitalize()}**\nВыберите целевую аудиторию рассылки:", reply_markup=reply_markup, parse_mode='Markdown')
elif data == 'mailing_target_group' and is_teacher_user(user_id):
# Запросить сообщение для рассылки
await query.edit_message_text("📝 **Введите сообщение для рассылки вашей группе:**")
context.user_data['awaiting_mailing_message'] = True
elif data == 'send_global_announcement' and is_teacher_user(user_id):
# Выбор типа глобальной рассылки
buttons = [
[InlineKeyboardButton("📚 Домашнее задание", callback_data='global_mailing_homework')],
[InlineKeyboardButton("🔄 Изменения в расписании", callback_data='global_mailing_changes')],
[InlineKeyboardButton("📢 Оповещения группы", callback_data='global_mailing_announcements')],
[InlineKeyboardButton("🏫 Оповещения университета", callback_data='global_mailing_university')],
[InlineKeyboardButton("🔙 Назад", callback_data='announcements_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🗂 **Выберите тип глобальной рассылки:**", reply_markup=reply_markup, parse_mode='Markdown')
# Выбор типа рассылки для глобальной отправки
elif data.startswith('global_mailing_') and is_teacher_user(user_id):
mailing_type = data.split('_')[2]
context.user_data['mailing_type'] = mailing_type
context.user_data['mailing_target'] = 'global'
# Запросить сообщение для глобальной рассылки
await query.edit_message_text("📝 **Введите сообщение для глобальной рассылки:**")
context.user_data['awaiting_mailing_message'] = True
# Меню блокировок
elif data == 'blocking_menu' and is_teacher_user(user_id):
# Меню блокировок
buttons = [
[InlineKeyboardButton("🔒 Заблокировать пользователя", callback_data='block_user')],
[InlineKeyboardButton("🔓 Разблокировать пользователя", callback_data='unblock_user')],
[InlineKeyboardButton("👤 Просмотреть заблокированных пользователей", callback_data='view_blocked_users')],
[InlineKeyboardButton("🔙 Назад", callback_data='admin_panel')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🔒 **Меню блокировок:**", reply_markup=reply_markup, parse_mode='Markdown')
# Просмотр заблокированных пользователей
elif data == 'view_blocked_users' and is_teacher_user(user_id):
await view_blocked_users(update, context)
elif data.startswith('page-') and is_teacher_user(user_id):
# Обработка пагинации заблокированных пользователей
try:
page_number = int(data.split('-')[1])
await view_blocked_users(update, context, page_number=page_number)
except (IndexError, ValueError):
await query.answer("❌ Неверный номер страницы.", show_alert=True)
elif data == 'personal_cabinet' or data == 'back_to_admin':
await personal_cabinet(update, context)
else:
await query.answer("❓ Неизвестная команда.", show_alert=True)
async def collect_mailing_message(update: Update, context: CallbackContext) -> None:
user_id = update.message.from_user.id
if not is_teacher_user(user_id) or not context.user_data.get('awaiting_mailing_message', False):
return
mailing_type = context.user_data.get('mailing_type')
mailing_target = context.user_data.get('mailing_target', 'group')
message_text = update.message.text
if not message_text:
await update.message.reply_text("❌ Сообщение не может быть пустым.")
return
context.user_data['message_text'] = message_text
context.user_data['awaiting_mailing_message'] = False
confirm_message = f"📝 **Вот ваш текст:**\n\n{message_text}\n\n📤 Хотите отправить?"
buttons = [
[
InlineKeyboardButton("✅ Да", callback_data='confirm_send_mailing'),
InlineKeyboardButton("❌ Нет", callback_data='cancel_send_mailing')
]
]
reply_markup = InlineKeyboardMarkup(buttons)
await update.message.reply_text(confirm_message, reply_markup=reply_markup, parse_mode='Markdown')
async def handle_mailing_confirm(update: Update, context: CallbackContext) -> None:
query = update.callback_query
user_id = query.from_user.id
data = query.data
if data == 'confirm_send_mailing':
await send_mailing(update, context)
await query.edit_message_text("✅ Рассылка успешно отправлена.")
elif data == 'cancel_send_mailing':
await query.edit_message_text("❌ Отправка рассылки отменена.")
else:
await query.answer("❓ Неизвестная команда.")
async def send_mailing(update: Update, context: CallbackContext) -> None:
mailing_type = context.user_data.get('mailing_type')
mailing_target = context.user_data.get('mailing_target', 'group')
message_text = context.user_data.get('message_text')
if not mailing_type or not message_text:
return
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
if mailing_target == 'global':
# Получаем только пользователей, подписанных на данный тип рассылки
c.execute("SELECT id FROM users WHERE subscribed = 1 AND notifications LIKE ?", (f'%{mailing_type}%',))
users = c.fetchall()
# Включаем администраторов
c.execute("SELECT id FROM teachers")
admins = c.fetchall()
recipients = set(users) | set(admins)
elif mailing_target == 'group':
# Получаем курс и группу отправителя
c.execute("SELECT course, direction FROM users WHERE id = ?", (update.effective_user.id,))
user_info = c.fetchone()
if not user_info:
logger.error("Информация о пользователе не найдена для рассылки по группе.")
return
course, direction = user_info
c.execute("SELECT id FROM users WHERE course = ? AND direction = ? AND subscribed = 1 AND notifications LIKE ?",
(course, direction, f'%{mailing_type}%'))
users = c.fetchall()
# Включаем администраторов
c.execute("SELECT id FROM teachers")
admins = c.fetchall()
recipients = set(users) | set(admins)
else:
logger.error(f"Неизвестный тип целевой аудитории рассылки: {mailing_target}")
return
except sqlite3.Error as e:
logger.error(f"Ошибка при получении получателей рассылки: {e}")
return
finally:
conn.close()
# Отправка сообщений
for recipient in recipients:
try:
await context.bot.send_message(chat_id=recipient[0], text=message_text)
except Exception as e:
logger.error(f"Не удалось отправить сообщение пользователю {recipient[0]}: {e}")
# Очистка context
context.user_data['mailing_type'] = None
context.user_data['mailing_target'] = None
context.user_data['message_text'] = None
async def manage_notifications(update: Update, user_id):
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
c.execute("SELECT notifications FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_notifications = result[0].split(',')
else:
current_notifications = []
notification_map = {
'homework': '📚 Домашнее задание',
'changes': '🔄 Изменения в расписании',
'announcements': '📢 Оповещения группы',
'university': '🏫 Оповещения университета'
}
buttons = []
for n_type, label in notification_map.items():
enabled = n_type in current_notifications
text = f"{label}: {'✅' if enabled else '❌'}"
buttons.append([InlineKeyboardButton(text, callback_data=f"toggle_{n_type}")])
buttons.append([InlineKeyboardButton("🔙 Назад", callback_data="personal_cabinet")])
reply_markup = InlineKeyboardMarkup(buttons)
await update.callback_query.edit_message_text("📌 **Выберите типы уведомлений:**", reply_markup=reply_markup, parse_mode='Markdown')
except sqlite3.Error as e:
logger.error(f"Ошибка при управлении уведомлениями: {e}")
await update.callback_query.edit_message_text("❌ Произошла ошибка при управлении уведомлениями.")
finally:
conn.close()
async def toggle_notification(update: Update, user_id, notification_type):
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
c.execute("SELECT notifications FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_notifications = result[0].split(',')
else:
current_notifications = []
if notification_type in current_notifications:
current_notifications.remove(notification_type)
else:
current_notifications.append(notification_type)
updated_notifications = ','.join(current_notifications)
c.execute("UPDATE users SET notifications = ? WHERE id = ?", (updated_notifications, user_id))
conn.commit()
except sqlite3.Error as e:
logger.error(f"Ошибка при переключении уведомления '{notification_type}': {e}")
await update.callback_query.answer("❌ Произошла ошибка при изменении уведомлений.", show_alert=True)
return
finally:
conn.close()
await manage_notifications(update, user_id)
async def view_blocked_users(update: Update, context: CallbackContext, page_number=0):
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
c.execute(
"SELECT username, id, blocked_until FROM users WHERE blocked_until IS NOT NULL AND blocked_until > datetime('now','localtime')"
)
blocked_users = c.fetchall()
except sqlite3.Error as e:
logger.error(f"Ошибка при получении заблокированных пользователей: {e}")
await update.callback_query.edit_message_text("❌ Произошла ошибка при получении заблокированных пользователей.")
return
finally:
conn.close()
items_per_page = 10
total_pages = (len(blocked_users) + items_per_page - 1) // items_per_page
if total_pages == 0:
await update.callback_query.edit_message_text("🔒 **Заблокированные пользователи отсутствуют.**")
return
if page_number < 0 or page_number >= total_pages:
await update.callback_query.edit_message_text("❗ Неверный номер страницы.")
return
start_index = page_number * items_per_page
end_index = min(start_index + items_per_page, len(blocked_users))
blocked_list = blocked_users[start_index:end_index]
message_text = f"🔒 **Заблокированные пользователи (Страница {page_number + 1}/{total_pages}):**\n\n"
for i, user in enumerate(blocked_list, start=start_index + 1):
blocked_until = datetime.fromisoformat(user[2])
time_left = str(blocked_until - datetime.now()).split('.')[0]
message_text += f"{i}. {user[0]} (@{user[1]})\nДо разблокировки: {time_left}\n\n"
buttons = [
[
InlineKeyboardButton("⬅️ Назад", callback_data=f"page-{page_number - 1}") if page_number > 0 else InlineKeyboardButton("⬅️ Назад", callback_data='noop'),
InlineKeyboardButton(f"Страница {page_number + 1} из {total_pages}", callback_data='noop'),
InlineKeyboardButton("Вперёд ➡️", callback_data=f"page-{page_number + 1}") if page_number + 1 < total_pages else InlineKeyboardButton("Вперёд ➡️", callback_data='noop'),
],
[InlineKeyboardButton("🔙 Назад", callback_data='blocking_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await update.callback_query.edit_message_text(message_text.strip(), reply_markup=reply_markup, parse_mode='Markdown')
def is_teacher_user(user_id):
if user_id == MAIN_ADMIN_ID:
return True
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute("SELECT 1 FROM teachers WHERE id = ?", (user_id,))
result = c.fetchone()
conn.close()
return result is not None
if __name__ == '__main__':
# Создание конфигурационного файла, если он не существует
if not os.path.exists('config.ini'):
create_config_file()
print("📄 Пожалуйста, отредактируйте config.ini и установите токен бота.")
exit(1)
init_db()
# Создание и инициализация объекта application
application = ApplicationBuilder().token(TOKEN).build()
# Добавление обработчиков команд и сообщений
application.add_handler(CommandHandler("start", start))
application.add_handler(CallbackQueryHandler(handle_callback, pattern='^.*$'))
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
application.add_handler(MessageHandler(filters.TEXT & filters.Regex('^.*$'), collect_mailing_message))
application.add_handler(MessageHandler(filters.PHOTO, collect_mailing_message))
# Обработчик для подтверждения рассылки
application.add_handler(CallbackQueryHandler(handle_mailing_confirm, pattern='^(confirm_send_mailing|cancel_send_mailing)$'))
# Запуск бота
application.run_polling(). Помоги мне и реши проблемы с ошибкой. Вышли полный кодЗдравствуйте!
Ошибка, которую вы получаете:
```
SyntaxError: expected 'except' or 'finally' block
```
указывает на то, что у вас есть блок `try:`, который не содержит обязательных блоков `except:` или `finally:`. В вашем случае это происходит в функции `handle_callback`. Давайте рассмотрим, как это можно исправить.
### Причина ошибки
В вашем коде в функции `handle_callback` есть блок `try:`, но отсутствуют соответствующие блоки `except:` или `finally:`, что и вызывает синтаксическую ошибку. В Python после блока `try:` обязательно должны следовать `except:`, `finally:` или оба этих блока.
### Как исправить
Необходимо добавить блоки `except:` и `finally:` после блока `try:`. Это позволит корректно обрабатывать возможные исключения и закрывать соединение с базой данных независимо от того, произошло исключение или нет.
Ниже приведен исправленный вариант функции `handle_callback` с добавленными блоками `except:` и `finally:`:
```python
async def handle_callback(update: Update, context: CallbackContext):
query = update.callback_query
user_id = query.from_user.id
data = query.data
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
if data == 'change_course':
c.execute("SELECT course FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_course = result[0]
new_course = 1 if current_course == 4 else current_course + 1
c.execute("UPDATE users SET course = ? WHERE id = ?", (new_course, user_id))
conn.commit()
await query.answer(f"✅ Курс изменён на {new_course}")
await personal_cabinet(update, context)
else:
await query.answer("❌ Пользователь не найден.")
elif data == 'change_direction':
c.execute("SELECT direction FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_direction = result[0]
directions = ['Ф', 'У', 'ФМ', 'ГМУ', 'БИ']
try:
new_direction_index = (directions.index(current_direction) + 1) % len(directions)
new_direction = directions[new_direction_index]
c.execute("UPDATE users SET direction = ? WHERE id = ?", (new_direction, user_id))
conn.commit()
await query.answer(f"✅ Группа изменена на {new_direction}")
await personal_cabinet(update, context)
except ValueError:
await query.answer("❌ Некорректная текущая группа.")
else:
await query.answer("❌ Пользователь не найден.")
elif data.startswith("toggle_"):
notification_type = data.split("_")[1]
await toggle_notification(update, user_id, notification_type)
elif data == 'manage_notifications':
await manage_notifications(update, user_id)
elif data == 'admin_panel':
await admin_panel(update, context)
elif data == 'show_statistics' and is_teacher_user(user_id):
await show_statistics(update, context)
# Меню рассылок
elif data == 'announcements_menu' and is_teacher_user(user_id):
# Меню рассылок
buttons = [
[InlineKeyboardButton("📤 Отправить рассылку", callback_data='send_announcement')],
[InlineKeyboardButton("🗂 Глобальная рассылка", callback_data='send_global_announcement')],
[InlineKeyboardButton("🔙 Назад", callback_data='admin_panel')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="📢 **Меню рассылок:**", reply_markup=reply_markup, parse_mode='Markdown')
# Начало рассылки
elif data == 'send_announcement' and is_teacher_user(user_id):
# Выбор типа рассылки
buttons = [
[InlineKeyboardButton("📚 Домашнее задание", callback_data='mailing_homework')],
[InlineKeyboardButton("🔄 Изменения в расписании", callback_data='mailing_changes')],
[InlineKeyboardButton("📢 Оповещения группы", callback_data='mailing_announcements')],
[InlineKeyboardButton("🏫 Оповещения университета", callback_data='mailing_university')],
[InlineKeyboardButton("🔙 Назад", callback_data='announcements_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="📤 **Выберите тип рассылки:**", reply_markup=reply_markup, parse_mode='Markdown')
# Выбор типа рассылки для групповой отправки
elif data.startswith('mailing_') and not data.startswith('mailing_target'):
mailing_type = data.split('_')[1]
context.user_data['mailing_type'] = mailing_type
# Выбор целевой аудитории
buttons = [
[InlineKeyboardButton("📆 По курсу и группе", callback_data='mailing_target_group')],
[InlineKeyboardButton("🔙 Назад", callback_data='send_announcement')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text=f"📤 **Выбрано: {mailing_type.capitalize()}**\nВыберите целевую аудиторию рассылки:", reply_markup=reply_markup, parse_mode='Markdown')
elif data == 'mailing_target_group' and is_teacher_user(user_id):
# Запросить сообщение для рассылки
await query.edit_message_text("📝 **Введите сообщение для рассылки вашей группе:**")
context.user_data['awaiting_mailing_message'] = True
elif data == 'send_global_announcement' and is_teacher_user(user_id):
# Выбор типа глобальной рассылки
buttons = [
[InlineKeyboardButton("📚 Домашнее задание", callback_data='global_mailing_homework')],
[InlineKeyboardButton("🔄 Изменения в расписании", callback_data='global_mailing_changes')],
[InlineKeyboardButton("📢 Оповещения группы", callback_data='global_mailing_announcements')],
[InlineKeyboardButton("🏫 Оповещения университета", callback_data='global_mailing_university')],
[InlineKeyboardButton("🔙 Назад", callback_data='announcements_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🗂 **Выберите тип глобальной рассылки:**", reply_markup=reply_markup, parse_mode='Markdown')
# Выбор типа рассылки для глобальной отправки
elif data.startswith('global_mailing_') and is_teacher_user(user_id):
mailing_type = data.split('_')[2]
context.user_data['mailing_type'] = mailing_type
context.user_data['mailing_target'] = 'global'
# Запросить сообщение для глобальной рассылки
await query.edit_message_text("📝 **Введите сообщение для глобальной рассылки:**")
context.user_data['awaiting_mailing_message'] = True
# Меню блокировок
elif data == 'blocking_menu' and is_teacher_user(user_id):
# Меню блокировок
buttons = [
[InlineKeyboardButton("🔒 Заблокировать пользователя", callback_data='block_user')],
[InlineKeyboardButton("🔓 Разблокировать пользователя", callback_data='unblock_user')],
[InlineKeyboardButton("👤 Просмотреть заблокированных пользователей", callback_data='view_blocked_users')],
[InlineKeyboardButton("🔙 Назад", callback_data='admin_panel')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🔒 **Меню блокировок:**", reply_markup=reply_markup, parse_mode='Markdown')
# Просмотр заблокированных пользователей
elif data == 'view_blocked_users' and is_teacher_user(user_id):
await view_blocked_users(update, context)
elif data.startswith('page-') and is_teacher_user(user_id):
# Обработка пагинации заблокированных пользователей
try:
page_number = int(data.split('-')[1])
await view_blocked_users(update, context, page_number=page_number)
except (IndexError, ValueError):
await query.answer("❌ Неверный номер страницы.", show_alert=True)
elif data == 'personal_cabinet' or data == 'back_to_admin':
await personal_cabinet(update, context)
else:
await query.answer("❓ Неизвестная команда.", show_alert=True)
except sqlite3.Error as e:
logger.error(f"Ошибка базы данных: {e}")
await query.answer("❌ Произошла ошибка при обработке запроса.", show_alert=True)
finally:
conn.close()
```
### Пояснения к изменениям
1. **Добавлены блоки `except` и `finally`:**
- **`except sqlite3.Error as e:`** - Здесь мы ловим ошибки, связанные с базой данных SQLite и логируем их. Также отправляем пользователю сообщение об ошибке.
- **`finally:`** - В этом блоке мы гарантируем, что соединение с базой данных будет закрыто независимо от того, произошло исключение или нет.
2. **Обработка исключений:**
- Внутри блока `except` вы можете добавлять любую необходимую обработку ошибок, например, уведомление администратора или попытку повторного подключения.
3. **Логирование:**
- Использование `logger.error` позволяет вам вести журнал ошибок, что полезно для отладки и мониторинга работы бота.
### Рекомендации
1. **Проверьте другие функции на наличие некорректных блоков `try`:**
Убедитесь, что все блоки `try:` в вашем коде имеют соответствующие блоки `except:` и/или `finally:`.
2. **Оптимизация обработчиков сообщений:**
В вашем основном блоке добавления обработчиков сообщений есть перекрывающиеся условия:
```python
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
application.add_handler(MessageHandler(filters.TEXT & filters.Regex('^.*$'), collect_mailing_message))
```
Это может привести к тому, что сообщения будут обрабатываться несколькими обработчиками одновременно. Рекомендуется использовать более специфичные фильтры или объединить логику в одном обработчике.
3. **Тестирование:**
После внесения изменений тщательно протестируйте бота, чтобы убедиться, что все функции работают корректно и что блоки `try-except` охватывают необходимые участки кода.
### Полный исправленный код
Ниже приведен полный исправленный код с добавленными блоками `except` и `finally` в функции `handle_callback`. Если у вас есть другие функции с блоками `try:`, убедитесь, что они также правильно оформлены.
```python
import pytz
import sqlite3
from datetime import datetime, timedelta
from telegram import Update, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import (
ApplicationBuilder,
CommandHandler,
CallbackContext,
MessageHandler,
filters,
CallbackQueryHandler,
)
import configparser
import os
import logging
# Настройка логирования
logging.basicConfig(
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO
)
logger = logging.getLogger(__name__)
# Чтение конфигурационного файла
config = configparser.ConfigParser()
config.read('config.ini')
# Получение токена из конфигурационного файла
TOKEN = config.get('Telegram', 'Token')
# ID главного администратора
MAIN_ADMIN_ID = 1108429314
def create_config_file():
config['Telegram'] = {'Token': 'your_bot_token_here'}
with open('config.ini', 'w') as configfile:
config.write(configfile)
def init_db():
conn = sqlite3.connect('university.db')
c = conn.cursor()
# Создание таблицы schedule
c.execute('''CREATE TABLE IF NOT EXISTS schedule
(course INTEGER, direction TEXT, week_type TEXT, day TEXT, slot INTEGER, time TEXT, details TEXT)''')
# Создание таблицы users с колонкой created_at
c.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT,
course INTEGER,
direction TEXT,
subscribed INTEGER DEFAULT 1,
notifications TEXT DEFAULT 'homework,changes,announcements,university',
blocked_until TEXT DEFAULT NULL,
created_at TEXT DEFAULT (datetime('now','localtime'))
)
''')
# Создание таблицы teachers
c.execute('''CREATE TABLE IF NOT EXISTS teachers
(id INTEGER PRIMARY KEY)''')
# Добавить главного администратора в таблицу преподавателей
c.execute("INSERT OR IGNORE INTO teachers (id) VALUES (?)", (MAIN_ADMIN_ID,))
# Проверка и добавление колонки created_at, если таблица уже существует без неё
c.execute("PRAGMA table_info(users)")
columns = [info[1] for info in c.fetchall()]
if 'created_at' not in columns:
try:
c.execute("ALTER TABLE users ADD COLUMN created_at TEXT DEFAULT (datetime('now','localtime'))")
conn.commit()
print("Колонка 'created_at' успешно добавлена в таблицу 'users'.")
except sqlite3.OperationalError as e:
print(f"Ошибка при добавлении колонки 'created_at': {e}")
# Инициализация расписания
c.execute("DELETE FROM schedule")
times = ["8:30 - 10:00", "10:10 - 11:40", "12:30 - 14:00", "14:10 - 15:40", "15:50 - 17:20", "17:30 - 19:00"]
directions = ['Ф', 'У', 'ФМ', 'ГМУ', 'БИ']
schedule_bi = {
"чётная": {
"Понедельник": ["Матем (л) (222)", "Матем (с) (222)", "Физ-ра", "Англ (209)", "", ""],
"Вторник": ["История (с) (108)", "История (л) (108)", "ИТПД (с) (217)", "", "", ""],
"Среда": ["", "", "", "ИТПД (с) (217)", "Phython (л) (111)", "Phython (л) (111)"],
"Четверг": ["", "История (с) (204)", "Менеджмент (л) (209)", "", "", ""],
"Пятница": ["", "", "", "БЖД (л) (304)", "Менеджмент (с) (108)", "Физ-ра"]
},
"нечётная": {
"Понедельник": ["Матем (л) (222)", "Матем (с) (222)", "Физ-ра", "Англ (209)", "", ""],
"Вторник": ["Англ (209)", "История (л) (108)", "ИТПД (с) (217)", "История ФУ (л) (211)", "", ""],
"Среда": ["", "", "", "ИТПД (с) (217)", "Введение в спец (л) (111)", "Phython (с) (111)"],
"Четверг": ["Введение в спец (с) (213)", "Матем (с) (204)", "История (с) (209)", "", "", ""],
"Пятница": ["", "", "", "БЖД (с) (304)", "Менеджмент (с) (108)", "Физ-ра"]
}
}
for course in range(1, 5):
for direction in directions:
schedule_data = {
"чётная": {day: [""] * 6 for day in ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"]},
"нечётная": {day: [""] * 6 for day in ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"]}
}
if direction == 'БИ' and course == 1:
schedule_data = schedule_bi
else:
for week_type, days in schedule_data.items():
for day in days:
days[day][0] = f"Курс {course}, Группа {direction}"
for week_type, days in schedule_data.items():
for day, slots in days.items():
for slot in range(6):
time = times[slot]
detail = slots[slot]
c.execute(
"INSERT INTO schedule (course, direction, week_type, day, slot, time, details) VALUES (?, ?, ?, ?, ?, ?, ?)",
(course, direction, week_type, day, slot + 1, time, detail)
)
conn.commit()
conn.close()
def get_day_name(index):
days_rus = [
"Понедельник",
"Вторник",
"Среда",
"Четверг",
"Пятница",
"Суббота",
"Воскресенье"
]
return days_rus[index]
def is_even_week(date):
week_number = date.isocalendar()[1]
return week_number % 2 == 0
def get_week_type(date):
return 'чётная' if is_even_week(date) else 'нечётная'
def get_week_type_short(date):
return 'чёт' if is_even_week(date) else 'нечёт'
def get_schedule_for_day(course, direction, day_name, date):
week_type = get_week_type(date)
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute(
"SELECT slot, time, details FROM schedule WHERE course = ? AND direction = ? AND week_type = ? AND day = ? ORDER BY slot",
(course, direction, week_type, day_name)
)
results = c.fetchall()
conn.close()
if not results:
return "❌ Нет занятий"
day_schedule = ""
for slot, time, details in results:
if details:
day_schedule += f"{slot}. {time}\n{details}\n\n"
return day_schedule.strip() if day_schedule else "❌ Нет занятий"
async def start(update: Update, context: CallbackContext) -> None:
user_id = update.effective_user.id
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute("SELECT blocked_until FROM users WHERE id=?", (user_id,))
blocked_until = c.fetchone()
if blocked_until and blocked_until[0]:
blocked_until_date = datetime.fromisoformat(blocked_until[0])
if blocked_until_date > datetime.now():
await update.message.reply_text(f"❌ Вы заблокированы! До разблокировки {blocked_until_date}.")
conn.close()
return
conn.close()
# Генерация меню
keyboard = [
["🗓 Выбрать дату расписания", "📅 Расписание на завтра", "📅 Расписание на сегодня"],
["📆 Понедельник", "📆 Вторник", "📆 Среда"],
["📆 Четверг", "📆 Пятница", "📆 Суббота"],
["👤 Личный кабинет"]
]
reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True, resize_keyboard=True)
# Приветственное сообщение с меню
await update.message.reply_text(
'👋 Привет! Поменяй группу и курс на соответствующие в своём личном кабинете.',
reply_markup=reply_markup
)
# Отображение личного кабинета
await personal_cabinet(update, context)
async def handle_message(update: Update, context: CallbackContext) -> None:
user_id = update.message.from_user.id
user_message = update.message.text.strip().lower()
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute("SELECT blocked_until FROM users WHERE id=?", (user_id,))
blocked_until = c.fetchone()
if blocked_until and blocked_until[0]:
blocked_until_date = datetime.fromisoformat(blocked_until[0])
if blocked_until_date > datetime.now():
await update.message.reply_text(f"❌ Вы заблокированы! До разблокировки {blocked_until_date}.")
conn.close()
return
# Обработка специальных действий
if context.user_data.get('action') == 'promote_admin':
try:
promote_id = int(user_message)
c.execute("SELECT id FROM users WHERE id = ?", (promote_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не найден.")
else:
c.execute("INSERT OR IGNORE INTO teachers (id) VALUES (?)", (promote_id,))
conn.commit()
await update.message.reply_text(f"✅ Пользователь с ID {promote_id} назначен администратором.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при назначении администратора: {e}")
await update.message.reply_text("❌ Произошла ошибка при назначении администратора.")
finally:
conn.close()
context.user_data['action'] = None
return
if context.user_data.get('action') == 'demote_admin':
try:
demote_id = int(user_message)
if demote_id == MAIN_ADMIN_ID:
await update.message.reply_text("❌ Невозможно удалить главного администратора.")
else:
c.execute("SELECT id FROM teachers WHERE id = ?", (demote_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не является администратором.")
else:
c.execute("DELETE FROM teachers WHERE id = ?", (demote_id,))
conn.commit()
await update.message.reply_text(f"✅ Пользователь с ID {demote_id} удален из администраторов.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при удалении администратора: {e}")
await update.message.reply_text("❌ Произошла ошибка при удалении администратора.")
finally:
conn.close()
context.user_data['action'] = None
return
if context.user_data.get('action') == 'block_user':
try:
block_id = int(user_message)
c.execute("SELECT id FROM users WHERE id = ?", (block_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не найден.")
else:
# Установим блокировку на 24 часа
block_until = datetime.now() + timedelta(hours=24)
c.execute("UPDATE users SET blocked_until = ? WHERE id = ?", (block_until.isoformat(), block_id))
conn.commit()
await update.message.reply_text(f"🔒 Пользователь с ID {block_id} заблокирован до {block_until}.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при блокировке пользователя: {e}")
await update.message.reply_text("❌ Произошла ошибка при блокировке пользователя.")
finally:
conn.close()
context.user_data['action'] = None
return
if context.user_data.get('action') == 'unblock_user':
try:
unblock_id = int(user_message)
if unblock_id == MAIN_ADMIN_ID:
await update.message.reply_text("❌ Невозможно разблокировать главного администратора.")
conn.close()
context.user_data['action'] = None
return
c.execute("SELECT id FROM users WHERE id = ? AND blocked_until IS NOT NULL", (unblock_id,))
if not c.fetchone():
await update.message.reply_text("❌ Пользователь с таким ID не заблокирован.")
else:
c.execute("UPDATE users SET blocked_until = NULL WHERE id = ?", (unblock_id,))
conn.commit()
await update.message.reply_text(f"🔓 Пользователь с ID {unblock_id} разблокирован.")
except ValueError:
await update.message.reply_text("❌ Неверный формат ID. Пожалуйста, введите числовой ID пользователя.")
except sqlite3.Error as e:
logger.error(f"Ошибка при разблокировке пользователя: {e}")
await update.message.reply_text("❌ Произошла ошибка при разблокировке пользователя.")
finally:
conn.close()
context.user_data['action'] = None
return
conn.close()
# Основная логика обработки сообщений
logger.info(f"Received message: {user_message}")
if context.user_data.get('awaiting_date', False):
if user_message.isdigit():
day = int(user_message)
current_month = datetime.now(pytz.timezone('Asia/Omsk')).month
current_year = datetime.now(pytz.timezone('Asia/Omsk')).year
try:
date_obj = datetime(current_year, current_month, day, tzinfo=pytz.timezone('Asia/Omsk'))
if date_obj.month != current_month:
raise ValueError
await show_schedule_by_date(update, context, date_obj)
except ValueError:
await update.message.reply_text('❌ Неверный день. Пожалуйста, введи число от 1 до последнего дня месяца.')
finally:
context.user_data['awaiting_date'] = False
else:
await update.message.reply_text('❌ Пожалуйста, введи число для получения расписания.')
return
if user_message in ["расписание на сегодня", "расписание на завтра", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота"]:
await show_schedule(update, context, user_message)
elif user_message == "выбрать дату расписания":
logger.info("User chose to select a specific date for schedule")
context.user_data['awaiting_date'] = True
await update.message.reply_text("🗓 Введи необходимое число этого месяца:")
elif user_message == "личный кабинет":
logger.info("User accessing personal cabinet")
await personal_cabinet(update, context)
else:
logger.warning(f"Unrecognized menu option: {user_message}")
await update.message.reply_text('❓ Выбери опцию из меню.')
async def personal_cabinet(update: Update, context: CallbackContext):
user_id = update.effective_user.id
username = update.effective_user.username or ""
first_name = update.effective_user.first_name or ""
last_name = update.effective_user.last_name or ""
conn = sqlite3.connect('university.db')
c = conn.cursor()
# Проверка существования пользователя в базе данных
c.execute(
"SELECT username, course, direction, subscribed, notifications FROM users WHERE id = ?",
(user_id,)
)
user = c.fetchone()
# Добавление пользователя, если он ещё не существует
if not user:
current_time = datetime.now(pytz.timezone('Asia/Omsk')).isoformat()
c.execute(
"INSERT INTO users (id, username, course, direction, created_at) VALUES (?, ?, ?, ?, ?)",
(user_id, username, 1, "Ф", current_time)
)
conn.commit()
user = (username, 1, "Ф", 1, 'homework,changes,announcements,university')
# Проверка существования ID в таблице teachers, добавление, если его там нет
c.execute("SELECT 1 FROM teachers WHERE id = ?", (user_id,))
is_teacher = c.fetchone() is not None
if user_id == MAIN_ADMIN_ID and not is_teacher:
c.execute("INSERT INTO teachers (id) VALUES (?)", (user_id,))
conn.commit()
is_teacher = True
conn.close()
current_date = datetime.now(pytz.timezone('Asia/Omsk'))
week_number = current_date.isocalendar()[1]
week_type = get_week_type(current_date)
week_status = f"{week_number} {week_type}"
role = "Администратор" if is_teacher else "Студент"
message_text = (
f"👤 **Личный кабинет**\n\n"
f"**Имя:** {first_name} {last_name}\n"
f"**Ник:** @{user[0]}\n"
f"**ID:** `{user_id}`\n\n"
f"**Роль:** {role}\n"
f"**Группа:** {user[2]}\n"
f"**Курс:** {user[1]}\n\n"
f"**Неделя:** {week_status}\n"
f"**Дата:** {current_date.strftime('%d.%m.%Y')}\n"
f"**Время:** {current_date.strftime('%H:%M:%S')}\n"
)
buttons = [
[
InlineKeyboardButton("🔄 Изменить группу", callback_data='change_direction'),
InlineKeyboardButton("🔄 Изменить курс", callback_data='change_course')
],
[InlineKeyboardButton("🔔 Уведомления", callback_data='manage_notifications')]
]
if is_teacher:
buttons.append([InlineKeyboardButton("🔧 Для администраторов", callback_data='admin_panel')])
reply_markup = InlineKeyboardMarkup(buttons)
if update.callback_query:
await update.callback_query.edit_message_text(
text=message_text,
reply_markup=reply_markup,
parse_mode='Markdown'
)
else:
await update.message.reply_text(
text=message_text,
reply_markup=reply_markup,
parse_mode='Markdown'
)
async def admin_panel(update: Update, context: CallbackContext):
query = update.callback_query
user_id = query.from_user.id
logger.info(f"Accessing admin panel - User ID: {user_id}")
if not is_teacher_user(user_id):
await query.answer("❌ Нет доступа!", show_alert=True)
return
buttons = [
[InlineKeyboardButton("📈 Статистика", callback_data='show_statistics')],
[InlineKeyboardButton("📢 Рассылки", callback_data='announcements_menu')],
[InlineKeyboardButton("🔒 Блокировки", callback_data='blocking_menu')]
]
if user_id == MAIN_ADMIN_ID:
buttons.append([InlineKeyboardButton("➕ Назначить администратора", callback_data='promote_admin')])
buttons.append([InlineKeyboardButton("➖ Удалить администратора", callback_data='demote_admin')])
# Добавим кнопку "Назад" для возвращения в личный кабинет
buttons.append([InlineKeyboardButton("🔙 Назад", callback_data='personal_cabinet')])
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🔧 **Для администраторов:**", reply_markup=reply_markup, parse_mode='Markdown')
async def handle_callback(update: Update, context: CallbackContext):
query = update.callback_query
user_id = query.from_user.id
data = query.data
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
if data == 'change_course':
c.execute("SELECT course FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_course = result[0]
new_course = 1 if current_course == 4 else current_course + 1
c.execute("UPDATE users SET course = ? WHERE id = ?", (new_course, user_id))
conn.commit()
await query.answer(f"✅ Курс изменён на {new_course}")
await personal_cabinet(update, context)
else:
await query.answer("❌ Пользователь не найден.")
elif data == 'change_direction':
c.execute("SELECT direction FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_direction = result[0]
directions = ['Ф', 'У', 'ФМ', 'ГМУ', 'БИ']
try:
new_direction_index = (directions.index(current_direction) + 1) % len(directions)
new_direction = directions[new_direction_index]
c.execute("UPDATE users SET direction = ? WHERE id = ?", (new_direction, user_id))
conn.commit()
await query.answer(f"✅ Группа изменена на {new_direction}")
await personal_cabinet(update, context)
except ValueError:
await query.answer("❌ Некорректная текущая группа.")
else:
await query.answer("❌ Пользователь не найден.")
elif data.startswith("toggle_"):
notification_type = data.split("_")[1]
await toggle_notification(update, user_id, notification_type)
elif data == 'manage_notifications':
await manage_notifications(update, user_id)
elif data == 'admin_panel':
await admin_panel(update, context)
elif data == 'show_statistics' and is_teacher_user(user_id):
await show_statistics(update, context)
# Меню рассылок
elif data == 'announcements_menu' and is_teacher_user(user_id):
# Меню рассылок
buttons = [
[InlineKeyboardButton("📤 Отправить рассылку", callback_data='send_announcement')],
[InlineKeyboardButton("🗂 Глобальная рассылка", callback_data='send_global_announcement')],
[InlineKeyboardButton("🔙 Назад", callback_data='admin_panel')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="📢 **Меню рассылок:**", reply_markup=reply_markup, parse_mode='Markdown')
# Начало рассылки
elif data == 'send_announcement' and is_teacher_user(user_id):
# Выбор типа рассылки
buttons = [
[InlineKeyboardButton("📚 Домашнее задание", callback_data='mailing_homework')],
[InlineKeyboardButton("🔄 Изменения в расписании", callback_data='mailing_changes')],
[InlineKeyboardButton("📢 Оповещения группы", callback_data='mailing_announcements')],
[InlineKeyboardButton("🏫 Оповещения университета", callback_data='mailing_university')],
[InlineKeyboardButton("🔙 Назад", callback_data='announcements_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="📤 **Выберите тип рассылки:**", reply_markup=reply_markup, parse_mode='Markdown')
# Выбор типа рассылки для групповой отправки
elif data.startswith('mailing_') and not data.startswith('mailing_target'):
mailing_type = data.split('_')[1]
context.user_data['mailing_type'] = mailing_type
# Выбор целевой аудитории
buttons = [
[InlineKeyboardButton("📆 По курсу и группе", callback_data='mailing_target_group')],
[InlineKeyboardButton("🔙 Назад", callback_data='send_announcement')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text=f"📤 **Выбрано: {mailing_type.capitalize()}**\nВыберите целевую аудиторию рассылки:", reply_markup=reply_markup, parse_mode='Markdown')
elif data == 'mailing_target_group' and is_teacher_user(user_id):
# Запросить сообщение для рассылки
await query.edit_message_text("📝 **Введите сообщение для рассылки вашей группе:**")
context.user_data['awaiting_mailing_message'] = True
elif data == 'send_global_announcement' and is_teacher_user(user_id):
# Выбор типа глобальной рассылки
buttons = [
[InlineKeyboardButton("📚 Домашнее задание", callback_data='global_mailing_homework')],
[InlineKeyboardButton("🔄 Изменения в расписании", callback_data='global_mailing_changes')],
[InlineKeyboardButton("📢 Оповещения группы", callback_data='global_mailing_announcements')],
[InlineKeyboardButton("🏫 Оповещения университета", callback_data='global_mailing_university')],
[InlineKeyboardButton("🔙 Назад", callback_data='announcements_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🗂 **Выберите тип глобальной рассылки:**", reply_markup=reply_markup, parse_mode='Markdown')
# Выбор типа рассылки для глобальной отправки
elif data.startswith('global_mailing_') and is_teacher_user(user_id):
mailing_type = data.split('_')[2]
context.user_data['mailing_type'] = mailing_type
context.user_data['mailing_target'] = 'global'
# Запросить сообщение для глобальной рассылки
await query.edit_message_text("📝 **Введите сообщение для глобальной рассылки:**")
context.user_data['awaiting_mailing_message'] = True
# Меню блокировок
elif data == 'blocking_menu' and is_teacher_user(user_id):
# Меню блокировок
buttons = [
[InlineKeyboardButton("🔒 Заблокировать пользователя", callback_data='block_user')],
[InlineKeyboardButton("🔓 Разблокировать пользователя", callback_data='unblock_user')],
[InlineKeyboardButton("👤 Просмотреть заблокированных пользователей", callback_data='view_blocked_users')],
[InlineKeyboardButton("🔙 Назад", callback_data='admin_panel')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await query.edit_message_text(text="🔒 **Меню блокировок:**", reply_markup=reply_markup, parse_mode='Markdown')
# Просмотр заблокированных пользователей
elif data == 'view_blocked_users' and is_teacher_user(user_id):
await view_blocked_users(update, context)
elif data.startswith('page-') and is_teacher_user(user_id):
# Обработка пагинации заблокированных пользователей
try:
page_number = int(data.split('-')[1])
await view_blocked_users(update, context, page_number=page_number)
except (IndexError, ValueError):
await query.answer("❌ Неверный номер страницы.", show_alert=True)
elif data == 'personal_cabinet' or data == 'back_to_admin':
await personal_cabinet(update, context)
else:
await query.answer("❓ Неизвестная команда.", show_alert=True)
except sqlite3.Error as e:
logger.error(f"Ошибка базы данных: {e}")
await query.answer("❌ Произошла ошибка при обработке запроса.", show_alert=True)
finally:
conn.close()
async def collect_mailing_message(update: Update, context: CallbackContext) -> None:
user_id = update.message.from_user.id
if not is_teacher_user(user_id) or not context.user_data.get('awaiting_mailing_message', False):
return
mailing_type = context.user_data.get('mailing_type')
mailing_target = context.user_data.get('mailing_target', 'group')
message_text = update.message.text
if not message_text:
await update.message.reply_text("❌ Сообщение не может быть пустым.")
return
context.user_data['message_text'] = message_text
context.user_data['awaiting_mailing_message'] = False
confirm_message = f"📝 **Вот ваш текст:**\n\n{message_text}\n\n📤 Хотите отправить?"
buttons = [
[
InlineKeyboardButton("✅ Да", callback_data='confirm_send_mailing'),
InlineKeyboardButton("❌ Нет", callback_data='cancel_send_mailing')
]
]
reply_markup = InlineKeyboardMarkup(buttons)
await update.message.reply_text(confirm_message, reply_markup=reply_markup, parse_mode='Markdown')
async def handle_mailing_confirm(update: Update, context: CallbackContext) -> None:
query = update.callback_query
user_id = query.from_user.id
data = query.data
if data == 'confirm_send_mailing':
await send_mailing(update, context)
await query.edit_message_text("✅ Рассылка успешно отправлена.")
elif data == 'cancel_send_mailing':
await query.edit_message_text("❌ Отправка рассылки отменена.")
else:
await query.answer("❓ Неизвестная команда.")
async def send_mailing(update: Update, context: CallbackContext) -> None:
mailing_type = context.user_data.get('mailing_type')
mailing_target = context.user_data.get('mailing_target', 'group')
message_text = context.user_data.get('message_text')
if not mailing_type or not message_text:
return
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
if mailing_target == 'global':
# Получаем только пользователей, подписанных на данный тип рассылки
c.execute("SELECT id FROM users WHERE subscribed = 1 AND notifications LIKE ?", (f'%{mailing_type}%',))
users = c.fetchall()
# Включаем администраторов
c.execute("SELECT id FROM teachers")
admins = c.fetchall()
recipients = set(users) | set(admins)
elif mailing_target == 'group':
# Получаем курс и группу отправителя
c.execute("SELECT course, direction FROM users WHERE id = ?", (update.effective_user.id,))
user_info = c.fetchone()
if not user_info:
logger.error("Информация о пользователе не найдена для рассылки по группе.")
return
course, direction = user_info
c.execute("SELECT id FROM users WHERE course = ? AND direction = ? AND subscribed = 1 AND notifications LIKE ?",
(course, direction, f'%{mailing_type}%'))
users = c.fetchall()
# Включаем администраторов
c.execute("SELECT id FROM teachers")
admins = c.fetchall()
recipients = set(users) | set(admins)
else:
logger.error(f"Неизвестный тип целевой аудитории рассылки: {mailing_target}")
return
except sqlite3.Error as e:
logger.error(f"Ошибка при получении получателей рассылки: {e}")
return
finally:
conn.close()
# Отправка сообщений
for recipient in recipients:
try:
await context.bot.send_message(chat_id=recipient[0], text=message_text)
except Exception as e:
logger.error(f"Не удалось отправить сообщение пользователю {recipient[0]}: {e}")
# Очистка context
context.user_data['mailing_type'] = None
context.user_data['mailing_target'] = None
context.user_data['message_text'] = None
async def manage_notifications(update: Update, user_id):
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
c.execute("SELECT notifications FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_notifications = result[0].split(',')
else:
current_notifications = []
notification_map = {
'homework': '📚 Домашнее задание',
'changes': '🔄 Изменения в расписании',
'announcements': '📢 Оповещения группы',
'university': '🏫 Оповещения университета'
}
buttons = []
for n_type, label in notification_map.items():
enabled = n_type in current_notifications
text = f"{label}: {'✅' if enabled else '❌'}"
buttons.append([InlineKeyboardButton(text, callback_data=f"toggle_{n_type}")])
buttons.append([InlineKeyboardButton("🔙 Назад", callback_data="personal_cabinet")])
reply_markup = InlineKeyboardMarkup(buttons)
await update.callback_query.edit_message_text("📌 **Выберите типы уведомлений:**", reply_markup=reply_markup, parse_mode='Markdown')
except sqlite3.Error as e:
logger.error(f"Ошибка при управлении уведомлениями: {e}")
await update.callback_query.edit_message_text("❌ Произошла ошибка при управлении уведомлениями.")
finally:
conn.close()
async def toggle_notification(update: Update, user_id, notification_type):
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
c.execute("SELECT notifications FROM users WHERE id = ?", (user_id,))
result = c.fetchone()
if result:
current_notifications = result[0].split(',')
else:
current_notifications = []
if notification_type in current_notifications:
current_notifications.remove(notification_type)
else:
current_notifications.append(notification_type)
updated_notifications = ','.join(current_notifications)
c.execute("UPDATE users SET notifications = ? WHERE id = ?", (updated_notifications, user_id))
conn.commit()
except sqlite3.Error as e:
logger.error(f"Ошибка при переключении уведомления '{notification_type}': {e}")
await update.callback_query.answer("❌ Произошла ошибка при изменении уведомлений.", show_alert=True)
return
finally:
conn.close()
await manage_notifications(update, user_id)
async def view_blocked_users(update: Update, context: CallbackContext, page_number=0):
conn = sqlite3.connect('university.db')
c = conn.cursor()
try:
c.execute(
"SELECT username, id, blocked_until FROM users WHERE blocked_until IS NOT NULL AND blocked_until > datetime('now','localtime')"
)
blocked_users = c.fetchall()
except sqlite3.Error as e:
logger.error(f"Ошибка при получении заблокированных пользователей: {e}")
await update.callback_query.edit_message_text("❌ Произошла ошибка при получении заблокированных пользователей.")
return
finally:
conn.close()
items_per_page = 10
total_pages = (len(blocked_users) + items_per_page - 1) // items_per_page
if total_pages == 0:
await update.callback_query.edit_message_text("🔒 **Заблокированные пользователи отсутствуют.**")
return
if page_number < 0 or page_number >= total_pages:
await update.callback_query.edit_message_text("❗ Неверный номер страницы.")
return
start_index = page_number * items_per_page
end_index = min(start_index + items_per_page, len(blocked_users))
blocked_list = blocked_users[start_index:end_index]
message_text = f"🔒 **Заблокированные пользователи (Страница {page_number + 1}/{total_pages}):**\n\n"
for i, user in enumerate(blocked_list, start=start_index + 1):
blocked_until = datetime.fromisoformat(user[2])
time_left = str(blocked_until - datetime.now()).split('.')[0]
message_text += f"{i}. {user[0]} (@{user[1]})\nДо разблокировки: {time_left}\n\n"
buttons = [
[
InlineKeyboardButton("⬅️ Назад", callback_data=f"page-{page_number - 1}") if page_number > 0 else InlineKeyboardButton("⬅️ Назад", callback_data='noop'),
InlineKeyboardButton(f"Страница {page_number + 1} из {total_pages}", callback_data='noop'),
InlineKeyboardButton("Вперёд ➡️", callback_data=f"page-{page_number + 1}") if page_number + 1 < total_pages else InlineKeyboardButton("Вперёд ➡️", callback_data='noop'),
],
[InlineKeyboardButton("🔙 Назад", callback_data='blocking_menu')]
]
reply_markup = InlineKeyboardMarkup(buttons)
await update.callback_query.edit_message_text(message_text.strip(), reply_markup=reply_markup, parse_mode='Markdown')
def is_teacher_user(user_id):
if user_id == MAIN_ADMIN_ID:
return True
conn = sqlite3.connect('university.db')
c = conn.cursor()
c.execute("SELECT 1 FROM teachers WHERE id = ?", (user_id,))
result = c.fetchone()
conn.close()
return result is not None
if __name__ == '__main__':
# Создание конфигурационного файла, если он не существует
if not os.path.exists('config.ini'):
create_config_file()
print("📄 Пожалуйста, отредактируйте config.ini и установите токен бота.")
exit(1)
init_db()
# Создание и инициализация объекта application
application = ApplicationBuilder().token(TOKEN).build()
# Добавление обработчиков команд и сообщений
application.add_handler(CommandHandler("start", start))
application.add_handler(CallbackQueryHandler(handle_callback, pattern='^.*$'))
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
application.add_handler(MessageHandler(filters.TEXT & filters.Regex('^.*$'), collect_mailing_message))
application.add_handler(MessageHandler(filters.PHOTO, collect_mailing_message))
# Обработчик для подтверждения рассылки
application.add_handler(CallbackQueryHandler(handle_mailing_confirm, pattern='^(confirm_send_mailing|cancel_send_mailing)$'))
# Запуск бота
application.run_polling()
```
### Дополнительные рекомендации
1. **Проверка других блоков `try:`:**
Проверьте весь ваш код на наличие других блоков `try:`, которые не имеют соответствующих `except:` или `finally:`. Все блоки `try:` должны быть завершены этими блоками.
2. **Логирование и отладка:**
Убедитесь, что все возможные исключения логируются, чтобы облегчить отладку в будущем.
3. **Тестирование:**
После внесения изменений протестируйте бота в различных сценариях, чтобы убедиться, что все функции работают корректно и ошибки обрабатываются правильно.
4. **Оптимизация обработчиков:**
Рассмотрите возможность объединения обработчиков сообщений для избежания перекрытия и улучшения производительности.
Если у вас возникнут дополнительные вопросы или потребуется помощь с другими частями кода, пожалуйста, дайте знать!