Index: src/MTXClient.h ================================================================== --- src/MTXClient.h +++ src/MTXClient.h @@ -34,15 +34,15 @@ */ typedef void (^mtx_client_login_block_t)(MTXClient *_Nullable client, id _Nullable exception); /** - * @brief A block called when the device was logged out. + * @brief A block called when the response for an operation was received. * * @param exception `nil` on success, otherwise an exception */ -typedef void (^mtx_client_logout_block_t)(id _Nullable exception); +typedef void (^mtx_client_response_block_t)(id _Nullable exception); /** * @brief A block called when the room list was fetched. * * @param rooms An array of joined rooms, or nil on error @@ -59,17 +59,10 @@ * @param exception An exception if joining the room failed */ typedef void (^mtx_client_room_join_block_t)(OFString *_Nullable roomID, id _Nullable exception); -/** - * @brief A block called when a room was left. - * - * @param exception An exception if leaving the room failed - */ -typedef void (^mtx_client_room_leave_block_t)(id _Nullable exception); - /** * @brief A class that represents a client. */ @interface MTXClient: OFObject /** @@ -131,11 +124,11 @@ * * @warning The client can no longer be used after this succeeded! * * @param block A block to call when logging out succeeded or failed */ -- (void)logOutWithBlock: (mtx_client_logout_block_t)block; +- (void)logOutWithBlock: (mtx_client_response_block_t)block; /** * @brief Fetches the list of joined rooms. * * @param block A block to call with the list of joined room @@ -156,9 +149,20 @@ * * @param roomID The room ID to leave * @param block A block to call when the room was left */ - (void)leaveRoom: (OFString *)roomID - block: (mtx_client_room_leave_block_t)block; + block: (mtx_client_response_block_t)block; + +/** + * @brief Sends the specified message to the specified room ID. + * + * @param message The message to send + * @param roomID The room ID to which to send the message + * @param block A block to call when the message was sent + */ +- (void)sendMessage: (OFString *)message + roomID: (OFString *)roomID + block: (mtx_client_response_block_t)block; @end OF_ASSUME_NONNULL_END Index: src/MTXClient.m ================================================================== --- src/MTXClient.m +++ src/MTXClient.m @@ -26,10 +26,11 @@ #import "MTXFetchRoomListFailedException.h" #import "MTXJoinRoomFailedException.h" #import "MTXLeaveRoomFailedException.h" #import "MTXLoginFailedException.h" #import "MTXLogoutFailedException.h" +#import "MTXSendMessageFailedException.h" static void validateHomeserver(OFURL *homeserver) { if (![homeserver.scheme isEqual: @"http"] && @@ -186,11 +187,11 @@ return [MTXRequest requestWithPath: path accessToken: _accessToken homeserver: _homeserver]; } -- (void)logOutWithBlock: (mtx_client_logout_block_t)block +- (void)logOutWithBlock: (mtx_client_response_block_t)block { void *pool = objc_autoreleasePoolPush(); MTXRequest *request = [self requestWithPath: @"/_matrix/client/r0/logout"]; request.method = OF_HTTP_REQUEST_METHOD_POST; @@ -288,11 +289,11 @@ objc_autoreleasePoolPop(pool); } - (void)leaveRoom: (OFString *)roomID - block: (mtx_client_room_leave_block_t)block + block: (mtx_client_response_block_t)block { void *pool = objc_autoreleasePoolPush(); MTXRequest *request = [self requestWithPath: [OFString stringWithFormat: @"/_matrix/client/r0/rooms/%@/leave", roomID]]; request.method = OF_HTTP_REQUEST_METHOD_POST; @@ -309,12 +310,48 @@ statusCode: statusCode response: response client: self]); return; } + + block(nil); + }]; + + objc_autoreleasePoolPop(pool); +} + +- (void)sendMessage: (OFString *)message + roomID: (OFString *)roomID + block: (mtx_client_response_block_t)block; +{ + void *pool = objc_autoreleasePoolPush(); + OFString *path = [OFString stringWithFormat: + @"/_matrix/client/r0/rooms/%@/send/m.room.message", roomID]; + MTXRequest *request = [self requestWithPath: path]; + request.method = OF_HTTP_REQUEST_METHOD_POST; + request.body = @{ + @"msgtype": @"m.text", + @"body": message + }; + [request performWithBlock: ^ (mtx_response_t response, int statusCode, + id exception) { + if (exception != nil) { + block(exception); + return; + } + + if (statusCode != 200) { + block([MTXSendMessageFailedException + exceptionWithMessage: message + roomID: roomID + statusCode: statusCode + response: response + client: self]); + return; + } block(nil); }]; objc_autoreleasePoolPop(pool); } @end ADDED src/exceptions/MTXSendMessageFailedException.h Index: src/exceptions/MTXSendMessageFailedException.h ================================================================== --- src/exceptions/MTXSendMessageFailedException.h +++ src/exceptions/MTXSendMessageFailedException.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020, Jonathan Schleifer + * + * https://fossil.nil.im/objmatrix + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import + +#import "MTXClientException.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface MTXSendMessageFailedException: MTXClientException +@property (readonly, nonatomic) OFString *message; +@property (readonly, nonatomic) OFString *roomID; + ++ (instancetype)exceptionWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client OF_UNAVAILABLE; ++ (instancetype)exceptionWithMessage: (OFString *)message + roomID: (OFString *)roomID + statusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client; +- (instancetype)initWithStatusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client OF_UNAVAILABLE; +- (instancetype)initWithMessage: (OFString *)message + roomID: (OFString *)roomID + statusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client OF_DESIGNATED_INITIALIZER; +@end + +OF_ASSUME_NONNULL_END ADDED src/exceptions/MTXSendMessageFailedException.m Index: src/exceptions/MTXSendMessageFailedException.m ================================================================== --- src/exceptions/MTXSendMessageFailedException.m +++ src/exceptions/MTXSendMessageFailedException.m @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, Jonathan Schleifer + * + * https://fossil.nil.im/objmatrix + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "MTXSendMessageFailedException.h" + +#import "MTXClient.h" + +@implementation MTXSendMessageFailedException ++ (instancetype)exceptionWithMessage: (OFString *)message + roomID: (OFString *)roomID + statusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client +{ + return [[[self alloc] initWithMessage: message + roomID: roomID + statusCode: statusCode + response: response + client: client] autorelease]; +} + +- (instancetype)initWithMessage: (OFString *)message + roomID: (OFString *)roomID + statusCode: (int)statusCode + response: (mtx_response_t)response + client: (MTXClient *)client +{ + self = [super initWithStatusCode: statusCode + response: response + client: client]; + + @try { + _message = [message copy]; + _roomID = [roomID copy]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_message release]; + [_roomID release]; + + [super dealloc]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"Failed to send message to room %@ for %@: %@", + _roomID, self.client.userID, self.response]; +} +@end Index: src/exceptions/Makefile ================================================================== --- src/exceptions/Makefile +++ src/exceptions/Makefile @@ -6,12 +6,13 @@ SRCS = MTXClientException.m \ MTXFetchRoomListFailedException.m \ MTXJoinRoomFailedException.m \ MTXLeaveRoomFailedException.m \ MTXLoginFailedException.m \ - MTXLogoutFailedException.m + MTXLogoutFailedException.m \ + MTXSendMessageFailedException.m INCLUDES = ${SRCS:.m=.h} include ../../buildsys.mk CPPFLAGS += -I. -I.. Index: tests/tests.m ================================================================== --- tests/tests.m +++ tests/tests.m @@ -88,10 +88,27 @@ [OFApplication terminateWithStatus: 1]; } of_log(@"Joined room %@", roomID); + [self sendMessage: roomID]; + }]; +} + +- (void)sendMessage: (OFString *)roomID +{ + [_client sendMessage: @"ObjMatrix test successful!" + roomID: roomID + block: ^ (id exception) { + if (exception != nil) { + of_log(@"Failed to send message to room %@: %@", + roomID, exception); + [OFApplication terminateWithStatus: 1]; + } + + of_log(@"Message sent to %@", roomID); + [self leaveRoom: roomID]; }]; } - (void)leaveRoom: (OFString *)roomID