COMPLETELY REWROTE API
This commit is contained in:
parent
861f889375
commit
2a1d4a57c3
502
api.typ
502
api.typ
@ -13,15 +13,23 @@
|
||||
|
||||
#let funnyArrow = $~>$
|
||||
|
||||
|
||||
#let bigrect(cnt) = rect(stroke: 1pt, width: 100%, cnt)
|
||||
#let uritxt(string) = text(fill: rgb(12, 10, 255, 255), raw(string))
|
||||
|
||||
|
||||
#Heading([Руководство], 1)
|
||||
|
||||
Обозначается множество запросов,
|
||||
которые клиент может посыдать серверу для передачи и
|
||||
получения данных. На один запрос следует один ответ (как минимум что бы сообщить об
|
||||
успехе запроса).
|
||||
получения данных. На один запрос следует один ответ.
|
||||
|
||||
Обозначаются переменные, которые сервер передаёт nytl шаблонизатору, который в свою очередь
|
||||
засовывает в html тег ```html <script> ```, что бы при инициализации клиентсайдовой жс-программы,
|
||||
у неё были эти переменные.
|
||||
|
||||
Для каждого запроса есть свой URI адрес на сервере. Для сохранения порядка адреса всех
|
||||
запросов начинаются в #raw("/internalapi"). Объявление запроса состоит из указания его
|
||||
запросы начинаются с #uritxt("/api/"). Объявление запроса состоит из указания его
|
||||
адреса (выступающего чем-то вроде имени), формата отправляемых (на сервер) данных и
|
||||
формата принимаемых данных. После объявления (в прямоугольнике) идёт описание.
|
||||
Данные запроса и ответа передаются в формате JSON. Ключевое слово #bold[Sent] обозначает
|
||||
@ -29,6 +37,8 @@
|
||||
|
||||
#Heading([Структура], 1)
|
||||
|
||||
Здесь и далее слово "чат" означает то же самое что и комната.
|
||||
|
||||
У каждого чата поддерживается текущий HistoryID. Изначально эта переменная (на сервере)
|
||||
равна нулю, но с удалением, изменением и написанием новых сообщений, с добавлением
|
||||
и удалением пользователей из группы, изменяется состояние чата. Эти изменения называются
|
||||
@ -41,165 +51,69 @@
|
||||
списка событий клиент заменяет свой LocalHistoryID на полученный HistoryID.
|
||||
И так далее, по циклу. Любой запрос возвращает обновления.
|
||||
|
||||
Не только чаты могут получать события. Здесь "сущностями с историей" я называю чаты и
|
||||
список чатов пользователя.
|
||||
Не только чаты могут получать события.
|
||||
Список чатов, в которые входит пользователь, тоже может.
|
||||
Здесь "сущностями с историей" я называю чаты и список чатов пользователя.
|
||||
|
||||
Определим особую структуру hist_entity_request такого формата:
|
||||
Запросов много, но все они обновляют какую-то из "сущностей с историей".
|
||||
|
||||
`hist_entity_request["type"]` = `"chat"` или `"chatlist"`
|
||||
Например, #uritxt("/api/sendMessage") обновляет историю сообщений чата,
|
||||
в который новое сообщение было отправлено, а #uritxt("/api/createChat") обновляет
|
||||
список чатов пользователя, который вызвал этот запрос.
|
||||
|
||||
Если запрашивается чат, то нужно указать его ID.
|
||||
`hist_entity_request["chatId"]` = Число
|
||||
Значит, для каждого запроса нужен свой способ указать нужную сущность с историей и её
|
||||
LocalHistoryId, соответствующий сущности, изменяемой запросом, а также нужно уметь
|
||||
принимать ответ с сервера об изменениях, произошедших с сущностью, что бы уметь
|
||||
обновлять её состояние.
|
||||
|
||||
`hist_entity_request["LocalHistoryId"]` = Число #funnyArrow указывает версию чата в памяти
|
||||
клиента
|
||||
#Heading([Чаты], 2) <update_chat>
|
||||
|
||||
Пример: ```json {"type": "chat", "chatId": 2, "LocalHistoryId": 12312321}```
|
||||
```json {"type": "chatlist", "LocalHistoryId": 31231}```
|
||||
Запросы, изменяющие чат, содержат поле `"chatUpdReq"`, в котором хранится объект с полями
|
||||
chatId и LocalHistoryId,
|
||||
а получают ответ с полем `"chatUpdResp`с полями HistoryId (обновлённое), обновлённое lastMsgId,
|
||||
полем-массивом messages, полем-массивом `members` (о них расскажу позже).
|
||||
|
||||
Определим особую структуру hist_entity_response такого формата:
|
||||
`Sent.chatUpdReq.chatId = Integer` - указывает чат \
|
||||
`Sent.chatUpdReq.LocalHistoryId = Integer` - через это поле клиент сообщает свой
|
||||
(возможно устаревший) LocalHistoryId. \
|
||||
`Recv.chatUpdResp.HistoryId = Integer` - это новое значение HistoryId, прешедшее на замену LocalHistoryId \
|
||||
`Recv.chatUpdResp.lastMsgId = Integer` - Номер последнего сообщения в чате. -1 если чат пуст. \
|
||||
`Recv.chatUpdResp.messages = Array` - массив объектов типа `messageSt`. Они выглядят так так: \
|
||||
`messageSt.id = Integer` - id сообщения (в этом чате). \
|
||||
`messageSt.isSystem = Boolean` \
|
||||
`messageSt.exists = Boolean` \
|
||||
`messageSt.text` \
|
||||
`messageSt.senderUserId` \
|
||||
Это всё поля, определяющие внутренности сообщения. Структура messageSt не обязательно означает, что
|
||||
за время обновления чата появилось такое сообщение, она говорит, что в момент `HistoryId` было такое сообщение
|
||||
с таким id.\
|
||||
`Recv.chatUpdResp.members` - массив объектов типа `memberSt`. Они выглядят так: \
|
||||
`memberSt.userId = Integer` - id пользователя, о членстве которого в этом чате говорит этот объект \
|
||||
`memberSt.nickname = String` \
|
||||
`memberSt.name = String` \
|
||||
`memberSt.roleHere = String` \
|
||||
`roleHere` это что-то из "admin", "regular", "read-only", "not-a-member".
|
||||
Самое главное - различать роль "not-a-member" от остальных. Ведь она означает что человек
|
||||
больше не состоит в чате, и если он отображался в списке, то
|
||||
его надо удалить. Различие остальных ролей не так принципиально.
|
||||
|
||||
`hist_entity_response["type"]` = `"chat"` или `"chatlist"`
|
||||
#Heading([Список чатов, где есть пользователь], 2) <update_chatList>
|
||||
|
||||
Если запрашивается чат, то нужно указать его ID.
|
||||
`hist_entity_response["chatId"]` = Число
|
||||
Запросы, изменяющеи список чатов залогиненного пользователя содержат поле `"chatListUpdReq"`,
|
||||
который содержит объект с полем `LocalHistoryId` (сами понимаете для чего),
|
||||
а `"chatListUpdResp"`с полем HistoryId (обновлённое), массивом `myChats`
|
||||
|
||||
`hist_entity_response["HistoryId"]` = Число. Это новый HistoryId для этой сущности с историей.
|
||||
|
||||
`history_entity_response["events"]` = Список новых событий.
|
||||
|
||||
Событие - это словарь. Обозначим его ключемым словом #bold[event]:
|
||||
|
||||
`event["type"]` = Строка #funnyArrow Это тип события, которые бывают нескольких типов:
|
||||
|
||||
- `"newMessage"` - приходит новое сообщение в чат
|
||||
- `"addedMember"` - в чат добавили нового участника
|
||||
- `"removedMember"` - из чата выкинули участника
|
||||
- `"addedChat"` - клиента добавили в чат
|
||||
- `"removedChat"` - клиента выкинули из чата
|
||||
|
||||
#Heading([Событие `newMessage`], 2) <ev_newMessage>
|
||||
|
||||
```json
|
||||
{
|
||||
"type" : "newMessage",
|
||||
"previous": 111111 // Id of previous message
|
||||
"id": 1112 // Id of the message
|
||||
"content": {
|
||||
"isSystem": false // Булевое значение, true если это системное сообщение вроде "поприветствуйте нового пользователя". Такие должны выделяться особым цветом.
|
||||
"text": "<text of the message>" // Text of the message
|
||||
"sender": 1000 // User ID of sender
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#Heading([событие `deletedMessage`], 2)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "deletedMessage",
|
||||
"id": 1112 // If of deleted message
|
||||
}
|
||||
```
|
||||
|
||||
#Heading([Событие `addedMember`], 2)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "addedMember",
|
||||
"member": 1000 // User ID добавленного человека
|
||||
"content": {
|
||||
"name": "<users name>",
|
||||
"nickname": "<users nickname>"
|
||||
"role": "regular" // Роль это либо admin, либо regular, либо read-only
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#Heading([Событие `removedMember`], 2)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "removedMember",
|
||||
"member": 1000 // User ID удалённого человека (или покинувшего чат человека)
|
||||
}
|
||||
```
|
||||
|
||||
#Heading([Событие `addedChat`], 2)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "addedChat",
|
||||
"id": 228, // Chat ID
|
||||
"content": {
|
||||
"name": "<имя чата>",
|
||||
"nickname": "<nickname чата>",
|
||||
"lastMsgId": 1212, // Id последнего сообщения. -1 Если чат пуст
|
||||
"roleHere": "regular" // Роль текущего пользователя в этом чате
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#Heading([Событие `removedChat`], 2)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "removedChat",
|
||||
"id": 228, // Chat ID того чата, из которого клиента удалили / из которого клиент вышел
|
||||
}
|
||||
```
|
||||
|
||||
Пример объекта hist_entity_response:
|
||||
```json
|
||||
{
|
||||
"type": "chat", "chatId": 2, "HistoryId": 12312325,
|
||||
"events": [
|
||||
{
|
||||
"type": "newMessage",
|
||||
"previous": 111111,
|
||||
"id": 111115,
|
||||
"content": {
|
||||
"isSystem": false,
|
||||
"text": "私は頭骨を劇場から盗んだ",
|
||||
"sender": 666
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "newMessage",
|
||||
"previous": 111115,
|
||||
"id": 1000000,
|
||||
"content": {
|
||||
"isSystem": true,
|
||||
"text": "Father mushroom left chat",
|
||||
"dender": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "removedMember",
|
||||
"member": 666
|
||||
},
|
||||
{
|
||||
"type": "addedMember",
|
||||
"member": 777,
|
||||
"content": {
|
||||
"name": "'Definitely not theatre looter'",
|
||||
"nickname": "father-mushroom-2",
|
||||
"role": "regular"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "newMessage",
|
||||
"previous": 1000000,
|
||||
"id": 1000002,
|
||||
"content": {
|
||||
"isSystem": true,
|
||||
"text": "'Definitely not theatre looter' joined chat",
|
||||
"sender": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
`Recv.chatListUpdResp.myChats` - массив объектов типа `myMembershipSt`.
|
||||
Каждый такой объект говорит о состоянии "нахождения пользователя (залогиненного) в чате" в момент HistoryId.
|
||||
Он может говорить как о нахождении пользователя в чате, так и о его уходе оттуда.
|
||||
Он выглядит так:
|
||||
`myMembershipSt.chatId = Integer` - id чата \
|
||||
`myMembershipSt.chatName = String` \
|
||||
`myMembershipSt.chatNickname = String` \
|
||||
`myMembershipSt.myRoleHere = String` \
|
||||
`myRoleHere` это что-то из "admin", "regular", "read-only", "not-a-member".
|
||||
Самое главное - различать роль "not-a-member" от остальных. Ведь она означает что
|
||||
чат из списка чатов надо удалить. Различие остальных ролей не так принципиально.
|
||||
|
||||
#Heading([Запросы], 1)
|
||||
|
||||
@ -211,259 +125,219 @@
|
||||
Так же, в ответах может присутствовать поле `Recv["error"]`, которое описывает ошибку.
|
||||
Но оно не обязательно даже в случае когда ошибка есть, когда ошибки нет оно бесполезно.
|
||||
|
||||
#let bigrect(cnt) = rect(stroke: 1pt, width: 100%, cnt)
|
||||
#let uritxt(string) = text(fill: rgb(12, 10, 255, 255), raw(string))
|
||||
|
||||
#Heading([Read-only запросы], 2)
|
||||
|
||||
#Heading([Получение событий], 3) <api_pollEvents>
|
||||
#Heading([Получение событий в чате], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/pollEvents") \
|
||||
- URI: #uritxt("/api/chatPollEvents") \
|
||||
- Отправить: \
|
||||
`Sent["scope"]` = [hist_entity_request, ..., hist_entity_request] #funnyArrow Список
|
||||
сущностей, по которым клиент хочет получить обновления. \
|
||||
`Sent.chatUpdReq` - про это поле говорилось в @update_chat \
|
||||
- Получить: \
|
||||
`Recv["update"]` = [hist_entity_response, ..., hist_entity_response] #funnyArrow Список
|
||||
сущностей, для которых пришли изменения.
|
||||
`Recv.chatUpdResp` - про это поле тоже говорилось выше
|
||||
]
|
||||
|
||||
В списке `Recv["update"]` будут предоставлены обновления только для тех сущностей,
|
||||
которые были запрошены.
|
||||
Эта функция может только обновлять чат и только один чат.
|
||||
|
||||
#Heading([Получение списка чатов где пользователь состоит], 3)
|
||||
#Heading([Получение событий в списке чатов вошедшего пользователя], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/getChatList") \
|
||||
- Отправить: \
|
||||
`Sent = {}` #funnyArrow Да, мы просто отправляем пустой словарь \
|
||||
- Получить: \
|
||||
`Recv["chats"] = Array` \
|
||||
`Recv["chats"][i]["id"] = Integer` #funnyArrow Chat ID \
|
||||
`Recv["chats"][i]["content"]["name"] = "<chat name>"` \
|
||||
`Recv["chats"][i]["content"]["nickname"] = "<chat nickname>"` \
|
||||
`Recv["chats"][i]["content"]["lastMsgId"]` = Integer #funnyArrow Id последнего сообщения в чате
|
||||
]
|
||||
|
||||
#Heading([Получение информации о чате], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/getChatInfo") \
|
||||
- Отправить: \
|
||||
`Sent["id"] = Integer` Просто отправляем id чата \
|
||||
- Получить: \
|
||||
`Recv["name"] = "<chat name>"` \
|
||||
`Recv["nickname"] = "<chat nickname>"` \
|
||||
`Recv["lastMsgId"] = Integer` #funnyArrow ID последнего сообщения в чате.
|
||||
`Recv["roleHere"] = "admin" / "regular" / "read-only"`
|
||||
]
|
||||
|
||||
Если чат пуст, то lastMsgId равен -1.
|
||||
roleHere указывает роль залогиненного пользователя в этом чате.
|
||||
|
||||
#Heading([Получение списка участников чата], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/getChatMemberList") \
|
||||
- Отправить: \
|
||||
`Sent["chatId"] = Integer` Это id чата \
|
||||
- Получить: \
|
||||
`Recv["members"] = Array` Получаем мы только список участников чата. \
|
||||
Вот какие структуры находятся в этом списке: \
|
||||
`Recv["members"]["id"] = Integer` ID Пользователя \
|
||||
`Recv["members"]["content"]["name"] = "<users name>"` \
|
||||
`Recv["members"]["content"]["nickname"] = "<users nickname>"` \
|
||||
`Recv["members"]["content"]["role"] = "<Роль этого участника чата>"`
|
||||
]
|
||||
|
||||
Роль учатника это либо `"admin"`, либо `"regular"`, либо `"read-only"`. Думаю, семантика их ясна из
|
||||
названий.
|
||||
|
||||
#Heading([Получение информации о пользователе], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/getUserInfo") \
|
||||
- Отправить: \
|
||||
`Sent["id"] = Integer` #funnyArrow Id пользователя о котором хотим получить инфу. \
|
||||
- Получить: \
|
||||
`Recv["content"]["name"] = "<user name>"` \
|
||||
`Recv["content"]["nickname"] = "<user nickname>"` \
|
||||
]
|
||||
|
||||
#Heading([Получение информации о сообщении], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/getMessageInfo") \
|
||||
- Отправить: \
|
||||
`Sent["chatId"] = Integer` \
|
||||
`Sent["id"] = Integer` \
|
||||
- Получить: \
|
||||
`Sent["content"]["text"] = "<text of message>"` \
|
||||
`Sent["content"]["isSystem"] = Boolean` \
|
||||
`Sent["content"]["sender"] = 1100` #funnyArrow User ID отправителя \
|
||||
- URI: #uritxt("/api/chatListPollEvents") \
|
||||
- Отправить: \
|
||||
`Sent.chatListUpdReq` - про это поле говорилось в @update_chatList \
|
||||
- Получить: \
|
||||
`Recv.chatListUpdResp` - про это поле тоже говорилось выше
|
||||
]
|
||||
|
||||
#Heading([Получение соседей сообщения], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/getMessageNeighbours") \
|
||||
- URI: #uritxt("/api/getMessageNeighbours") \
|
||||
- Отправить: \
|
||||
`Sent["chatId"] = Integer` \
|
||||
`Sent["amount"] = Integer` \
|
||||
А далее либо \
|
||||
`Sent["direction"] = "forward"` \
|
||||
`Sent["id"] = Integer` #funnyArrow id сообщения, от которого начинается отсчет \
|
||||
Либо \
|
||||
`Sent["direction"] = "backward"` \
|
||||
`Sent["previousMsgId"] = Integer`
|
||||
`Sent.chatUpdReq` - это поле везде значит одно и то же. Отсюда берётся `chatId`,
|
||||
что бы значть о каком чате идёт речь \
|
||||
`Sent.msgId` - id сообщения, с которого начинается отсчет. Можно указать -1 при backward-запросе
|
||||
и отсчет пойдёт "со дна" чата \
|
||||
`Sent.direction = String` - это либо `"backward"`, либо `"forward"` \
|
||||
`Sent.amount = Integer` - Либит на пролистывание сообщений \
|
||||
- Получить: \
|
||||
`Recv["messages"] = Array` \
|
||||
Его элементы это ID сообщения и контент сообщения. \
|
||||
`Recv["messages"][i]["id"]` \
|
||||
`Recv["messages"][i]["previous"]` \
|
||||
`Recv["messages"][i]["content"]`
|
||||
`Sent.chatUpdResp`
|
||||
]
|
||||
|
||||
Структура контента сообщения описана в @ev_newMessage.
|
||||
Можно получать полную информацию об $n$ сообщения до или после определённого сообщения.
|
||||
$n = "Sent"."amount"$.
|
||||
Можно заметить, что в ответе мы не получаем никакого списка сообщений, только класический `chatUpdResp`.
|
||||
Дело в том, что поля `msgId`, `direction`, `amount` лишь говорят обнови состояние чата, но
|
||||
ОБ ЭТИХ ВОТ
|
||||
сообщениях пришли мне всю информацию, что только есть, даже если они не новые и никак не менялись.
|
||||
|
||||
Можно узнать соседей сообщения "сверху и снизу". Направление `"backward"` покажет $n$
|
||||
сообщений до переданного сообщения (они будут расположены в списке в обратном порядке),
|
||||
направление `"forward"` покажет $n$ сообщений до указанного. Здесь $n$ это выбранное клиентом
|
||||
количество желаемых сообщений (`Sent["amount"]`).
|
||||
Если направление forward, то нужно указать id сообщения, чьих соседей мы ищем.
|
||||
Если направление baackward, то нужно указать id ПРЕДЫДУЩЕГО сообщения, относительно
|
||||
нашего сообщения. Т.е. в ответ войдёт id сообщения, которое мы указали. Можно указать
|
||||
-1, тогда точно не вернётся ничего и это не ошибка.
|
||||
Сервер ОБЯЗАН вернуть ровно $n$ сообщений,
|
||||
если они есть и ОБЯЗАН вернуть все сообщения до определённого края истории чата, если край достигнут.
|
||||
Рекомендуется держать сообщения в разреженном массиве.
|
||||
|
||||
#Heading([Запросы изменения состояния одного чата], 2)
|
||||
|
||||
#Heading([Отправка сообщения], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/sendMessage") \
|
||||
- URI: #uritxt("/api/sendMessage") \
|
||||
- Отправить: \
|
||||
`Sent["chatId"] = Integer` #funnyArrow В какой чат мы хотим отправить сообщение \
|
||||
`Sent["LocalHistoryId"] = Integer` #funnyArrow HistoryID для состояния ЭТОГО чата (в памяти клиента) \
|
||||
`Sent["content"]["text"] = "<text of message>"` \
|
||||
`Sent.chatUpdReq` - ... \
|
||||
`Sent.content.text = "<text of message>"` \
|
||||
- Получить: \
|
||||
`Recv["update"] = Array`
|
||||
`Recv.chatUpdResp = Array`
|
||||
]
|
||||
|
||||
Здесь поле `Recv["update"]` означает то же самое, что и в
|
||||
#uritxt("/internalapi/pollEvents") (см. @api_pollEvents). Там
|
||||
содержатся только один hist_entity_response - указывающий изменения в этом чате.
|
||||
Клиент ОБЯЗАН отображать отправленное пользователем сообщение только тогда, когда
|
||||
он найдёт сообщение с сервера о событии с отправкой этого сообщения.
|
||||
|
||||
#Heading([Удаление сообщения], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/deleteMessage") \
|
||||
- URI: #uritxt("/api/deleteMessage") \
|
||||
- Отправить: \
|
||||
`Sent["chatId"] = Integer` \
|
||||
`Sent["LocalHistoryId"] = Integer` #funnyArrow HistoryID для состояния ЭТОГО чата \
|
||||
`Sent["id"] = Integer` \
|
||||
`Sent.chatUpdReq` \
|
||||
`Sent.id = Integer` - Id выбранного на расстрел сообщения \
|
||||
- Получить: \
|
||||
`Recv["update"] = Array of hist_entity_response` #funnyArrow Всё то же самое (См. @api_pollEvents)
|
||||
`Recv.chatUpdResp`
|
||||
]
|
||||
|
||||
Recv.update будет содержать только один hist_entity_response (для этого чата)
|
||||
|
||||
#Heading([Добавление участника в чат], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/addMemberToChat") \
|
||||
- URI: #uritxt("/api/addMemberToChat") \
|
||||
- Отправить: \
|
||||
`Sent["chatId"] = Integer` #funnyArrow К какому чату добавляем \
|
||||
`Sent["LocalHistoryId"] = Integer` #funnyArrow HistoryID для состояния ЭТОГО чата \
|
||||
`Sent["userId"] = Integer` #funnyArrow ID добавляемого пользователя \
|
||||
`Sent.chatUpdReq` \
|
||||
`Sent.nickname = String` - никнейм того участника, которого мы хотим добавить.
|
||||
- Получить: \
|
||||
`Recv["update"]`
|
||||
`Recv.chatUpdResp`
|
||||
]
|
||||
|
||||
Никнейм добавляемого участника должен быть введён безошибочно, иначе вернётся ошибка.
|
||||
Выполняется только администратором.
|
||||
|
||||
#Heading([Удаление участнка из чата], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/removeMemberFromChat") \
|
||||
- URI: #uritxt("/api/removeMemberFromChat") \
|
||||
- Отправить:
|
||||
`Sent["chatId"] = Integer` #funnyArrow Из какого чата удалить \
|
||||
`Sent["LocalHistoryId"] = Integer` #funnyArrow HistoryID для состояния ЭТОГО чата \
|
||||
`Sent["userId"] = Integer` #funnyArrow ID удаляемого пользователя \
|
||||
`Sent.chatUpdReq` \
|
||||
`Sent.userId` - id пользователя, которого мы хотим удалить из чата
|
||||
- Получить: \
|
||||
`Recv["update"]`
|
||||
`Recv.chatUpdResp`
|
||||
]
|
||||
|
||||
Как можно заметить, все запросы, изменяющие состояние чата имеют побочную функцию: обновление.
|
||||
Она обязательна, клиент ОБЯЗАН указывать LocalHistoryID,
|
||||
в запросе приутствует мнимый hist_entity_requet. Он аддресует тот чат, к котороу применим запрос
|
||||
Она обязательна, клиент ОБЯЗАН указывать chatUpdReq.
|
||||
Выполняется только администратором.
|
||||
|
||||
#Heading([Запросы изменения состояния списка чатов пользователя], 2)
|
||||
|
||||
В запросах этой категории есть ключ `"LocalHistoryId"`, а в ответах на них есть ключ `"update"`,
|
||||
но они нужны для поллинга событий не какого-то чата, а списка чатов где есть пользователь.
|
||||
|
||||
#Heading([Создание чата], 3)
|
||||
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/createChat")
|
||||
- URI: #uritxt("/api/createChat")
|
||||
- Отправить: \
|
||||
`Sent["LocalHistoryId"] = Integer` #funnyArrow Это ID состояния списка чатов пользователя \
|
||||
`Sent["content"]["name"] = "<name of chat>"` \
|
||||
`Sent["content"]["nickname"] = "<nickname of chat>"` \
|
||||
`Sent.chatListUpdReq` \
|
||||
`Sent.content.name = String` \
|
||||
`Sent.content.nickname = String` \
|
||||
- Получить: \
|
||||
`Sent["update"]` #funnyArrow События, произошедшие cо списком чатов пользователя.
|
||||
`Sent["chatId"] = Integer`
|
||||
`Recv.chatListUpdResp`
|
||||
]
|
||||
|
||||
`Sent["chatId"]` это id созданного по этому запросу чата
|
||||
`Sent.content.name` и `Sent.content.nickname` это выбранные названия для нашего чата.
|
||||
Если введены невозможные названия (слишком длинные или занятый никнейм), то вернётся ошибка
|
||||
Пользователь, создавший чат, мгновенно вступает в него в качестве администратора.
|
||||
|
||||
#Heading([Удаление чата], 3)
|
||||
#Heading([Удаление чата (Уйти из чата)], 3)
|
||||
#bigrect[
|
||||
- URI: #uritxt("/internalapi/leaveChat")
|
||||
- URI: #uritxt("/api/leaveChat")
|
||||
- Отправить: \
|
||||
`Sent["LocalHistoryId"] = Integer` #funnyArrow Это ID состояния списка чатов пользователя \
|
||||
`Sent["id"] = Integer` #funnyArrow Chat Id того чата, который должен быть удалён \
|
||||
`Sent.chatListUpdReq`
|
||||
`Sent.chatId` - id чата, который мы покидаем.
|
||||
- Получить: \
|
||||
`Sent["update"]` #funnyArrow События, произошедшие cо списком чатов пользователя.
|
||||
`chatListUpdResp`
|
||||
]
|
||||
|
||||
#Heading([Инициализация списка чатов], 1)
|
||||
|
||||
Возникает вопрос "А как программа-клиент узнаёт с какого состояния чата начинать работу,
|
||||
если мы можем запрашивать только обновление сущностей с историей? Откуда брать 'стартовые данные'?"
|
||||
|
||||
Эту информацию сервер динамически вставляет в отправляемую страничку (в тег ```html <script>```) с помощью
|
||||
New York Transit Line.
|
||||
|
||||
Вот какие переменные доступны жс-коду на странице списка чатов:
|
||||
|
||||
`pres` - локаль (описана в config.json, поле "presentation". (pres - от слова presentation)) \
|
||||
`userinfo.id`, `userinfo.nickname`, `userinfo.name` - это всё информация о вошедшем пользователе.
|
||||
(О себе).
|
||||
|
||||
`initial_chatListUpdResp` - этот объект имеет такую же структуру, как и
|
||||
`Recv.chatListUpdResp` из всех тех запросов к списку чатов, отсюда и название.
|
||||
В `initial_chatListUpdResp.HistoryId` хранится то значение LocalHistoryId, с которого мы начинаем,
|
||||
а каждый объект типа `myMembershipSt` в `initial_chatListUpdResp.myChats` говорит об одном чате,
|
||||
в котором мы членствуем.
|
||||
|
||||
Эти данные эквивалентны запросу #uritxt("/api/chatListPollEvents") с LocalHistoryId = 0,
|
||||
но сейчас вы увидете, что в случае инициализации ЧАТА подобный подход НЕОБХОДИМ.
|
||||
|
||||
#Heading([Инициализация чата], 1)
|
||||
|
||||
Вот какие переменные доступны жс-коду на странице списка чатов:
|
||||
|
||||
`pres` - локаль (описана в config.json, поле "presentation". (pres - от слова presentation)) \
|
||||
`userinfo.id`, `userinfo.nickname`, `userinfo.name` - это всё информация о вошедшем пользователе.
|
||||
(О себе). Это всё было и в list-rooms, но теперь добавляются такие переменные:
|
||||
|
||||
`chatinfo.id`, `chatinfo.name`, `chatinfo.nickname` - это всё информация о том чате, что мы открыли.
|
||||
|
||||
`initial_chatUpdResp` - этот объект имеет такую же структуру, как и `Recv.chatUpdResp`
|
||||
из всех тех запросов к чату, отсюда и название.
|
||||
|
||||
Здесь поля `.HistoryId`, `.members` и `.lastMsgId` имеют то же значение, что и при обычном запросе обновления
|
||||
(в массиве `members` будет информация о ВСЕХ участниках чата (в начале выполнения жс-кода)),
|
||||
но массив `.messages` немного хитрее. Если мы зашли по ссылке #uritxt("/chat/<nickanme>"), то
|
||||
мы просматриваем чат "с конца". И ".messages" будет пустовать. Если же мы зашли по ссылке
|
||||
#uritxt("/chat/<nickname>/m/<msgId>"), то массив .messages будет содержать ровно один объект типа `messageSt`:
|
||||
о том самом сообщении, с которого начался просмотр чата. Т.е. отличие от запроса #uritxt("/api/chatPollEvents")
|
||||
в том, что мы НЕ ХОТИМ ПОЛУЧАТЬ СРАЗУ ВСЕ СООБЩЕНИЯ ИЗ ЧАТА. Мы хотим начать только с одного.
|
||||
|
||||
#Heading([Расположение ресурсов веб-чата по URI], 1)
|
||||
Эта секция не касается internalApi, она о самом веб-чате.
|
||||
Эта секция не касается API, она о самом веб-чате.
|
||||
|
||||
#Heading([Заглавная страница], 2)
|
||||
По адресу #uritxt("/") показывается список чатов пользователя. Еезалогиненный пользователь,
|
||||
По адресу #uritxt("/") показывается список чатов пользователя. Незалогиненный пользователь,
|
||||
зашедший сюда, будет переслан на #uritxt("/login")
|
||||
|
||||
#uritxt("/list-rooms") это алиас для #uritxt("/")
|
||||
|
||||
#Heading([Логин], 2)
|
||||
Адрес: #uritxt("/login"). На этой странице нет ничего кроме формы для входа.
|
||||
Форма отправляет данные на #uritxt("/login") POST запросом.
|
||||
Успешный вход пересылает на #uritxt("/"), неуспешный показывает ошибку на этой же странице.
|
||||
|
||||
#Heading([Создание чата], 2)
|
||||
Форма создания чата отправляет данные на #uritxt("/createChat") POST запросом.
|
||||
При успехе пользователь пересылается на страницу чата (#uritxt("/chatRoom/..."))
|
||||
|
||||
Где находится форма - сами решайте.
|
||||
|
||||
#Heading([Редактирование профиля], 2)
|
||||
Свой профиль редактируется на странице #uritxt("/mirror"). Туда же отправляются данные формы
|
||||
Свой профиль редактируется на странице #uritxt("/user/<your nickname>"). Туда же отправляются данные формы
|
||||
(POST запросом). При успехе пользователь пересылается на главную страницу,
|
||||
иначе, остаётся на прежней.
|
||||
иначе, остаётся на прежней и высвечиваются красные коробочки с ошибкой.
|
||||
|
||||
#Heading([Просмотр профиля], 2)
|
||||
Профиль любого человека можно посмотреть на странице #uritxt("/user/<nickname>")
|
||||
|
||||
#Heading([Страница чата], 2)
|
||||
Адрес: #uritxt("/chat/<nickname>"). Об остальных страницах договаривайтесь сами.
|
||||
Адрес: #uritxt("/chat/<nickname>").
|
||||
|
||||
Можно при открытии чата сразу перейти к сообщению с заранее известным id:
|
||||
#uritxt("/chat/<nickname>/m/<id of message in chat>")
|
||||
|
||||
Просмотр списка пользователей на странице #uritxt("/chat-members/<nickname>")
|
||||
Здесь же администратор может добавлять и удалять участников чата (через api запросы)
|
||||
|
||||
#Heading([Замечания], 1)
|
||||
Эта спецификация не утверждает, что Id для одних и тех же чатов и сообщений
|
||||
сохраняется междй пользователями. Так же не утверждается, что Id сообщений
|
||||
сохраняются между чатами. Для именования чатов и пользователей,
|
||||
пользователи должны использовать никнеймы.
|
||||
Утверждается, что id сообщений в чате последовательны (от 1 до какого-то натурального числа).
|
||||
Т.е. все участники чата видят одни и те же номера ссобщений.
|
||||
Эта спецификация не утверждает, что id для одних и тех же чатов и людей сохраняется между
|
||||
сессиями. Для именования чатов и пользователей, пользователи должны использовать никнеймы.
|
||||
|
||||
Когда чат или пользователь занимают никнейм, а потом заменяют его на новый,
|
||||
старый не перестаёт им пренадлежать, и не может уже никогда быть занятым другим
|
||||
чем-либо. Имя чата или пользователя может содержать любые символы. Никнейм
|
||||
|
Loading…
Reference in New Issue
Block a user