Привет, помоги мне с кодом. В details должен быть только учебный предмет, указанный под конец. А перед предметом должно быть время, взятое из time, а перед time должен быть slot с точкой, как ты понимаешь. То есть, это должно выглядеть так: slot. time details. Ещё сделай так, чтобы рассылка отправлялась тем, на ком стоит администратор. То есть, если у администратора выставлена группа ФМ 1 курса, то и рассылка уйдёт только тем, кто с такими же параметрами, в том числе и администраторам. Добавь и кнопку с Глобальной рассылкой, чтобы можно было оповестить всех людей из всех групп и курсов. Также я заметил, что у меня в боте "Нет занятий" постоянно. Исправь все ошибки и вышли полный рабочий код. import pytz
import sqlite3
from datetime import datetime, timedelta
from telegram import Update, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes, MessageHandler, filters, CallbackQueryHandler, CallbackContext
def init_db():
    conn = sqlite3.connect('university.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS schedule
                 (course INTEGER, direction TEXT, week_type TEXT, day TEXT, slot INTEGER, time TEXT, details TEXT)''')
    c.execute('''CREATE TABLE IF NOT EXISTS users
                 (id INTEGER PRIMARY KEY, username TEXT, course INTEGER, direction TEXT, subscribed INTEGER DEFAULT 1)''')
    c.execute('''CREATE TABLE IF NOT EXISTS admins
                 (id INTEGER PRIMARY KEY)''')
    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 = {
        "чёт": {
            "Понедельник": "1. 8:30 - 10:00\nМатем (л) (222)\n\n2. 10:10 - 11:40\nМатем (с) (222)\n\n3. 12:30 - 14:00\nФиз-ра\n\n4. 14:10 - 15:40\nАнгл (209)",
            "Вторник": "1. 8:30 - 10:00\nИстория (с) (108)\n\n2. 10:10 - 11:40\nИстория (л) (108)\n\n3. 12:30 - 14:00\nИТПД (с) (217)",
            "Среда": "4. 14:10 - 15:40\nИТПД (с) (217)\n\n5. 15:50 - 17:20\nPhython (л) (111)\n\n6. 17:30 - 19:00\nPhython (л) (111)",
            "Четверг": "2. 10:10 - 11:40\nИстория (с) (204)\n\n3. 12:30 - 14:00\nМенеджмент (л) (209)",
            "Пятница": "4. 14:10 - 15:40\nБЖД (л) (304)\n\n5. 15:50 - 17:20\nМенеджмент (с) (108)\n\n6. 17:30 - 19:00\nФиз-ра"
        },
        "нечёт": {
            "Понедельник": "1. 8:30 - 10:00\nМатем (л) (222)\n\n2. 10:10 - 11:40\nМатем (с) (222)\n\n3. 12:30 - 14:00\nФиз-ра\n\n4. 14:10 - 15:40\nАнгл (209)",
            "Вторник": "1. 8:30 - 10:00\nАнгл (209)\n\n2. 10:10 - 11:40\nИстория (л) (108)\n\n3. 12:30 - 14:00\nИТПД (с) (217)\n\n4. 14:10 - 15:40\nИстория ФУ (л) (211)",
            "Среда": "4. 14:10 - 15:40\nИТПД (с) (217)\n\n5. 15:50 - 17:20\nВведение в спец (л) (111)\n\n6. 17:30 - 19:00\nPhython (с) (111)",
            "Четверг": "1. 8:30 - 10:00\nВведение в спец (с) (213)\n\n2. 10:10 - 11:40\nМатем (с) (204)\n\n3. 12:30 - 14:00\nИстория (с) (209)",
            "Пятница": "4. 14:10 - 15:40\nБЖД (с) (304)\n\n5. 15:50 - 17:20\nМенеджмент (с) (108)\n\n6. 17:30 - 19:00\nФиз-ра"
        }
    }
    for course in range(1, 5):
        for direction in directions:
            schedule_data = {
                "чёт": {
                    "Понедельник": [f"{direction}{course}"] + [""] * 5,
                    "Вторник": [""] * 6,
                    "Среда": [""] * 6,
                    "Четверг": [""] * 6,
                    "Пятница": [""] * 6,
                    "Суббота": [""] * 6
                },
                "нечёт": {
                    "Понедельник": [f"{direction}{course}"] + [""] * 5,
                    "Вторник": [""] * 6,
                    "Среда": [""] * 6,
                    "Четверг": [""] * 6,
                    "Пятница": [""] * 6,
                    "Суббота": [""] * 6
                }
            }
            if direction == 'БИ':
                schedule_data = schedule_bi
            for week_type, days in schedule_data.items():
                for day, details in days.items():
                    slots = details.split("\n\n") if isinstance(details, str) else details
                    for slot in range(6):
                        time = times[slot]
                        detail = slots[slot] if slot < len(slots) else ""
                        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_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: ContextTypes.DEFAULT_TYPE) -> None:
    await update.message.reply_text('Привет! Поменяй группу и курс на соответствующие в своём личном кабинете.')
    await personal_cabinet(update, context)
    keyboard = [
        ["Выбрать дату расписания", "Расписание на завтра", "Расписание на сегодня"],
        ["Понедельник", "Вторник", "Среда"],
        ["Четверг", "Пятница", "Суббота"],
        ["Личный кабинет"]
    ]
    reply_markup = ReplyKeyboardMarkup(keyboard, one_time_keyboard=True, resize_keyboard=True)
    await update.message.reply_text('Выбери опцию:', reply_markup=reply_markup)
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    user_message = update.message.text
    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.lower() in ["расписание на сегодня", "расписание на завтра", "понедельник", "вторник", "среда", "четверг", "пятница", "суббота"]:
        await show_schedule(update, context, user_message)
    elif user_message.lower() == "выбрать дату расписания":
        context.user_data['awaiting_date'] = True
        await update.message.reply_text("Введи необходимое число этого месяца:")
    elif user_message.lower() == "личный кабинет":
        await personal_cabinet(update, context)
    else:
        await update.message.reply_text('Выбери опцию из меню.')
async def personal_cabinet(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user_id = update.effective_user.id
    username = update.effective_user.username
    first_name = update.effective_user.first_name
    last_name = update.effective_user.last_name or ""
    conn = sqlite3.connect('university.db')
    c = conn.cursor()
    c.execute("SELECT username, course, direction, subscribed FROM users WHERE id = ?", (user_id,))
    user = c.fetchone()
    if not user:
        c.execute("INSERT INTO users (id, username, course, direction) VALUES (?, ?, ?, ?)", (user_id, username, 1, "Ф"))
        conn.commit()
        user = (username, 1, "Ф", 1)
    c.execute("SELECT 1 FROM admins WHERE id = ?", (user_id,))
    is_admin = c.fetchone() is not None
    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_admin else "Студент"
    subscribe_state = "Отписаться от уведомлений университета" if user[3] 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')}"
    )
    keyboard = [
        [
            InlineKeyboardButton("Изменить группу", callback_data='change_direction'),
            InlineKeyboardButton("Изменить курс", callback_data='change_course')
        ],
        [
            InlineKeyboardButton(subscribe_state, callback_data='toggle_subscription')
        ]
    ]
    if is_admin:
        keyboard.append([
            InlineKeyboardButton("Сделать рассылку", callback_data='send_announcement')
        ])
    reply_markup = InlineKeyboardMarkup(keyboard)
    if update.callback_query:
        await update.callback_query.edit_message_text(text=message_text, reply_markup=reply_markup)
    else:
        await update.message.reply_text(text=message_text, reply_markup=reply_markup)
async def handle_callback(update: Update, context: CallbackContext):
    query = update.callback_query
    user_id = query.from_user.id
    conn = sqlite3.connect('university.db')
    c = conn.cursor()
    if query.data == 'change_course':
        c.execute("SELECT course FROM users WHERE id = ?", (user_id,))
        current_course = c.fetchone()[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)
    elif query.data == 'change_direction':
        c.execute("SELECT direction FROM users WHERE id = ?", (user_id,))
        current_direction = c.fetchone()[0]
        directions = ['Ф', 'У', 'ФМ', 'ГМУ', 'БИ']
        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)
    elif query.data == 'toggle_subscription':
        c.execute("SELECT subscribed FROM users WHERE id = ?", (user_id,))
        current_subscription = c.fetchone()[0]
        new_subscription = 0 if current_subscription else 1
        c.execute("UPDATE users SET subscribed = ? WHERE id = ?", (new_subscription, user_id))
        conn.commit()
        await query.answer("Подписка обновлена")
        await personal_cabinet(update, context)
    elif query.data == 'send_announcement' and is_admin_user(user_id):
        context.user_data['is_sending_announcement'] = True
        await query.answer("Введите сообщение для рассылки")
    conn.close()
async def collect_announcement(update: Update, context: ContextTypes.DEFAULT_TYPE):
    user_id = update.message.from_user.id
    if not context.user_data.get('is_sending_announcement'):
        return
    message_text = update.message.text
    if not message_text:
        return
    context.user_data['is_sending_announcement'] = False
    conn = sqlite3.connect('university.db')
    c = conn.cursor()
    c.execute("SELECT id FROM users WHERE subscribed = 1")
    subscribers = c.fetchall()
    conn.close()
    for subscriber in subscribers:
        try:
            await context.bot.send_message(chat_id=subscriber[0], text=message_text)
        except Exception as e:
            print(f"Не удалось отправить сообщение пользователю {subscriber[0]}: {e}")
    await update.message.reply_text("Рассылка завершена.")
def is_admin_user(user_id):
    conn = sqlite3.connect('university.db')
    c = conn.cursor()
    c.execute("SELECT 1 FROM admins WHERE id = ?", (user_id,))
    is_admin = c.fetchone() is not None
    conn.close()
    return is_admin
async def show_schedule(update: Update, context: ContextTypes.DEFAULT_TYPE, day_command: str):
    user_id = update.message.from_user.id
    conn = sqlite3.connect('university.db')
    c = conn.cursor()
    c.execute("SELECT course, direction FROM users WHERE id = ?", (user_id,))
    user_info = c.fetchone()
    if not user_info:
        await personal_cabinet(update, context)
        c.execute("SELECT course, direction FROM users WHERE id = ?", (user_id,))
        user_info = c.fetchone()
        if not user_info:
            await update.message.reply_text("Ошибка при добавлении пользователя в базу данных.")
            return
    course, direction = user_info
    conn.close()
    current_date = datetime.now(pytz.timezone('Asia/Omsk'))
    current_weekday = current_date.weekday()
    days_map = {
        "расписание на сегодня": 0,
        "расписание на завтра": 1,
        "понедельник": 0,
        "вторник": 1,
        "среда": 2,
        "четверг": 3,
        "пятница": 4,
        "суббота": 5
    }
    day_index = days_map[day_command.lower()]
    if day_command.lower() == "расписание на сегодня":
        target_date = current_date
    elif day_command.lower() == "расписание на завтра":
        target_date = current_date + timedelta(days=1)
    else:
        if day_index < current_weekday:
            day_offset = day_index + 7 - current_weekday
        else:
            day_offset = day_index - current_weekday
        target_date = current_date + timedelta(days=day_offset)
    day_name = get_day_name(target_date.weekday())
    week_type = get_week_type(target_date)
    day_schedule = get_schedule_for_day(course, direction, day_name, target_date)
    message_text = (
        f"{day_name} {target_date.strftime('%d.%m')} ({week_type}):\n\n{day_schedule}"
    )
    await update.message.reply_text(message_text)
async def show_schedule_by_date(update: Update, context: ContextTypes.DEFAULT_TYPE, date_obj):
    user_id = update.message.from_user.id
    conn = sqlite3.connect('university.db')
    c = conn.cursor()
    c.execute("SELECT course, direction FROM users WHERE id = ?", (user_id,))
    user_info = c.fetchone()
    if not user_info:
        await personal_cabinet(update, context)
        c.execute("SELECT course, direction FROM users WHERE id = ?", (user_id,))
        user_info = c.fetchone()
        if not user_info:
            await update.message.reply_text("Ошибка при добавлении пользователя в базу данных.")
            return
    course, direction = user_info
    conn.close()
    day_name = get_day_name(date_obj.weekday())
    week_type = get_week_type(date_obj)
    day_schedule = get_schedule_for_day(course, direction, day_name, date_obj)
    message_text = (
        f"{day_name} {date_obj.strftime('%d.%m')} ({week_type}):\n\n{day_schedule}"
    )
    await update.message.reply_text(message_text)
if __name__ == '__main__':
    init_db()
    application = ApplicationBuilder().token("<TRUFFLEHOG_REDACTED_TELEGRAMBOTTOKEN>").build()
    application.add_handler(CommandHandler("start", start))
    application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
    application.add_handler(CallbackQueryHandler(handle_callback))
    application.run_polling()