10 Commits 568aa860c2 ... 7e02804736

Autore SHA1 Messaggio Data
  Endes 7e02804736 Implemented content repo and Room history visibility 6 anni fa
  Endes 36986eb929 Some fixes 6 anni fa
  Endes e572581dcd Implemented presence and another fix. 6 anni fa
  Endes 8a8a0017e1 Important fixes 6 anni fa
  Endes f44dc5ac9a Implemeted Receipt and minor fixes 6 anni fa
  Endes 6aca9311cd Implemented typing module 6 anni fa
  Endes 592d34ea3a Implemented VOIP and minor fixes 6 anni fa
  Endes afed7980cf Calculating display names 6 anni fa
  Endes 54f1700f47 Implemented instant message events 6 anni fa
  Endes 2d2c9aac44 Fixes. 6 anni fa

+ 3 - 3
README.md

@@ -1,7 +1,7 @@
 # haxe-matrix-im
 
 Implementation of client-server matrix API in haxe using akifox-asynchttp. Because of it, tecnically only works on **CPP/NEKO/JAVA/FLASH** and not in **JAVASCRIPT**
-because **JAVASCRIPT** only support *POST and Get* Methods. But, is only tested on neko Platform, if you test in other, let me know.
+because **JAVASCRIPT** only support *POST and Get* Methods. But, is only tested on neko platform, if you test in other, let me know.
 
 ## Usage
 Importing:
@@ -17,14 +17,14 @@ con.on_error = function( e : Dynamic ) : Void {
 }
 
 con.session.login_with_pass(user, password, device_name, function( respose : Dynamic ) : Void {
-  trace(respose.access_token); //The access_token will be actumaticaly stored for duture request until you logout.
+  trace(respose.access_token); //The access_token will be actumaticaly stored for future request until you logout.
 });
 ```
 
 Log out:
 ```haxe
 con.session.logout(function() : Void {
-  //Logued out
+  //Loged out
 });
 ```
 

+ 1 - 1
build-test.hxml

@@ -6,6 +6,6 @@
 -lib akifox-asynchttp
 -lib mohxa
 
-#-debug
+-debug
 
 -neko bin/test.n

+ 26 - 2
src/beartek/matrix_im/client/Conection.hx

@@ -5,7 +5,7 @@ import com.akifox.asynchttp.*;
 import beartek.matrix_im.client.handlers.*;
 import beartek.matrix_im.client.Check_reply;
 import beartek.matrix_im.client.types.replys.Error;
-
+import beartek.matrix_im.client.types.replys.Login_data;
 
 class Conection {
   public var server : Server_adm;
@@ -15,11 +15,16 @@ class Conection {
   public var sync : Sync;
   public var rooms : Rooms;
   public var profile : Profile;
+  public var voip : Voip;
+  public var receipt : Receipt;
+  public var presence : Presence;
+  public var content : Content;
+
 
   public var server_url(default, null) : String;
   var responses_handlers : Map<Int,Array<Int -> Dynamic -> Bool>> = new Map();
 
-  public function new( server_url : String ) {
+  public function new(server_url : String) {
     this.server_url = server_url;
 
     this.session = new Session(this.on_responses, this.send_request, server_url);
@@ -28,6 +33,9 @@ class Conection {
     this.server.get_versions();
 
     this.account = new Account(this.on_responses, this.send_request, server_url);
+    this.account.on_login_data = function(l: Login_data) {
+      this.session.fallback_handler(l)
+    }
 
     this.filters = new Filters(this.on_responses, this.send_request, server_url);
 
@@ -36,6 +44,22 @@ class Conection {
     this.rooms = new Rooms(this.on_responses, this.send_request, server_url);
 
     this.profile = new Profile(this.on_responses, this.send_request, server_url);
+
+    this.voip = new Voip(this.on_responses, this.send_request, server_url);
+
+    this.receipt = new Receipt(this.on_responses, this.send_request, server_url);
+
+    this.presence = new Presence(this.on_responses, this.send_request, server_url);
+
+    this.content = new Content(this.on_responses, this.send_request, server_url);
+
+
+    this.session.onopen(function (t:String) {
+      this.rooms.update_joined_room();
+      this.rooms.check_typing(2500, this.session.user);
+      this.presence.user = this.session.user;
+      this.presence.update_list(this.session.user, 30000);
+    });
   }
 
   public static function to_object_map<T>( o : Dynamic ) : Map<String,T> {

+ 8 - 2
src/beartek/matrix_im/client/handlers/Account.hx

@@ -12,6 +12,9 @@ import beartek.matrix_im.client.types.User;
 import beartek.matrix_im.client.auths.Auth;
 
 class Account extends Handler {
+  public dynamic function on_login_data(data: Login_data) {
+    trace('This function must be repalced');
+  }
 
   public function new( on_responses : Int -> Dynamic -> ?Bool -> Bool, send_request : HttpRequest -> (Int -> Dynamic -> Void) -> ?Bool -> Void, server : String ) {
     super(on_responses, send_request, server);
@@ -23,6 +26,10 @@ class Account extends Handler {
     }
 
     var request = {bind_email: bind_email, username: username, password: password, display_name : display_name};
+    var fallback = function(l: Login_data) {
+      on_login_data(l);
+      on_response(l);
+    };
 
     this.send_request(Conection.make_request('POST', server + '/_matrix/client/r0/register?kind=' + kind, request), function( status : Int, response : Dynamic ) : Void {
       if( Check_reply.is_UIA(response) ) {
@@ -35,8 +42,7 @@ class Account extends Handler {
           data.select_flow(UIA.fast_flow(data.get()));
         }
 
-        //TODO: anadir login_data a session
-        data.start(on_stage, on_response, request, this.send_request);
+        data.start(on_stage, fallback, request, this.send_request);
       } else {
         throw 'Error on trying UIA: ' + response;
       }

+ 71 - 0
src/beartek/matrix_im/client/handlers/Content.hx

@@ -0,0 +1,71 @@
+//Under GNU AGPL v3, see LICENCE
+package beartek.matrix_im.client.handlers;
+
+import com.akifox.asynchttp.HttpRequest;
+import com.akifox.asynchttp.HttpMethod;
+import com.akifox.asynchttp.HttpResponse;
+import beartek.matrix_im.client.types.Content_uri;
+import beartek.matrix_im.client.types.enums.Thumb_method;
+
+class Content extends Handler {
+
+  public function new( on_responses : Int -> Dynamic -> ?Bool -> Bool, send_request : HttpRequest -> (Int -> Dynamic -> Void) -> ?Bool -> Void, server : String ) {
+    super(on_responses, send_request, server);
+  }
+
+  public function upload(filename: String, mimetype: String, content: haxe.io.Bytes, ?on_response: Content_uri -> Void) : Void {
+    var request = new HttpRequest({
+      url: server + '/_matrix/media/r0/upload?filename=' + filename,
+      method: HttpMethod.POST,
+      contentType: mimetype,
+      content: content.getString(0, content.length);
+    });
+    this.send_request(request, function ( status : Int, data: {content_uri: String} ) : Void {
+      on_response(new Content_uri(content_uri));
+    }
+  }
+
+  public function download(content_uri: Content_uri, ?on_response: String -> String -> haxe.io.Bytes -> Void) : Void {
+    var request = new HttpRequest({
+      url: server + '/_matrix/media/r0/download/' + content_uri.get_server() + '/' content_uri.get_media_id(),
+      method: HttpMethod.GET
+    });
+
+    #if debug
+    trace( 'sending request: ' + request.content );
+    #end
+    request.callback = function( response : HttpResponse ) : Void {
+      #if debug
+      trace( 'callback called' );
+      #end
+      on_response(response.headers.get('Content-Type'), response.headers.get('Content-Disposition'), response.contentRaw);
+    };
+    request.async = false;
+    request.send();
+  }
+
+  public function download_thumbnail(content_uri: Content_uri, width: Int, height: Int, thumb_method: Thumb_method, ?on_response: String -> haxe.io.Bytes -> Void) : Void {
+    var request = new HttpRequest({
+      url: server + '/_matrix/media/r0/thumbnail/' + content_uri.get_server() + '/' + content_uri.get_media_id() + '?width=' + width + '&height=' + height + '&method=' + thumb_method,
+      method: HttpMethod.GET
+    });
+
+    #if debug
+    trace( 'sending request: ' + request.content );
+    #end
+    request.callback = function( response : HttpResponse ) : Void {
+      #if debug
+      trace( 'callback called' );
+      #end
+      on_response(response.headers.get('Content-Type'), response.contentRaw);
+    };
+    request.async = false;
+    request.send();
+  }
+
+  public function preview_url(url: String, time: Int, ?on_response: Map<String, String> -> Void) : Void {
+    this.send_request(Conection.make_request(HttpMethod.GET, server + '/_matrix/media/r0/preview_url?url=' + url + '&ts=' + time, null), function ( status : Int, data: Dynamic ) : Void {
+      on_response(Conection.to_object_map(data));
+  }
+
+}

+ 70 - 0
src/beartek/matrix_im/client/handlers/Presence.hx

@@ -0,0 +1,70 @@
+//Under GNU AGPL v3, see LICENCE
+package beartek.matrix_im.client.handlers;
+
+import com.akifox.asynchttp.HttpRequest;
+import com.akifox.asynchttp.HttpMethod;
+import beartek.matrix_im.client.types.User;
+import beartek.matrix_im.client.types.enums.event.Types;
+import beartek.matrix_im.client.types.replys.User_status;
+
+class Presence extends Handler {
+  public var user: User;  
+  public var list(default, set): Map<User, User_status>;
+
+  public function set_list(list: Map<User, User_status>): Map<User, User_status> {
+      var remove = [];
+      var add = [];
+      for(user in list.keys()) {
+          if(list[user] == null && this.list.exists(user)) {
+              remove.push(user);
+          } else if(list[user] == null && !this.list.exists(user)) {
+              add.push(user);
+          }
+      }
+      this.edit_list(this.user, add, remove);
+      this.list = list;
+      return list;
+  }
+
+public function new( on_responses : Int -> Dynamic -> ?Bool -> Bool, send_request : HttpRequest -> (Int -> Dynamic -> Void) -> ?Bool -> Void, server : String ) {
+    super(on_responses, send_request, server);
+  }
+
+public function set_presence(user: User, presence: beartek.matrix_im.client.types.enums.Presences, ?status: String, ?on_response: Void -> Void) : Void {
+    if(status == null) {
+        this.send_request(Conection.make_request(HttpMethod.PUT, server + '/_matrix/client/r0/presence/' + user + '/status', {presence: presence}), function ( status : Int, data: Dynamic ) : Void {
+            on_response();
+        }
+    } else {
+        this.send_request(Conection.make_request(HttpMethod.PUT, server + '/_matrix/client/r0/presence/' + user + '/status', {presence: presence, status_msg: status}), function ( status : Int, data: Dynamic ) : Void {
+            on_response();
+        }
+    }
+ }
+
+public function get_presence(user: User, ?on_response: User_status -> Void) : Void {
+    this.send_request(Conection.make_request(HttpMethod.GET, server + '/_matrix/client/r0/presence/' + user + '/status', null), function ( status : Int, data: User_status ) : Void {
+        on_response(data);
+    }
+ }
+
+public function edit_list(user: User, add: Array<User>, remove: Array<User>, ?on_response: Void -> Void) : Void {
+    this.send_request(Conection.make_request(HttpMethod.POST, server + '/_matrix/client/r0/presence/list/' + user , {invite: add, drop: remove}), function ( status : Int, data: Dynamic ) : Void {
+        on_response();
+    }
+ }
+
+public function update_list(user: User, time: Int, ?on_response: Array<{content: User_status, type: Types}> -> Void) : Void {
+    this.send_request(Conection.make_request(HttpMethod.GET, server + '/_matrix/client/r0/presence/list/' + user , null), function ( status : Int, data: Array<{content: User_status, type: Types}>) : Void {
+        for(e in data) {
+            this.list[new User(e.content.user_id)] = e.content;
+        }
+        on_response(data);
+    }
+
+    haxe.Timer.delay(function() {
+        this.update_list(user, time, on_response);
+    }, time);
+ }
+
+}

+ 2 - 2
src/beartek/matrix_im/client/handlers/Profile.hx

@@ -25,13 +25,13 @@ class Profile extends Handler {
   }
 
   public inline function set_avatar( url : String, user : User, on_response : Void -> Void ) : Void {
-    this.send_request(Conection.make_request('PUT', this.server + '/_matrix/client/r0/profile/' + user + '/displayname', {avatar_url: url}), function( status : Int, data : Dynamic ) : Void {
+    this.send_request(Conection.make_request('PUT', this.server + '/_matrix/client/r0/profile/' + user + '/avatar_url', {avatar_url: url}), function( status : Int, data : Dynamic ) : Void {
       on_response();
     });
   }
 
   public inline function get_avatar( user : User, on_response : String -> Void ) : Void {
-    this.send_request(Conection.make_request('GET', this.server + '/_matrix/client/r0/profile/' + user + '/displayname', null), function( status : Int, data : Dynamic ) : Void {
+    this.send_request(Conection.make_request('GET', this.server + '/_matrix/client/r0/profile/' + user + '/avatar_url', null), function( status : Int, data : Dynamic ) : Void {
       on_response(data.avatar_url);
     });
   }

+ 35 - 0
src/beartek/matrix_im/client/handlers/Receipt.hx

@@ -0,0 +1,35 @@
+//Under GNU AGPL v3, see LICENCE
+package beartek.matrix_im.client.handlers;
+
+import com.akifox.asynchttp.HttpRequest;
+import com.akifox.asynchttp.HttpMethod;
+import beartek.matrix_im.client.types.Event;
+import beartek.matrix_im.client.types.User;
+import beartek.matrix_im.client.types.Room;
+import m.Receipt;
+
+class Receipt extends Handler {
+
+  public function new( on_responses : Int -> Dynamic -> ?Bool -> Bool, send_request : HttpRequest -> (Int -> Dynamic -> Void) -> ?Bool -> Void, server : String ) {
+    super(on_responses, send_request, server);
+  }
+
+  public function send_read(room: Room, event: String, ?on_response: Void -> Void) : Void {
+    this.send_request(Conection.make_request(HttpMethod.POST, server + '/_matrix/client/r0/rooms/' + room + '/receipt/m.read/' + event, null), function ( status : Int, data: Dynamic ) : Void {
+      on_response();
+  }
+
+  public function get_receipt_event(event: Event<Dynamic>): Event<Receipt_event> {
+      var first: Map<String, Dynamic> = Conection.to_object_map(event.content);
+      for (ev in first.keys()) {
+          var data: Map<String, {ts: Int}> = Conection.to_object_map(Reflect.field(first[ev], 'm.read'));
+          first[ev] = {
+              user: new User(data.keys().next()),
+              ts: data[data.keys().next()].ts
+          }
+      }
+
+      event.content = first;
+      return event;
+  }
+}

+ 64 - 4
src/beartek/matrix_im/client/handlers/Rooms.hx

@@ -14,13 +14,10 @@ import beartek.matrix_im.client.types.enums.Directions;
 
 class Rooms extends Handler {
   public var joined_rooms : Array<Room>;
+  public var typing_on: Map<Room, Bool>;
 
   public function new( on_responses : Int -> Dynamic -> ?Bool -> Bool, send_request : HttpRequest -> (Int -> Dynamic -> Void) -> ?Bool -> Void, server : String ) {
     super(on_responses, send_request, server);
-
-    this.list(function( rooms : Array<Room> ) : Void {
-      this.joined_rooms = rooms;
-    });
   }
 
   public inline function create( ?visibility : Visibilities, ?alias : String, ?name : String, ?topic : String, ?invite : Array<String>, ?invite_3pid : Array<{id_server : String, medium : String, address: String}>, ?creation_content: Dynamic, ?initial_state : Array<{type: String, state_key: String, content: String}>, ?preset : Room_preset, is_direct : Bool = false, on_response : Room -> Void ) : Void {
@@ -40,6 +37,27 @@ class Rooms extends Handler {
     });
   }
 
+  public inline function typing(room : Room, user : User, timeout=3000, stop=false, on_response : Bool -> Void) {
+     this.send_request(Conection.make_request('PUT', this.server + '/_matrix/client/r0/rooms/' + room + '/typing/' + user, {typing: if(stop) false else true, timeout: timeout}), function( status : Int, data : Dynamic ) : Void {
+      on_response(if(stop) false else true);
+    });
+  }
+
+  public function check_typing(time: Int, user: User) {
+    for(room in typing_on.keys()) {
+      if(typing_on[room] == true) {
+        this.typing(room, user, time + 200);
+      } else if(typing_on[room] == false) {
+        this.typing(room, user, time, true);
+        typing_on[room] == null;
+      }
+    }
+
+    haxe.Timer.delay(time, function (){
+      check_typing(time, user);
+    });
+  }
+
   public inline function invite( room : Room, user : User, on_response : Void -> Void ) : Void {
     this.send_request(Conection.make_request('POST', this.server + '/_matrix/client/r0/rooms/' + room + '/invite', {user_id: user.toString()}), function( status : Int, data : Dynamic ) : Void {
       on_response();
@@ -82,12 +100,19 @@ class Rooms extends Handler {
     });
   }
 
+
   public inline function list( on_response : Array<Room> -> Void ) : Void {
     this.send_request(Conection.make_request('GET', this.server + '/_matrix/client/r0/joined_rooms', null), function( status : Int, data : {joined_rooms: Array<String>} ) : Void {
       on_response(data.joined_rooms.map(Room.new));
     });
   }
 
+  public inline function update_joined_room() {
+    this.list(function( rooms : Array<Room> ) : Void {
+      this.joined_rooms = rooms;
+    });
+  }
+
   public inline function list_public( ?server : String, limit: Int = 100, ?since : String, ?search : String, on_response : Array<{aliases: Array<String>, canonical_alias: String, name: String, num_joined_members: Int, room_id: String, topic: String, world_readable: Bool, guest_can_join: Bool, avatar_url: String}> -> String -> String -> Int -> Void ) : Void {
     var req : Dynamic = {limit: limit};
     if(server != null) req.server = server;
@@ -137,6 +162,41 @@ class Rooms extends Handler {
     });
   }
 
+  public inline function get_display_names(members: Map<String,{display_name: String, avatar_url: String}>): Map<String,{id: User, avatar_url: String}> {
+    var result: Map<String,{id: User, avatar_url: String}> = new Map();
+
+    for(id in members.keys()) {
+     result = check_name_collision(result, {id: new User(id), display_name: members[id].display_name, avatar_url: members[id].avatar_url});
+    };
+
+    return result;
+  }
+
+  public inline function get_display_names_events(events: Array<Event<m.room.Member>>): Map<String,{id: User, avatar_url: String}> {
+    //TODO: memberships and is_direct
+    var result: Map<String,{id: User, avatar_url: String}> = new Map();
+
+    for(e in events) {
+     result = check_name_collision(result, {id: new User(e.state_key), display_name: e.content.displayname, avatar_url: e.content.avatar_url});
+    };
+
+    return result;
+  }
+
+  public function check_name_collision(members: Map<String,{id: User, avatar_url: String}>, new_member: {id: User, display_name: String, avatar_url: String}): Map<String,{id: User, avatar_url: String}> {
+    //TODO: This algo only works on pairs numbers of collisions
+    if(new_member.display_name != null) {
+      if(members[new_member.display_name] != null) {
+      members[new_member.display_name + ' (' + members[new_member.display_name].id + ')'] = members[new_member.display_name];
+      members[new_member.display_name + ' (' + new_member.id + ')'] = {id: new_member.id, avatar_url: new_member.avatar_url};
+      members[new_member.display_name] = null;
+      }
+    } else {
+      members[new_member.id] = {id: new_member.id, avatar_url: new_member.avatar_url};
+    }
+    return members;
+  }
+
   public inline function get_messages( room : Room, from : String, ?to : String, dir : Directions, limit : Int = 10, ?filter : Dynamic, on_response : Array<Event<Dynamic>> -> Void ) : Void {
     this.send_request(Conection.make_request('GET', server + '/_matrix/client/r0/rooms/' + room + '/messages', {from: from, to: to, dir: dir, limit: limit, filter: filter}), function( status : Int, data : Dynamic ) : Void {
       on_response(data.chunk);

+ 0 - 0
src/beartek/matrix_im/client/handlers/Server_adm.hx


Some files were not shown because too many files changed in this diff