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
{
"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"
}
}
//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" (Новое голосование).
//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": "Петя"
}
}