diff --git a/src/matrix-client.vala b/src/matrix-client.vala index 82d4ba6..4fc4ecb 100644 --- a/src/matrix-client.vala +++ b/src/matrix-client.vala @@ -217,4 +217,34 @@ public interface Matrix.Client : GLib.Object { public abstract Room get_room_by_alias(string room_alias) throws Matrix.Error; + + /** + * Callback type for {@link Matrix.Client.send}. + * + * @param event_id the event_id returned by the server + * @param err an error raised during event sending, if any + */ + public delegate void + SendCallback(string? event_id, GLib.Error? err); + + /** + * Send an event to the given room. This will use the + * /room/{roomId}/send or /room/{roomId}/state API depending on + * the event: if the event has a state key (there is a state_key + * key in the generated JSON), even if an empty one, it will use + * the latter. + * + * @param room_id the room to send the event to + * @param evt the event to send + * @param cb the callback function to call when the request is + * finished + * @param txn_id the transaction ID used by this request. In case + * of a state event, it will be untouched + */ + public abstract void + send(string room_id, + Matrix.Event.Base evt, + SendCallback? cb, + out ulong txn_id) + throws Matrix.Error; } diff --git a/src/matrix-http-client.vala b/src/matrix-http-client.vala index 58abec1..674a470 100644 --- a/src/matrix-http-client.vala +++ b/src/matrix-http-client.vala @@ -30,6 +30,7 @@ public class Matrix.HTTPClient : Matrix.HTTPAPI, Matrix.Client { new Gee.HashMap(); private Gee.HashMap _rooms = new Gee.HashMap(); + private ulong _last_txn_id = 0; public HTTPClient(string base_url) @@ -487,4 +488,80 @@ public class Matrix.HTTPClient : Matrix.HTTPAPI, Matrix.Client { throw new Matrix.Error.UNAVAILABLE( "Noo room data found for alias %s", room_alias); } + + /** + * Get the next transaction ID to use. It increments the + * internally stored value and returns that, so it is guaranteed + * to be unique until we run out of ulong boundaries. + * + * It is called internally by send(). + */ + public ulong + next_txn_id() + { + return ++_last_txn_id; + } + + private void + send_callback(Json.Node? json_content, + GLib.Error? err, + Matrix.Client.SendCallback? cb) + { + string? event_id = null; + GLib.Error? new_err = err; + + // If there is no callback, there is no point to continue + if (cb == null) { + return; + } + + if (err == null) { + var root = json_content.get_object(); + + if (root.has_member("event_id")) { + event_id = root.get_string_member("event_id"); + } else { + new_err = new Matrix.Error.BAD_RESPONSE( + "event_id is missing from an event response"); + } + } + + cb(event_id, new_err); + } + + public void + send(string room_id, + Matrix.Event.Base evt, + Matrix.Client.SendCallback? cb, + out ulong txn_id) + throws Matrix.Error + { + var evt_node = evt.json; + var evt_root = evt_node.get_object(); + string? state_key = null; + + if (evt_root.has_member("state_key")) { + state_key = evt_root.get_string_member("state_key"); + } + + if (state_key != null) { + txn_id = 0; + send_state_event( + (i, ct, json_node, rc, err) => + send_callback(json_node, err, cb), + room_id, + evt.event_type, + (state_key == "") ? null : state_key, + evt_root.get_member("content")); + } else { + txn_id = next_txn_id(); + send_event( + (i, ct, json_node, rc, err) => + send_callback(json_node, err, cb), + room_id, + evt.event_type, + "%lu".printf(txn_id), + evt_root.get_member("content")); + } + } }