MadelineProto 8.20: video calls, group calls, livestreams and E2E conferences in pure PHP, 250x faster crypto, rich messages and much more
MadelineProto 8.20.1 is out, and it’s the biggest update since MadelineProto 8.0!
Since 8.7.0, MadelineProto has gained video calls, group calls, livestreams, live stories and end-to-end encrypted conference calls, all in pure PHP: calls run on a pure PHP WebRTC stack, with pure PHP OPUS and AAC codecs. No FFI, no extensions, no shell commands and no ffmpeg, so everything works on webhosts, too! Over 43 releases (8.8.8 → 8.20.1), we also added rich messages, resumable uploads and downloads, a native crypto extension, a new web UI, Windows IPC support, layers 231 to 233, and hundreds of fixes.
Also, MadelineProto just passed 1 million installs on Packagist. Thank you so much ❤️

Calls, calls, calls
Everything runs in pure PHP, on any webhost. Calls use php-rtc, a pure PHP WebRTC stack (ICE, STUN, TURN, DTLS, SRTP, SCTP), and audio goes through a pure PHP OPUS encoder and AAC decoder: no FFI, no extensions, no shell commands, no ffmpeg.
<?php
if (!file_exists('madeline.php')) {
copy('https://phar.madelineproto.xyz/madeline.php', 'madeline.php');
}
include 'madeline.php';
use danog\MadelineProto\API;
use danog\MadelineProto\RemoteUrl;
$MadelineProto = new API('session.madeline');
$MadelineProto->start();
// Video call a user
$MadelineProto->requestCall('@username', video: true)
->play(new RemoteUrl('https://paste.daniil.it/oncall.webm'));
// Stream into the video chat of a group or the livestream of a channel
$MadelineProto->joinGroupCall('@mygroup')
->play(new RemoteUrl('https://paste.daniil.it/oncall.webm'));
See examples/bot.php for a full example, and the call documentation » for everything else.
- Video calls: play WebM/MKV files with VP8, VP9, H.264, H.265 (or AV1, in one-to-one calls) video and OPUS audio into any call. Frames are demuxed in pure PHP and sent as they are, with no transcoding.
- Screen sharing: play a video as a screen share next to the camera stream with
MediaDestination::Presentation. - Group calls (video chats and livestreams): create them (scheduled and RTMP livestreams too), join them (as a channel or via an invite link too), stream audio and video, share the screen, record, and manage them: title, invites and invite links, muting, volume, raised hands, server-side recording, kicking participants, in-call messages and reactions.
- Livestreams: record stream mode livestreams (RTMP ones included) with the new pure PHP MP4 demuxer, and publish to RTMP livestreams with
play(): MP4 or Matroska files with H.264 video and AAC audio are published by MadelineProto itself, in pure PHP and without transcoding. - Stream to any Telegram livestream or user profile with the new
startRtmpStream(), using the RTMP URL and key that the Telegram apps show when starting a stream “with another app”. No admin rights or joining needed, and the stream is resumed after restarts. Bots that accept URLs from users can restrict where streams connect withRtmpServers::TelegramorRtmpServers::Internet:
$stream = $MadelineProto->startRtmpStream('rtmps://dc4-1.rtmp.t.me/s/123456:AbCdEf', servers: RtmpServers::Telegram);
$stream->play(new RemoteUrl('https://example.com/video.mp4'));
- Live stories: start one with
startLive(), stream into it, comment, and handle Telegram Stars donations. - End-to-end encrypted conference calls: create them, join them via a link or an invitation, invite users, verify the key emojis, send end-to-end encrypted in-call messages and remove participants (rekeying the call), or upgrade a one-to-one call with
migrateCallToConference(). - Call recording:
setOutput()records a participant’s microphone, camera and screen share into a Matroska (or OGG OPUS) file, andsetOutputFolder()records every participant into a directory, starting a new file whenever their streams change. Recordings are written in pure PHP, without decoding anything. - AAC audio in calls, on webhosts too: the AAC audio of MP4 files is now played into one-to-one calls, video chats and conferences, thanks to a new pure PHP AAC decoder and OPUS encoder (ffmpeg and libopus are only used to speed it up when available, never required).
- Any OGG OPUS file (mono or stereo, any frame size) can now be played as it is: no more pre-converting with @libtgvoip_bot or
Ogg::convert. - One API for all calls:
PrivateCall,GroupCall,LiveStoryandConferenceCallall implement theCallinterface, with the same playlist, mute, screen sharing and recording controls. - Calls survive restarts: playlists (continuing the current file where it left off), hold files (which now loop) and recordings are saved in the session.
- New updates:
CallStreams,GroupCall,GroupCallParticipants,GroupCallMessage,GroupCallMessagesDeleted,LiveStory,LiveStoryMessageandConferenceCallMessage. Message filters (commands, text, regexes, sender, incoming/outgoing, peer) work on in-call messages too. - Messages have new
getCall(),canRequestCall(),requestCall()andgetCallDenialReason()methods, so$message->requestCall()->join()works in every chat.
Want to try it out? Talk to magnaluna, our open-source pure PHP webradio: @magicalcrazypony. Call it, send it /call, send it the link of a group call or a conference call, or use /join in a group. It can play any YouTube video or livestream, without ffmpeg or yt-dlp. Its source code runs on any PHP webhost, so you can host your own!
Much, much faster
- MadelineCrypto, a new optional native extension contributed by xtrime, with further improvements by danog: about 250x faster file downloads and uploads, native per-packet crypto for calls (AES-GCM/CTR/CBC/IGE, HMAC, hashes), and 40x faster factorization of
pqwhen creating auth keys. It’s preinstalled in the official Docker image, and you can install it anywhere with PIE, the successor of PECL (prebuilt binaries are available for Linux, macOS and Windows):
pie install danog/madelinecrypto
- Webhosts got faster too: 10x faster file downloads thanks to a new pure PHP AES-IGE implementation (with one OpenSSL call per encryption, and no
evalwithout OpenSSL), and up to 90% faster SHA-256 hashes (the fastest backend is picked for each input size). - Bulk downloads and uploads no longer time out: MadelineProto learns how many file requests each media DC accepts at once (like a TCP congestion window) and spreads them over several sockets, instead of piling them all on one.
- Short download ranges (like a browser probing a video’s metadata) now fetch small parts instead of a whole megabyte.
- Cryptography now uses OpenSSL and libsodium when available, with phpseclib only as the fallback.
Rich messages
Full support for rich messages and all message entities:
$message->replyRich("# Hello!\n\nThis is a **rich** message.", RichParseMode::MARKDOWN);
- New
sendRichMessage(),sendRichMessageDraft()(stream drafts while they are generated, perfect for AI bots) andsendRichMessageToAdmins()methods, andreplyRich(),sendRich(),editRich()andreplyOrEditRich()in the event handler. - Write them in Markdown, HTML, typed blocks (
new Block\Paragraph(new Text\Bold('Hello'))), Bot API blocks or raw MTProto page blocks, and embed media from files, URLs, streams or file IDs. - Received rich messages are in
Message::$richMessage, with the same API as messages ($message,$entities,getHTML(),$media) and typed$blocks. Filter them withFilterRichMessage.
Nothing gets lost
- Resumable uploads: interrupted uploads of files, URLs, Telegram files and streams (encrypted ones too) resume after a restart, without uploading the parts already sent again.
- Resumable method calls: interrupted method calls and
send*calls uploading such files are resumed after a restart (disable it with the newresumeInterruptedCallsfile setting). - Resumable download streams:
downloadToReturnedStream()andMedia::getStream()return aResumableDownloadStream, which can be serialized and resumes from where it left off. - Network outages are survived: requests are queued and sent once the connection is back, with reconnections retried in the background (previously, a script whose network went down for a minute could die).
- Dead connections are detected: connections that silently stop working (half-open TCP connections, dropped by a NAT for example) are replaced as soon as a state request goes unanswered.
- Session files are synced to disk before replacing the old one: a crash or power loss could leave a session file full of zeroes.
- Requests whose result can’t be deserialized now fail with an error, instead of being resent forever.
- A new file reference database, driven by Telegram’s file reference map, refreshes expired file references automatically.
New in the event handler and API
- Thumbnails are media:
Media::$thumbsandMedia::$videoThumbsholdThumbnailandVideoThumbnailobjects, which can be downloaded, streamed, linked withgetDownloadLink()and reused as upload sources or asthumb:. Inline (stripped or cached) thumbnails are served from memory. - Locations:
Message::$locationholdsStaticLocation,LiveLocationandVenueobjects, with the newHasLocation,HasLiveLocationandHasVenuefilters. - Cross-chat replies:
$replyToChatIdtells which chat a reply points to,getReply()fetches it there, and thereply*()methods take a$peerto reply to a message of another chat.getReply()also works when the replying message was deleted. getUpdates()can be cancelled with aCancellation, through IPC too.- Communities, ephemeral messages, wallets, bot widgets, audio file transcription and everything else from layers 231, 232 and 233.
Web UI and login
- Web UI v3: a new login interface in the style of Telegram Web K, made of liquid glass.
- With
startAndLoopMulti(), the web login now lets you choose which session to log in. - The CLI login hides the bot token, API hash, 2FA password and login code as they’re typed, and so does the web login.
- MadelineProto is now available in 21 languages: Spanish, Arabic, Turkish, Ukrainian, Indonesian, Hindi, Vietnamese, Japanese and Korean are new, and the German, Polish, French, Central Kurdish, Portuguese and Russian translations are complete. Translations now live in
langs/on GitHub.
Security
- Files served to browsers are sandboxed: the download script (
getDownloadLink()) anddownloadToResponse()used the MIME type and the name chosen by whoever sent the file, so a link could serve HTML with scripts from your website. Every file is now served withContent-Security-Policy: sandbox,X-Content-Type-Options: nosniffand a normalized MIME type; only media and plain text are shown inline. Websites serving trusted files can turn this off withFiles::setSandboxDownloads(false). - Secrets passed as parameters are marked
#[\SensitiveParameter], so they don’t show up in stack traces.
Proxies, Windows, Docker and the phar
- Fake TLS MTProxy secrets (
ee) are now supported, implemented like TDLib, as are base64 secrets (URL-safe anddd-prefixed ones included). Failing proxies are no longer bypassed with direct connections. - The IPC server now starts automatically on Windows (Windows 10 1803+, with
ext-sockets), like on the other platforms. - Docker image: based on Debian trixie, extensions installed with PIE, MadelineCrypto preinstalled, a non-root user, and PHP updated automatically (it already had the fixes for the latest PHP vulnerabilities). The
latesttag now only moves on releases. - Phar: now named
madeline.phar; phar updates work on php-fpm withopcache.validate_timestamps=0; startup no longer waits up to 18 seconds when the phar server can’t be reached. The latest phars are also on @madelineprotophar.
Notable fixes
- Clicking the buttons of received keyboards works again, and Bot API keyboards are converted correctly.
- The forum topic methods (
createTopic(),editTopic(),openTopic(),closeTopic(),deleteTopic()) work again. - Sessions saved by older versions (before 8.4.7, or with phpseclib 3) load again, and Telegram Desktop sessions can be converted again.
- Changing the database settings no longer loses data.
- Fixed an endless loop that used all the memory when the event handler failed to start.
- Fixed the update loop dying on RPC timeouts in
getDifference/getChannelDifference. - Fixed a hang while starting the IPC worker on Windows, IPC request floods, and many more IPC issues.
- Fixed the speed and time passed to upload and download progress callbacks, and
downloadToBrowser()continuing after the browser closed the connection. - Fixed stale user statuses in
getInfo(), and thereplyToMsgIdof sent messages. - Sending a message no longer fails with
RANDOM_ID_DUPLICATEwhen it was resent while the server was still processing the first copy (the message was sent anyway). - Calls failing with server-side errors (
RPC_CALL_FAIL, -503) are now retried with a growing delay, and fail with the server’s error instead of a timeout. - Round videos are video notes again, encrypted downloads starting from a non-zero offset work, and sending media with a TTL works.
See the full changelogs on GitHub for everything else.
Upgrade notes
Media::$thumb,$thumbWidthand$thumbHeightwere replaced by theInlineThumbnailinMedia::$thumbs.- Starting with layer 231, keyboard buttons carry their action in their
type(thekeyboardButtonUrl,keyboardButtonCallback… constructors were removed). - Files served by the download script are now sandboxed (see above).
Thanks
Thanks to everyone who contributed to these releases: @danog, @xtrime-ru, @xcgu, @bertmsk, @mtalaeii and @junwei-qu!
Get it
composer require danog/madelineproto:^8.20
Or just include madeline.php, which updates itself automatically.
Release announcements now go to @madelineprotophar, while @MadelineProto is kept for the big ones like this. Happy hacking!