A native macOS app for finding and downloading attachments from Telegram chats without losing the filenames Telegram supplied.
Interface · Features · Quick start · Documentation
Unspool browses a selected Telegram chat, scans the requested part of its message history, and presents every attachment format exposed by Telegram in a compact file table. It uses the official TDLib client library and runs as a sandboxed SwiftUI application.
Important
Unspool is currently a source-only development build. There is no signed, notarized, or App Store release yet.
Unspool is an independent project and is not affiliated with, maintained by, or endorsed by Telegram.
These screenshots come from Unspool's isolated preview fixtures. Every chat, filename, count, credential fragment, and destination shown below is synthetic; the capture process never opens a personal Telegram session.
|
|
| Filter the active chat | Control scanning and downloads |
- Browse main and archived Telegram chats with lazily loaded chat avatars.
- Scan the latest 25, 50, 100, 250, or 500 messages, or continue through the complete available history.
- Find documents, music, audio, video, images, archives, lyrics, and arbitrary file extensions exposed by TDLib.
- Keep browse state isolated per chat and message range.
- Search by filename and combine sender, category, extension, size, date, and sort controls.
- Save reusable global or per-chat filter presets without sending filter data anywhere.
- Use Telegram's original filename whenever Telegram supplies one.
- Never rebuild music filenames from artist, title, caption, or media tags.
- Use a neutral deterministic fallback only when Telegram provides no filename.
- Display a consistent extension badge such as
FLAC,LRC,PNG,MOV, orFILE.
- Download between one and six files concurrently.
- Pause, resume, cancel, and retry download work.
- Restore an interrupted batch after relaunch.
- Checkpoint every terminal file result before scheduling more work, so a crash cannot silently discard completed outcomes.
- Save into a user-selected security-scoped folder, optionally grouped into per-chat subfolders.
- Preflight destination capacity, unsafe filenames, and existing files before starting a batch.
- Default conflicts to Skip. Replace and Save a Copy require an explicit user choice; ordinary downloads never overwrite or silently rename a file.
- Keep a local download timeline and avoid downloading the same Telegram file twice unless the user explicitly chooses Download Again.
- Preview completed local files with system Quick Look and export an explicit JSON or CSV history manifest to a user-selected location.
- SwiftUI interface with Browse, Downloads, and Settings destinations.
- Standard Command-click, Shift-click, Command-A, and Escape selection behavior.
- System, Light, and Dark appearances.
- VoiceOver labels, keyboard navigation, Reduce Motion, Reduce Transparency, and Increase Contrast support.
- Finder reveal actions for the destination and completed downloads.
- Apple silicon Mac
- macOS 26 or later
- Xcode 27 or later with a compatible macOS SDK
- Homebrew
- A Telegram account and an API ID/API hash created at my.telegram.org/apps
git clone https://github.com/QenTerra/unspool.git
cd unspool
brew bundle
xcodegen generate --spec project.yml
open Unspool.xcodeprojBefore building, provide a locally built universal TDLib XCFramework at
Vendor/TDLib/TDLib.xcframework. The generated binary is intentionally not
committed. Use the pinned revision under Vendor/TDLib with
TDLib's official build instructions.
In Xcode, select the Unspool scheme and run the app.
- Enter the API ID and API hash issued for your application by Telegram.
- Complete the account authorization steps requested by Telegram.
- Choose a writable download folder.
- Select a chat and a history range.
- Select attachments, review the preflight summary, and start the download.
API credentials are stored in the macOS Keychain. Login codes and two-step verification passwords are submitted to Telegram through TDLib and are not written to Unspool's persistent stores. See the Privacy Policy for the complete data map.
AttachmentHistoryServicerequests Telegram history page by page.- TDLib media objects are normalized into attachment records while preserving Telegram filename provenance.
- Filters and selection operate only on the active chat.
- A preflight checks destination access, available capacity, previous downloads, and filename conflicts.
DownloadCoordinatordownloads through TDLib with bounded concurrency.DestinationAccessatomically places each completed file into the selected folder using the conflict action chosen by the user.
The complete flow and concurrency boundaries are documented in Architecture.
| Capability | Why Unspool needs it |
|---|---|
| Outgoing network access | Authorize the account and request chats, messages, and files from Telegram through TDLib |
| User-selected file access | Write downloads only to the folder selected by the user |
| App sandbox | Keep application data and filesystem access inside macOS security boundaries |
| Keychain | Store the Telegram API ID and API hash on this Mac |
Unspool has no analytics, advertising SDK, crash-reporting service, developer-operated backend, or remote-code system. Telegram necessarily processes account and chat requests made through its service. Review PRIVACY.md, TERMS_OF_USE.md, and Telegram's own terms before using the app.
project.yml is the source of truth for the generated Xcode project.
brew bundle
xcodegen generate --spec project.yml
swiftformat Unspool UnspoolTests UnspoolUITests --lint
swiftlint lint --config .swiftlint.yml
xcodebuild -project Unspool.xcodeproj -scheme Unspool test
xcodebuild -project Unspool.xcodeproj -scheme Unspool build-for-testingThese commands:
- regenerate
Unspool.xcodeproj; - run SwiftFormat and SwiftLint in validation mode;
- run the unit test suite;
- compile the UI test suite and Debug app.
If multiple Xcode installations are present, select one for the current shell with a standard path such as:
export DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer"The commands do not change the global xcode-select setting.
Unspool/
App/ application state and intents
DesignSystem/ Soft Graphite tokens and components
Domain/ attachments, filters, selection, download models
Features/ Browse, Downloads, Settings, Setup, shared views
Infrastructure/
Downloads/ preflight, placement, scheduling
Persistence/ local JSON stores
Security/ Keychain credential storage
TDLib/ Telegram client and mapping boundary
PreviewSupport/ synthetic UI fixtures
TDLibBridge/ C++ bridge to TDLib's JSON C interface
UnspoolTests/ unit and integration-style tests with fakes
UnspoolUITests/ macOS UI tests
Vendor/TDLib/ pinned metadata and license texts
For setup details, dependency pinning, and common failures, use the technical documents below instead of guessing at generated paths.
- There is no prebuilt signed distribution.
- Apple silicon is the validated development target. The project expects a universal TDLib artifact, but Intel application execution is not a release gate yet.
- Complete-history scans depend on chat size, Telegram availability, network conditions, and rate limits.
- Telegram can omit an original filename for some media.
- Real-account acceptance still requires manual checks for long histories, music sent as FLAC, Unicode filenames, connection loss, insufficient disk space, relaunch during a batch, and macOS accessibility modes.
- Complete product and engineering wiki
- Documentation index
- Building from source
- Architecture
- Product identity and interface system
- Dependencies
- Troubleshooting
- Contributing
- Changelog
- Privacy Policy
- Terms of Use
- Security Policy
- Third-party notices
- MIT License
Unspool is created and maintained by QenTerra. If it saves you time, you can buy me a coffee.
Bug reports and focused pull requests are welcome. Read CONTRIBUTING.md before opening one and use SECURITY.md for private vulnerability reports.
Unspool source code and first-party documentation are available under the MIT License. TDLib, OpenSSL, Apple frameworks, and development tools remain subject to their own licenses. See Third-party notices and Dependencies.



