matrixroom.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #include "matrixroom.h"
  2. #include "core/io/json.h"
  3. void MatrixRoom::_put_event(Dictionary event) {
  4. if (!event_ids.has(event["event_id"])) {
  5. event_ids.insert(event["event_id"]);
  6. events.push_back(event);
  7. emit_signal("timeline_event", event);
  8. }
  9. }
  10. void MatrixRoom::_put_old_event(Dictionary event) {
  11. if (!event_ids.has(event["event_id"])) {
  12. event_ids.insert(event["event_id"]);
  13. events.push_front(event);
  14. emit_signal("old_timeline_event", event);
  15. }
  16. }
  17. void MatrixRoom::_put_ephemeral_event(Dictionary event) {
  18. emit_signal("ephemeral_event", event);
  19. }
  20. Error MatrixRoom::_process_state_event(Dictionary event) {
  21. if (!event.has("type")) {
  22. return MATRIX_INVALID_RESPONSE;
  23. }
  24. String event_type = event["type"];
  25. if (event_type == "m.room.member") {
  26. String member = event["state_key"];
  27. if (((Dictionary)event["content"])["membership"] == "join") {
  28. members[member] = (Dictionary)event["content"];
  29. } else if (members.has(member)) {
  30. members.erase(member);
  31. }
  32. } else if (event_type == "m.room.name") {
  33. if (((Dictionary)event["content"]).has("name")) {
  34. name = ((Dictionary)event["content"])["name"];
  35. } else {
  36. name = String();
  37. }
  38. } else if (event_type == "m.room.topic") {
  39. if (((Dictionary)event["content"]).has("topic")) {
  40. topic = ((Dictionary)event["content"])["topic"];
  41. } else {
  42. topic = String();
  43. }
  44. } else if (event_type == "m.room.aliases") {
  45. if (((Dictionary)event["content"]).has("aliases")) {
  46. aliases[((Dictionary)event["state_key"])] = ((Dictionary)event["content"])["aliases"];
  47. }
  48. }
  49. emit_signal("state_event", event);
  50. return MATRIX_OK;
  51. }
  52. String MatrixRoom::get_name(bool sync) {
  53. if (sync) {
  54. Variant response_v;
  55. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/state/m.room.name", Dictionary(), HTTPClient::Method::METHOD_GET, response_v);
  56. if (status == 200) {
  57. Dictionary response = response_v;
  58. name = response["name"];
  59. } else {
  60. WARN_PRINT("Unable to get room name!");
  61. }
  62. }
  63. return name;
  64. }
  65. String MatrixRoom::get_friendly_name(bool sync) {
  66. get_name(sync);
  67. if (name.length() != 0) {
  68. return name;
  69. } else {
  70. return room_id;
  71. }
  72. }
  73. String MatrixRoom::get_topic(bool sync) {
  74. if (sync) {
  75. Variant response_v;
  76. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/state/m.room.topic", Dictionary(), HTTPClient::Method::METHOD_GET, response_v);
  77. if (status == 200) {
  78. Dictionary response = response_v;
  79. topic = response["topic"];
  80. } else {
  81. WARN_PRINT("Unable to get room topic!");
  82. }
  83. }
  84. return topic;
  85. }
  86. Array MatrixRoom::get_events() const {
  87. return events;
  88. }
  89. Error MatrixRoom::get_old_events(int num_events) {
  90. String from;
  91. if (backfill_prev_batch.length() != 0) {
  92. from = backfill_prev_batch;
  93. } else {
  94. from = prev_batch;
  95. }
  96. Variant response_v;
  97. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/messages?from="+from.http_escape()+"&dir=b&limit="+String::num_int64(num_events), Dictionary(), HTTPClient::Method::METHOD_GET, response_v);
  98. if (status == 200) {
  99. Dictionary response = response_v;
  100. backfill_prev_batch = response["end"]; //TODO: check if this is correct or if it should be "start" instead
  101. Array events = response["chunk"];
  102. for (int i=0; i<events.size(); i++) {
  103. _put_old_event(events[i]);
  104. }
  105. return MATRIX_OK;
  106. } else {
  107. WARN_PRINT("Unable to get old events!");
  108. return MATRIX_UNABLE;
  109. }
  110. }
  111. Dictionary MatrixRoom::get_aliases() const {
  112. return aliases;
  113. }
  114. Error MatrixRoom::send_text_message(String text) {
  115. Dictionary request_body = Dictionary();
  116. request_body["msgtype"] = "m.text";
  117. request_body["body"] = text;
  118. return send_event("m.room.message", request_body);
  119. }
  120. Error MatrixRoom::send_event(String event_type, Dictionary event) {
  121. String txn_id = String::num_int64((OS::get_singleton()->get_unix_time()*1000)+(OS::get_singleton()->get_ticks_msec()%1000));
  122. Dictionary request_body = event;
  123. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/send/"+event_type+"/"+txn_id, request_body, HTTPClient::Method::METHOD_PUT);
  124. if (status == 200) {
  125. return MATRIX_OK;
  126. } else if (status == 403) {
  127. ERR_PRINT("Not allowed to send event");
  128. return MATRIX_UNAUTHORIZED;
  129. } else {
  130. return MATRIX_NOT_IMPLEMENTED;
  131. }
  132. }
  133. Error MatrixRoom::set_typing(bool typing, int timeout_ms) {
  134. Dictionary request_body;
  135. request_body["typing"] = typing;
  136. request_body["timeout"] = timeout_ms;
  137. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/typing/"+client->get_user_id().http_escape(), request_body, HTTPClient::Method::METHOD_PUT);
  138. if (status == 200) {
  139. return MATRIX_OK;
  140. } else if (status == 403) {
  141. ERR_PRINT("Not allowed to set typing status");
  142. return MATRIX_UNAUTHORIZED;
  143. } else if (status == 429) {
  144. ERR_PRINT("Ratelimited");
  145. return MATRIX_RATELIMITED;
  146. } else {
  147. return MATRIX_NOT_IMPLEMENTED;
  148. }
  149. }
  150. Dictionary MatrixRoom::get_members(bool sync) {
  151. if (sync) {
  152. Variant response_v;
  153. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/members", Dictionary(), HTTPClient::Method::METHOD_GET, response_v);
  154. if (status == 200) {
  155. Dictionary response = response_v;
  156. if (response.has("chunk")) {
  157. Array chunk = response["chunk"];
  158. for (int i=0; i<chunk.size(); i++) {
  159. Dictionary event = chunk[i];
  160. if (((Dictionary)event["content"])["membership"] == "join") {
  161. members[event["state_key"]] = (Dictionary)event["content"];
  162. }
  163. }
  164. }
  165. }
  166. }
  167. return members;
  168. }
  169. String MatrixRoom::get_member_display_name(String id, bool sync) {
  170. if (sync) {
  171. Variant response_v;
  172. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/state/m.room.member/"+id.http_escape(), Dictionary(), HTTPClient::Method::METHOD_GET, response_v);
  173. if (status == 200) {
  174. Dictionary response = response_v;
  175. print_line(JSON::print(response_v));
  176. members[id] = response;
  177. } else if (status == 404) {
  178. WARN_PRINT("Tried to look up non-existent room member!");
  179. }
  180. }
  181. if (members.has(id) && ((Dictionary)members[id]).has("displayname")) {
  182. return ((Dictionary)members[id])["displayname"]; //TODO: disambiguate display names
  183. } else {
  184. return id;
  185. }
  186. }
  187. void MatrixRoom::_state_sync(Variant userdata) {
  188. Variant response_v;
  189. HTTPClient::ResponseCode status = client->request_json("/_matrix/client/r0/rooms/"+room_id.http_escape()+"/state", Dictionary(), HTTPClient::Method::METHOD_GET, response_v);
  190. if (status == 200) {
  191. Array response = response_v;
  192. for (int i=0; i<response.size(); i++) {
  193. _process_state_event(response[i]);
  194. }
  195. }
  196. }
  197. Variant MatrixRoom::state_sync() {
  198. Ref<_Thread> state_thread = memnew(_Thread);
  199. state_thread->start(this, "_state_sync");
  200. return state_thread;
  201. }
  202. Variant MatrixRoom::leave_room() {
  203. return client->leave_room(room_id);
  204. }
  205. MatrixRoom::MatrixRoom() {
  206. }
  207. void MatrixRoom::init(MatrixClient *c, String id) {
  208. client = c;
  209. room_id = id;
  210. }
  211. void MatrixRoom::_bind_methods() {
  212. ClassDB::bind_method("get_name", &MatrixRoom::get_name);
  213. ClassDB::bind_method("get_friendly_name", &MatrixRoom::get_friendly_name);
  214. ClassDB::bind_method("get_topic", &MatrixRoom::get_topic);
  215. ClassDB::bind_method("get_events", &MatrixRoom::get_events);
  216. ClassDB::bind_method("get_aliases", &MatrixRoom::get_aliases);
  217. ClassDB::bind_method("get_old_events", &MatrixRoom::get_old_events);
  218. ClassDB::bind_method("get_members", &MatrixRoom::get_members);
  219. ClassDB::bind_method("get_member_display_name", &MatrixRoom::get_member_display_name);
  220. ClassDB::bind_method("send_text_message", &MatrixRoom::send_text_message);
  221. ClassDB::bind_method("_state_sync", &MatrixRoom::_state_sync);
  222. ClassDB::bind_method("state_sync", &MatrixRoom::state_sync);
  223. ClassDB::bind_method("leave_room", &MatrixRoom::leave_room);
  224. ADD_SIGNAL( MethodInfo("timeline_event") ); //new event inserted at most recent point in timeline
  225. ADD_SIGNAL( MethodInfo("old_timeline_event") ); //old event inserted at beginning of timeline
  226. ADD_SIGNAL( MethodInfo("ephemeral_event") );
  227. ADD_SIGNAL( MethodInfo("state_event") );
  228. }