Создание стороннего чат клиента для WorldOfTanks с использованием XMPP протокола. Часть 2

Posted January 18, 2022 by  ‐ 4 min read

Снифинг XMPP пакетов через Wireshark

С прошлой статьи я немного переделал структуру проекта и теперь репозиторий подключен как субмодуль. По идее боле правильный подход и упростит обновление в будущем.

Так же самым удачным решением было разобраться с фильтрами в Wireshark. Оказалось, что xmpp протокол очень легко отфильтровать и это позволило полностью разобраться в том в каком порядке происходи отправка сообщений, и какой ресурс используется для соединения с клановым чатом (до этого я на это убил кучу времени так и не добившись ничего)

Для отображения только xmpp протокола введите xmpp в поле фильтра и нажмите Enter

Диалог танкового клиента с серверами по протоколу xmpp

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

  • используется для личных сообщений
  • используется для клановых сообщений
  • сообщает об изменении статусе игрока из клана или друзей (на каком сервере, онлайн ли он, в бою ли он)
  • отвечает за добавление в друзья
  • отвечает за блокировку пользователей (это пока точно не подтвердил)
  • ресурс для конекта к клановому чату clan-<clan id>@clans.wot.wot-ru.loc/<username>
  • ресурс для конекта к серверу личных сообщений <account id>@wot-ru.loc/wot

К сожалению общий чат видимо использует танковый протокол, который работает на основе UDP. Признаюсь честно, до общего чата мне хотелось добраться больше всего :)

Создание эхо бота

Я вдохновился кодом из репозитория galaxy-integration-wargaming, а именно частью xmpp клиента и примерам из репозитория slixmpp

import logging
import slixmpp
# часть которая генерирует token2 для моего аккаунта.
# Подумаю как красиво ее опубликовать чуть позже 
from get_chat_token import get_token


class EchoBot(slixmpp.ClientXMPP):
    def __init__(self, jid, password):
        slixmpp.ClientXMPP.__init__(self, jid, str(password), sasl_mech='PLAIN')

        self.register_plugin('xep_0199')  # XMPP Ping

        # enable authentication via unencrypted PLAIN
        self['feature_mechanisms'].unencrypted_plain = True

        # disable aiodns
        self.use_aiodns = False

        # The session_start event will be triggered when
        # the bot establishes its connection with the server
        # and the XML streams are ready for use. We want to
        # listen for this event so that we we can initialize
        # our roster.
        self.add_event_handler("session_start", self.start)

        # The message event is triggered whenever a message
        # stanza is received. Be aware that that includes
        # MUC messages and error messages.
        self.add_event_handler("message", self.message)

    def connect(self):
        return super().connect(("xmppcs.worldoftanks.net", "5222"))

    async def start(self, event):
        """
        Process the session_start event.
        Typical actions for the session_start event are
        requesting the roster and broadcasting an initial
        presence stanza.
        Arguments:
            event -- An empty dictionary. The session_start
                     event does not provide any additional
                     data.
        """
        self.send_presence(pfrom=jid, ppriority=0)
        await self.get_roster()

    def message(self, msg):
        """
        Process incoming message stanzas. Be aware that this also
        includes MUC messages and error messages. It is usually
        a good idea to check the messages's type before processing
        or sending replies.
        Arguments:
            msg -- The received message stanza. See the documentation
                   for stanza objects and the Message stanza to see
                   how it may be used.
        """
        if msg['type'] in ('chat', 'normal'):
            msg.reply("Thanks for sending\n%(body)s" % msg).send()


if __name__ == '__main__':
    # Setup logging.
    logging.basicConfig(
	    level=logging.DEBUG,
        format='%(levelname)-8s %(message)s'
    )

	# Ваш jid для подключения
	# Вам нужно использовать ваш танковый account id вместо 123123123
    jid = "123123123@wot-ru.loc/wot_general"
    password = get_token()

    # Setup the EchoBot and register plugins. Note that while plugins may
    # have interdependencies, the order in which you register them does
    # not matter.
    xmpp = EchoBot(jid, password)

    # Connect to the XMPP server and start processing XMPP stanzas.
    xmpp.connect()
    xmpp.process()

Несколько важных моментов в строчке jid = "123123123@wot-ru.loc/wot_general" 1. вместо 123123123 должен быть ваш танковый id 2. в качестве ресурса для подключения используется wot_general а не wot - это позволяет побороть проблему с единой сессией xmpp сервера. Если использовать wot то вы будете конфликтовать с чатом из запущенных танков.

После запуска скрипта любой кто нам напишет - тут же получит свое сообщение в ответ в формате Thanks for sending <сообщение>

Автоматом в друзья!

В процессе тестирования получил неожиданный эффект - заявки в друзья автоматом принимаются. Пока не знаю как именно контролировать это поведение, но думаю это будет несложно.

Я знаю, где мой друг

В сообщении о смене статуса приходит так же поле: GAME-HOST login.p2.worldoftanks.net:20014 и оно несет в себе информацию о том на каком сейчас сервере находится игрок. Пока не сильно понятно где такое может пригодится, но находка есть находка)

В следующей статье мы начнем оформлять наши наработки в что-то полезное.

Есть несколько идей:

  • трансляция кланового чата в телеграм канал (read-only)
  • телеграм бот для отправки и получения личных сообщений пока ты в дали от танков
  • отслеживание онлайна друзей в танках (или определенного друга)

Но перед этим всем придется копнуть в устройство galaxy-integration-wargaming, чтобы понять как безопасно хранить секреты и получать token2