├── .github └── workflows │ └── test.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.sh ├── docs ├── binary │ └── img │ │ ├── icloud_download.png │ │ └── safari_local_file_restrictions.png ├── diagnostics.md ├── faq.md ├── features.md ├── hero.png └── tables │ ├── attachment.md │ ├── chat.md │ ├── duplicates.md │ ├── handle.md │ └── messages.md ├── imessage-database ├── Cargo.toml ├── README.md ├── build.rs ├── src │ ├── error │ │ ├── attachment.rs │ │ ├── handwriting.rs │ │ ├── message.rs │ │ ├── mod.rs │ │ ├── plist.rs │ │ ├── query_context.rs │ │ ├── streamtyped.rs │ │ ├── table.rs │ │ └── typedstream.rs │ ├── lib.rs │ ├── message_types │ │ ├── app.rs │ │ ├── app_store.rs │ │ ├── collaboration.rs │ │ ├── digital_touch │ │ │ ├── digital_touch.proto │ │ │ ├── digital_touch_proto.rs │ │ │ ├── mod.rs │ │ │ └── models.rs │ │ ├── edited.rs │ │ ├── expressives.rs │ │ ├── handwriting │ │ │ ├── handwriting.proto │ │ │ ├── handwriting_proto.rs │ │ │ ├── mod.rs │ │ │ └── models.rs │ │ ├── mod.rs │ │ ├── music.rs │ │ ├── placemark.rs │ │ ├── sticker.rs │ │ ├── text_effects.rs │ │ ├── url.rs │ │ └── variants.rs │ ├── tables │ │ ├── attachment.rs │ │ ├── chat.rs │ │ ├── chat_handle.rs │ │ ├── handle.rs │ │ ├── messages │ │ │ ├── body.rs │ │ │ ├── message.rs │ │ │ ├── mod.rs │ │ │ ├── models.rs │ │ │ ├── query_parts.rs │ │ │ └── tests │ │ │ │ ├── announcement.rs │ │ │ │ ├── date_tests.rs │ │ │ │ ├── edited_tests.rs │ │ │ │ ├── expressive_tests.rs │ │ │ │ ├── guid_tests.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── query_tests.rs │ │ │ │ └── variant.rs │ │ ├── mod.rs │ │ └── table.rs │ └── util │ │ ├── bundle_id.rs │ │ ├── dates.rs │ │ ├── dirs.rs │ │ ├── mod.rs │ │ ├── output.rs │ │ ├── platform.rs │ │ ├── plist.rs │ │ ├── query_context.rs │ │ ├── size.rs │ │ ├── streamtyped.rs │ │ └── typedstream.rs └── test_data │ ├── app_message │ ├── ApplePayRecurring.plist │ ├── Business.plist │ ├── CheckinEnded.plist │ ├── CheckinLate.plist │ ├── CheckinLocation.plist │ ├── CheckinTimer.plist │ ├── FindMy.plist │ ├── Game.plist │ ├── OpenTableInvited.plist │ ├── Sent265.plist │ └── Slideshow.plist │ ├── app_store │ └── AppStoreLink.plist │ ├── chat_properties │ ├── ChatProp1.plist │ ├── ChatProp2.plist │ ├── ChatProp3.plist │ └── ChatProp4.plist │ ├── collaboration_message │ └── Freeform.plist │ ├── db │ └── test.db │ ├── digital_touch_message │ ├── fireball.bin │ ├── heartbeat.bin │ ├── heartbreak.bin │ ├── kiss.bin │ ├── sketch.bin │ └── tap.bin │ ├── edited_message │ ├── Deleted.plist │ ├── Edited.plist │ ├── EditedAndDeleted.plist │ ├── EditedAndUnsent.plist │ ├── EditedToLink.plist │ ├── EditedToLinkAndBack.plist │ ├── EditedWithFormatting.plist │ └── MultiPartOneDeleted.plist │ ├── handwritten_message │ ├── handwriting.ascii │ ├── handwriting.bin │ ├── handwriting.svg │ ├── handwriting_half.ascii │ ├── hello.ascii │ ├── hello.bin │ ├── hello.svg │ ├── pollock.ascii │ ├── pollock.bin │ ├── pollock.svg │ ├── test.ascii │ ├── test.bin │ └── test.svg │ ├── music_message │ ├── AppleMusic.plist │ └── AppleMusicLyrics.plist │ ├── shared_placemark │ └── SharedPlacemark.plist │ ├── stickers │ ├── comic.heic │ ├── no_effect.heic │ ├── outline.heic │ ├── puffy.heic │ └── shiny.heic │ ├── typedstream │ ├── 35123 │ ├── 0123456789 │ ├── AppMessage │ ├── AppleMusicLyrics │ ├── Array │ ├── Attachment │ ├── AttachmentI16 │ ├── AttributedBodyTextOnly │ ├── AttributedBodyTextOnly2 │ ├── Blank │ ├── Code │ ├── CustomReaction │ ├── Date │ ├── EditedWithFormatting │ ├── Email │ ├── EmojiBoldUnderline │ ├── ExtraData │ ├── Formatted │ ├── LongMessage │ ├── Mention │ ├── MultiAttachment │ ├── MultiPart │ ├── MultiPartWithDeleted │ ├── OverlappingFormat │ ├── PhoneNumber │ ├── SingleLink │ ├── StyledLink │ ├── TextEffects │ ├── TextStyles │ ├── TextStylesMixed │ ├── TextStylesSingleRange │ ├── Transcription │ ├── URL │ ├── URLMessage │ └── WeirdText │ └── url_message │ ├── MetadataURL.plist │ ├── Reminder.plist │ ├── Twitter.plist │ └── URL.plist └── imessage-exporter ├── Cargo.toml ├── README.md └── src ├── app ├── compatibility │ ├── attachment_manager.rs │ ├── backup.rs │ ├── converters │ │ ├── audio.rs │ │ ├── common.rs │ │ ├── image.rs │ │ ├── mod.rs │ │ ├── sticker.rs │ │ └── video.rs │ ├── mod.rs │ └── models.rs ├── error.rs ├── export_type.rs ├── mod.rs ├── options.rs ├── progress.rs ├── runtime.rs └── sanitizers.rs ├── exporters ├── exporter.rs ├── html.rs ├── mod.rs ├── resources │ ├── attachments │ │ ├── boar.png │ │ └── davis.jpeg │ ├── example.html │ └── style.css └── txt.rs └── main.rs /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - release 7 | - develop 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | Test: 14 | name: iMessage Exporter Tests 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - run: rustup update stable && rustup default stable 20 | - run: cargo clippy 21 | - run: cargo build 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Development environment 2 | /.vscode 3 | 4 | # Generated files 5 | /target 6 | /output 7 | 8 | # OS Files 9 | .DS_Store 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["imessage-database", "imessage-exporter"] 4 | 5 | [profile.release] 6 | # Perform Link Time Optimization 7 | lto = true 8 | # Use a single codegen unit for the entire crate 9 | codegen-units = 1 10 | # Do not unwind stack on crash 11 | panic = "abort" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # imessage-exporter 2 | 3 | This crate provides both a library to interact with iMessage data as well as a binary that can perform some useful read-only operations using that data. The aim of this project is to provide the most comprehensive and accurate representation of iMessage data available. 4 | 5 | This free and open-source software can: 6 | 7 | - Save, export, backup, and archive iMessage data to open, portable formats 8 | - Preserve multimedia content (images, videos, audio) from conversations 9 | - Facilitate easy migration of message history between devices and platforms 10 | - Run diagnostics on the iMessage database 11 | - Give you full ownership and control over your communication history 12 | - Support compliance with data retention policies or legal requirements 13 | - Run on macOS, Linux, and Windows 14 | 15 | ## Example Export 16 | 17 | ![HTML Export Sample](/docs/hero.png) 18 | 19 | ## Binary 20 | 21 | The `imessage-exporter` binary exports iMessage data to `txt` or `html` formats. It can also run diagnostics to find problems with the iMessage database. 22 | 23 | Installation instructions for the binary are located [here](imessage-exporter/README.md). 24 | 25 | ## Library 26 | 27 | The `imessage_database` library provides models that allow us to access iMessage information as native, cross-platform data structures. 28 | 29 | Documentation for the library is located [here](imessage-database/README.md). 30 | 31 | ### Supported Features 32 | 33 | This crate supports every iMessage feature as of macOS 15.5 (24F74) and iOS 18.5 (22F75): 34 | 35 | - iMessage, RCS, SMS, and MMS 36 | - Multi-part messages 37 | - Replies/Threads 38 | - Formatted text 39 | - Attachments 40 | - Expressives 41 | - Tapbacks 42 | - Stickers 43 | - Apple Pay 44 | - Group chats 45 | - Digital Touch 46 | - URL Previews 47 | - Audio messages 48 | - App Integrations 49 | - Edited messages 50 | - Handwritten messages 51 | 52 | See more detail about supported features [here](docs/features.md). 53 | 54 | ## Frequently Asked Questions 55 | 56 | The FAQ document is located [here](/docs/faq.md). 57 | 58 | ## Special Thanks 59 | 60 | - All of my friends, for putting up with me sending them random messages to test things 61 | - [SQLiteFlow](https://www.sqliteflow.com), the SQL viewer I used to explore and reverse engineer the iMessage database 62 | - [Xplist](https://github.com/ic005k/Xplist), an invaluable tool for reverse engineering the `payload_data` plist format 63 | - [Compart](https://www.compart.com/en/unicode/), an amazing resource for looking up esoteric unicode details 64 | - [GNU Project](https://github.com/gnustep/libobjc) and [Archive.org](https://archive.org/details/darwin_0.1), for hosting source code referenced to reverse engineer the `typedstream` format 65 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | # Create output directory 2 | mkdir -p output 3 | 4 | # Get latest tags 5 | git pull 6 | 7 | # Get version number from tag name 8 | export VERSION=$(git describe --tags $(git rev-list --tags --max-count=1)) 9 | 10 | if [ -n "$VERSION" ]; then 11 | # Confirm version number with user before build 12 | read -p "Build with version $VERSION? C-c to cancel, any key to continue " -n 1 -r 13 | echo 14 | 15 | # Update version number in Cargo.toml for build 16 | # macOS sed requires the weird empty string param 17 | # Otherwise it returns `invalid command code C` 18 | sed -i '' "s/version = \"0.0.0\"/version = \"$VERSION\"/g" imessage-database/Cargo.toml 19 | 20 | if [ -n "$PUBLISH" ]; then 21 | echo 'Publishing database library...' 22 | cargo publish -p imessage-database --allow-dirty 23 | else 24 | echo 'PUBLISH env var not set!' 25 | fi 26 | 27 | # Update version number in Cargo.toml for build 28 | sed -i '' "s/version = \"0.0.0\"/version = \"$VERSION\"/g" imessage-exporter/Cargo.toml 29 | sed -i '' s/'{ path = "..\/imessage-database" }'/\"$VERSION\"/g imessage-exporter/Cargo.toml 30 | 31 | if [ -n "$PUBLISH" ]; then 32 | echo 'Publishing exporter binary...' 33 | cargo publish -p imessage-exporter --allow-dirty 34 | else 35 | echo 'PUBLISH env var not set!' 36 | fi 37 | 38 | # Build for Apple Silicon 39 | cargo build --target aarch64-apple-darwin --release 40 | cp target/aarch64-apple-darwin/release/imessage-exporter output/imessage-exporter-aarch64-apple-darwin 41 | cd target/aarch64-apple-darwin/release 42 | tar -czf ../../../output/imessage-exporter-aarch64-apple-darwin.tar.gz imessage-exporter 43 | cd ../../.. 44 | 45 | # Build for 64-bit Intel macOS 46 | cargo build --target x86_64-apple-darwin --release 47 | cp target/x86_64-apple-darwin/release/imessage-exporter output/imessage-exporter-x86_64-apple-darwin 48 | cd target/x86_64-apple-darwin/release/ 49 | tar -czf ../../../output/imessage-exporter-x86_64-apple-darwin.tar.gz imessage-exporter 50 | cd ../../.. 51 | 52 | # Build for 64-bit Intel Windows (requires `brew install mingw-w64`) 53 | cargo build --target x86_64-pc-windows-gnu --release 54 | cp target/x86_64-pc-windows-gnu/release/imessage-exporter.exe output/imessage-exporter-x86_64-pc-windows-gnu.exe 55 | cd target/x86_64-pc-windows-gnu/release/ 56 | tar -czf ../../../output/imessage-exporter-x86_64-pc-windows-gnu.tar.gz imessage-exporter.exe 57 | cd ../../.. 58 | 59 | # Put the version number back 60 | sed -i '' "s/version = \"$VERSION\"/version = \"0.0.0\"/g" imessage-database/Cargo.toml 61 | sed -i '' "s/version = \"$VERSION\"/version = \"0.0.0\"/g" imessage-exporter/Cargo.toml 62 | sed -i '' s/\"$VERSION\"/'{path = "..\/imessage-database"}'/g imessage-exporter/Cargo.toml 63 | 64 | unset VERSION 65 | else 66 | echo 'No version tag set!' 67 | fi 68 | -------------------------------------------------------------------------------- /docs/binary/img/icloud_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReagentX/imessage-exporter/b20e0086125f5c65cb6ef094da1757be26798c2b/docs/binary/img/icloud_download.png -------------------------------------------------------------------------------- /docs/binary/img/safari_local_file_restrictions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReagentX/imessage-exporter/b20e0086125f5c65cb6ef094da1757be26798c2b/docs/binary/img/safari_local_file_restrictions.png -------------------------------------------------------------------------------- /docs/diagnostics.md: -------------------------------------------------------------------------------- 1 | # Diagnostics 2 | 3 | Diagnostic output from `imessage-exporter` looks like: 4 | 5 | ```txt 6 | iMessage Database Diagnostics 7 | 8 | Handle diagnostic data: 9 | Contacts with more than one ID: 2 10 | Message diagnostic data: 11 | Total messages: 183453 12 | Messages not associated with a chat: 43210 13 | Messages belonging to more than one chat: 36 14 | Attachment diagnostic data: 15 | Total attachments: 49422 16 | Data referenced in table: 44.13 GB 17 | Data present on disk: 31.31 GB 18 | Missing files: 15037 (30%) 19 | No path provided: 14929 20 | No file located: 108 21 | Thread diagnostic data: 22 | Chats with no handles: 2 23 | Global diagnostic data: 24 | Total database size: 339.88 MB 25 | Duplicated contacts: 78 26 | Duplicated chats: 16 27 | 28 | Environment Diagnostics 29 | 30 | Detected converters: 31 | Image converter: sips 32 | Audio converter: afconvert 33 | Video converter: ffmpeg 34 | ``` 35 | 36 | ## Handle diagnostic data 37 | 38 | ### Contacts with more than one ID 39 | 40 | The number of contacts that have multiple entries in the `handle` table, deduplicated by matching their `person_centric_id` across rows. The `person_centric_id` is a field used by Apple to disambiguate contacts. Further deduplication also happens, as noted below. 41 | 42 | ## Message diagnostic data 43 | 44 | ### Total messages 45 | 46 | The total number of rows in the `messages` table. 47 | 48 | ### Messages not associated with a chat 49 | 50 | If a message exists in the `messages` table but does not have an entry in the `chat_message_join` table, it is considered orphaned and will be listed in either the `Orphaned.html` or `Orphaned.txt` file in the export directory. Likely, these come from messages that were deleted and the chat removed from the `chat_message_join` table, but the corresponding messages were not removed from the `messages` table. 51 | 52 | ### Messages belonging to more than one chat 53 | 54 | If a message exists in the `messages` table and maps to multiple chats in `chat_message_join`, the message will exist in all of those chats when exported. 55 | 56 | ## Attachment diagnostic data 57 | 58 | ### Total attachments 59 | 60 | The total number of rows in the `attachments` table 61 | 62 | #### Data referenced in table 63 | 64 | The sum of the `total_bytes` column in the `attachments` table. I don't know why they are different, but the former is the actual storage taken up by iMessage attachments. 65 | 66 | #### Data present on disk 67 | 68 | Represents the total size of the attachments listed in the `attachments` when following the listed path to the respective file. Missing files may have been removed by the user or not properly downloaded from iCloud. 69 | 70 | ### Missing files 71 | 72 | The first line shows the count and the percentage of files missing. In the example above, `15037 (30%)` means that `15,037` files (`30%` of the total number of attachments) are referenced in the table but do not exist. 73 | 74 | There are two different types of missing files: 75 | 76 | #### No path provided 77 | 78 | This means there was a row in the `attachments` table that did not contain a path to a file. 79 | 80 | #### No file located 81 | 82 | This means there was a path provided, but there was no file at the specified location. 83 | 84 | ## Thread diagnostic data 85 | 86 | Emits the count of chats that contain no chat participants. 87 | 88 | ## Global diagnostic data 89 | 90 | ### Total database size 91 | 92 | The total size of the database file on the disk. 93 | 94 | ### Duplicated contacts 95 | 96 | Duplicated contacts occur when a single contact has multiple valid phone numbers or iMessage email addresses. The iMessage database stores handles as rows, and multiple rows can match to the same contact. 97 | 98 | ### Duplicated chats 99 | 100 | The number of separate chats that contain the same participants. See the [duplicates](/docs/tables/duplicates.md) for a detailed explanation of the logic used to determine this number. 101 | 102 | ## Detected converters 103 | 104 | `imessage-exporter` uses third-party tools to convert images when using `--copy-method basic` or `--copy-method full`. This section shows what programs are detected on the current system. 105 | 106 | ### Image converter 107 | 108 | The currently detected image converter, if present. 109 | 110 | One of: 111 | 112 | - `sips` (macOS Builtin) 113 | - `magick` (`imagemagick`) 114 | - None 115 | 116 | ### Audio converter 117 | 118 | The currently detected audio converter, if present. 119 | 120 | One of: 121 | 122 | - `afconvert` (macOS Builtin) 123 | - `ffmpeg` 124 | - None 125 | 126 | ### Video converter 127 | 128 | The currently detected video converter, if present. 129 | 130 | One of: 131 | 132 | - `ffmpeg` 133 | - None 134 | -------------------------------------------------------------------------------- /docs/faq.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | ## I cannot connect to the messages database. What do I do? 4 | 5 | Ensure your terminal emulator has [full disk access](https://kb.synology.com/en-us/C2/tutorial/How_to_enable_Full_Disk_Access_on_a_Mac) if using the default location or ensure that the path to the database file is correct. 6 | 7 | *** 8 | 9 | ## Are emojis, tapbacks (reactions), and other special message features preserved in the export? 10 | 11 | Yes, all iMessage features are supported. See [here](features.md) for more detail. 12 | 13 | *** 14 | 15 | ## Can it export messages from third-party apps that integrate with iMessage? 16 | 17 | Yes. See [here](features.md) for more detail on supported features. 18 | 19 | *** 20 | 21 | ## Does `imessage-exporter` export message conversations that are in iCloud or on a user's iPhone/iPad but not on the user's Mac? 22 | 23 | `imessage-exporter` only reads data present in the provided source, which can be either macOS's `chat.db` or a local full iOS backup. It cannot read data that is only stored in iCloud. 24 | 25 | *** 26 | 27 | ## Can I force iCloud to download attachments that were offloaded? 28 | 29 | In the Messages app, if you click the info (`ⓘ`) button for a conversation and scroll to the bottom, there is a button that downloads all of the attachments for that conversation. This works on both macOS and iOS. 30 | 31 | ![](../docs/binary/img/icloud_download.png) 32 | 33 | ## Can it export group conversations as well as individual chats? 34 | 35 | Yes. 36 | 37 | *** 38 | 39 | ## How does the exporter handle previously exported messages? 40 | 41 | If files with the current output type exist in the output directory, `imessage-exporter` will alert the user that they will overwrite existing exported data and the export will be cancelled. If the export directory is clear, `imessage-exporter` will export all messages by default. Alternatively, it will export messages between the dates specified by the `--start-date` and `--end-date` arguments. 42 | 43 | See [here](../imessage-exporter/README.md#how-to-use) for details on `imessage-exporter` arguments. 44 | 45 | *** 46 | 47 | ## Is it possible to export a conversation and re-integrate it back onto another Apple ID? 48 | 49 | No, I do not want to be trusted with write access to your iMessage data. This software is *read only*. 50 | 51 | *** 52 | 53 | ## Is there a search function? 54 | 55 | No, this software just builds exports. I use [`ripgrep`](https://github.com/BurntSushi/ripgrep) to search though the exported files. 56 | 57 | *** 58 | 59 | ## Will it run on Windows/Linux? 60 | 61 | I don't pre-build binaries for Windows or Linux, but it should compile to those [targets](https://doc.rust-lang.org/nightly/rustc/platform-support.html). As long as you can point it at an iMessage database, it should work. 62 | 63 | *** 64 | 65 | ## Can it export messages between a specific date range? 66 | 67 | Yes, the `--start-date` and `--end-date` arguments specify date ranges for exports. 68 | 69 | See [here](../imessage-exporter/README.md#how-to-use) for details on `imessage-exporter` arguments. 70 | 71 | *** 72 | 73 | ## Are voice messages be saved? 74 | 75 | Expired ones cannot because they are deleted. If you kept them then they are included in the exports. 76 | 77 | *** 78 | 79 | ## Are messages deleted from the messages app erased from the database? 80 | 81 | This software can recover some, but not all, deleted messages. 82 | 83 | Messages removed by deleting an entire conversation or by deleting a single message from a conversation are moved to a separate collection for up to 30 days. Messages present in this collection are restored to the conversations they belong to. Apple details this process [here](https://support.apple.com/en-us/HT202549#delete). 84 | 85 | Messages that have expired from this restoration process are permanently deleted and cannot be recovered. 86 | 87 | In some instances, deleted messages are removed from the `chat_message_join` table but not from the `messages` table. These messages will populate in `Orphaned.html` or `Orphaned.txt`. 88 | 89 | *** 90 | 91 | ## How fast is `imessage-exporter`? 92 | 93 | This is a complicated question that depends on CPU, database size, chosen export type, encryption state, and chosen attachment handling style. 94 | 95 | On my M1 Max MacBook Pro, approximate performance is as follows: 96 | 97 | | `--copy-method` | Messages exported per second | 98 | |---|---| 99 | | `disabled` | > 100,000 | 100 | | `clone` | ≈ 42,000 | 101 | | `basic` | ≈ 350 | 102 | | `full` | ≈ 250 | 103 | 104 | For more information on `--copy-method`, see [here](../imessage-exporter/README.md#how-to-use) and [here](./features.md#supported-message-features). 105 | 106 | However, if you recently deleted a large amount of data from Messages, the database will be slow for awhile, resulting in significantly reduced performance from `imessage-exporter`. 107 | -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | # Features 2 | 3 | The library and binary crates aim to provide the most comprehensive and accurate representation of iMessage data available. 4 | 5 | ## Targeted Versions 6 | 7 | This tool targets the current latest public release for Messages.app. It may work with older databases, but all features may not be available. 8 | 9 | ## Supported data sources 10 | 11 | - Local macOS messages 12 | - Encrypted or unencrypted local iOS backups 13 | - Unencrypted backups are resolved normally 14 | - Uses [crabapple](https://github.com/ReagentX/crabapple) to decrypt data from encrypted iOS backups 15 | 16 | ## Supported Message Features 17 | 18 | - Plain Text 19 | - Correctly extracts time-zone corrected timestamps 20 | - Detects when a message was read and calculates the time until read for both parties 21 | - Humanizes display of time-until-read duration 22 | - Parses `typedstream` message body data 23 | - Detects the service a message was sent from 24 | - In HTML exports, balloons are colored correctly for the service they were sent with 25 | - Supports iMessage, SMS, MMS, and RCS 26 | - Formatted Text 27 | - Parses formatted text ranges from `typedstream` message body data 28 | - Supports all iMessage text format ranges: 29 | - [Mentions](https://support.apple.com/guide/messages/mention-a-person-icht306ee34b/mac) 30 | - Hyperlinks 31 | - OTP/2FA 32 | - Unit Conversions 33 | - [Animations and Styles](https://support.apple.com/guide/iphone/style-and-animate-messages-iphe5c5af4d4/ios) 34 | - Edited and Unsent messages 35 | - Detects if messages components were edited or unsent 36 | - [Edited messages](https://support.apple.com/guide/iphone/unsend-and-edit-messages-iphe67195653/ios) 37 | - Parses `typedstream` edited body data 38 | - Displays content and timestamps for each edit 39 | - Humanizes display of edit timestamp gaps 40 | - Edited messages received before Ventura display as normal messages without history 41 | - Unsent messages 42 | - No content, but are noted in context 43 | - Multi-part messages 44 | - iMessages can have multiple parts, denoted by ranges in `typedstream` message body data 45 | - Parts are displayed as 46 | - New lines in TXT exports 47 | - Separate balloons in HTML exports 48 | - Handles Edited and Unsent parts 49 | - Threads and Message Replies 50 | - [Threads](https://support.apple.com/en-us/104974) are displayed both threaded under the parent as well as in-place 51 | - This is to preserve context, which can be lost if replying to older messages 52 | - Messages from a thread and were rendered in-place are annotated as such 53 | - In HTML exports, threaded messages are hyperlinked to allow for easy reading in context 54 | - For multi-part messages, replies are threaded under the correct message part 55 | - Attachments 56 | - Any type of attachment that can be displayed on the web is embedded in the HTML exports 57 | - Attachments can be copied to the export directory or referenced in-place 58 | - Less-compatible attachments can be converted for even more portable exports: 59 | - Image `HEIC` files convert to `JPEG` 60 | - Sticker `HEIC` files convert to `PNG` 61 | - Animated Sticker `HEICS` (HEIC sequence) files convert to `GIF` 62 | - Video `MOV` files convert to `mp4` 63 | - Audio `CAF` files convert to `mp4` 64 | - Attachments are displayed as 65 | - File paths in TXT exports 66 | - Embeds in HTML exports (including ``, `