### Create Message with Tapback - Objective-C Source: https://docs.bluebubbles.app/private-api/imcore-documentation Creates a new message, potentially with a tapback (reaction). It handles different message types, including attachments, and constructs a message summary for display. Requires BlueBubblesHelper for reaction conversion and message GUID generation. ```objectivec createMessage(newAttributedString, subjectAttributedString, effectId, nil, [NSString stringWithFormat:@"bp:%@", [message guid]], &reactionLong, [item messagePartRange], messageSummary, nil, false); } else { createMessage(newAttributedString, subjectAttributedString, effectId, nil, [NSString stringWithFormat:@"p:%@/%@", data[@"partIndex"], [message guid]], &reactionLong, [item messagePartRange], messageSummary, nil, false); } } } else { messageSummary = @{@"amc":@1,@"ams":message.text.string}; NSData *dataenc = [[message text].string dataUsingEncoding:NSNonLossyASCIIStringEncoding]; NSString *encodevalue = [[NSString alloc]initWithData:dataenc encoding:NSUTF8StringEncoding]; NSRange range = NSMakeRange(0, [message text].string.length); if ([encodevalue isEqualToString:@"\ufffc"] || [encodevalue length] == 0) { NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:(@"an attachment")]]; createMessage(newAttributedString, subjectAttributedString, effectId, nil, [message guid], &reactionLong, range, @{}, nil, false); } else { NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:([NSString stringWithFormat:(@"“%@”"), [message text].string])]]; createMessage(newAttributedString, subjectAttributedString, effectId, nil, [message guid], &reactionLong, range, messageSummary, nil, false); } } }]; ``` -------------------------------- ### Get IMMessage for Reply/Tapback (Objective-C) Source: https://docs.bluebubbles.app/private-api/imcore-documentation Asynchronously retrieves an `IMMessage` object using its GUID, which is necessary for creating replies or tapbacks. It utilizes `IMChatHistoryController` and requires a completion block to handle the result. ```objectivec +(void) getMessageItem:(IMChat *)chat :(NSString *)actionMessageGuid completionBlock:(void (^)(IMMessage *message))block { [[IMChatHistoryController sharedInstance] loadMessageWithGUID:(actionMessageGuid) completionBlock:^(IMMessage *message) { block(message); }]; } ``` -------------------------------- ### Get IMChat Object by GUID Source: https://docs.bluebubbles.app/private-api/imcore-documentation Retrieves an existing IMChat instance using its unique GUID. Requires the IMChatRegistry.h header. Returns an IMChat object or nil if the GUID is invalid. ```objectivec // Retrieve a IMChat instance from a given guid // // Uses the chat registry to get an existing instance of a chat based on the chat guid +(IMChat *) getChat: (NSString *) guid { if(guid == nil) return nil; IMChat* imChat = [[IMChatRegistry sharedInstance] existingChatWithGUID: guid]; return imChat; } ``` -------------------------------- ### Reboot to Recovery Mode (Intel Macs) Source: https://docs.bluebubbles.app/private-api/installation These commands reboot an Intel-based Mac into recovery mode, which is necessary for disabling System Integrity Protection (SIP). This action will instantly reboot your machine, so save all work beforehand. This is intended for official macOS installations. ```bash sudo nvram internet-recovery-mode=RecoveryModeDisk sudo reboot recovery ``` -------------------------------- ### Delete Chat (Objective-C) Source: https://docs.bluebubbles.app/private-api/imcore-documentation Deletes a chat and all its associated messages permanently. It retrieves the chat using its GUID and then uses the `IMChatRegistry` to remove it. This action is irreversible. ```objectivec // Get the chat IMChat *chat = [BlueBubblesHelper getChat: data[@"chatGuid"] :transaction]; // Use the chat registry to remove the chat if (chat != nil) { [[IMChatRegistry sharedInstance] _chat_remove:(chat)]; } ``` -------------------------------- ### Send Tapback (macOS 11+) - Objective-C Source: https://docs.bluebubbles.app/private-api/imcore-documentation This Objective-C code snippet demonstrates sending a Tapback on macOS 11 and later. It includes logic for creating an attributed string for the message content and defines a block for sending the message. It handles attachments and constructs the message summary accordingly. ```objectivec if (attributedString == nil) { NSString *message = data[@"message"]; // Tapbacks will not have message text, but messages sent must have some sort of text if (message == nil) { message = @"TEMP"; } attributedString = [[NSMutableAttributedString alloc] initWithString: message]; } void (^createMessage)(NSAttributedString*, NSAttributedString*, NSString*, NSString*, NSString*, long long*, NSRange, NSDictionary*, NSArray*, BOOL) = ^(NSAttributedString *message, NSAttributedString *subject, NSString *effectId, NSString *threadIdentifier, NSString *associatedMessageGuid, long long *reaction, NSRange range, NSDictionary *summaryInfo, NSArray *transferGUIDs, BOOL isAudioMessage) { messageToSend = [messageToSend initWithSender:(nil) time:(nil) text:(message) messageSubject:(subject) fileTransferGUIDs:(nil) flags:(5) error:(nil) guid:(nil) subject:(nil) associatedMessageGUID:(associatedMessageGuid) associatedMessageType:*(reaction) associatedMessageRange:(range) messageSummaryInfo:(summaryInfo)]; [chat sendMessage:(messageToSend)]; }; [BlueBubblesHelper getMessageItem:(chat) :(data[@"selectedMessageGuid"]) completionBlock:^(IMMessage *message) { IMMessageItem *messageItem = (IMMessageItem *)message._imMessageItem; NSObject *items = messageItem._newChatItems; IMMessagePartChatItem *item; // sometimes items is an array so we need to account for that if ([items isKindOfClass:[NSArray class]]) { for (IMMessagePartChatItem *i in (NSArray *) items) { if ([i index] == [data[@"partIndex"] integerValue]) { item = i; break; } } } else { item = (IMMessagePartChatItem *)items; } NSString *reaction = data[@"reactionType"]; long long reactionLong = [BlueBubblesHelper parseReactionType:(reaction)]; NSDictionary *messageSummary; // if we actually got an item, proceed, otherwise use a fallback if (item != nil) { NSAttributedString *text = [item text]; if (text == nil) { text = [message text]; } messageSummary = @{@"amc":@1,@"ams":text.string}; // Send the tapback // check if the body happens to be an object (ie an attachment) and send the tapback accordingly to show the proper summary NSData *dataenc = [text.string dataUsingEncoding:NSNonLossyASCIIStringEncoding]; NSString *encodevalue = [[NSString alloc]initWithData:dataenc encoding:NSUTF8StringEncoding]; if ([encodevalue isEqualToString:@"\\ufffc"]) { NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:( @"an attachment")]]; createMessage(newAttributedString, subjectAttributedString, effectId, nil, [NSString stringWithFormat:@"p:%@/%@", data[@"partIndex"], [message guid]], &reactionLong, [item messagePartRange], @{}, nil, false); } else { NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:([NSString stringWithFormat:( @"%@"), text.string])]]; if ([item text] == nil) { ``` -------------------------------- ### Check SIP Status (macOS) Source: https://docs.bluebubbles.app/private-api/installation This command checks the current status of System Integrity Protection (SIP) on your macOS system. It is useful for troubleshooting and verifying if SIP has been successfully disabled or is still enabled. The output should be checked and provided when seeking further assistance. ```bash csrutil status ``` -------------------------------- ### Reboot to Recovery Mode (Apple Silicon Macs) Source: https://docs.bluebubbles.app/private-api/installation This procedure forces an Apple Silicon Mac to reboot into recovery mode by holding the power button during startup. This is a necessary step for disabling System Integrity Protection (SIP) on these devices. Follow the on-screen prompts to access startup options and then recovery. ```bash # Shut down the Mac normally # Press and hold the power button on your Mac until you see "Loading startup options." # Click Options, then click Continue, and enter the admin password if requested. ``` -------------------------------- ### Send Message with Effects and Replies (Objective-C) Source: https://docs.bluebubbles.app/private-api/imcore-documentation This Objective-C code snippet demonstrates how to send a message using the BlueBubbles API. It handles constructing messages with attributes like mentions, subjects, effects, and replies to existing threads. It accounts for variations in how message items are returned and generates thread identifiers as needed. This function is intended for use on Big Sur and later. ```objectivec BlueBubblesHelper getMessageItem:(chat) :(data[@"selectedMessageGuid"]) completionBlock:^(IMMessage *message) { IMMessageItem *messageItem = (IMMessageItem *)message._imMessageItem; NSObject *items = messageItem._newChatItems; IMMessagePartChatItem *item; // sometimes items is an array so we need to account for that if ([items isKindOfClass:[NSArray class]]) { for(IMMessagePartChatItem* imci in (NSArray *)items) { if([imci._item.guid isEqualToString:(data[@"selectedMessageGuid"])]) { item = imci; } } } else { item = (IMMessagePartChatItem *)items; } NSString *identifier = @""; // either reply to an existing thread or create a new thread if (message.threadIdentifier != nil) { identifier = message.threadIdentifier; } else { identifier = IMCreateThreadIdentifierForMessagePartChatItem(item); } createMessage(attributedString, subjectAttributedString, effectId, identifier); }]; // otherwise just send a regular message } else { createMessage(attributedString, subjectAttributedString, effectId, nil); } ``` -------------------------------- ### Send Tapback (macOS 10) - Objective-C Source: https://docs.bluebubbles.app/private-api/imcore-documentation This Objective-C code snippet demonstrates how to send a Tapback (message reaction) on macOS 10. It maps the reaction type, retrieves the target message item, and sends the acknowledgment with message summary information. It handles cases where the message body might be an attachment. ```objectivec //Map the reaction type long long reactionLong = [BlueBubblesHelper parseReactionType:(data[@"reactionType"])]; // Get the messageItem [BlueBubblesHelper getMessageItem:(chat) :(data[@"selectedMessageGuid"]) completionBlock:^(IMMessage *message) { IMMessageItem *imMessage = (IMMessageItem *)message._imMessageItem; NSObject *items = imMessage._newChatItems; IMChatItem *item; // sometimes items is an array so we need to account for that if ([items isKindOfClass:[NSArray class]]) { for(IMChatItem* imci in (NSArray *)items) { if([imci._item.guid isEqualToString:(data[@"selectedMessageGuid"])]) { item = imci; } } } else { item = (IMChatItem *)items; } //Build the message summary NSDictionary *messageSummary = @{@"amc":@1,@"ams":[imMessage body].string}; // Send the tapback // check if the body happens to be an object (ie an attachment) and send the tapback accordingly to show the proper summary NSData *dataenc = [[imMessage body].string dataUsingEncoding:NSNonLossyASCIIStringEncoding]; NSString *encodevalue = [[NSString alloc]initWithData:dataenc encoding:NSUTF8StringEncoding]; if ([encodevalue isEqualToString:@"\\ufffc"]) { [chat sendMessageAcknowledgment:(reactionLong) forChatItem:(item) withMessageSummaryInfo:(@{})]; } else { [chat sendMessageAcknowledgment:(reactionLong) forChatItem:(item) withMessageSummaryInfo:(messageSummary)]; } }]; ``` -------------------------------- ### Send Message (macOS 11+, With Replies) Source: https://docs.bluebubbles.app/private-api/imcore-documentation This Objective-C code snippet demonstrates sending a message with reply functionality on macOS 11 and later. It includes handling attributed bodies, subjects, and effect IDs, similar to the macOS 10 version. A key addition is the `messageToSend.threadIdentifier` property, enabling replies. ```objectivec // now we will deserialize the attributedBody if it exists NSDictionary *attributedDict = data[@"attributedBody"]; // we'll create the NSMutableAttributedString with the associatedBody string if we can, // else we'll fall back to using the message text NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: data[@"message"]]; // if associateBody exists, we iterate through it if (attributedDict != NULL && attributedDict != (NSDictionary*)[NSNull null]) { attributedString = [[NSMutableAttributedString alloc] initWithString: attributedDict[@"string"]]; NSArray *attrs = attributedDict[@"runs"]; for(NSDictionary *dict in attrs) { // construct range and attributes from dict and add to NSMutableAttributedString NSArray *rangeArray = dict[@"range"]; NSRange range = NSMakeRange([(NSNumber*)[rangeArray objectAtIndex:0] intValue], [(NSNumber*)[rangeArray objectAtIndex:1] intValue]); NSDictionary *attrsDict = dict[@"attributes"]; [attributedString addAttributes:attrsDict range:range]; } } // if we've got a subject, make the string NSMutableAttributedString *subjectAttributedString = nil; if (data[@"subject"] != [NSNull null] && [data[@"subject"] length] != 0) { subjectAttributedString = [[NSMutableAttributedString alloc] initWithString: data[@"subject"]]; } // if we've got an effect ID, make the string NSString *effectId = nil; if (data[@"effectId"] != [NSNull null] && [data[@"effectId"] length] != 0) { effectId = data[@"effectId"]; } // function we can call to create the message easily void (^createMessage)(NSAttributedString*, NSAttributedString*, NSString*, NSString*) = ^(NSAttributedString *message, NSAttributedString *subject, NSString *effectId, NSString *threadIdentifier) { IMMessage *messageToSend = [[IMMessage alloc] init]; messageToSend = [messageToSend initWithSender:(nil) time:(nil) text:(message) messageSubject:(subject) fileTransferGUIDs:(nil) flags:(100005) error:(nil) guid:(nil) subject:(nil) balloonBundleID:(nil) payloadData:(nil) expressiveSendStyleID:(effectId)]; // remove this line of code if not on Big Sur and up!! messageToSend.threadIdentifier = threadIdentifier; [chat sendMessage:(messageToSend)]; }; // create a thread identifier if we are replying (ONLY WORKS ON BIG SUR) if (data[@"selectedMessageGuid"] != [NSNull null] && [data[@"selectedMessageGuid"] length] != 0) { ``` -------------------------------- ### Start/Stop Typing Indicator in Chat Source: https://docs.bluebubbles.app/private-api/imcore-documentation Sets the local user's typing status for a given chat. This is a simple boolean toggle to indicate if the user is currently typing or has stopped typing. ```objectivec [chat setLocalUserIsTyping:YES]; [chat setLocalUserIsTyping:NO]; ``` -------------------------------- ### Listen to Typing Indicator Status with ZKSwizzle Source: https://docs.bluebubbles.app/private-api/imcore-documentation Intercepts typing indicator events using ZKSwizzle to provide more reliable typing status updates. It handles both incoming typing messages and cancellation of typing indicators, with added checks to prevent timing issues and infinite typing indicators. ```objectivec // Credit to w0lf // Handles all of the incoming typing events ZKSwizzleInterface(BBH_IMMessageItem, IMMessageItem, NSObject) @implementation BBH_IMMessageItem - (BOOL)isCancelTypingMessage { // isCancelTypingMessage seems to also have some timing issues and adding a delay would fix this // But I would rather not rely on delays to have this program work properly // // We would rather that the typing message be cancelled prematurely rather // than having the typing indicator stuck permanently NSString *guid = [self getGuid]; if(guid != nil) { if([self isLatestMessage]) { // handle stopped typing status here } } return ZKOrig(BOOL); } - (BOOL)isIncomingTypingMessage { // We do this because the isIncomingTypingMessage seems to have some timing // issues and will sometimes notify after the isCancelTypingMessage so we need to confirm // that the sender actually is typing [self updateTypingState]; // This is here to ensure that no infinite typing occurs // If for whatever reason the isCancelTypingMessage does not occur, // this should catch the error in 2 seconds double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ if(self != nil) { NSString *guid = [self getGuid]; if(guid != nil) { if([BlueBubblesHelper isTyping:guid] == NO) { // handle stopped typing status here } } } }); return ZKOrig(BOOL); } // Check to see if this IMMessageItem matches the last IMChat's message // This helps to avoid spamming of the tcp socket - (BOOL) isLatestMessage { NSString *guid = [self getGuid]; // Fetch the current IMChat to get the IMMessage IMChat *chat = [BlueBubblesHelper getChat:guid]; IMMessageItem *item = (IMMessageItem*) self; IMMessage *message = item.message; if(message.isFromMe) return NO; // If the IMChat's last message matches our own IMMessage, then we can proceed // this should avoid spamming of the tcp socket return chat.lastIncomingMessage.guid == message.guid; } // Update the typing state by checking the message state - (void) updateTypingState { if(![self isLatestMessage]) return; NSString *guid = [self getGuid]; // If we failed to get the guid for whatever reason, then we can't do anything if(guid != nil) { IMChat *chat = [BlueBubblesHelper getChat:guid]; // Send out the correct response over the tcp socket if(chat.lastIncomingMessage.isTypingMessage == YES) { // handle started typing status here } else { // handle stopped typing status here } } } @end ``` -------------------------------- ### Send Message (macOS 10, No Replies) Source: https://docs.bluebubbles.app/private-api/imcore-documentation This Objective-C code snippet demonstrates how to send a message without reply functionality on macOS 10. It handles deserializing attributed bodies, creating attributed strings for messages and subjects, and setting effect IDs. The `createMessage` block constructs and sends the `IMMessage` object. ```objectivec // now we will deserialize the attributedBody if it exists NSDictionary *attributedDict = data[@"attributedBody"]; // we'll create the NSMutableAttributedString with the associatedBody string if we can, // else we'll fall back to using the message text NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: data[@"message"]]; // if associateBody exists, we iterate through it if (attributedDict != NULL && attributedDict != (NSDictionary*)[NSNull null]) { attributedString = [[NSMutableAttributedString alloc] initWithString: attributedDict[@"string"]]; NSArray *attrs = attributedDict[@"runs"]; for(NSDictionary *dict in attrs) { // construct range and attributes from dict and add to NSMutableAttributedString NSArray *rangeArray = dict[@"range"]; NSRange range = NSMakeRange([(NSNumber*)[rangeArray objectAtIndex:0] intValue], [(NSNumber*)[rangeArray objectAtIndex:1] intValue]); NSDictionary *attrsDict = dict[@"attributes"]; [attributedString addAttributes:attrsDict range:range]; } } NSMutableAttributedString *subjectAttributedString = nil; if (data[@"subject"] != [NSNull null] && [data[@"subject"] length] != 0) { subjectAttributedString = [[NSMutableAttributedString alloc] initWithString: data[@"subject"]]; } NSString *effectId = nil; if (data[@"effectId"] != [NSNull null] && [data[@"effectId"] length] != 0) { effectId = data[@"effectId"]; } void (^createMessage)(NSAttributedString*, NSAttributedString*, NSString*, NSString*) = ^(NSAttributedString *message, NSAttributedString *subject, NSString *effectId, NSString *threadIdentifier) { IMMessage *messageToSend = [[IMMessage alloc] init]; messageToSend = [messageToSend initWithSender:(nil) time:(nil) text:(message) messageSubject:(subject) fileTransferGUIDs:(nil) flags:(100005) error:(nil) guid:(nil) subject:(nil) balloonBundleID:(nil) payloadData:(nil) expressiveSendStyleID:(effectId)]; [chat sendMessage:(messageToSend)]; if (transaction != nil) { [[NetworkController sharedInstance] sendMessage: @{@"transactionId": transaction, @"identifier": [[chat lastMessage] guid]}]; } }; createMessage(attributedString, subjectAttributedString, effectId, nil); ``` -------------------------------- ### Add or Remove Participants from Group Chat (Objective-C) Source: https://docs.bluebubbles.app/private-api/imcore-documentation This function allows adding or removing participants from an iMessage group chat. It requires IMHandle objects for the participants and checks if they can be added/removed. The `reason` argument must be 0. Note: Participants must exist in `chat.db`. ```objectivec NSArray *handles = [[IMHandleRegistrar sharedInstance] getIMHandlesForID:(@"some address here")]; // when removing participants, you don't need to do this if block if (handles != nil) { IMAccountController *sharedAccountController = [IMAccountController sharedInstance]; IMAccount *myAccount = [sharedAccountController mostLoggedInAccount]; IMHandle *handle = [[IMHandle alloc] initWithAccount:(myAccount) ID:(@"some address here") alreadyCanonical:(YES)]; handles = @[handle]; } if([chat canAddParticipants:(handles)]) { // to add [chat inviteParticipantsToiMessageChat:(handles) reason:(0)]; // to remove [chat removeParticipantsFromiMessageChat:(handles) reason:(0)]; } ``` -------------------------------- ### Parse Tapback Reaction Type to ID (Objective-C) Source: https://docs.bluebubbles.app/private-api/imcore-documentation This Objective-C function, `parseReactionType`, converts human-readable tapback reaction types (e.g., 'love', 'like') into their corresponding integer IDs used by iMessage. It handles both positive and negative reactions by checking the lowercase version of the input string. If the reaction type is not recognized, it returns 0. ```objectivec +(long long) parseReactionType:(NSString *)reactionType { NSString *lowerCaseType = [reactionType lowercaseString]; if([@"love" isEqualToString:(lowerCaseType)]) return 2000; if([@"like" isEqualToString:(lowerCaseType)]) return 2001; if([@"dislike" isEqualToString:(lowerCaseType)]) return 2002; if([@"laugh" isEqualToString:(lowerCaseType)]) return 2003; if([@"emphasize" isEqualToString:(lowerCaseType)]) return 2004; if([@"question" isEqualToString:(lowerCaseType)]) return 2005; if([@"-love" isEqualToString:(lowerCaseType)]) return 3000; if([@"-like" isEqualToString:(lowerCaseType)]) return 3001; if([@"-dislike" isEqualToString:(lowerCaseType)]) return 3002; if([@"-laugh" isEqualToString:(lowerCaseType)]) return 3003; if([@"-emphasize" isEqualToString:(lowerCaseType)]) return 3004; if([@"-question" isEqualToString:(lowerCaseType)]) return 3005; return 0; } ``` -------------------------------- ### Disable SIP (macOS Recovery) Source: https://docs.bluebubbles.app/private-api/installation This command disables System Integrity Protection (SIP) when executed from the Terminal within macOS Recovery Mode. SIP must be disabled to enable certain functionalities required by the Private API. After execution, your Mac should be restarted. ```bash csrutil disable ``` -------------------------------- ### Update Chat Pinned Status (Objective-C) Source: https://docs.bluebubbles.app/private-api/imcore-documentation Toggles the pinned status of a chat. It retrieves the list of pinned conversations, adds or removes the chat's pinning identifier, and updates the controller. The `withUpdateReason` argument must be 'contextMenu'. This code is only compatible with macOS Big Sur. ```objectivec // if the chat is pinned, unpin it, otherwise pin it if (!chat.isPinned) { // get the pinned conversation set, make it mutable, and then add the chat to be pinned NSArray* arr = [[[IMPinnedConversationsController sharedInstance] pinnedConversationIdentifierSet] array]; NSMutableArray* chatArr = [[NSMutableArray alloc] initWithArray:(arr)]; [chatArr addObject:(chat.pinningIdentifier)]; // convert mutable back to immutable NSArray* finalArr = [chatArr copy]; // update the pinned conversation array IMPinnedConversationsController* controller = [IMPinnedConversationsController sharedInstance]; // contextMenu is an arbitrary value, other values may work as well [controller setPinnedConversationIdentifiers:(finalArr) withUpdateReason:( @"contextMenu")]; } else { // get the pinned conversation set, make it mutable, and then remove the chat to be unpinned NSArray* arr = [[[IMPinnedConversationsController sharedInstance] pinnedConversationIdentifierSet] array]; NSMutableArray* chatArr = [[NSMutableArray alloc] initWithArray:(arr)]; [chatArr removeObject:(chat.pinningIdentifier)]; // convert mutable back to immutable NSArray* finalArr = [chatArr copy]; // update the pinned conversation array IMPinnedConversationsController* controller = [IMPinnedConversationsController sharedInstance]; // contextMenu is an arbitrary value, other values may work as well [controller setPinnedConversationIdentifiers:(finalArr) withUpdateReason:( @"contextMenu")]; } ``` -------------------------------- ### Disable Library Validation (macOS) Source: https://docs.bluebubbles.app/private-api/installation This command disables library validation on macOS, a prerequisite for enabling the Private API. It requires administrator privileges and prompts for a password. Ensure you are running this in the Terminal application. ```bash sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation -bool true ``` -------------------------------- ### Unsend Message Objective-C Source: https://docs.bluebubbles.app/private-api/imcore-documentation This Objective-C code snippet demonstrates how to unsend a message using the BlueBubbles private API. It retrieves the chat and message, identifies the specific message part, and then uses the `retractMessagePart` method to unsend it. The `partIndex` is critical for correctly identifying the message part to be retracted. This functionality may fail for older messages where the `IMMessagePartChatItem` might not be available. ```objectivec // get the chat IMChat *chat = [BlueBubblesHelper getChat: data[@"chatGuid"] :transaction]; // get the message [BlueBubblesHelper getMessageItem:(chat) :(data[@"messageGuid"]) completionBlock:^(IMMessage *message) { // find the message item corresponding to the part index IMMessageItem *messageItem = (IMMessageItem *)message._imMessageItem; NSObject *items = messageItem._newChatItems; IMMessagePartChatItem *item; // sometimes items is an array so we need to account for that if ([items isKindOfClass:[NSArray class]]) { for (IMMessagePartChatItem *i in (NSArray *) items) { if ([i index] == [data[@"partIndex"] integerValue]) { item = i; break; } } } else { item = (IMMessagePartChatItem *)items; } // retract (unsend) the message [chat retractMessagePart:(item)]; }]; ``` -------------------------------- ### Mark Chat as Read Source: https://docs.bluebubbles.app/private-api/imcore-documentation Marks all messages in a given chat as read, removing the unread indicator in the macOS iMessage client. This is a straightforward method call on the chat object. ```objectivec [chat markAllMessagesAsRead]; ``` -------------------------------- ### Change Group Chat Name Source: https://docs.bluebubbles.app/private-api/imcore-documentation Sets a new display name for a group chat. This method is prefixed with an underscore, suggesting it might be an internal or less commonly used API. ```objectivec [chat _setDisplayName:(@"new name here")]; ``` -------------------------------- ### Mark Chat as Unread Source: https://docs.bluebubbles.app/private-api/imcore-documentation Marks the last message in a given chat as unread, adding an unread indicator to the chat in the macOS iMessage client. This method requires macOS Ventura (13.0) or higher. ```objectivec [chat markLastMessageAsUnread]; ``` -------------------------------- ### Check if Receiving Typing Indicator Source: https://docs.bluebubbles.app/private-api/imcore-documentation Determines if the last incoming message in a chat is a typing indicator. Returns a boolean value. Note: This does not provide live updates and may require ZKSwizzle for real-time status. ```objectivec chat.lastIncomingMessage.isTypingMessage ``` -------------------------------- ### Edit Message - Objective-C Source: https://docs.bluebubbles.app/private-api/imcore-documentation Edits an existing message in a chat. It requires the chat object, the message to be edited, the part index of the message segment to change, and the new attributed strings for the message body and backward compatibility. Uses BlueBubblesHelper to retrieve chat and message objects. ```objectivec // get the chat IMChat *chat = [BlueBubblesHelper getChat: data[@"chatGuid"] :transaction]; // get the message [BlueBubblesHelper getMessageItem:(chat) :(data[@"messageGuid"]) completionBlock:^(IMMessage *message) { // generate the two NSMutableAttributedStrings NSMutableAttributedString *editedString = [[NSMutableAttributedString alloc] initWithString: data[@"editedMessage"]]; NSMutableAttributedString *bcString = [[NSMutableAttributedString alloc] initWithString: data[@"backwardsCompatibilityMessage"]]; NSInteger index = data["@partIndex"]; // send the edit [chat editMessage:(message) atPartIndex:(index) withNewPartText:(editedString) backwardCompatabilityText:(bcString)]; }]; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.