|
1 | 1 | package org.synyx.matrix.bot;
|
2 | 2 |
|
3 |
| -import com.fasterxml.jackson.databind.DeserializationFeature; |
4 |
| -import com.fasterxml.jackson.databind.ObjectMapper; |
5 |
| -import com.fasterxml.jackson.databind.SerializationFeature; |
6 |
| -import com.fasterxml.jackson.databind.json.JsonMapper; |
7 |
| -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; |
8 |
| -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; |
9 |
| -import lombok.extern.slf4j.Slf4j; |
10 | 3 | import org.synyx.matrix.bot.domain.MatrixEventId;
|
11 | 4 | import org.synyx.matrix.bot.domain.MatrixRoomId;
|
12 |
| -import org.synyx.matrix.bot.domain.MatrixUserId; |
13 |
| -import org.synyx.matrix.bot.internal.MatrixAuthentication; |
14 |
| -import org.synyx.matrix.bot.internal.MatrixEventNotifier; |
15 |
| -import org.synyx.matrix.bot.internal.MatrixStateSynchronizer; |
16 |
| -import org.synyx.matrix.bot.internal.api.MatrixApi; |
17 |
| -import org.synyx.matrix.bot.internal.api.dto.MessageDto; |
18 |
| -import org.synyx.matrix.bot.internal.api.dto.ReactionDto; |
19 |
| -import org.synyx.matrix.bot.internal.api.dto.ReactionRelatesToDto; |
| 5 | +import org.synyx.matrix.bot.internal.MatrixClientImpl; |
20 | 6 |
|
21 | 7 | import java.util.Optional;
|
22 | 8 |
|
23 |
| -@Slf4j |
24 |
| -public class MatrixClient { |
25 |
| - |
26 |
| - private final MatrixAuthentication authentication; |
27 |
| - private final ObjectMapper objectMapper; |
28 |
| - private final MatrixApi api; |
29 |
| - private MatrixState state; |
30 |
| - private MatrixStateSynchronizer stateSynchronizer; |
31 |
| - private MatrixPersistedState persistedState; |
32 |
| - private MatrixEventNotifier eventNotifier; |
33 |
| - private boolean interruptionRequested; |
34 |
| - |
35 |
| - public MatrixClient(String hostname, String username, String password) { |
36 |
| - |
37 |
| - this.authentication = new MatrixAuthentication(username, password); |
38 |
| - this.objectMapper = JsonMapper.builder() |
39 |
| - .addModule(new Jdk8Module()) |
40 |
| - .addModule(new JavaTimeModule()) |
41 |
| - .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) |
42 |
| - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) |
43 |
| - .configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, true) |
44 |
| - .build(); |
45 |
| - this.api = new MatrixApi(hostname, authentication, objectMapper); |
46 |
| - this.state = null; |
47 |
| - this.eventNotifier = null; |
48 |
| - this.interruptionRequested = false; |
| 9 | +/** |
| 10 | + * An interface for a client connecting to a matrix server. |
| 11 | + * Serves as the main method of communicating with the server. |
| 12 | + */ |
| 13 | +public interface MatrixClient { |
| 14 | + |
| 15 | + /** |
| 16 | + * Creates a new matrix client to connect to the specified server. |
| 17 | + * |
| 18 | + * @param url The url for connecting to the intended matrix server. Must start with http:// or https:// |
| 19 | + * @param username The username for logging into the matrix server. |
| 20 | + * @param password The password for logging into the matrix server. |
| 21 | + * @return A {@link MatrixClient} implementation that connects to the specified matrix server. |
| 22 | + */ |
| 23 | + static MatrixClient create(String url, String username, String password) { |
| 24 | + |
| 25 | + return new MatrixClientImpl(url, username, password); |
49 | 26 | }
|
50 | 27 |
|
51 |
| - public void setEventCallback(MatrixEventConsumer eventConsumer) { |
52 |
| - |
53 |
| - this.eventNotifier = MatrixEventNotifier.from(objectMapper, eventConsumer).orElse(null); |
54 |
| - } |
55 |
| - |
56 |
| - public void setPersistedState(MatrixPersistedState persistedState) { |
57 |
| - |
58 |
| - this.persistedState = persistedState; |
59 |
| - } |
60 |
| - |
61 |
| - public void requestStopOfSync() { |
62 |
| - |
63 |
| - interruptionRequested = true; |
64 |
| - api.terminateOpenConnections(); |
65 |
| - } |
66 |
| - |
67 |
| - public void syncContinuous() { |
68 |
| - |
69 |
| - if (!authentication.isAuthenticated()) { |
70 |
| - if (api.login()) { |
71 |
| - log.info("Successfully logged in to matrix server as {}", |
72 |
| - authentication.getUserId() |
73 |
| - .map(MatrixUserId::toString) |
74 |
| - .orElse("UNKNOWN") |
75 |
| - ); |
76 |
| - } else { |
77 |
| - return; |
78 |
| - } |
79 |
| - } |
80 |
| - |
81 |
| - state = new MatrixState(authentication.getUserId().orElseThrow(IllegalStateException::new)); |
82 |
| - stateSynchronizer = new MatrixStateSynchronizer(state, objectMapper); |
83 |
| - |
84 |
| - var maybeSyncResponse = api.syncFull(); |
85 |
| - String lastBatch; |
86 |
| - if (maybeSyncResponse.isPresent()) { |
87 |
| - final var syncResponse = maybeSyncResponse.get(); |
88 |
| - lastBatch = syncResponse.nextBatch(); |
89 |
| - |
90 |
| - stateSynchronizer.synchronizeState(syncResponse); |
91 |
| - } else { |
92 |
| - log.error("Failed to perform initial sync"); |
93 |
| - return; |
94 |
| - } |
95 |
| - |
96 |
| - if (eventNotifier != null) { |
97 |
| - eventNotifier.getConsumer().onConnected(state); |
98 |
| - } |
99 |
| - |
100 |
| - if (persistedState != null) { |
101 |
| - final var maybePersistedLastBatch = persistedState.getLastBatch(); |
102 |
| - if (maybePersistedLastBatch.isPresent()) { |
103 |
| - lastBatch = maybePersistedLastBatch.get(); |
104 |
| - } else { |
105 |
| - persistedState.setLastBatch(lastBatch); |
106 |
| - } |
107 |
| - } |
108 |
| - |
109 |
| - while (!interruptionRequested) { |
110 |
| - maybeSyncResponse = api.sync(lastBatch); |
111 |
| - if (maybeSyncResponse.isPresent()) { |
112 |
| - final var syncResponse = maybeSyncResponse.get(); |
113 |
| - lastBatch = syncResponse.nextBatch(); |
114 |
| - |
115 |
| - stateSynchronizer.synchronizeState(syncResponse); |
116 |
| - |
117 |
| - if (eventNotifier != null) { |
118 |
| - eventNotifier.notifyFromSynchronizationResponse(state, syncResponse); |
119 |
| - } |
120 |
| - |
121 |
| - if (persistedState != null) { |
122 |
| - persistedState.setLastBatch(lastBatch); |
123 |
| - } |
124 |
| - } |
125 |
| - } |
126 |
| - |
127 |
| - interruptionRequested = false; |
128 |
| - } |
129 |
| - |
130 |
| - public boolean isConnected() { |
131 |
| - |
132 |
| - return state != null; |
133 |
| - } |
134 |
| - |
135 |
| - public Optional<MatrixState> getState() { |
136 |
| - |
137 |
| - return Optional.ofNullable(state); |
138 |
| - } |
139 |
| - |
140 |
| - public boolean sendMessage(MatrixRoomId roomId, String messageBody) { |
141 |
| - |
142 |
| - return api.sendEvent(roomId.getFormatted(), "m.room.message", new MessageDto(messageBody, "m.text")); |
143 |
| - } |
144 |
| - |
145 |
| - public boolean addReaction(MatrixRoomId roomId, MatrixEventId eventId, String reaction) { |
146 |
| - |
147 |
| - final var reactionDto = new ReactionDto(new ReactionRelatesToDto(eventId.getFormatted(), reaction)); |
148 |
| - return api.sendEvent(roomId.getFormatted(), "m.reaction", reactionDto); |
149 |
| - } |
150 |
| - |
151 |
| - public boolean joinRoom(MatrixRoomId roomId) { |
152 |
| - |
153 |
| - return api.joinRoom(roomId.getFormatted(), "hello there"); |
154 |
| - } |
155 |
| - |
156 |
| - public boolean leaveRoom(MatrixRoomId roomId) { |
157 |
| - |
158 |
| - return api.leaveRoom(roomId.getFormatted(), "bai"); |
159 |
| - } |
| 28 | + /** |
| 29 | + * Sets a consumer object that gets called on events happening on the matrix server. |
| 30 | + * Only one consumer can be set at any time. |
| 31 | + * Calling this method again replaces any previous event callback. |
| 32 | + * |
| 33 | + * @param eventConsumer The consumer to call on events. |
| 34 | + */ |
| 35 | + void setEventCallback(MatrixEventConsumer eventConsumer); |
| 36 | + |
| 37 | + /** |
| 38 | + * Optionally provides an interface to provide the current state of the matrix client. |
| 39 | + * If not provided, any startup will act like the first startup and will ignore any previously sent messages. |
| 40 | + * Providing a persisted state will make the client be able to determine which events happened while offline. |
| 41 | + * |
| 42 | + * @param persistedState An interface for persisting the matrix client state |
| 43 | + */ |
| 44 | + void setPersistedStateProvider(MatrixPersistedStateProvider persistedState); |
| 45 | + |
| 46 | + |
| 47 | + /** |
| 48 | + * The main matrix client event loop that continuously syncs all events happening on the matrix server to the client. |
| 49 | + * This is a blocking call, so make sure to call it from a different thread if needed. |
| 50 | + * |
| 51 | + * @throws InterruptedException The sync has been interrupted |
| 52 | + */ |
| 53 | + void syncContinuous() throws InterruptedException; |
| 54 | + |
| 55 | + /** |
| 56 | + * Requests the matrix client to stop syncing and terminate. |
| 57 | + * May be called from a different thread. |
| 58 | + */ |
| 59 | + void requestStopOfSync(); |
| 60 | + |
| 61 | + |
| 62 | + /** |
| 63 | + * Returns whether the matrix client is currently connected to the server or not. |
| 64 | + * |
| 65 | + * @return {@code true} if the client is currently connected to the server, {@code false} otherwise. |
| 66 | + */ |
| 67 | + boolean isConnected(); |
| 68 | + |
| 69 | + /** |
| 70 | + * Returns the current state of the matrix client. |
| 71 | + * |
| 72 | + * @return A {@link MatrixState} object if currently connected to a server, {@link Optional#empty()} otherwise. |
| 73 | + */ |
| 74 | + Optional<MatrixState> getState(); |
| 75 | + |
| 76 | + /** |
| 77 | + * Attempts to send a message to the specified room. |
| 78 | + * |
| 79 | + * @param roomId The id of the room to send the message to. |
| 80 | + * @param messageBody The body of the message to send. |
| 81 | + * @return A {@link MatrixEventId} containing the id of the event that was sent or {@link Optional#empty()} if sending the message did not succeed. |
| 82 | + */ |
| 83 | + Optional<MatrixEventId> sendMessage(MatrixRoomId roomId, String messageBody); |
| 84 | + |
| 85 | + /** |
| 86 | + * Attempts to add a reaction to an event (a message of the time). |
| 87 | + * |
| 88 | + * @param roomId The id of the room to send the message in. |
| 89 | + * @param eventId The id of the event to react to. |
| 90 | + * @param reaction The reaction to send. |
| 91 | + * @return A {@link MatrixEventId} containing the id of the event that was sent or {@link Optional#empty()} if sending the reaction did not succeed. |
| 92 | + */ |
| 93 | + Optional<MatrixEventId> addReaction(MatrixRoomId roomId, MatrixEventId eventId, String reaction); |
| 94 | + |
| 95 | + /** |
| 96 | + * Attempts to join a room. |
| 97 | + * |
| 98 | + * @param roomId The id of the room to join. |
| 99 | + * @return {@code true} if joining the room was successful, {@code false} otherwise. |
| 100 | + */ |
| 101 | + boolean joinRoom(MatrixRoomId roomId); |
| 102 | + |
| 103 | + /** |
| 104 | + * Attempts to leave a room. |
| 105 | + * |
| 106 | + * @param roomId The id of the room to leave. |
| 107 | + * @return {@code true} if leaving the room was successful, {@code false} otherwise. |
| 108 | + */ |
| 109 | + boolean leaveRoom(MatrixRoomId roomId); |
160 | 110 | }
|
0 commit comments