syntax = "proto3";
package pbx;

// This is the single method that needs to be implemented by a gRPC client.
service Node {
	// Client sends a stream of ClientMsg, server responds with a stream of ServerMsg
  	rpc MessageLoop(stream ClientMsg) returns (stream ServerMsg) {}
}

// Plugin interface.
service Plugin {
	// This plugin method is called by Tinode server for every message received from the clients. The
	// method returns a ServerCtrl message. Non-zero ServerCtrl.code indicates that no further
	// processing is needed. The Tinode server will generate a {ctrl} message from the returned ServerCtrl
	// and forward it to the client session.
	// ServerCtrl.code equals to 0 instructs the server to continue with default processing of the client message.
  	rpc FireHose(ClientReq) returns (ServerResp) {}

	// An alteranative user and topic discovery mechanism.
	// A search request issued on a 'fnd' topic. This method is called to generate an alternative result set.
	rpc Find(SearchQuery) returns (SearchFound) {}

	// The following methods are for the Tinode server to report individual events.

	// Account created, updated or deleted
	rpc Account(AccountEvent) returns (Unused) {}

	// Topic created, updated [or deleted -- not supported yet]
	rpc Topic(TopicEvent) returns (Unused) {}

	// Subscription created, updated or deleted
	rpc Subscription(SubscriptionEvent) returns (Unused) {}

	// Message published or deleted
	rpc Message(MessageEvent) returns (Unused) {}
}

// Dummy placeholder message.
message Unused {
}

// Common client/server messages

// Authentication level
enum AuthLevel {
	NONE = 0;
	ANON = 10;
	AUTH = 20;
	ROOT = 30;
}

// Client messages

// Topic default access mode
message DefaultAcsMode {
	string auth = 1;
	string anon = 2;
}

// Actual access mode
message AccessMode {
	// Access mode requested by the user
	string want = 1;
	// Access mode granted to the user by the admin
	string given = 2;
}

// SetSub: payload in set.sub request to update current subscription or invite another user, {sub.what} == "sub"
message SetSub {
	// User affected by this request. Default (empty): current user
	string user_id = 1;

	// Access mode change, either Given or Want depending on context
	string mode = 2;
}

// Credentials such as email or phone number
message ClientCred {
	// Credential type, i.e. `email` or `tel`.
	string method = 1;
	// Value to verify, i.e. `user@example.com` or `+18003287448`
	string value = 2;
	// Verification response
	string response = 3;
	// Request parameters, such as preferences.
	bytes params = 4;
}

// SetDesc: C2S in set.what == "desc" and sub.init message
message SetDesc {
	DefaultAcsMode default_acs = 1;
	bytes public = 2;
	bytes private = 3;
}

message GetOpts {
	// Timestamp in milliseconds since epoch 01/01/1970
	int64 if_modified_since = 1;
	// Limit search to this user ID
	string user = 2;
	// Limit search results to one topic;
	string topic = 3;
	// Load messages with seq id equal or greater than this
	int32 since_id = 4;
	// Load messages with seq id lower than this
	int32 before_id = 5;
	// Maximum number of results to return
	int32 limit = 6;
}

message GetQuery {
	string what = 1;

	// Parameters of "desc" request
	GetOpts desc = 2;
	// Parameters of "sub" request
	GetOpts sub = 3;
	// Parameters of "data" request
	GetOpts data = 4;
}

message SetQuery {
	// Topic metadata, new topic & new subscriptions only
	SetDesc desc = 1;
	// Subscription parameters
	SetSub sub = 2;
	// Indexable tags
	repeated string tags = 3;
	// Credential being updated.
	ClientCred cred = 4;
}

message SeqRange {
	int32 low = 1;
	int32 hi = 2;
}

// Client handshake
message ClientHi {
	string id = 1;
	string user_agent = 2;
	string ver = 3;
	string device_id = 4;
	string lang = 5;
	string platform = 6;
	bool background = 7;
}

// User creation message {acc}
message ClientAcc {
	string id = 1;
	// User being created or updated
	string user_id = 2;
	// The initial authentication scheme the account can use
	string scheme = 3;
	// Shared secret
	bytes secret = 4;
	// Authenticate session with the newly created account
	bool login = 5;
	// Indexable tags for user discovery
	repeated string tags = 6;
	// User initialization data when creating a new user, otherwise ignored
	SetDesc desc = 7;
	// Credentials for verification.
	repeated ClientCred cred = 8;
	// Authentication token used for resetting a password.
	bytes token = 9;
	// Account state: normal ("ok"), suspended
	string state = 10;
}

// Login {login} message
message ClientLogin  {
	string id = 1;
	// Authentication scheme
	string scheme  = 2;
	// Shared secret
	bytes secret = 3;
	// Credentials for verification.
	repeated ClientCred cred = 4;
}

// Subscription request {sub} message
message ClientSub {
	string id = 1;
	string topic  = 2;

	// mirrors {set}
	SetQuery set_query = 3;

	// mirrors {get}
	GetQuery get_query = 4;
}

// Unsubscribe {leave} request message
message ClientLeave {
	string id = 1;
	string topic  = 2;
	bool unsub = 3;
}

// ClientPub is client's request to publish data to topic subscribers {pub}
message ClientPub {
	string id = 1;
	string topic = 2;
	bool no_echo = 3;
	map<string, bytes> head = 4;
	bytes content = 5;
}

// Query topic state {get}
message ClientGet {
	string id = 1;
	string topic = 2;
	GetQuery query = 3;
}

// Update topic state {set}
message ClientSet {
	string id = 1;
	string topic = 2;
	SetQuery query = 3;
}

// ClientDel delete messages or topic
message ClientDel {
	string id = 1;
	string topic = 2;
	// What to delete, either "msg" to delete messages (default) or "topic" to delete the topic or "sub"
	// to delete a subscription to topic.
	enum What {
		MSG = 0;
		TOPIC = 1;
		SUB = 2;
		USER = 3;
		CRED = 4;
	}
	What what = 3;
	// Delete messages by id or range of ids
	repeated SeqRange del_seq = 4;
	// User ID of the subscription to delete
	string user_id = 5;
	// Credential to delete.
	ClientCred cred = 6; 
	// Request to hard-delete messages for all users, if such option is available.
	bool hard = 7;
}

enum InfoNote {
	READ = 0;
	RECV = 1;
	KP = 2;
}

// ClientNote is a client-generated notification for topic subscribers
message ClientNote {
	string topic = 1;
	// what is being reported: "recv" - message received, "read" - message read, "kp" - typing notification
	InfoNote what = 2;
	// Server-issued message ID being reported
	int32 seq_id = 3;
}

message ClientMsg {
	oneof Message {
		ClientHi hi = 1;
		ClientAcc acc = 2;
		ClientLogin login = 3;
		ClientSub sub = 4;
		ClientLeave leave = 5;
		ClientPub pub = 6;
		ClientGet get = 7;
		ClientSet set = 8;
		ClientDel del = 9;
		ClientNote note = 10;
	}
	// Root user may send messages on behalf of other users.
	string on_behalf_of = 11;
	AuthLevel auth_level = 12;
}

// ************************
// Server response messages

// Credentials
message ServerCred {
	// Credential type, i.e. `email` or `tel`.
	string method = 1;
	// Value to verify, i.e. `user@example.com` or `+18003287448`
	string value = 2;
	// Indicator that the credential is validated
	bool done = 3;
}

// Topic description, S2C in Meta message
message TopicDesc {
	int64 created_at = 1;
	int64 updated_at = 2;
	int64 touched_at = 3;
	DefaultAcsMode defacs = 4;
	AccessMode acs = 5;
	int32 seq_id = 6;
	int32 read_id = 7;
	int32 recv_id = 8;
	int32 del_id = 9;
	bytes public = 10;
	bytes private = 11;
	string state = 12;
	int64 state_at = 13;
}

// MsgTopicSub: topic subscription details, sent in Meta message
message TopicSub {
	int64 updated_at = 1;
	int64 deleted_at = 2;

	bool online = 3;

	AccessMode acs = 4;
	int32 read_id = 5;
	int32 recv_id = 6;
	bytes public  = 7;
	bytes private = 8;

	// Response to non-'me' topic

	// Uid of the subscribed user
	string user_id = 9;

	// 'me' topic only

	// Topic name of this subscription
	string topic = 10;
	int64 touched_at = 11;
	// ID of the last {data} message in a topic
	int32 seq_id = 12;
	// Messages are deleted up to this ID
	int32 del_id = 13;

	// P2P topics only:

	// Other user's last online timestamp & user agent
	int64 last_seen_time = 14;
	string last_seen_user_agent = 15;
}

message DelValues {
	int32 del_id = 1;
	repeated SeqRange del_seq = 2;
}


// {ctrl} message
message ServerCtrl {
	string id = 1;
	string topic = 2;
	int32 code = 3;
	string text = 4;
	map<string, bytes> params = 5;
}

// {data} message
message ServerData {
	string topic = 1;
	// ID of the user who originated the message as {pub}, could be empty if sent by the system
	string from_user_id = 2;
	// Timestamp when the message was sent.
	int64 timestamp = 7;
	// Timestamp when the message was deleted or 0. Milliseconds since the epoch 01/01/1970
	int64 deleted_at = 3;
	int32 seq_id = 4;
	map<string, bytes> head = 5;
	bytes content = 6;
}

// {pres} message
message ServerPres {
	string topic = 1;
	string src = 2;
	enum What {
		ON = 0;
		OFF = 1;
		UA = 3;
		UPD = 4;
		GONE = 5;
		ACS = 6;
		TERM = 7;
		MSG = 8;
		READ = 9;
		RECV = 10;
		DEL = 11;
		TAGS = 12;
	}
	What what = 3;
	string user_agent = 4;
	int32 seq_id = 5;
	int32 del_id = 6;
	repeated SeqRange del_seq = 7;
	string target_user_id = 8;
	string actor_user_id = 9;
	AccessMode acs = 10;
}

// {meta} message
message ServerMeta {
	string id = 1;
	string topic = 2;

	TopicDesc desc = 3;
	repeated TopicSub sub = 4;
	DelValues del = 5;
	repeated string tags = 6;
	repeated ServerCred cred = 7;
}

// {info} message: server-side copy of ClientNote with From added
message ServerInfo {
	string topic = 1;
	string from_user_id = 2;
	InfoNote what = 3;
	int32 seq_id = 4;
}

// Cumulative message
message ServerMsg {
	oneof Message {
		ServerCtrl ctrl = 1;
		ServerData data = 2;
		ServerPres pres = 3;
		ServerMeta meta = 4;
		ServerInfo info = 5;
	}
	// When response is sent to Root, send internal topic name too.
	string topic = 6;
}

// Plugin response codes
enum RespCode {
	// Instruct Tinode server to continue with default processing of the client request.
	CONTINUE = 0;
	// Drop the request as if the client did not send it
	DROP = 1;
	// Send the the provided response to the client.
	RESPOND = 2;
	// Replace client's original request with the provided request then continue with
	// processing.
	REPLACE = 3;
}

message ServerResp {
	RespCode status = 1;
	ServerMsg srvmsg = 2;
	ClientMsg clmsg = 3;
}

// Context message
message Session {
	string session_id = 1;
	string user_id = 2;
	AuthLevel auth_level = 3;
	string remote_addr = 4;
	string user_agent = 5;
	string device_id = 6;
	string language = 7;
}

message ClientReq {
	ClientMsg msg = 1;
	Session sess = 2;
}

// Search

message SearchQuery {
	string user_id = 1;
	string query = 2;
}

message SearchFound {
	RespCode status = 1;
	// New search query If status == REPLACE, otherwise unset.
	string query = 2;
	// Search results.
	repeated TopicSub result = 3;
}

// CRUD event messages

enum Crud {
	CREATE = 0;
	UPDATE = 1;
	DELETE = 2;
}

message TopicEvent {
	Crud action = 1;
	string name = 2;
	TopicDesc desc = 3;
}

message AccountEvent {
	Crud action = 1;
	string user_id = 2;

	DefaultAcsMode default_acs = 3;
	bytes public = 4;

	// Indexable tags for user discovery
	repeated string tags = 8;
}

message SubscriptionEvent {
	Crud action = 1;
	string topic = 2;
	string user_id = 3;

	int32 del_id = 4;
	int32 read_id = 5;
	int32 recv_id = 6;

	AccessMode mode = 7;

	bytes private = 8;
}

message MessageEvent {
	Crud action = 1;
	ServerData msg = 2;
}
