package com.b44t.messenger.rpc; import com.b44t.messenger.DcJsonrpcInstance; import com.b44t.messenger.util.concurrent.SettableFuture; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; public class Rpc { private final Map> requestFutures = new ConcurrentHashMap<>(); private final DcJsonrpcInstance dcJsonrpcInstance; private int requestId = 0; private boolean started = false; private final Gson gson = new GsonBuilder().serializeNulls().create(); public Rpc(DcJsonrpcInstance dcJsonrpcInstance) { this.dcJsonrpcInstance = dcJsonrpcInstance; } private void processResponse() throws JsonSyntaxException { String jsonResponse = dcJsonrpcInstance.getNextResponse(); Response response = gson.fromJson(jsonResponse, Response.class); if (response.id == 0) { // Got JSON-RPC notification/event, ignore return; } SettableFuture future = requestFutures.remove(response.id); if (future == null) { // Got a response with unknown ID, ignore return; } if (response.error != null) { future.setException(new RpcException(response.error.toString())); } else if (response.result != null) { future.set(response.result); } else { future.setException(new RpcException("Got JSON-RPC response without result or error: " + jsonResponse)); } } public void start() { started = true; new Thread(() -> { while (true) { try { processResponse(); } catch (JsonSyntaxException e) { e.printStackTrace(); } } }, "jsonrpcThread").start(); } public SettableFuture call(String method, Object... params) throws RpcException { if (!started) throw new RpcException("RPC not started yet."); int id; synchronized (this) { id = ++requestId; } String jsonRequest = gson.toJson(new Request(method, params, id)); SettableFuture future = new SettableFuture<>(); requestFutures.put(id, future); dcJsonrpcInstance.request(jsonRequest); return future; } public JsonElement getResult(String method, Object... params) throws RpcException { try { return call(method, params).get(); } catch (ExecutionException e) { throw (RpcException)e.getCause(); } catch (InterruptedException e) { throw new RpcException(e.getMessage()); } } public List parseVcard(String path) throws RpcException { TypeToken> listType = new TypeToken>(){}; return gson.fromJson(getResult("parse_vcard", path), listType.getType()); } public String makeVcard(int accountId, int... contacts) throws RpcException { return gson.fromJson(getResult("make_vcard", accountId, contacts), String.class); } public List importVcard(int accountId, String path) throws RpcException { TypeToken> listType = new TypeToken>(){}; return gson.fromJson(getResult("import_vcard", accountId, path), listType.getType()); } public HttpResponse getHttpResponse(int accountId, String url) throws RpcException { return gson.fromJson(getResult("get_http_response", accountId, url), HttpResponse.class); } public Reactions getMsgReactions(int accountId, int msgId) throws RpcException { return gson.fromJson(getResult("get_message_reactions", accountId, msgId), Reactions.class); } public int sendReaction(int accountId, int msgId, String... reaction) throws RpcException { return getResult("send_reaction", accountId, msgId, reaction).getAsInt(); } public int draftSelfReport(int accountId) throws RpcException { return getResult("draft_self_report", accountId).getAsInt(); } public void sendWebxdcRealtimeData(Integer accountId, Integer instanceMsgId, List data) throws RpcException { getResult("send_webxdc_realtime_data", accountId, instanceMsgId, data); } public void sendWebxdcRealtimeAdvertisement(Integer accountId, Integer instanceMsgId) throws RpcException { getResult("send_webxdc_realtime_advertisement", accountId, instanceMsgId); } public void leaveWebxdcRealtime(Integer accountId, Integer instanceMessageId) throws RpcException { getResult("leave_webxdc_realtime", accountId, instanceMessageId); } public int getAccountFileSize(int accountId) throws RpcException { return getResult("get_account_file_size", accountId).getAsInt(); } public int addAccount() throws RpcException { return getResult("add_account").getAsInt(); } private static class Request { private final String jsonrpc = "2.0"; public final String method; public final Object[] params; public final int id; public Request(String method, Object[] params, int id) { this.method = method; this.params = params; this.id = id; } } private static class Response { public final int id; public final JsonElement result; public final JsonElement error; public Response(int id, JsonElement result, JsonElement error) { this.id = id; this.result = result; this.error = error; } } }