Skip to content

Latest commit

 

History

History
883 lines (738 loc) · 30.3 KB

protocol.md

File metadata and controls

883 lines (738 loc) · 30.3 KB

Протокол асинхронного взаимодействия между клиентом и сервером для чата GG.

Warning

Данная версия протокола устарела, см. новую версию

Этот документ описывает протокол общения между клиентом GG-чата и сервером GG-чата. Сам документ находится в стадии черновика и любая его часть может быть изменена. После того, как протокол будет зафиксирован, ему будет присвоена соответсвующая версия. Предпологается, что протокол открытый и не привязан к конкретной клиентской технологии. Любой желающий может реализовать клиента, поддерживающего этот протокол. (Например клиент для iOS или Android.)

Общая информация

  • Основан на JSON-кодировании.
  • Максимальная длина сообщения 4кб, включая JSON-разметку.
  • Под асинхронностью понимается то, что после отправки сообщения одной из сторон, не обязательно прийдет ответ именно на это сообщение.
  • За 1 раз отсылается 1 сообщение.
  • Обмен возможен только между клиентом и сервером, т.е. минуя сервер, клиенты общаться не могут.
  • Одно соединение может обслуживать несколько каналов (стримов).

Условные обозначения

req_to_server - запрос клиента к серверу res_to_client - ответ сервера клиенту res_to_all - ответ сервера всем клиентам res_to_all_in_channel - ответ сервера всем клиентам в определенном канале channel_id - идентификатор канала

Техническая информация

Для подключения к серверу чата следует использовать websocket-соединение по адресу:

wss://chat.goodgame.ru/chat/websocket

Общий вид JSON-сообщения

{
    "type": "", // заголовок сообщения
    "data": {
        //Дополнительная информация.
    }
}

data - вынесена в отдельную json сущность, для более удобного разбора параметров сообщения.

Протокол

После установления соединения на "траспортном" уровне, сервер немедленно отвечает сообщением

//res_to_client
{
    "type": "welcome",
    "data": {
        "protocolVersion": 1.1,
        "serverIdent": "GG-chat/1.0 beta"
    }
}

Клиент опционально, сверяет версию протокола. После чего либо отключается, либо продолжает работать.

Авторизация на сервере (необязательная), если не проводилась то клиент считается гостем.

//req_to_server
{
    "type": "auth",
    "data": {
        "user_id": "123",         // идентификатор пользователя на сайте, либо 0 для гостей
        "token": "123123fhjdhfjd" // ключ авторизации. Если не указан, то будет запрошен гостевой доступ.
    }
}

//res_to_client
{
    "type": "success_auth",
    "data": {
        "user_id": "123", // id-пользователя на сайте, для гостей 0
        "user_name": "Василий" // nick на сайте, для гостей ""
    }
}

Получение токена для авторизации

Для того чтобы получить токен авторизации необходимо отправить POST-запрос по адресу https://goodgame.ru/ajax/chatlogin Через запрос нужно передать два поля:

  • login // Имя пользователя
  • password // Пароль

Ответ придёт в формате JSON:

//res_to_client
{
    "code": 4,
    "user_id": "12345", // id пользователя
    "login_page": "",
    "settings": "{\"alignType\":0,\"pekaMod\":1,\"sound\":true,\"smilesType\":4,\"hide\":0,\"noBan\":1}",
    "token": "fa2e9010cb9a4228215be14fbde13226", // токен авторизации
    "result": true, // результат операции
    "return": false,
    "response": "\u0412\u044b \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0432\u043e\u0448\u043b\u0438 \u043d\u0430 \u0441\u0430\u0439\u0442" // "дружелюбный" результат операции
}

Получение списка каналов

//req_to_server
{
    "type": "get_channels_list",
    "data": {
        "start": 0, // стартовая позиция (отсчет с 0)
        "count": 50 // количество каналов на страницу (max - 50)
    }
}

//res_to_client
{
    "type": "channels_list",
    "data": {
        "channels": [{
            "channel_id": "5",
            "channel_name": "имя канала",
            "clients_in_channel": 545, // всего клиентов в канале, включая гостей
            "users_in_channel": 332,   // всего авторизованных пользователей в канале
        },
        ...] // массив каналов, где есть хотя бы 1 пользователь, гости не считаются.
    }
}

Список каналов сортируется по количеству пользователей. На первой позиции чат с наибольшим показателем.

Если channel_id известен заранее, то это сообщение можно не отсылать.

Клиент посылает сообщение о намерении присоединиться к каналу.

//req_to_server
{
    "type": "join",
    "data": {
        "channel_id": "5" // идентификатор канала
        "hidden": false   // для "модераторов": не показывать ник в списке юзеров
    }
}

Если сервер присоединяет клиента к каналу, то клиент информируется сообщением.

//res_to_client
{
    "type": "success_join",
    "data": {
        "channel_id": "5",
        "channel_name": "имя канала",
        "motd": "Сообщение дня",  // сообщение дня
        "slowmod": 0, // задержка в секундах между отправкой сообщений
        "smiles": 1,
        "smilePeka": 1,
        "clients_in_channel": 545, // всего клиентов в канале, включая гостей
        "users_in_channel": 332,   // всего авторизованных пользователей в канале
        "user_id": "123", // для гостей "0"
        "name": "Василий", // для гостей ""
        "access_rights": "1", // по этому полю клиент понимает права пользователя, в этом канале.
        "premium": true,
        "is_banned": false,   // забанен или нет в этом канале
        "banned_time": 0, // до какого времени забанен.
        "reason": 'Провоцирование', // текстовая строка с причиной бана.
        "payments": "128.30",
        "paidsmiles": ["1","2","3"]
    }
}

В данной реализации протокола, гости находятся в состоянии readonly.

Отключение пользователя от канала

//req_to_server
{
    "type": "unjoin",
    "data": {
        "channel_id": "5" // идентификатор канала
    }
}

//res_to_client
{
    "type": "success_unjoin",
    "data": {
        "channel_id": "5"
    }
}

Одобрение запроса на подключение к комнате при получении inline-команды /join

//res_to_client
{
    "type": 'join_to_room',
    "data": {
        "channel_id": "123", // id-канала, из которого посылалась inline-команда /join
        "room_id": "5" || "r5" // id-канала или если начинается с префикса "r" id-комнаты
    }
}

В любой момент, клиент может получить список всех пользователей в канале.

//req_to_server
{
    "type": "get_users_list",
    "data": {
        "channel_id": "5"
    }
}

Список пользователей в канале. Гости не учитываются.

//res_to_client
{
    "type": "users_list",
    "data": {
        "channel_id": "5",
        "clients_in_channel": 545, // всего клиентов в канале, включая гостей
        "users_in_channel": 332,   // всего авторизованных пользователей в канале
        "users": [{
            "id": '55828',
            "name": 'dfcz',
            "rights": 20,
            "premium": false,
            "payments": 'null',
            "mobile": false,
            "hidden": false
        },
        ...] // Массив пользователей которые в данный момент находятся в канале
        // для экономии памяти, клиент может игнорировать этот список.
    }
}

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

//req_to_server
{
    "type": "get_channel_counters",
    "data": {
        "channel_id": "5"
    }
}

Счетчик клиентов и пользователей, подключенных к каналу.

//res_to_client
{
    "type": "channel_counters",
    "data": {
        "channel_id": "5",
        "clients_in_channel": 545, // всего клиентов в канале, включая гостей
        "users_in_channel": 332,   // всего авторизованных пользователей в канале
    }
}

Список модераторов в канале

inline-команда /list

//res_to_client
{
    "type": "list",
    "data": {
        "channel_id": "5",
        "users": [{
            "id": "6",
            "name": "Василий"
        },
        ...
        ]
    }
}

Если пользователь залогинен, то он может получить игнор-лист

//req_to_server
{
    "type": "get_ignore_list",
    "data": {
    }
}

Список общий для всех каналов.

//res_to_client
{
    "type": "ignore_list",
    "data": {
        "users": [{
            "id": "6",
            "name": "Василий"
        },
        ...
        ]
    }
}

Добавить в игнор лист

//req_to_server
{
    "type": "add_to_ignore_list",
    "data": {
        "user_id": "77"
    }
}

Ответ аналогичен команде get_ignore_list

Удалить из игнор листа

//req_to_server
{
    "type": "del_from_ignore_list",
    "data": {
        "user_id": "77"
    }
}

Ответ аналогичен команде get_ignore_list

Получение истории сообщений канала

//req_to_server
{
    "type": "get_channel_history",
    "data": {
        "channel_id": "5"
    }
}

//res_to_client
{
    "type": "channel_history",
    "data": {
        "channel_id": "5",
        messages:[{
            "user_id": "123", // id юзера.
            "user_name": "Василий",
            "user_group": 1, // на основе группы, определяется каким цветом выводить сообщения
            "message_id": "100", // номер сообщения, нужно для удаления сообщения из чата.
            "timestamp": unixtime, // время прихода сообщения на сервер.
            "text": "Всем чмоки в этом чатике." // оригинальное сообщение, за исключением того, что html-разметка эскейпится.
            // клиент сам занимается преобразованием спец. символов (например подстановка смайлов).
        },
        ...] // Массив сообщений, более раннии сообщения первее.
    }
}

Получение сообщения дня, инициатор сервер.

//res_to_client
{
    "type": "motd",
    "data": {
        "channel_id": "5",
        "moder_id": 123,
        "moder_name": 'Валера',
        "moder_group": 1,
        "text": "Сообщение дня"
    }
}

Получение значения задержки в сек., между отправкой сообщений.

//res_to_client
{
    "type": "slowmod",
    "data": {
        "channel_id": "5",
        "moder_id": 123,
        "moder_name": 'Валера',
        "moder_group": 1,
        "slowmod": 0 // в секундах
    }
}

Отправка сообщения от клиента на сервер.

//req_to_server
{
    "type": "send_message",
    "data": {
        "channel_id": "5",
        "text": "Всем чмоки в этом чатике.", //html-разметка эскейпится
        "hideIcon": false, // используется в служебных целях на стороне клиента
        "mobile": false // используется в служебных целях на стороне клиента
    }
}

Если сообщение начинается с "/", то оно считается командой и соответствующим образом обрабатывается на сервере.

Список доступных команд:

  • /list - аналогично сообщению "get_users_list"
  • /nick - аналогично сообщению "success_join", только поле type будет "user_info"
  • /me - сервер отошлет всем клиентам в канале специально сформированное сообщение
  • /slap - сервер отошлет всем клиентам в канале специально сформированное сообщение
  • /motd - устанавливает сообщение дня
  • /smiles 1 | 0 - включение, отключение смайлов

Отправка сообщения всем клиентам в канале.

//res_to_all_in_channel
{
    "type": "message",
    "data": {
        "channel_id": "5",
        "user_id": "123", // id юзера.
        "user_name": "Василий",
        "user_rights": 10,  // на основе прав, определяется каким цветом выводить сообщения
        "premium": false,   // премиум статус пользователя отправившего сообщение
        "hideIcon": false,  // используется в служебных целях на стороне клиента
        "mobile": false,    // используется в служебных целях на стороне клиента
        "payments": "123.45",
        "paidsmiles": [],
        "message_id": "100", // номер сообщения, нужно для удаления сообщения из чата.
        "timestamp": unixtime, // время прихода сообщения на сервер.
        color:"#6633FF", // цвет сообщения
        "text": "Всем чмоки в этом чатике." // оригинальное сообщение, за исключением того, что html-разметка эскейпится.
        // клиент сам занимается преобразованием спец. символов (например подстановка смайлов).
    }
}

Отправить приватное сообщение

//req_to_server
{
    "type": "send_private_message",
    "data": {
        "channel_id": "5",
        "user_id": "124" //получатель
        "text": "Привет, как дела?" // обрабатывается аналогично всем сообщениям
    }
}

Получение приватного сообщения

//res_to_client
{
    "type": "private_message",
    "data": {
        "channel_id": "5",
        "user_id": "124", // отправитель
        "user_name": "Василий",
        "target_id": 124, // получатель
        "target_name": "Валера",
        "timestamp": unixtime,
        "text": "Привет, как дела?" // обрабатывается аналогично всем сообщениям
    }
}

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

//req_to_server
{
    "type": "remove_message",
    "data": {
        "channel_id": "5",
        "message_id": "100" //номер сообщения которое нужно удалить.
    }
}

При этом сервер отсылает также всем клиентам в канале сообщение

//res_to_all_in_channel
{
    "type": "remove_message",
    "data": {
        "channel_id": "5",
        "message_id": "100" //номер сообщения которое нужно удалить.
    }
}

Бан пользователей

//req_to_server
{
    "type": "ban",
    "data": {
        "channel_id": "5", // канал в котором вынесен бан
        "ban_channel": "5", // канал в котором необходимо забанить, если 0 - то на все каналы
        "user_id": "124",
        "duration": 3600, // время бана в секундах
        "reason": "Плохо себя вёл", //причина
        "comment": "Я вас всех шатал", // текст сообщения, за который вынесен бан
        "show_ban": true // показывать ли бан
    }
}

Что бы всем в чате было видно, кого и за что.

//res_to_all_in_channel
{
    "type": "user_ban",
    "data": {
        "channel_id": "5",
        "user_id": "124", // id забаненого пользователя
        "user_name": "Василий", // ник забаненого пользователя
        "moder_id": "123", // id пользователя, вынесшего бан
        "moder_name": "Валера", // ник пользователя, вынесшего бан
        "moder_group": 1, // на основе группы, определяется каким цветом выводить сообщения
        "duration":  3600, время на сколько забанен пользователь в секундах
        "reason": "Плохо себя вёл"
    }
}

Предупреждение пользователей.

//req_to_server
{
    "type": "warn",
    "data": {
        "channel_id": "5",
        "user_id": "124",  // кого предупреждаем
        "reason": "Плохо себя ведешь" //причина
    }
}

Что бы всем в чате было видно, кого и за что.

//res_to_all_in_channel
{
    "type": "user_warn",
    "data": {
        "channel_id": "5",
        "user_id": "124", // id пользователя, кому вынесено предупреждение
        "user_name": "Василий", // ник забаненого пользователя
        "moder_id": "123", // id пользователя, вынесшего предупреждение
        "moder_name": "Валера", // ник пользователя, вынесшего бан
        "moder_group": 1, // на основе группы, определяется каким цветом выводить сообщения
        "reason": "Плохо себя ведешь"
    }
}

Создание голосования.

//req_to_server
{
    "type": "new_poll",
    "data": {
        "channel_id": "5",
        "title": "Зеленое или старкрафт?", // заголовок голосования
        "answers": [{"text": "Зеленое"} {"text": "старкрафт"}, {"text": "я упырь"}], // массив вариантов, не больше 6
    }
}

После создания голосования, клиенты получают сообщение

//res_to_all_in_channel
{
    "type": "new_poll",
    "data": {
        "channel_id": "5",
        "moder_id": 6,
        "moder_name": "Василий",
        "title": "Зеленое или старкрафт?", // заголовок голосования
        "answers": [{"id": 1, "text": "Зеленое"}, {"id": 2, "text": "красное"}, {"id": 3, "text": "я упырь"}], // массив вариантов, не больше 6
    }
}

Запросить текущее голосование для канала

//req_to_server
{
    "type": "get_poll",
    "data": {
        "channel_id": "5"
    }
}

Ответом будет сообщение new_poll, если этот пользователь еще не голосовал или poll_results - если голосовал

Выбор варианта пользователем

//req_to_server
{
    "type": "vote",
    "data": {
        "channel_id": "5",
        "answer_id": 1
    }
}

Запрос результатов голосования

//req_to_server
{
    "type": "get_poll_results",
    "data": {
        "channel_id": "5",
    }
}

Получение результатов голосования

//res_to_client
{
    "type": "poll_results",
    "data": {
        "channel_id": "5",
        "voters": 200, // количество проголосовавших
        "title": "Зеленое или старкрафт?", // заголовок голосования
        "answers": [{"id": 1, "text": "Зеленое", "voters": 100}, {"id": 2, "text": "красное", "voters": 50}, {"id": 3, "text": "я упырь", "voters": 50}] // массив вариантов, не больше 6
    }
}

Клиент сам строит график и высчитывает процентное соотношение. Так же сам решает, что делать, если во время отображения результатов голосования, приходит сообщение "new_poll" (Новое голосование).

Получение ника пользователя по его id

//req_to_server
{
    "type": "get_user_info",
    "data": {
        "user_id": "124"
    }
}
//res_to_client
{
    "type": 'user',
    "data": {
        "user_id": "124",
        "name": "Abcd"
    }
}

Назначение/снятие прав помошника стримера

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

Уровни прав описаны ниже.

//req_to_server
{
    "type": "make_moderator",
    "data": {
        "channel_id": "5",
        "user_id": "124"
    }
}
//req_to_server
{
    "type": "clean_moderator",
    "data": {
        "channel_id": "5",
        "user_id": "124"
    }
}

Обновление прав пользователя

Если изменяются права текущего пользователя в чате то клиент получает следующее сообщение.

//res_to_client
{
    "type": 'update_rights',
    "data": {
        "channel_id": "5",
        "access_rights": 10
    }
}

Обновление статуса премиум пользователя

Клиент может запросить статус премиума текущего пользователя для выбранного канала.

//req_to_server
{
    "type": "refresh_premium",
    "data": {
        "channel_id": 5
    }
}

Если текущий пользователь имеет премиум для этого канала, то сервер вернет следующее сообщение

//res_to_client
{
    "type": 'update_premium',
    "data": {
        "channel_id": 5,
        "premium": true
    }
}

В любой момент, с сервера может прийти сообщение, информирующее об ошибке.

//res_to_client
{
    "type": "error",
    "data": {
        "channel_id": "5",
        error_num : 201, // Идентификатор ошибки, разбиты по уровням.
        "errorMsg": 'Не достаточно прав' // Готовое сообщение.
    }
}

Уровни ошибок:

  • 0..100 технические ошибки, например неверный синтаксис сообщений, или проблемы на сервере с обработкой запроса.
  • 101..200 ошибки связанные с данными, например неверный идентификатор канала, несуществующий id пользователя
  • 201..300 ошибки связанные с правами пользователя, например не достаточно прав на удаление сообщения.

Клиент по номеру ошибки, должен соответствующим образом проинформировать пользователя.

Уровни прав

Код Название Уровень
casual Обычный 0
stream_moder Помошник стримера 10
streamer Стример 20
moderator Модератор 30
smoderator Супермодератор 40
admin Администратор 50

Обработка смайлов на стороне клиента

Для разных каналов доступен разный набор смайлов. Весь набор смайлов, а также их тип можно получить по url:

http://goodgame.ru/js/minified/global.js

Рассмотрим структуру ответа:

var Global = {
    Smiles : [{"bind":"11","name":"thup","donat":0,"premium":1,"paid":0}, ... ],
    Channel_Smiles : {"1577":[{"bind":"1","name":"shimoro2","donat":0,"premium":1,"paid":0}, ... }
    ...
}

Ключ Smiles отвечает за общедоступные смайлы, Channel_Smiles - за смайлы, привязанные к конкретному каналу.

Так же, каждый смайл описан следующим набором характеристик:

  • bind - для внутренних нужд,
  • name - код смайла в тексте сообщения,
  • donat - признак донатного смайла (уровни доната расписаны ниже),
  • premium - премиум смайл (является ли пользователь премиум-клиентом в данном канале можно узнать из собщений success_join, message, private_message или отправив refresh_premium)
  • paid - признак платного смайла (узнать список доступных платных смайлов можно в поле paidsmiles из собщений success_join, message, private_message)

Уровни доната для смайлов зависят от доната пользователя, которое можно узнать из собщений success_join, message, private_message поле payments:

  • donat0 < 100
  • 1 >= 100, < 300
  • 2 >= 300, < 500
  • 3 >= 500, < 3000
  • 4 >= 3000, < 10000
  • 5 >= 10000

Так же, все платные смайлы доступны клиентам с правами больше чем стримерские (стримеру, модераторам, супермодераторам и админам).

Если текущий пользователь является стримером, то сервер дополнительно уведомляет о донате и активации премиумов:

Донат:

// res_to_client
// Поля total и title передаются только при донате в призовой фонд турнира
{
    "type": "payment",
    "data": {
        "channel_id": "5",
        "userName": "Петя",
        "amount": 199,
        "message": "Эгегей!",
        "total": 230,
        "title": "Турнир номер 1",
        "is_commission_covered": false
    }
}
  • is_commission_covered - поле типа boolean, указывающее покрыл ли донатер комиссию платежной системы

Активация премиума:

//res_to_client
{
    "type": "premium",
    "data": {
        "channel_id": "5",
        "userName": "Петя"
    }
}