Page: unichat API groups
2020-02-11 12:02
Groups
Please read unichat groups and unichat subscriptions first to clearly understand what are these and how it works.
Get subscriptions
For each group used is subscribed to there is a corresponding subscription which carries a group information plus user rights in the group and more important data, see more in unichat subscription.
So, instead of querying groups user has access to, client calls for subscriptions:
get_subscriptions(short: false,limit: 0,offset: 1000)
-> { subscriptions: [<<unichat subscription record>,...]
See unichat subscription record for the record structure, and unichat groups and unichat subscriptions for overall explanation.
This command supports paging. Sunscriptions are ordered by subscription.id ascending, so just set
limit
andoffset
to some non-default values to iterate through subscriptions.set
short: true
to get subscription information without group participants. the short form carries all the information to show groups bar, while heavygroup.participants
section is needed only when the group is opened.
We recommend first to scan subscriptions by chunks of 10-100 records in short
mode to quickly display them, and use get_subscription(id)
when opening a group
UI, or scan them all in the background wither way.
Get single subscription
Use it when you need to get or refresh only one subscription with known id
,
for exmaple, after receiving the notification.
get_subscription(subscription_id:<long>)
-> {subscription: <subscription_record>}
It is not equal to get_subscriptions()
as it provides access to the goven id
that can't be done otherwise without scanning all (in worst case) subscriptions.
Create room
A room is a general purpose chat. Creator becomes owner and admins. As ususal with universa chat, the result is the subscription of the creator.
create_room name: <string>, user_ids: []. is_space: <opt_boolean>
-> { subscription: <<unichat subscription record>}
with parameters:
name
: required room name. Any string.user_ids
: optional array ofuser_id
to be invited immediately.is_space
: if not empty, the group attribute is_space will be changed. Default value:false
.
It is possible to create room without users: it will create only creator (which becomes an owner) subscription.
It returns the creator's unichat subscription record as {subscription:
<<unichat subscription record>}
. Creator becomes an admin,
all invited users will get write permissions. IT is possible to add participants
later.
When creating subscriptions for the room, each involved user, including creator, will receive new subscription notificaton, e.g.
{"event": "new", "object_name": "subscription", "object": {<subscription record>}}
Also, new created room will be posted with one unichat system message with
xtag: 'creation'
from room owner and one xtag: 'invite', reference_type: 'user',
reference_id: user_id
for each invited user.
Message record
Carries information about teh chat message. It can be short and long. Short version represents deleted messages, where text and some other fieds are not available. Here is the example:
{
"message": {
"id": 34,
"user_id": 367,
"group_id": 54,
"deleted_at": null, // meaning it is not deleted
"serial": 38,
"text": "hello u2!",
"xtag": null,
"attachment": null,
"attachments": [],
"mentions": [], // or null, or array of mention records
"reference": {type: "user", id: "1"},
"forwarded_message_id": 11, // optional: only if not null
"in_reply_to_message_id": 22, // optional: only if not null
"edited_at": null, // was not edited
"created_at": "2018-10-15T18:33:56Z"
"tasks": [<task_record>]
}
}
attachment
field is obsolete, useattachments
.tassk
- array of tasks. For more information, see task_record.
reply and forward
If the message is the forward of another message, it will have forwarded_message_id
field pointing to the original message. Same way in_reply_to_message_id
if
exists, points to the original message to which this one is a reply. To
create forward/reply just add correspodning ids when posting the message.
Message serial
Every time message is changed, say, its text is modified, its serial
field
gets some new value, which is guaranteed to be bigger than it was. This way
the new and changed messages coudl easily be loaded by calling all messages
with serial
bigger than the last (e.g. greatest) known to the client.
This query will automatically add new and changed messages alltogether.
xtag
String tag user with unichat system messages to specify type of special message.
Reference
With some unichat system messages referenced are used to specify some connected object, in which case its type and id are passed in this field.
Files Attachments
If message has attachments, it will be included into message record with
the attachments
key. for example:
"attachments": [
0 => {
"content_type": "image/jpeg",
"byte_size": 1597, // size in bytes
"url": <download_url_string>, // fownload link
"preview": <preview_url_string>, // only for images
"filename":"test.jpg"
}
],
"attachment": {
"content_type": "image/jpeg",
"byte_size": 1597, // size in bytes
"url": <download_url_string>, // fownload link
"preview": <preview_url_string> // only for images
}
- The
attachment
field is obsolete, but left for compatibility with older versions and contains the latest attachment.
The attachment could be of any type, but previews are only available for images.
Post message
User can post messages to groups where it is subscribed and has write permission.
post(group_id:<opt_long>,subscription_id:<opt_long>,text:<string>,
uid:<string>, attachment:<opt_string>, attachment_filename:<string>, in_reply_to_message_id: <opt_long>,
forwarded_message_id: <opt_long>, upload_id: [],
mentions: [{user_id:<long>,text: <string>},...], task_ids: [<opt_long>])
->{message: <message_record>}
Parameters are:
group_id
ORsubscription_id
: one of two is required. Call it wuth subscripion whereever possible to reduce server load.text
: message text, required as for now.uid
: some generally unique identifier, random string of at least 48 characters is advised. Maximum allowed size is 64 characters. Can use GUID though we do not recommend it as most RNG give better entropy.uid
must be unique for a user ofr medium time intervals.attachment
: if present, must be a valid and full data-url string, e.g.data:<mediatype>;base64,<data>
. For mediatypesimage/*
the system will prepare also preview image automaticlly.attachment_filename
: you can change the name of the transmitted file.in_reply_to_message_id
: if present, must point to the original message to which new one will reply.forwarded_to_message_id
: if present, must point to the original message to which new one will reply.upload_id
: are identifiers of attachments made through theattachment_by_chunks
method. If the string is empty, all attachments will be destroyed. Available: nil, empty string, array, number.mentions
: optional array of mentions, see below. important do not passnull
, just omit this field if not needed.task_ids
: optional array of tasks. Attaching tasks to message
Returned value is a {message: <<message record>}
containing a created message.
It is safe to call it repeatedly with the same message and same uid
, no
duplication will happen and the proper message object will be returned.
Creating messages cause notification to be sent to all group members, therefore, the postin user will also receive it. Notification will arive at any time, before (unlikely) or after the call returns.
Important note. Notification passed to the message owner connection will
contain object.uid
field first few hours after message creation at least.
Other recipients or past the time this field could be null or omitted.
Large attachment
If the attachment is large, it is necessary to upload the file in chunks.
attachment_by_chunks(file_chunk:<string>, upload_id:<long>, file_size:<long>, file_checksum:<int>, file_name:<string>, async:<boolean>, chunk_id:<integer>)
-> {upload_id:<long>, file_size:<long>, file_checksum:<int>, content_type: <opt_string>, voice_recognition: <opt_boolean>}
with parameters:
async
: file sending format (default: false). Ifasync = true
then thechunk_id
field is required. Chunks can be sent in a different order. When assembling the file, the pieces will be combined into a single file, sorted bychunk_id
in turn. Ifasync = false
, then the sending of the next chunk should be done after successfully receiving a response about sending the previous chunk.file_chunk
: a chunk of file encoded in Base64. Ifasync=falsе
then the size of the chunk should be 64,000 bytes, otherwise ifasync=true
then the chunk should not exceed 1 megabyte (1048576 bytes).upload_id
: upload file ID; the first time is NULL, the next time is the value that the method returnedfile_size
: file size in bytesfile_checksum
: file checksum in CRC32 for file verificationfile_name
: file name to be assigned after it is fully uploadedchunk_id
: serial number of the chunk (0, 1, 2... etc.). Only ifasync=true
content_type
: if present, sets the content type attribute of the filevoice_recognition
: true or false. True for speech recognition. See "Voice Recognition" below for more information.subscription_id
: only withvoice_recognition
=true
. Subscription where the attachment will be sent.
The file is divided into chunks and uploaded to the server.
Response example:
{
upload_id: 14,
file_size: 14243242,
file_checksum: 3423521938426
}
where:
upload_id
: upload file IDfile_size
: ifasync=false
then the upload file size, otherwise the upload chunk sizefile_checksum
: ifasync=false
then the upload file checksum, otherwise the upload chunk checksum
After the file has been completely uploaded, and the file size and checksum match, the key upload_id
will be included into message record. For example:
"upload_id": 14
or as an array, if several attachments are loadedy:
"upload_id": [14, 21]
Voice recognition
You can attach the file as a message with voice recognition. In the method attachment_by_chunks
you need to pass additional parameters:
voice_recognition
is a boolean variable, ifTRUE
- speech recognition is enable.subscription_id
- ID of subscription where the attachment will be sent. It is necessary in order to check whether the user has the right to use the voice recognition function. This rule is set for the organization as a timestamp to which this right exists.
After the file has been uploaded, you can call the voice_recognition
to start recognizing audio:
voice_recognition(upload_id:<long>)
-> {
result: {
status: "pending"
transcript: NULL,
error: NULL,
created_at: "2019-01-01 01:02:03",
updated_at: "2019-01-01 01:02:03",
audio: {
filename: "audio.ogg",
content_type: "audio/ogg",
byte_size: "2432543",
url: "http://example/audio.ogg",
}
}
}
And call the voice_recognition_result
method to get recognition results.
voice_recognition_result(upload_id:<long>)
-> {
result: {
status: "completed"
transcript: "Hello world",
error: NULL,
created_at: "2019-01-01 01:02:03",
updated_at: "2019-01-01 01:02:03",
audio: {
filename: "audio.ogg",
content_type: "audio/ogg",
byte_size: "2432543",
url: "http://example/audio.ogg",
}
}
}
If the status
= pending
, then processing is still in progress.
If the status
= failed
, the error
field must be filled.
If the status
= completed
, the transcript
field must be filled.
Mentions
Mentions are @somebody
-style mentions of some group participant in the message.
The client must detect them (could be in any form) and fill the mentions array
as stated above, where text
is a subsctring in the source message, that the
client could use to highlight or substitute to the link, and user_id
is the
id of the mentioned user as it was when the message was comosing.
This was different clients could properly show and process mentions despite on the format and algorythm of mentions entering/detecting, and does not depends on the users changing their nicks in futire.
Mentions are reported in the message record as .mention
array. Also
the system will set subscription.last_mentioned_in_message_id
accordingly. This
chainge of the subscription will not trigger subscription change notification,
as the recipient will already get the new message notification, which will
contain mentions
so the client software could derive the necessary information
of it, so issuing separate subscription change is redundant.
Why uid?
When client software attempts to post a message, it may happen it will not arrive to the service, or the service couls be in error state, ir, worst of all, the client may not receive acknowledgment that the message was actually created.
When client software does not receive answer for the post()
call, it should
retry until succeeded. It may therefore cose unintentional message duplication
when the server has actually posted the message but the client software did not
receive result. Simplest is the user has get out of mobile internet coverage.
To avoid it, the client software should generate a more or less unique random
string, uid
and store it locally with the message, posting it on every try.
This way the system will detect and ignore unintentional duplications.
Load messages
This function allow reading exisitng messages in a group with paging in tow modes: get most recently created and get created and added after some point. It requires authenticaion.
get_messages(subscription_id:<opt_long>,group_id:<opt_long>,
limit:100,offset:0, before_id:<opt_long>,after_serial:<opt_long)
-> { messages: [<<message_record>,...] }
Parameters are:
group_id
orsubscription_id
: the group to read messages from, current user must have read access to it. Please use subscription_id where possible.limit
andoffset
allow paging in usual sensebefore_id
if present, select messages that are older than a given id, in most recent first.after_serial
if present, select messages that are created and modified path one with such serial number, most recent last.- if neither
before_id
notafter_serial
are specified, selects most recently created messages, most recent first.
In other words, to get latest messages, specify no selection arguments and use
offset
, otherwise use before_id
which is roughly the same. If you want to
pull all the messages and their changes, pull it all using after_serial
using
the biggest serial you have preloaded, and you will get them all and most recent
versions of them too.
Get single message
It may happen, for example, when received new message notification, get a message just by its id, bypassing looking up the subscription for its group:
get_message(message_id:<long>) -> {message:<message_record>}
Edit own message
Editing own messages is only allowed within certain time period after its creation.
Message editing does not prolong this period. Edited messages has non-null
last_edited_at
field so edited messages can be shown in a different way. The
system does not keep the message edition history, so it is the only evidence
that the text was changed.
edit_message(message_id:<long>, text:<opt_string>, attahcment: <opt_string>,
clear_in_reply_to_message_id: false, task_ids: [<opt_long>])
-> {message: <message_record>}
requires at least one of attachment
and text
. Use empty string for each to
clear it without deleting the message. I we not recommend to leave empty messages,
the system might decide to delete completely empty messages of regular type.
text
: if present, changes message text. Pass empty string to delete the text only. Passnull
to leave text unchanfed.attachment
: if present, changes the attachment. must be a valid and full data-url string, e.g.data:<mediatype>;base64,<data>
. For mediatypesimage/*
the system will prepare also preview image automaticlly. Use empty string "" to delete attachment keeping the message. Passnull
leave attachment unchanged.clear_in_reply_to_message_id
if set to true, dropsin_reply_to_message_id
. Note that currently it can not be set to anything but dropped while editing the message.task_ids
optional array of tasks. if present, attach the tasks to the message. Empty string for detach a task from a message.
It will broadcast notification of message change to a group, e.g.
{event: 'changed', object_type: 'message', object: <message_record>}
Notice that the updated message has increased serial
field value (by some
unknown positive number), so it is possible to get new and edited messages
alltogether as descibed above in "load messages".
Delete own message
Could be done by the author at any time. This operation is irreversible.
delete_message(message_id: <long>)
-> {message: <message_record>
If the message is already deleted, it is not changed, error is not reported.
Note that the deleted message record contain less information: it has no text,
attachemt, edited_at and created_at
fields.
It will broadcast notification of message deletion to a group, e.g.
{event: 'deleted', object_type: 'message', object: <message_record>}
Manage own subscription
Susbscrption have several fields writable by its owner that allow implement better UX:
last_read_message_id
: set it to the message id that was likely read by the user. Setting this field may notify other group members about it. Note that service does not check the value against message ids so you can write there zero, negative value and whatever you might find useful.draft
: save here the text entered by the user to not to loose it. Convenient way to share partially written message among sessions and devices. Set to empty string to clear it.tags
: allow tag the subscription with an array of string tags. This is per- subscription (not per-group which also exists) tagging, allowing each user keep some information joined with the subscription. Tags are not visible to others.mute_until
: if set to some time, notifications to this subscription should not be shown to the user clearly. This setting can only be interpretated by the client software.
To change it:
update_subscription(subscription_id:<long>,draft:<opt_string>,
last_read_message_id:<opt_long>,
mute_until:<opt_is08601_datetime>)
-> { subscription: <subscription_record> }
Only supplied fields will be changed. If subscription is changes, changed
notification for it will be sent to the owner or to the group, depending on
change relevance. For example, last_read_message_id
causes group
broadcasting.
To clear mute_until
set it to any moment in past. Setting to null will not change
its value.
Setting last_read_message_id
will also clear last_mentioned_in_message_id
if it is lesser or equal to newly set last_read_message_id
.
Invite to the room
Regular rooms by default allow everybody with write permission to invite others to the room with:
invite(subscription_id:<long>,user_id:[<long>])
-> {subscription:<subscription_record>}
where
-subscription_id
is a subscription of the current user to the room to which
he or she wants to invite
user_id
: id or array of the users IDs which should be invited to the room.
on success, returns the subscription of newly added user, send a notification
of the new subscription and post a unichat system message from inviter
with xtag:"invite"
and reference
pointing to the new user. Of course posting
message causes also new message notification.
Use invite code
If the user has the invite code, it should use it. See unichat invite codes for explanations on how to obtain and process it with the client software.
use_invite_code(code:<string_code>) -> {}
There is no returned data on successful call. Instead, the application will receive notifications and messages depedning on the code.
When user joins the group using the invite code, all participants (include new one)
will receive usual new subscription event. Also system posts unichat system message
from new user with xtag:"joined"
.
Leaving the room
To do it, just unsubscribe your subscription:
unsubscribe(subscription_id:<long>) -> {}
On successful unsubscription, the notification of a deleted object is propagated among the group subscribers:
{
event:'deleted',
object_name:'subscription',
object: {id: deleted_subscription_id}
}
Notice that on the deleted object notification, only the id
field is guaranteed to
be present, the other fields may be all omitted.
Also, the system posts unichat system message from leaving user with xtag: leave
just before unsubscribing, so the leaving user will receive its notification.
Please note that after unsubscribing the user may loose access to the group entirely depending on its nature and settings.
When the group owner leaves
A group can not exist without owner, who is its unrevokable admin. The only way for owner to dismiss is to call unsubscribe. It is not possible to remove owner's admin role or kick him or her out of the group. So when the owner leaves, the group risks to get the failed state with no admins left.
So when the owner leaves by their good will, the system tries to find the best candidate for this role (the oldest administrator, or oldest writer or oldest reader without mutes and bans), and assign it on this role. The subscription of the new role is promoted to the admin level if need. The usual subscription change notification is sent.
When the last participant leves
The group is destroyed by the system if there are no more participants. No archived groups exist at this time.