diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml
new file mode 100644
index 0000000..5f5d24a
--- /dev/null
+++ b/.github/workflows/maven-publish.yml
@@ -0,0 +1,34 @@
+# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
+# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path
+
+name: Maven Package
+
+on:
+ release:
+ types: [created]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ packages: write
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
+ settings-path: ${{ github.workspace }} # location for the settings.xml file
+
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+
+ - name: Publish to GitHub Packages Apache Maven
+ run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
new file mode 100644
index 0000000..c77d6c3
--- /dev/null
+++ b/.github/workflows/maven.yml
@@ -0,0 +1,42 @@
+# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+name: Java CI with Maven
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: '21'
+ distribution: 'temurin'
+ cache: maven
+ - name: Build with Maven
+ run: mvn -B package --file pom.xml
+
+ # Upload the build artifacts
+ - name: Upload build artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: build-artifacts
+ path: target/*.jar
+
+ # Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
+ - name: Update dependency graph
+ uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
diff --git a/README.md b/README.md
index a8348c1..4ead3c5 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,22 @@
+
# Matrix-ClientServer-API-java
A small and simple java API for the Matrix ClientServer Protocol (see [clientServer api](https://matrix.org/docs/spec/client_server/latest))
The API is still in Beta and known for bugs. If you found or missing a feature one you can create a new issue.
+Fork of https://github.com/JojiiOfficial/Matrix-ClientServer-API-java with multiple adaptations
+
+* Add Client#getOrCreateDirectChatRoomSync
+* Add Client#getDirectChatRoomsMapSync
+* Add Client#resolveRoomAliasSync
+* Add Client#loginWithJWTSync
+* HttpHelper do not store token, fetch it via Supplier
+* Do not copy info (e.g. derive `isLoggedin` via `loginData`)
+* HttpHelper pass Authorization via HTTP header not as query parameter
+* ...
+
+
## Usage
### Login
diff --git a/src/main/java/de/jojii/matrixclientserver/Bot/Client.java b/src/main/java/de/jojii/matrixclientserver/Bot/Client.java
index 87a80ee..8564895 100644
--- a/src/main/java/de/jojii/matrixclientserver/Bot/Client.java
+++ b/src/main/java/de/jojii/matrixclientserver/Bot/Client.java
@@ -1,24 +1,33 @@
package de.jojii.matrixclientserver.Bot;
-import org.jetbrains.annotations.Nullable;
-import de.jojii.matrixclientserver.Bot.Events.RoomEvent;
-import de.jojii.matrixclientserver.Callbacks.*;
-import de.jojii.matrixclientserver.Networking.HttpHelper;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import org.jetbrains.annotations.Nullable;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import de.jojii.matrixclientserver.Bot.Events.RoomEvent;
+import de.jojii.matrixclientserver.Callbacks.DataCallback;
+import de.jojii.matrixclientserver.Callbacks.EmptyCallback;
+import de.jojii.matrixclientserver.Callbacks.LoginCallback;
+import de.jojii.matrixclientserver.Callbacks.MemberCallback;
+import de.jojii.matrixclientserver.Callbacks.RoomEventCallback;
+import de.jojii.matrixclientserver.Callbacks.RoomEventsCallback;
+import de.jojii.matrixclientserver.Networking.HttpHelper;
public class Client {
private String host;
private LoginData loginData;
- private boolean isLoggedIn = false;
private final HttpHelper httpHelper;
private Syncee syncee;
@@ -28,33 +37,42 @@ public void login(String username, String password, LoginCallback onResponse) th
object.put("user", username);
object.put("password", password);
httpHelper.sendRequestAsync(host, HttpHelper.URLs.login, object, data -> {
- JSONObject object1 = new JSONObject((String) data);
- LoginData loginData = new LoginData();
- if (object1.has("response") && object1.getString("response").equals("error") && object1.has("code")) {
- loginData.setSuccess(false);
- } else {
- loginData.setSuccess(true);
- isLoggedIn = true;
- }
- if (loginData.isSuccess()) {
- loginData.setAccess_token(object1.getString("access_token"));
- loginData.setDevice_id(object1.getString("device_id"));
- loginData.setHome_server(object1.getString("home_server"));
- loginData.setUser_id(object1.getString("user_id"));
- this.loginData = loginData;
- httpHelper.setAccess_token(loginData.getAccess_token());
- syncee.startSyncee();
- }
+ LoginData loginData = Helper.ofPasswordLoginResponse((String) data);
+ if (loginData.isSuccess()) {
+ this.loginData = loginData;
+ syncee.startSyncee();
+ }
if (onResponse != null) {
onResponse.onResponse(loginData);
}
});
}
+ public void loginSync(String username, String password) throws IOException {
+ JSONObject object = new JSONObject();
+ object.put("type", "m.login.password");
+ object.put("user", username);
+ object.put("password", password);
+ String loginResponse = httpHelper.sendRequest(host, HttpHelper.URLs.login, object, false, "POST");
+ LoginData loginData = Helper.ofPasswordLoginResponse(loginResponse);
+ if (loginData.isSuccess()) {
+ this.loginData = loginData;
+ syncee.startSyncee();
+ }
+ }
+
+ /**
+ * Login using a matrix access
+ * token.
+ *
+ * @param userToken
+ * @param onResponse
+ * @throws IOException
+ */
public void login(String userToken, LoginCallback onResponse) throws IOException {
- httpHelper.setAccess_token(userToken);
httpHelper.sendRequestAsync(host, HttpHelper.URLs.whoami, null, "GET", data -> {
- this.isLoggedIn = false;
+ this.loginData = null;
JSONObject object = new JSONObject((String) data);
LoginData loginData = new LoginData();
@@ -63,7 +81,6 @@ public void login(String userToken, LoginCallback onResponse) throws IOException
loginData.setHome_server(host);
loginData.setAccess_token(userToken);
loginData.setSuccess(true);
- isLoggedIn = true;
this.loginData = loginData;
syncee.startSyncee();
} else {
@@ -73,7 +90,38 @@ public void login(String userToken, LoginCallback onResponse) throws IOException
onResponse.onResponse(loginData);
}
});
-
+ }
+
+ /**
+ * Perform a synchronous login using a JWT token. The matrix server has to
+ * support this authentication method, else it will fail.
+ *
+ * @param jwtToken
+ * @param deviceId this token is allocated to
+ * @throws IOException on technical error, or
+ * @see https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#jwt_config
+ */
+ public void loginWithJWTSync(String jwtToken, @Nullable String deviceId) throws IOException {
+ JSONObject object = new JSONObject();
+ object.put("type", "org.matrix.login.jwt");
+ object.put("token", jwtToken);
+ if (deviceId != null) {
+ object.put("device_id", deviceId);
+ }
+ String loginResponse = httpHelper.sendRequest(host, HttpHelper.URLs.login, object, false, "POST", true);
+ JSONObject _loginResponse = new JSONObject(loginResponse);
+ LoginData loginData = new LoginData();
+ if (_loginResponse.has("user_id")) {
+ loginData.setUser_id(_loginResponse.getString("user_id"));
+ loginData.setHome_server(_loginResponse.getString("home_server"));
+ loginData.setAccess_token(_loginResponse.getString("access_token"));
+ loginData.setDevice_id(_loginResponse.getString("device_id"));
+ loginData.setSuccess(true);
+ this.loginData = loginData;
+// syncee.startSyncee();
+ } else {
+ loginData.setSuccess(false);
+ }
}
public void registerRoomEventListener(RoomEventsCallback event) {
@@ -85,11 +133,11 @@ public void removeRoomEventListener(RoomEventsCallback event) {
}
public void logout(EmptyCallback onLoggedOut) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.logout, null, data -> {
- this.isLoggedIn = false;
+ this.loginData = null;
if (onLoggedOut != null) {
onLoggedOut.onRun();
}
@@ -97,11 +145,11 @@ public void logout(EmptyCallback onLoggedOut) throws IOException {
}
public void logoutAll(EmptyCallback onLoggedOut) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.logout_all, null, data -> {
- this.isLoggedIn = false;
+ this.loginData = null;
if (onLoggedOut != null) {
onLoggedOut.onRun();
}
@@ -109,7 +157,7 @@ public void logoutAll(EmptyCallback onLoggedOut) throws IOException {
}
public void whoami(DataCallback iam) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.whoami, null, "GET", data -> {
@@ -128,7 +176,7 @@ public void setPresence(String presence, String msg, EmptyCallback onStateChange
public void setPresence(String userid, String presence, String msg, EmptyCallback onStateChanged) throws
IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
JSONObject jsonObject = new JSONObject();
@@ -143,7 +191,7 @@ public void setPresence(String userid, String presence, String msg, EmptyCallbac
}
public void joinRoom(String roomID, DataCallback onJoined) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.rooms + roomID + "/join", null, "POST", data -> {
@@ -154,7 +202,7 @@ public void joinRoom(String roomID, DataCallback onJoined) throws IOException {
}
public void leaveRoom(String roomID, EmptyCallback onGone) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.rooms + roomID + "/leave", null, "POST", data -> {
@@ -164,12 +212,97 @@ public void leaveRoom(String roomID, EmptyCallback onGone) throws IOException {
});
}
+ /**
+ * Get or create a direct chat room with a user. If created the room will be
+ * registered in m.direct account data, as required in the specification.
+ *
+ * @param userID the user id to create the direct chat room with
+ * @return null if not logged in otherwise the direct chat room id
+ * @throws IOException
+ */
+ public String getOrCreateDirectChatRoomSync(String userID) throws IOException {
+ if (!isLoggedIn()) {
+ return null;
+ }
+
+ Map> directChatRooms = getDirectChatRoomsMapSync();
+ List list = directChatRooms.get(userID);
+ if (list != null && !list.isEmpty()) {
+ return list.get(0);
+ }
+
+ // create the direct chat room
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("is_direct", Boolean.TRUE);
+ jsonObject.put("preset", "trusted_private_chat");
+ jsonObject.put("invite", new JSONArray().put(userID));
+
+ String response = httpHelper.sendRequest(host, HttpHelper.URLs.client + "createRoom", jsonObject, true, "POST",
+ true);
+ JSONObject object = new JSONObject(response);
+ String roomID = object.getString("room_id");
+
+ // register in users account_data m.direct
+ directChatRooms.put(userID, Collections.singletonList(roomID));
+ JSONObject mDirect = new JSONObject(directChatRooms);
+ httpHelper.sendRequest(host, HttpHelper.URLs.user + loginData.getUser_id() + "/account_data/m.direct", mDirect,
+ true, "PUT");
+
+ return roomID;
+ }
+
+ /**
+ * Returns the map of the registered user's direct chat rooms.
+ *
+ * @return null if not logged in, otherwise a map containing the
+ * users id as keys and the respective direct chat room ids.
+ * @throws IOException
+ * @see https://spec.matrix.org/v1.11/client-server-api/#mdirect
+ */
+ public Map> getDirectChatRoomsMapSync() throws IOException {
+ if (loginData == null) {
+ return null;
+ }
+ String response = httpHelper.sendRequest(host,
+ HttpHelper.URLs.user + loginData.getUser_id() + "/account_data/m.direct", null, true, "GET");
+ JSONObject jsonObject = new JSONObject(response);
+ return jsonObject.keySet().stream()
+ .collect(Collectors.toMap(key -> (String) key,
+ key -> IntStream.range(0, jsonObject.getJSONArray((String) key).length())
+ .mapToObj(i -> jsonObject.getJSONArray((String) key).getString(i))
+ .collect(Collectors.toList())));
+ }
+
+ /**
+ * Requests that the server resolve a room alias to a room ID.
+ *
+ * @param roomID
+ * @return the resolved room id or null if no room_id value was
+ * found
+ * @throws IOException
+ * @see https://spec.matrix.org/v1.11/client-server-api/#get_matrixclientv3directoryroomroomalias
+ */
+ public String resolveRoomAliasSync(String roomID) throws IOException {
+ if (roomID.startsWith("#")) {
+ String response = httpHelper.sendRequest(host, HttpHelper.URLs.directory + "room/" + roomID, null, true,
+ "GET");
+ JSONObject object = new JSONObject(response);
+ if (object.has("room_id")) {
+ return object.getString("room_id");
+ } else {
+ return null;
+ }
+ }
+ return roomID;
+ }
+
public void sendText(String roomID, String message, DataCallback response) throws IOException {
sendText(roomID, message, false, "", response);
}
public void sendText(String roomID, String message, boolean formatted, String formattedMessage, DataCallback response) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
JSONObject data = new JSONObject();
@@ -184,7 +317,7 @@ public void sendText(String roomID, String message, boolean formatted, String fo
}
public void sendMessage(String roomID, JSONObject messageObject, DataCallback response) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
sendRoomEvent("m.room.message", roomID, messageObject, response);
@@ -200,7 +333,7 @@ public void sendRoomEvent(String event, String roomID, JSONObject content, DataC
}
public void kickUser(String roomID, String userID, String reason, DataCallback response) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
JSONObject ob = new JSONObject();
@@ -215,7 +348,7 @@ public void kickUser(String roomID, String userID, String reason, DataCallback r
}
public void banUser(String roomID, String userID, String reason, DataCallback response) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
JSONObject ob = new JSONObject();
@@ -230,7 +363,7 @@ public void banUser(String roomID, String userID, String reason, DataCallback re
}
public void unbanUser(String roomID, String userID, DataCallback response) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
JSONObject ob = new JSONObject();
@@ -245,7 +378,7 @@ public void unbanUser(String roomID, String userID, DataCallback response) throw
public void sendReadReceipt(String roomID, String eventID, String receiptType, DataCallback response) throws
IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.rooms + roomID + "/receipt/" + receiptType + "/" + eventID, null, "POST", data -> {
@@ -260,7 +393,7 @@ public void setTyping(boolean typing, String roomID, int timeout, EmptyCallback
}
public void setTyping(boolean typing, String userid, String roomID, int timeout, EmptyCallback response) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
JSONObject object = new JSONObject();
@@ -275,7 +408,7 @@ public void setTyping(boolean typing, String userid, String roomID, int timeout,
}
public void getRoomMembers(String roomID, MemberCallback memberCallback) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.rooms + roomID + "/joined_members", null, "GET", data -> {
@@ -307,7 +440,7 @@ public void getRoomMembers(String roomID, MemberCallback memberCallback) throws
}
public void getRoomEventFromId(String roomID, String eventID, RoomEventCallback callback) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
httpHelper.sendRequestAsync(host, HttpHelper.URLs.rooms + URLEncoder.encode(roomID) + "/event/" + URLEncoder.encode(eventID), null, "GET", data -> {
@@ -325,7 +458,7 @@ public void getRoomEventFromId(String roomID, String eventID, RoomEventCallback
public void createRoom(String preset, String visibility, @Nullable String alias, String name, @Nullable String topic, @Nullable List invitations, @Nullable String roomVersion, DataCallback callback) throws IOException {
- if (!isLoggedIn)
+ if (!isLoggedIn())
return;
JSONObject object = new JSONObject();
@@ -394,7 +527,9 @@ public static class Room {
public Client(String host) {
this.host = host;
- this.httpHelper = new HttpHelper();
+ this.httpHelper = new HttpHelper(() -> {
+ return loginData != null ? loginData.getAccess_token() : null;
+ });
this.syncee = new Syncee(this, httpHelper);
if (!host.endsWith("/"))
this.host += "/";
@@ -406,7 +541,12 @@ public Client(String host) {
public Client(HttpHelper httpHelper, boolean isLoggedIn) {
this.httpHelper = httpHelper;
this.syncee = new Syncee(this, httpHelper);
- this.isLoggedIn = isLoggedIn;
+ if (isLoggedIn) {
+ LoginData loginData = new LoginData();
+ loginData.setSuccess(true);
+ loginData.setUser_id("@data:starship-enterprise.com");
+ this.loginData = loginData;
+ }
}
public String getHost() {
@@ -418,6 +558,6 @@ public LoginData getLoginData() {
}
public boolean isLoggedIn() {
- return isLoggedIn;
+ return loginData != null ? loginData.isSuccess() : false;
}
}
\ No newline at end of file
diff --git a/src/main/java/de/jojii/matrixclientserver/Bot/Helper.java b/src/main/java/de/jojii/matrixclientserver/Bot/Helper.java
index f532cb5..a7a0676 100644
--- a/src/main/java/de/jojii/matrixclientserver/Bot/Helper.java
+++ b/src/main/java/de/jojii/matrixclientserver/Bot/Helper.java
@@ -2,9 +2,30 @@
import java.util.Random;
+import org.json.JSONObject;
+
public class Helper {
public static int randomInt(int min, int max){
Random r = new Random();
return r.nextInt((max - min) + 1) + min;
}
+
+ public static LoginData ofPasswordLoginResponse(String loginResponse) {
+ JSONObject _loginResponse = new JSONObject(loginResponse);
+ LoginData loginData = new LoginData();
+ if (_loginResponse.has("response") && _loginResponse.getString("response").equals("error")
+ && _loginResponse.has("code")) {
+ loginData.setSuccess(false);
+ } else {
+ loginData.setSuccess(true);
+ }
+ if (loginData.isSuccess()) {
+ loginData.setAccess_token(_loginResponse.getString("access_token"));
+ loginData.setDevice_id(_loginResponse.getString("device_id"));
+ loginData.setHome_server(_loginResponse.getString("home_server"));
+ loginData.setUser_id(_loginResponse.getString("user_id"));
+ }
+ return loginData;
+ }
+
}
diff --git a/src/main/java/de/jojii/matrixclientserver/Networking/HttpHelper.java b/src/main/java/de/jojii/matrixclientserver/Networking/HttpHelper.java
index 43e3c42..4f6a623 100644
--- a/src/main/java/de/jojii/matrixclientserver/Networking/HttpHelper.java
+++ b/src/main/java/de/jojii/matrixclientserver/Networking/HttpHelper.java
@@ -5,9 +5,11 @@
import java.io.*;
import java.net.HttpURLConnection;
+import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
+import java.util.function.Supplier;
public class HttpHelper {
public static class URLs{
@@ -15,6 +17,7 @@ public static class URLs{
public static String client = root+"client/r0/";
public static String media = root+"media/r0/";
+ public static String directory = client + "directory/";
public static String login = client+"login";
public static String logout = client+"logout";
public static String logout_all = client+"logout/all";
@@ -25,56 +28,76 @@ public static class URLs{
public static String user = client+"user/";
public static String upload = media+"upload/";
}
- private String access_token;
- public void setAccess_token(String access_token) {
- this.access_token = access_token;
- }
-
- public String sendRequest(String host, String path, JSONObject data, boolean useAccesstoken, String requestMethod) throws IOException {
- String surl = host+path + (useAccesstoken ? "?access_token="+access_token : "");
- /*if(!path.contains("sync")){
- System.out.println(surl);
- }*/
- URL obj = new URL(surl);
- URLConnection con = obj.openConnection();
- HttpURLConnection http = (HttpURLConnection)con;
- http.setRequestMethod(requestMethod);
- http.setDoOutput(true);
- http.setReadTimeout(60000);
+ public HttpHelper(Supplier accessTokenSupplier) {
+ this.accessTokenSupplier = accessTokenSupplier;
+ }
- if(data != null){
- int length = data.toString().length();
- http.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
- }
-
- http.connect();
+ private Supplier accessTokenSupplier;
- if(data != null){
- try(OutputStream os = http.getOutputStream()) {
- os.write(data.toString().getBytes());
- }
- }
-
- try(BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
- StringBuilder response = new StringBuilder();
- String responseLine = null;
- while ((responseLine = br.readLine()) != null) {
- response.append(responseLine.trim());
- }
- return response.toString();
- }catch (IOException e){
- return "{\n" +
- " \"response\":\"error\",\n" +
- " \"code\":"+http.getResponseCode()+"\n" +
- "}";
- }
+ public String sendRequest(String host, String path, JSONObject data, boolean useAccesstoken, String requestMethod) throws IOException {
+ return sendRequest(host, path, data, useAccesstoken, requestMethod, false);
+ }
+
+ public String sendRequest(String host, String path, JSONObject data, boolean useAccesstoken, String requestMethod,
+ boolean throwAll) throws IOException {
+
+ URL obj = URI.create(host + path).toURL();
+ URLConnection con = obj.openConnection();
+
+ HttpURLConnection http = (HttpURLConnection) con;
+ http.setRequestMethod(requestMethod);
+ http.setDoOutput(true);
+ http.setReadTimeout(60000);
+
+ if (useAccesstoken) {
+ http.setRequestProperty("Authorization", "Bearer " + accessTokenSupplier.get());
+ }
+
+ if (data != null) {
+ int length = data.toString().length();
+ http.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+ }
+
+ http.connect();
+
+ if (data != null) {
+ try (OutputStream os = http.getOutputStream()) {
+ os.write(data.toString().getBytes());
+ }
+ }
+
+ try (BufferedReader br = new BufferedReader(
+ new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
+ StringBuilder response = new StringBuilder();
+ String responseLine = null;
+ while ((responseLine = br.readLine()) != null) {
+ response.append(responseLine.trim());
+ }
+ return response.toString();
+ } catch (IOException e) {
+
+ String errorResponse = null;
+ InputStream errorStream = http.getErrorStream();
+ if (errorStream != null) {
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(errorStream))) {
+ StringBuilder response = new StringBuilder();
+ String responseLine = null;
+ while ((responseLine = br.readLine()) != null) {
+ response.append(responseLine.trim());
+ }
+ errorResponse = response.toString();
+ }
+ }
+ if (throwAll) {
+ throw new IOException(errorResponse, e);
+ }
+ return "{\n" + " \"response\":\"error\",\n" + " \"code\":" + http.getResponseCode() + "\n" + "}";
+ }
}
public String sendStream(String host, String path, String contentType, InputStream data, int contentLength, boolean useAccesstoken, String requestMethod) throws IOException {
- String surl = host+path + (useAccesstoken ? "?access_token="+access_token : "");
-
- URL obj = new URL(surl);
+ URL obj = URI.create(host + path).toURL();
URLConnection con = obj.openConnection();
HttpURLConnection http = (HttpURLConnection)con;
http.setRequestMethod(requestMethod);
@@ -82,6 +105,10 @@ public String sendStream(String host, String path, String contentType, InputStre
http.setRequestProperty("Content-Type", contentType);
http.addRequestProperty("Content-Length", Integer.toString(contentLength));
+ if (useAccesstoken) {
+ http.setRequestProperty("Authorization", "Bearer " + accessTokenSupplier.get());
+ }
+
try(OutputStream os = http.getOutputStream()) {
int i = 0;
int bytes = 0;
@@ -129,11 +156,11 @@ public void sendStreamAsync(String host, String path, String contentType, int co
}
public void sendRequestAsync(String host, String path, JSONObject data, DataCallback callback) throws IOException {
- sendRequestAsync(host, path, data, callback, access_token != null, "POST");
+ sendRequestAsync(host, path, data, callback, accessTokenSupplier.get() != null, "POST");
}
public void sendRequestAsync(String host, String path, JSONObject data, String requestMethd, DataCallback callback) throws IOException {
- sendRequestAsync(host, path, data, callback, access_token != null, requestMethd);
+ sendRequestAsync(host, path, data, callback, accessTokenSupplier.get() != null, requestMethd);
}
public void sendRequestAsync(String host, String path, JSONObject data, DataCallback callback, boolean useAccesstoken, String requestMethod) throws IOException {
diff --git a/src/test/java/de/jojii/matrixclientserver/Bot/ClientTest.java b/src/test/java/de/jojii/matrixclientserver/Bot/ClientTest.java
index 59916f6..0a909c6 100644
--- a/src/test/java/de/jojii/matrixclientserver/Bot/ClientTest.java
+++ b/src/test/java/de/jojii/matrixclientserver/Bot/ClientTest.java
@@ -1,90 +1,175 @@
package de.jojii.matrixclientserver.Bot;
-import de.jojii.matrixclientserver.Callbacks.DataCallback;
-import de.jojii.matrixclientserver.Callbacks.EmptyCallback;
-import de.jojii.matrixclientserver.Networking.HttpHelper;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNotNull;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.startsWith;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.internal.matchers.Any;
-import java.io.IOException;
-
-import static org.mockito.Mockito.*;
+import de.jojii.matrixclientserver.Callbacks.DataCallback;
+import de.jojii.matrixclientserver.Callbacks.EmptyCallback;
+import de.jojii.matrixclientserver.Networking.HttpHelper;
class ClientTest {
- private final HttpHelper httpHelper = Mockito.mock(HttpHelper.class);
+ private final HttpHelper httpHelper = Mockito.mock(HttpHelper.class, withSettings().verboseLogging());
+
+ @Test
+ void joinRoom_notLoggedIn() throws IOException {
+ Client client = new Client(httpHelper, false);
+ DataCallback callback = Mockito.mock(DataCallback.class);
+
+ client.joinRoom("myRoom", callback);
+
+ verify(callback, never()).onData(Any.ANY);
+ }
+
+ @Test
+ void joinRoom() throws IOException {
+ final String expectedURL = "_matrix/client/r0/rooms/myRoom/join";
+
+ final Client client = new Client(httpHelper, true);
+ final DataCallback onJoined = Mockito.mock(DataCallback.class);
+
+ // invoke method
+ client.joinRoom("myRoom", onJoined);
+
+ final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
+ verify(httpHelper).sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
+ callbackCaptor.getValue().onData("test");
+
+ verify(onJoined, times(1)).onData("test");
+ }
+
+ @Test
+ void joinRoom_callbackIsNull() throws IOException {
+ final String expectedURL = "_matrix/client/r0/rooms/myRoom/join";
+
+ final Client client = new Client(httpHelper, true);
+ client.joinRoom("myRoom", null);
+
+ final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
+ verify(httpHelper).sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
+ callbackCaptor.getValue().onData("test");
+ }
+
+ @Test
+ void leaveRoom_callbackIsNull() throws IOException {
+ final String expectedURL = "_matrix/client/r0/rooms/myRoom/leave";
+
+ final Client client = new Client(httpHelper, true);
+ client.leaveRoom("myRoom", null);
+
+ final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
+ verify(httpHelper).sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
+ callbackCaptor.getValue().onData("test");
+ }
+
+ @Test
+ void leaveRoom() throws IOException {
+ final String expectedURL = "_matrix/client/r0/rooms/familyChat/leave";
+
+ final Client client = new Client(httpHelper, true);
+ final EmptyCallback onGone = Mockito.mock(EmptyCallback.class);
+
+ // invoke method
+ client.leaveRoom("familyChat", onGone);
+
+ final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
+ verify(httpHelper).sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
+ callbackCaptor.getValue().onData("test");
- @Test
- void joinRoom_notLoggedIn() throws IOException {
- Client client = new Client(httpHelper, false);
- DataCallback callback = Mockito.mock(DataCallback.class);
+ verify(onGone, times(1)).onRun();
+ }
- client.joinRoom("myRoom", callback);
+ @Test
+ void resolveRoomAliasSync() throws IOException {
- verify(callback, never()).onData(Any.ANY);
- }
+ final String expectedURL = "_matrix/client/r0/directory/room";
- @Test
- void joinRoom() throws IOException {
- final String expectedURL = "_matrix/client/r0/rooms/myRoom/join";
+ final Client client = new Client(httpHelper, true);
- final Client client = new Client(httpHelper, true);
- final DataCallback onJoined = Mockito.mock(DataCallback.class);
+ JSONObject jsonResponse = new JSONObject();
+ jsonResponse.put("room_id", "!hCBUUsLgnlXIvwmnkT:starship-enterprise.com");
+ JSONArray servers = new JSONArray();
+ servers.put("starship-enterprise.com");
+ jsonResponse.put("servers", servers);
- // invoke method
- client.joinRoom("myRoom", onJoined);
+ when(httpHelper.sendRequest(isNull(), startsWith(expectedURL), isNull(), eq(true), eq("GET")))
+ .thenReturn(jsonResponse.toString());
- final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
- verify(httpHelper)
- .sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
- callbackCaptor.getValue().onData("test");
+ String resolvedId = client.resolveRoomAliasSync("#holodeck:starship-enterprise.com");
+ assertEquals("!hCBUUsLgnlXIvwmnkT:starship-enterprise.com", resolvedId);
+ }
- verify(onJoined, times(1)).onData("test");
- }
+ @Test
+ void getDirectRoomMapSync() throws IOException {
+ final Client client = new Client(httpHelper, true);
+ performLogin(client);
- @Test
- void joinRoom_callbackIsNull() throws IOException {
- final String expectedURL = "_matrix/client/r0/rooms/myRoom/join";
+ final String expectedURL = "_matrix/client/r0/user/@data:starship-enterprise.com/account_data/m.direct";
- final Client client = new Client(httpHelper, true);
- client.joinRoom("myRoom", null);
+ JSONObject jsonResponse = new JSONObject();
+ JSONArray jsonArray = new JSONArray();
+ jsonArray.put("!rGkalNKwzOdTviJzbY:starship-enterprise.com");
+ jsonResponse.put("@picard:starship-enterprise.com", jsonArray);
- final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
- verify(httpHelper)
- .sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
- callbackCaptor.getValue().onData("test");
- }
+ when(httpHelper.sendRequest(isNull(), startsWith(expectedURL), isNull(), eq(true), eq("GET")))
+ .thenReturn(jsonResponse.toString());
- @Test
- void leaveRoom_callbackIsNull() throws IOException {
- final String expectedURL = "_matrix/client/r0/rooms/myRoom/leave";
+ Map> directRoomMap = client.getDirectChatRoomsMapSync();
+ List list = directRoomMap.get("@picard:starship-enterprise.com");
+ assertEquals("!rGkalNKwzOdTviJzbY:starship-enterprise.com", list.get(0));
+ }
- final Client client = new Client(httpHelper, true);
- client.leaveRoom("myRoom", null);
+ @Test
+ void getOrCreateDirectChatRoomSync() throws IOException {
+ final Client client = new Client(httpHelper, true);
+ performLogin(client);
- final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
- verify(httpHelper)
- .sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
- callbackCaptor.getValue().onData("test");
- }
+ // no direct message room for @geordi found, will need to create
+ String expectedURL = "_matrix/client/r0/user/@data:starship-enterprise.com/account_data/m.direct";
+ when(httpHelper.sendRequest(isNull(), startsWith(expectedURL), isNull(), eq(true), eq("GET")))
+ .thenReturn(new JSONObject().toString());
+ // TODO do not deliver empty but other, then test if correct update
- @Test
- void leaveRoom() throws IOException {
- final String expectedURL = "_matrix/client/r0/rooms/familyChat/leave";
+ expectedURL = "_matrix/client/r0/createRoom";
+ when(httpHelper.sendRequest(isNull(), startsWith(expectedURL), isNotNull(), eq(true), eq("POST"), eq(true)))
+ .thenReturn("{\"room_id\":\"!geordihere:starship-enterprise.com\"}");
- final Client client = new Client(httpHelper, true);
- final EmptyCallback onGone = Mockito.mock(EmptyCallback.class);
+ String roomId = client.getOrCreateDirectChatRoomSync("@geordi:starship-enterprise.com");
+ assertEquals("!geordihere:starship-enterprise.com", roomId);
+ }
- // invoke method
- client.leaveRoom("familyChat", onGone);
+ private void performLogin(Client client) throws IOException {
+ JSONObject loginResponse = new JSONObject();
+ loginResponse.put("user_id", "@data:starship-enterprise.com");
+ loginResponse.put("access_token", "syt_c2VydmljZS1hY2NvdW50LWVsZXhpcy1zZXJ2ZXI_ZwTAYorZbOeMzLGRBDTt_3ltbrk");
+ loginResponse.put("home_server", "starship-enterprise.com");
+ loginResponse.put("device_id", "mockito");
- final ArgumentCaptor callbackCaptor = ArgumentCaptor.forClass(DataCallback.class);
- verify(httpHelper)
- .sendRequestAsync(isNull(), eq(expectedURL), isNull(), eq("POST"), callbackCaptor.capture());
- callbackCaptor.getValue().onData("test");
+ when(httpHelper.sendRequest(isNull(), eq(HttpHelper.URLs.login), isNotNull(), eq(false), eq("POST")))
+ .thenReturn(loginResponse.toString());
+ client.loginSync("@data:starship-enterprise.com", "doesnotcompute");
+ assertTrue(client.isLoggedIn());
+ }
- verify(onGone, times(1)).onRun();
- }
}
\ No newline at end of file