Перейти к содержимому

Создание бота отображающего онлайн серверов гаррис мод.

Творчество
2 1 33 1
  • Гайд составлен Mr. Geo.
    Контакты:
    discord: lkjohhn12

    Приветствую, в этом небольшом гайде я вам расскажу как быстро и просто создать бота для отображения онлайна серверов Garry’s Mod на языке python.

    Почему творчество? Потому что в гайды пишут только какие-то бесполезные рп штучки и взаимодействия именно с окто рп сервером, идеальнее места для этого топика я не нашел.

    Данный бот, я бы даже сказал не бот, это скрипт который раз в определенное время будет отсылать вебхук на сервер дискорд раз в определенный тайминг. В следующем гайде мы рассмотрим создание именно полноценного бота на библиотеке disnake с своими фишками, сейчас же мы сделаем его упрощенную версию используя лишь вебхуки.

    И так, мы будем писать бота на языке python версии 3.10 и выше, для проекта нам понадобится несколько библиотек, вот их список:

    • python-a2s
    • discord_webhook

    И так приступим!

    Для самого начала вам нужно изучить python, его основы, для понимания происходящего.
    Вот полезные статьи:

    И так мы выучили основы python и готовы!

    Для начала, определимся с средой разработки, удобнее всего использовать Visual Studio Code или PyCharm. Разницы особо нет, но PyCharm удобнее для новичков и рассматривать будем именно его.

    Стартуем, при первом запуске у нас будет окон Welcome to PyCharm, в нем тыкаем New Project.

    2025-12-03_13-49-09.png

    Дальше необходимо указать где будут храниться ваш проект, в моем случае D:/PycharmProjects/online-gmod-bot
    2025-12-03_13-50-38.png

    Чем удобен pycharm, он автоматически создает виртуальное окружение, благодаря виртуальному окружению можно изолировать проекты друг от друга и не засорять глобальное окружение, все библиотеки скаченные в окружении будут находится в этом самом окружение и не вызовут зависимости при запуске какой-то программы вне этого окружения.

    Может не очень понятно, проще говоря это удобно и правильно.

    И так перед нами сейчас такое вот окно:
    2025-12-03_13-54-10.png

    Для начала нам нужно скачать необходимые библиотеки, это можно сделать через консоль или же через интерфейс pycharm, разницы особо нет, я уже привык через консоль. Для этого слева внизу находим иконку консоли и тыкаем.

    Открывается powershell с уже активированным venv, опять же очень удобно, экономит время.

    2025-12-03_13-58-48.png

    Чтобы запустить любой файл python в pycharm, достаточно нажать на зеленую кнопочку play справа сверху, или слева в поле с файлами нажать на него правой кнопкой мыши -> Run.

    Так как вы уже знаете основы python, ну предполагается, качаем библиотеки python-a2s и discord-webhook, для этого вводим комманды:

    pip install python-a2s
    pip install discord-webhook
    

    Все теперь приступаем к написанию кода. Для начала очистим main и импортируем наши библиотеки:

    import a2s
    import discord_webhook
    

    Подробный функционал о этих библиотеках можно найти в интернете, достаточно ввести pypi <название бибилиотеки>.
    Для начала напишем функцию которая будет возвращать нам информацию о сервере, название, карту, кол-во игроков и прочее.

    Для начала изучим библиотеку a2s, там написано что для того чтобы получить информацию нужно использовать функцию info, вот таким образом:

    a2s.info(address, timeout=DEFAULT_TIMEOUT, encoding=DEFAULT_ENCODING)
    

    Но нам не обязательно указывать timeout и encoding, потому что по она указаны в функции но умолчанию. Также надо сразу понять что именно нужно передать в поле address, и это тоже описано в документации:

    address: Tuple[str, int] - Address of the server.
    
    Пример:
    address = ("chi-1.us.uncletopia.com", 27015)
    

    В поле адрес нужно передать список где первое будет это ip сервера или его домен, а второе его порт.

    Отлично, теперь создадим функцию которая нам будет возвращать информацию о сервере и постепенно улучшим её:

    def get_server_info(address: tuple[str, int]):
        info = a2s.info(address)
        return info
    

    Как видим я сразу указал какие данные необходимо передать в address, это называется аннотация типов, я вам рекомендую так делать всегда и с любыми даже простыми функциями, благодаря этому можно сразу понять что именно передавать в функцию.

    И теперь воспользуемся нашей функцией выведя результат сразу в консоль, в качестве теста будем использовать ip доброграда, итоговый код:

    import a2s
    import discord_webhook
    
    def get_server_info(address: tuple[str, int]):
        info = a2s.info(address)
        return info
    
    print(get_server_info(("46.174.50.203", 27015)))
    

    И в консоль нам выведется:

    SourceInfo(protocol=17, server_name='[#] Доброград – Осень 🍂', map_name='rp_evocity_dbg_251105', folder='garrysmod', game='base', app_id=4000, player_count=34, max_players=88, bot_count=0, server_type='d', platform='l', password_protected=False, vac_enabled=True, version='2025.03.26', edf=177, ping=0.0, port=27015, steam_id=85568392923369091, stv_port=None, stv_name=None, keywords='gm:darkrp gmc:rp loc:ru ver:251112', game_id=4000)
    

    Вауля, с этим уже можно работать, но вот в чем беда, если мы укажем какой-то несуществующий адрес или же адрес сервера который оффлайн, то нам выведет ошибку в консоль, чтобы избежать остановки кода напишем обработчик ошибок и укажем ошибки которые мы хотим получить, это timeout (окончание счетчика запроса), gaierror (исключение связанное с доменными именами) и a2s.BrokenMessageError. Это все ошибки которые у меня попадались, но также добавим стандартный обработчик с выводом неизвестной ошибки:

    def get_server_info(address: tuple[str, int]) -> a2s.SourceInfo | None:
        try:
            info = a2s.info(address)
            return info
        except(gaierror, timeout, a2s.BrokenMessageError) as e:
            print(f"Не удалось получить информацию для {address}: {e}")
            return None
        except Exception as e:
            print(f"Неизвестная ошибка при получении информации для адреса {address}: {e}")
            return None
    

    Также можно заметить я указал: -> a2s.SourceInfo | None
    Это тоже аннотация, которая показывает что возвращает функция, в нашем случае это либо класс из библиотеки a2s, либо None.

    Теперь это уже выглядит правильно и не вызывает ошибок, а лишь информирует о них. Далее как мы и планировали, необходимо эту информацию обработать и отправить вебхуком в дискорд, для этого напишем новую функцию, назовем её send_to_discord, где вначале будем получать информацию о сервере:

    def send_to_discord():
        ip = ("46.174.50.203", 27015)
        info = get_server_info(ip)
    

    Для начала разобьем информацию на удобные для нас переменные,мы будем получать основную информацию: название сервера, карту, максимальное кол-во игроков, сколько сейчас играет игроков и есть ли пароль на сервере

    def send_to_discord():
        ip = ("46.174.50.203", 27015)
        info = get_server_info(ip)
    
        server_name = info.server_name
        map_name = info.map_name
        player_count = info.player_count
        max_players = info.max_players
        password_protected = info.password_protected
    

    И теперь мы опять столкнемся с новой проблемой, а именно, если у нас не получилось получить информацию о сервере, тогда при присваивании переменных нам выдаст ошибку, ведь info у нас None. Чтобы избежать этого необходимо кэшировать данные, для этого добавим в начало кода переменную server_info_cache и отредактируем наши функции:

    import a2s
    import discord_webhook
    from socket import gaierror, timeout
    import time
    
    server_info_cache = {}
    
    class DefaultInfo:
        server_name = "Сервер не отвечает"
        map_name = "Неизвестно"
        player_count = 0
        max_players = 0
        password_protected = False
    
    def get_server_info(address: tuple[str, int]) -> a2s.SourceInfo | None:
        try:
            info = a2s.info(address)
    
            # добавлено!
            server_info_cache[address] = (time.time(), info)
    
            return info
        except(gaierror, timeout, a2s.BrokenMessageError) as e:
            print(f"Не удалось получить информацию для {address}: {e}")
            return None
        except Exception as e:
            print(f"Неизвестная ошибка при получении информации для адреса {address}: {e}")
            return None
    
    def send_to_discord():
        ip = ("46.174.52.203", 27015)
        info = get_server_info(ip)
        if not info:
            info = ip in server_info_cache and server_info_cache[ip] or DefaultInfo
    
        server_name = info.server_name
        map_name = info.map_name
        player_count = info.player_count
        max_players = info.max_players
        password_protected = info.password_protected
    
    
    send_to_discord()
    

    Реализовать можно разными способами, это уже так сказать на усмотрение каждого, здесь же я сделал так, чтобы точно не было ошибок при выполнении кода.

    И так, информация есть, осталось все это отправить в дискорд, для этого откроем документацию и посмотрим каким образом работает discord_webhook. Для начала надо инициализировать класс:

    webhook = DiscordWebhook(
            url=url,
            username=username,
            id=message_id,
            avatar_url=""
        )
    

    Здесь из основного нам надо указать url, остальное по желанию и не обязательно, укажем url и имя.

    webhook = DiscordWebhook(
            url="https://discord.com/api/webhooks/******",
            username="Онлайн серверов",
        )
    

    Чтобы получить вебхук, надо зайти на свой сервер дискорд или же если у вас есть доступ, навестись на канал куда вы хотите получать сообщения, и нажать на шестеренку. Там будет вкладка интеграция, в ней нажимаете “Вебхуки” и создаете вебхук или копируете url уже созданного.

    Теперь из функционала библиотеки, можно добавлять embed, это красивые сообщения которые могут отправляться только ботами или вебхуками или же просто отправлять текст. Для этого есть функции: add_embed и set_content.

    В нашем гайде мы рассмотрим только обычные сообщения, как заполняются embed можете изучить самостоятельно.

    И так сначала мы создадим строку с информацией и добавим её:

    password = "Сервер под паролем" and password_protected or ""
    text = f"Название сервера: {server_name}\nКарта: {map_name}\nИгроков: {player_count} из {max_players}\n{password}"
    
    webhook.set_content(text)
    

    Осталось лишь отправить наш вебхук, для этого есть комманда execute, и вот такой код у нас по итогу получается:

    import a2s
    from discord_webhook import DiscordWebhook
    from socket import gaierror, timeout
    import time
    
    server_info_cache = {}
    
    class DefaultInfo:
        server_name = "Сервер не отвечает"
        map_name = "Неизвестно"
        player_count = 0
        max_players = 0
        password_protected = False
    
    def get_server_info(address: tuple[str, int]) -> a2s.SourceInfo | None:
        try:
            info = a2s.info(address)
    
            # добавлено!
            server_info_cache[address] = (time.time(), info)
    
            return info
        except(gaierror, timeout, a2s.BrokenMessageError) as e:
            print(f"Не удалось получить информацию для {address}: {e}")
            return None
        except Exception as e:
            print(f"Неизвестная ошибка при получении информации для адреса {address}: {e}")
            return None
    
    def send_to_discord():
        ip = ("46.174.50.203", 27015)
        info = get_server_info(ip)
        if not info:
            info = ip in server_info_cache and server_info_cache[ip] or DefaultInfo
    
        server_name = info.server_name
        map_name = info.map_name
        player_count = info.player_count
        max_players = info.max_players
        password_protected = info.password_protected
    
        webhook = DiscordWebhook(
            url="https://discord.com/api/webhooks/*****",
            username="Онлайн серверов",
        )
        password = "Сервер под паролем" and password_protected or ""
        text = f"Название сервера: {server_name}\nКарта: {map_name}\nИгроков: {player_count} из {max_players}\n{password}"
    
        webhook.set_content(text)
        webhook.execute()
        
    send_to_discord()
    

    Запускаем и смотрим в дискорд!
    2025-12-03_15-17-48.png

    Вот мы и получили сообщение! Теперь сделаем нашего бота цикличным, чтоб он раз допустим в 2 минуты обновлял информацию. И это очень просто, добавим в конце этот код и все:

    while True:
        send_to_discord()
        time.sleep(120)
    

    Но есть проблема, каждые 2 минуты бот отправляет новое сообщение! А было бы удобно если бы он редактировал его. Так сделать можно! Для этого есть у discord_webhook есть функция .edit, внесем правки в нашу функцию send_to_discord и создадим переменную message_id:

    message_id = None
    
    def send_to_discord():
        global message_id
        ip = ("46.174.50.203", 27015)
        info = get_server_info(ip)
        if not info:
            info = ip in server_info_cache and server_info_cache[ip] or DefaultInfo
    
        server_name = info.server_name
        map_name = info.map_name
        player_count = info.player_count
        max_players = info.max_players
        password_protected = info.password_protected
    
        webhook = DiscordWebhook(
            url="https://discord.com/api/webhooks/*****",
            username="Онлайн серверов",
            id=message_id
        )
        password = "Сервер под паролем" and password_protected or ""
        text = f"Название сервера: {server_name}\nКарта: {map_name}\nИгроков: {player_count} из {max_players}\n{password}"
    
        webhook.set_content(text)
    
        if message_id:
            response = webhook.edit()
            if response.status_code == 200:
                return
        response = webhook.execute()
        message_id = response.json().get('id')
    

    И получаем такой итоговый код:

    import a2s
    from discord_webhook import DiscordWebhook
    from socket import gaierror, timeout
    import time
    
    server_info_cache = {}
    
    class DefaultInfo:
        server_name = "Сервер не отвечает"
        map_name = "Неизвестно"
        player_count = 0
        max_players = 0
        password_protected = False
    
    def get_server_info(address: tuple[str, int]) -> a2s.SourceInfo | None:
        try:
            info = a2s.info(address)
    
            # добавлено!
            server_info_cache[address] = (time.time(), info)
    
            return info
        except(gaierror, timeout, a2s.BrokenMessageError) as e:
            print(f"Не удалось получить информацию для {address}: {e}")
            return None
        except Exception as e:
            print(f"Неизвестная ошибка при получении информации для адреса {address}: {e}")
            return None
    
    message_id = None
    
    def send_to_discord():
        global message_id
        ip = ("46.174.50.203", 27015)
        info = get_server_info(ip)
        if not info:
            info = ip in server_info_cache and server_info_cache[ip] or DefaultInfo
    
        server_name = info.server_name
        map_name = info.map_name
        player_count = info.player_count
        max_players = info.max_players
        password_protected = info.password_protected
    
        webhook = DiscordWebhook(
            url="https://discord.com/api/webhooks/*****",
            username="Онлайн серверов",
            id=message_id
        )
        password = "Сервер под паролем" and password_protected or ""
        text = f"Название сервера: {server_name}\nКарта: {map_name}\nИгроков: {player_count} из {max_players}\n{password}"
    
        webhook.set_content(text)
    
        if message_id:
            response = webhook.edit()
            if response.status_code == 200:
                return
        response = webhook.execute()
        message_id = response.json().get('id')
    
    while True:
        send_to_discord()
        time.sleep(30)
    

    Вот я показал вам как сделать простенькую основу для скрипта который показывает онлайн, тут не надо создавать бота, настраивать, добавлять бота на сервер, нужна лишь ссылка на вебхук и все. Надеюсь было интересно.

    Если у вас будут вопросы, пишите их в этом топике на все со временем отвечу.

  • @chelog допили форум, я указываю ```python, а у меня код не подсвечивается.

Похожие темы

  • Заменки и тд

    Творчество
    25
    -2 Лайки
    25 Сообщений
    850 Показы
    unki333U
    Ну такое се
  • ayawnis vils // bigd rumchik AKA 7-WAI

    Творчество
    45
    1
    3 Лайки
    45 Сообщений
    1k Показы
    NameGureN
    [image: image.png]
  • Топик деда (CEET)

    Творчество
    54
    2
    8 Лайки
    54 Сообщений
    1k Показы
    .beaverzzz.
    от дедулины ссылка прилетела, я не знаю что на ней друзья https://drive.google.com/file/d/10EEiOvEjGiIe2TeJaS8gT5wnhZkO3_Cz/view?usp=sharing https://drive.google.com/file/d/1PYKWJNqqUQzaq6MAaN0itlRRIrm91N73/view?usp=sharing [image: 1764756500172-0e6b41ef-27df-4dc7-92fd-9c5895314040-image.png]
  • Наборы модов на все случаи жизни

    Творчество
    5
    0 Лайки
    5 Сообщений
    156 Показы
    ТалантТ
    тоже стрелялка ,но другого рода пак с оружие MW и нормальные гранаты для компании весь пак с скинами и оружием:https://steamcommunity.com/sharedfiles/filedetails/?id=3568064399 Карты можно юзать любые , но для меня любимыми будет BbiCOTKA cs_Kvartal Баня
  • роско шоурум

    Творчество
    147
    1
    4 Лайки
    147 Сообщений
    13k Показы
    roscoeR
    Скоро еще кое чо на серваке будет [image: IMG_8025.jpg?ex=692e3fe4&is=692cee64&hm=43ff1c15060d38c18f6d7192c5456f05379acaa0d98f0093397bbbd76a28726c&]