Создание бота отображающего онлайн серверов гаррис мод.
-
Гайд составлен 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.

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

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

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

Чтобы запустить любой файл 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()Запускаем и смотрим в дискорд!

Вот мы и получили сообщение! Теперь сделаем нашего бота цикличным, чтоб он раз допустим в 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, а у меня код не подсвечивается.
Похожие темы