Пользователь спросил: 'У меня есть канал @cybersec_forever, как мне сделать, чтобы бот мог проверять есть ли пользователь там или нет?'. Ответь четко и ясно на его вопрос.
История беседы:
Пожалуйста, используй обычные математические символы и знаки вместо HTML-форматирования. Например, используй *, /, ^, ≤, ≥, ≠, ∑, ∏, √ и другие символы напрямую.
import g4f
import asyncio
from aiogram import Bot, Dispatcher, types
from aiogram.filters import Command
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import State, StatesGroup
from aiogram.fsm.storage.memory import MemoryStorage
import re
import html

BOT_TOKEN = "API"
CHANNEL_ID = "@mgicode"

bot = Bot(token=BOT_TOKEN)
storage = MemoryStorage()
dp = Dispatcher(storage=storage)


class ChatStates(StatesGroup):
    chatting = State()


chat_contexts = {}


async def check_subscription(user_id: int) -> bool:
    try:
        member = await bot.get_chat_member(CHANNEL_ID, user_id)
        return member.status not in ['left', 'kicked', 'banned']
    except Exception:
        return False


def get_subscription_keyboard():
    keyboard = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="📢 Подписаться на канал", url="https://t.me/mgicode")]
    ])
    return keyboard


def get_main_keyboard():
    keyboard = [
        [KeyboardButton(text="❓ Задать вопрос")],
        [KeyboardButton(text="🚀 Наши проекты")]
    ]
    return ReplyKeyboardMarkup(keyboard=keyboard, resize_keyboard=True)


def get_chat_keyboard():
    keyboard = InlineKeyboardMarkup(inline_keyboard=[
        [InlineKeyboardButton(text="↪️ Продолжить беседу", callback_data="continue_chat")],
        [InlineKeyboardButton(text="🔄 Закончить беседу", callback_data="end_chat")]
    ])
    return keyboard


def escape_markdown_v2(text):
    return re.sub(r'([_*$begin:math:display$$end:math:display$()~`>#+\-=|{}.!])', r'\\\1', text)


def split_long_message(message, chunk_size=4096):
    """Функция для разделения длинных сообщений"""
    return [message[i:i + chunk_size] for i in range(0, len(message), chunk_size)]


@dp.message(Command("start"))
async def cmd_start(message: types.Message):
    try:
        if not await check_subscription(message.from_user.id):
            await message.answer(
                "Для использования бота необходимо подписаться на наш канал!",
                reply_markup=get_subscription_keyboard()
            )
            return
        await message.answer(
            "Привет! Я GPT-4 бот. Чем могу помочь?",
            reply_markup=get_main_keyboard()
        )
    except Exception as e:
        print(f"Error in start command: {e}")


@dp.message(lambda message: message.text == "❓ Задать вопрос")
async def start_chat(message: types.Message, state: FSMContext):
    if not await check_subscription(message.from_user.id):
        await message.answer(
            "Для использования бота необходимо подписаться на наш канал!",
            reply_markup=get_subscription_keyboard()
        )
        return
    if message.from_user.id in chat_contexts:
        chat_contexts.pop(message.from_user.id)
    await message.answer("Пожалуйста, задайте ваш вопрос:")
    await state.set_state(ChatStates.chatting)
    chat_contexts[message.from_user.id] = [{"role": "system",
                                            "content": "Пожалуйста, используй обычные математические символы и знаки вместо HTML-форматирования. Например, используй *, /, ^, ≤, ≥, ≠, ∑, ∏, √ и другие символы напрямую."}]


@dp.message(lambda message: message.text == "🚀 Наши проекты")
async def show_projects(message: types.Message):
    if not await check_subscription(message.from_user.id):
        await message.answer(
            "Для использования бота необходимо подписаться на наш канал!",
            reply_markup=get_subscription_keyboard()
        )
        return
    await message.answer(
        "Наши текущие проекты:\n"
        "1. Телеграм канал с проектами: @mgicode A\n"
        "2. Чат по программированию: https://t.me/+Kh3VGrqQfhdmZmQ6 B\n"
    )
Код, который вы предоставили, выглядит как фрагмент кода на Python, использующий библиотеку aiogram для создания телеграм-бота. Он включает в себя определение различных функций, состояний чата, проверку подписки пользователя на канал, создание различных видов клавиатур (Inline и Reply), а также обработчики сообщений для команд "start", "❓ Задать вопрос" и "🚀 Наши проекты".

Давайте разберемся с вашим вопросом. Напишите, пожалуйста, свой вопрос ясно и я отвечу на него.
@dp.message(ChatStates.chatting)
async def handle_message(message: types.Message, state: FSMContext):
    if not await check_subscription(message.from_user.id):
        await message.answer(
            "Для использования бота необходимо подписаться на наш канал!",
            reply_markup=get_subscription_keyboard()
        )
        return

    user_id = message.from_user.id
    user_input = message.text.strip()

    if user_id not in chat_contexts:
        chat_contexts[user_id] = [
            {"role": "system", "content": "Пожалуйста, ответь на вопрос пользователя четко и понятно."}]
    chat_contexts[user_id].append({"role": "user", "content": user_input})

    await message.bot.send_chat_action(message.chat.id, "typing")

    prompt = f"Пользователь спросил: '{user_input}'. Ответь четко и ясно на его вопрос."
    prompt += "\nИстория беседы:\n" + "\n".join([item["content"] for item in chat_contexts[user_id]])

    try:
        response = g4f.ChatCompletion.create(
            model="gpt-4",
            messages=[{"role": "user", "content": prompt}],
        )

        if isinstance(response, dict) and 'choices' in response:
            assistant_message = response['choices'][0]['message']['content']
        else:
            assistant_message = str(response)

    except Exception as e:
        assistant_message = f"Произошла ошибка при генерации ответа: {str(e)}"

    chat_contexts[user_id].append({"role": "assistant", "content": assistant_message})

    decoded_response = html.unescape(assistant_message)
    split_messages = split_long_message(decoded_response)

    for msg in split_messages:
        await message.answer(msg, reply_markup=get_chat_keyboard(), parse_mode="Markdown")


@dp.callback_query(lambda c: c.data == "continue_chat")
async def continue_chat(callback: types.CallbackQuery):
    if not await check_subscription(callback.from_user.id):
        await callback.message.answer(
            "Для использования бота необходимо подписаться на наш канал!",
            reply_markup=get_subscription_keyboard()
        )
        return
    await callback.answer()
    await callback.message.answer(
        "Продолжайте, я вас слушаю:",
        reply_markup=get_main_keyboard()
    )


@dp.callback_query(lambda c: c.data == "end_chat")
async def end_chat(callback: types.CallbackQuery, state: FSMContext):
    if not await check_subscription(callback.from_user.id):
        await callback.message.answer(
            "Для использования бота необходимо подписаться на наш канал!",
            reply_markup=get_subscription_keyboard()
        )
        return
    user_id = callback.from_user.id
    if user_id in chat_contexts:
        chat_contexts.pop(user_id)

    await callback.answer()
    await state.clear()
    await callback.message.answer(
        "Беседа завершена. Чем еще могу помочь?",
        reply_markup=get_main_keyboard()
    )


async def main():
    try:
        await dp.start_polling(bot)
    finally:
        await bot.session.close()


if name == 'main':
    asyncio.run(main())

Как этот бот проверяет подписку на канал и как настроить его
Этот бот использует функцию `check_subscription`, чтобы проверить, подписан ли пользователь на определенный Telegram канал. Давайте разберем, как эта функция работает и как можно настроить бота.

### Проверка подписки

Функция `check_subscription` проверяет статус подписки пользователя следующим образом:

1. **Вызов метода**: Она использует метод `get_chat_member` Telegram API через объект `bot` для получения информации о текущем статусе пользователя в канале.
   
2. **Проверка статуса**: Возвращаемое значение `member` содержит информацию о статусе пользователя (`left`, `kicked`, `banned`, `member`, `administrator`, `creator`). Функция возвращает `True`, если пользователь не входит в категории `left`, `kicked`, или `banned`, то есть если он подписан на канал.

3. **Исключения**: Если возникает какая-либо ошибка при получении информации о пользователе, функция возвращает `False`.

```python
async def check_subscription(user_id: int) -> bool:
    try:
        member = await bot.get_chat_member(CHANNEL_ID, user_id)
        return member.status not in ['left', 'kicked', 'banned']
    except Exception:
        return False
```

### Настройка бота

Чтобы настроить бота с проверкой подписки:

1. **Создайте Bot и Канал**: Убедитесь, что у вас есть Telegram-канал и бот. Вы можете создать бота через [@BotFather](https://t.me/BotFather) в Telegram.

2. **Получите токен бота**: После создания бота, BotFather предоставит вам API токен, который необходимо вставить в переменную `BOT_TOKEN`.

3. **Укажите ID канала**: В переменной `CHANNEL_ID` в коде бота замените `@mgicode` на ваше действительное имя пользователя канала. Например, если ваш канал называется `@examplechannel`, используйте `CHANNEL_ID = "@examplechannel"`.

4. **Добавьте бота в канал**: Убедитесь, что боту дан доступ ко всем необходимым правам в канале (в качестве администратора), чтобы он мог проверять подписку пользователя.

5. **Запуск**: Запустите бота, используя Python, убедившись, что у вас установлены все необходимые библиотеки (`aiogram`, `g4f`, и т.д.).

Это основные шаги для настройки бота с проверкой подписки на канал. Убедитесь, что вся информация о боте и канале корректна, чтобы избежать ошибок в работе.
У меня есть канал @cybersec_forever, как мне сделать, чтобы бот мог проверять есть ли пользователь там или нет?