view release on metacpan or search on metacpan
Revision history for EV-Telegram-TDLib
0.05 2026-10-20
Fix inclusive history paging and cancellation from download progress
Preserve submitted requests across rate-limit retries
0.04 2026-09-22
Stories, stickers, Stars, gifts, Business, routers, ask(), 429 retry
SECURITY: an unusable TDLIB_LOG_VERBOSITY no longer logs credentials
INCOMPATIBLE: database_encryption_key is encoded; a 0.03 key needs
decode_base64. Invalid arguments croak, and some defaults changed
0.03 2026-08-27
Mini Apps, forum topics, chat and group management, folders,
contacts, payments, secret chats, bot provisioning, and
schema validated call()
0.02 2026-08-26
Entity text slicing, unicode documentation, GTK4/Tickit/MCP examples
The bundled TDLib is 1.8.66, pinned by Alien::TDLib at commit
022d60202e446ad1287b9fb68e687c8a0760788b.
CONSTRUCTOR
new(%opt)
Creates a client and registers it in a process-global registry. The
client is held under a strong reference until close() completes; see
"CAVEATS". Options:
api_id, api_hash
Telegram application credentials from https://my.telegram.org. Keep
them in the environment, not in source; see "SECURITY".
phone_number
Phone number in international format for user authorization. Used
when the state machine reaches authorizationStateWaitPhoneNumber,
unless bot_token is present. Setting on_qr as well does not override
it: a QR link is requested only when on_qr is set and no
phone_number was given.
bot_token
value when the client is constructed, so the failure arrives with an
explanation instead.
Any accepted value works, but the value still matters. Real clients send
a conventional identifier ("android", "ios", "macos", "tdesktop",
"weba", "webk") and Mini App pages branch on it to pick layout, theming
and available features. An invented name passes validation and then
lands in whatever an app does with an unrecognised platform. The default
is "tdesktop".
Launch URLs carry credentials
The URL returned by open_web_app and the web_app_*_url methods has the
signed init data in its fragment: the user's name, username, photo URL
and an authentication hash. It is a credential. Do not log it, paste it
into a bug report, or store it anywhere the page itself would not go.
AUTHORIZATION
TDLib drives authorization as a state machine reported through
updateAuthorizationState; "auth_state()" exposes the current state. With
auto_auth on (the default), each state is answered automatically or
routed to a credential callback:
authorizationStateWaitTdlibParameters
setTdlibParameters is sent automatically from the constructor
options. No callback. An error reply (bad api credentials, an
unwritable database_directory) fails login: the values come from the
constructor, so there is no interactive channel to retry through.
authorizationStateWaitPhoneNumber
bot_token is sent when given; otherwise requestQrCodeAuthentication
when on_qr is set and no phone_number was given; otherwise the
phone_number is sent. No callback in any branch. An error reply (an
invalid phone number or bot token) fails login, for the same reason
as above.
Raise this on a heavily loaded machine or under a sanitizer, where
everything runs several times slower.
TDLIB_LOG_VERBOSITY
TDLib's log verbosity level, applied once when the module is loaded.
Defaults to 1; TDLib's own default of 5 is very noisy on stderr.
TD_API_ID, TD_API_HASH, TD_PHONE, TD_BOT_TOKEN, TD_DATABASE_DIRECTORY
Not the module's API: the credential convention shared by the
scripts in eg/ and by xt/live_auth.t. The module itself takes
credentials only as constructor options; see "new(%opt)".
EXAMPLES
Runnable scripts live in eg/. Once the module is installed, run one from
anywhere with "perl eg/NAME.pl"; from an unpacked distribution "perl
-Mblib eg/NAME.pl" works after "make", and not before it, since there is
no blib until then. Credentials come from the environment, see
"ENVIRONMENT":
eg/01-login.pl
user login with phone, SMS code and 2FA; creates the session
something the module can fix: disable cores where it matters.
Set database_encryption_key so the local database is encrypted at rest,
and keep the key out of source control.
api_id and api_hash identify your application to Telegram. Pass them
from your environment rather than hardcoding them; new() does not read
any environment variable itself, but the examples in eg/ take them from
TD_API_ID and TD_API_HASH.
The login callbacks receive credentials from wherever you choose to read
them. If that is a terminal, do not echo the 2FA password: it lands in
the scrollback, in a screen recording and in whatever the session is
logged to. eg/01-login.pl shows the shape -- turn echo off through a
guard object, so an interrupt cannot leave the terminal silent for
whatever runs next.
TDLib's log can carry credentials. Above the lowest levels it writes the
requests it makes, and those include the ones carrying your api_hash and
bot token; logging in with verbosity raised has been observed to put
both on stderr in clear. TDLib's own default is 5, so this module sets 1
at load, and a "TDLIB_LOG_VERBOSITY" it cannot read as a level falls
back to 1 rather than leaving TDLib at 5. Raise it to debug something
and whatever collects your stderr collects that too.
REQUIREMENTS
* perl 5.12 or later, built with 64-bit integers. Makefile.PL refuses
to configure when ivsize is below 8: Telegram chat and user ids are
eg/01-login.pl view on Meta::CPAN
#
# Demonstrates: phone_number authorization, the on_code and on_password
# credential callbacks, me(), and a clean close.
#
# This is the script that creates the session database; the other examples
# reuse it. The database directory (default ./tdlib-db) holds the session:
# it is exactly as sensitive as a password, so do not commit it, back it up,
# or leave it world-readable.
#
# Environment:
# TD_API_ID, TD_API_HASH application credentials from https://my.telegram.org
# TD_PHONE phone number in international format, e.g. +10000000000
# TD_DATABASE_DIRECTORY optional, default ./tdlib-db
#
# Run: perl eg/01-login.pl (add -Mblib to run from a built checkout)
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
eg/02-bot-echo.pl view on Meta::CPAN
# 02-bot-echo.pl - bot that echoes every text message back to its chat
#
# Demonstrates: bot_token authorization (no credential callbacks needed),
# the on_message handler, send_message, and running until Ctrl-C.
#
# The database directory (default ./tdlib-bot-db) holds the session: it is
# exactly as sensitive as a password. A bot session needs its own directory;
# do not point it at the user session created by 01-login.pl.
#
# Environment:
# TD_API_ID, TD_API_HASH application credentials from https://my.telegram.org
# TD_BOT_TOKEN bot token from BotFather
# TD_DATABASE_DIRECTORY optional, default ./tdlib-bot-db
#
# Run: perl eg/02-bot-echo.pl (add -Mblib to run from a built checkout)
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
eg/03-list-chats.pl view on Meta::CPAN
#
# Demonstrates: load_chats() paged until TDLib reports the list exhausted,
# chats collected through on_chat, and titles read back from the chat()
# cache. Uses the session created by 01-login.pl. A user account only:
# TDLib refuses to load a chat list for a bot.
#
# The database directory (default ./tdlib-db) holds the session: it is
# exactly as sensitive as a password.
#
# Environment:
# TD_API_ID, TD_API_HASH application credentials from https://my.telegram.org
# TD_PHONE phone number in international format
# TD_DATABASE_DIRECTORY optional, default ./tdlib-db
#
# Run: perl eg/03-list-chats.pl (add -Mblib to run from a built checkout)
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
eg/04-send-message.pl view on Meta::CPAN
#
# Demonstrates: send_message with parse_mode => 'markdown' and the default
# wait => 'sent', so the callback fires on updateMessageSendSucceeded and
# carries the final message id. Uses the session created by 01-login.pl
# (or a bot token).
#
# The database directory (default ./tdlib-db) holds the session: it is
# exactly as sensitive as a password.
#
# Environment:
# TD_API_ID, TD_API_HASH application credentials from https://my.telegram.org
# TD_PHONE phone number in international format, unless TD_BOT_TOKEN
# TD_BOT_TOKEN bot token from BotFather, alternative to TD_PHONE
# TD_DATABASE_DIRECTORY optional, default ./tdlib-db
#
# Run: perl eg/04-send-message.pl CHAT_ID [TEXT]
# (add -Mblib to run from a built checkout)
# TEXT is MarkdownV2, where . ! # - ( ) and the like are reserved: escape
# each with a backslash, or the parse error is all you get back.
use strict;
eg/05-download-file.pl view on Meta::CPAN
# Demonstrates: download() with on_progress printing a percentage, and the
# local path reported once is_downloading_completed is reached. Uses the
# session created by 01-login.pl (or a bot token). File ids come from
# document/photo/video message content; they are per-session.
#
# The database directory (default ./tdlib-db) holds the session: it is
# exactly as sensitive as a password. Downloaded files land in the same
# directory tree unless files_directory says otherwise.
#
# Environment:
# TD_API_ID, TD_API_HASH application credentials from https://my.telegram.org
# TD_PHONE phone number in international format, unless TD_BOT_TOKEN
# TD_BOT_TOKEN bot token from BotFather, alternative to TD_PHONE
# TD_DATABASE_DIRECTORY optional, default ./tdlib-db
#
# Run: perl eg/05-download-file.pl FILE_ID
# (add -Mblib to run from a built checkout)
use strict;
use warnings;
use EV;
eg/06-raw-method.pl view on Meta::CPAN
#
# Demonstrates: calling TDLib methods the mixins do not wrap.
# call({...}) is the usual way in: it fills in @type from the method
# name and rejects an argument name TDLib does not have, so a typo
# fails here instead of coming back as an opaque server error.
# execute({...}) is synchronous td_execute: no network, no authorization.
# send({...}) is asynchronous: the reply arrives on the loop, correlated
# by @extra. @extra is assigned by send() itself (and returned), so it
# must never be set by hand; a caller-supplied value is overwritten.
# Both requests used here (getOption, parseTextEntities) work without any
# credentials, so auto_auth is off and no session database is written.
#
# Environment: none required. TD_API_ID/TD_API_HASH are picked up if set,
# but the requests below do not need them.
#
# Run: perl eg/06-raw-method.pl (add -Mblib to run from a built checkout)
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
eg/10-webapp-bot.pl view on Meta::CPAN
# without writing a page, drive the other side from a user session with
# send_web_app_data (see xt/live_webapp.t).
#
# The Mini App must exist first: create it with /newapp in BotFather and
# point TD_WEBAPP_URL at the URL you gave it.
#
# The database directory (default ./tdlib-bot-db) holds the session: it is
# exactly as sensitive as a password.
#
# Environment:
# TD_API_ID, TD_API_HASH application credentials from https://my.telegram.org
# TD_BOT_TOKEN bot token from BotFather
# TD_WEBAPP_URL the Mini App URL
# TD_DATABASE_DIRECTORY optional, default ./tdlib-bot-db
#
# Run: perl eg/10-webapp-bot.pl (add -Mblib to run from a built checkout)
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
lib/EV/Telegram/TDLib.pm view on Meta::CPAN
=head2 new(%opt)
Creates a client and registers it in a process-global registry. The
client is held under a strong reference until close() completes; see
L</CAVEATS>. Options:
=over 4
=item api_id, api_hash
Telegram application credentials from https://my.telegram.org. Keep
them in the environment, not in source; see L</SECURITY>.
=item phone_number
Phone number in international format for user authorization. Used
when the state machine reaches authorizationStateWaitPhoneNumber,
unless bot_token is present. Setting on_qr as well does not override
it: a QR link is requested only when on_qr is set and no phone_number
was given.
lib/EV/Telegram/TDLib.pm view on Meta::CPAN
validates the value when the client is constructed, so the failure
arrives with an explanation instead.
Any accepted value works, but the value still matters. Real clients
send a conventional identifier (C<android>, C<ios>, C<macos>,
C<tdesktop>, C<weba>, C<webk>) and Mini App pages branch on it to pick
layout, theming and available features. An invented name passes
validation and then lands in whatever an app does with an unrecognised
platform. The default is C<tdesktop>.
=head2 Launch URLs carry credentials
The URL returned by open_web_app and the web_app_*_url methods has the
signed init data in its fragment: the user's name, username, photo URL
and an authentication hash. It is a credential. Do not log it, paste it
into a bug report, or store it anywhere the page itself would not go.
=head1 AUTHORIZATION
TDLib drives authorization as a state machine reported through
updateAuthorizationState; L</auth_state()> exposes the current state.
With auto_auth on (the default), each state is answered automatically
or routed to a credential callback:
=over 4
=item authorizationStateWaitTdlibParameters
setTdlibParameters is sent automatically from the constructor options.
No callback. An error reply (bad api credentials, an unwritable
database_directory) fails login: the values come from the constructor,
so there is no interactive channel to retry through.
=item authorizationStateWaitPhoneNumber
bot_token is sent when given; otherwise requestQrCodeAuthentication
when on_qr is set and no phone_number was given; otherwise the
phone_number is sent. No callback in any branch. An error reply
(an invalid phone number or bot token) fails login, for the same
reason as above.
lib/EV/Telegram/TDLib.pm view on Meta::CPAN
=item TDLIB_LOG_VERBOSITY
TDLib's log verbosity level, applied once when the module is loaded.
Defaults to 1; TDLib's own default of 5 is very noisy on stderr.
=item TD_API_ID, TD_API_HASH, TD_PHONE, TD_BOT_TOKEN, TD_DATABASE_DIRECTORY
Not the module's API: the credential convention shared by the scripts
in F<eg/> and by F<xt/live_auth.t>. The module itself takes
credentials only as constructor options; see L</new(%opt)>.
=back
=head1 EXAMPLES
Runnable scripts live in F<eg/>. Once the module is installed, run one
from anywhere with C<perl eg/NAME.pl>; from an unpacked distribution
C<perl -Mblib eg/NAME.pl> works after C<make>, and not before it, since
there is no F<blib> until then. Credentials come from the environment,
see L</ENVIRONMENT>:
lib/EV/Telegram/TDLib.pm view on Meta::CPAN
something the module can fix: disable cores where it matters.
Set database_encryption_key so the local database is encrypted at
rest, and keep the key out of source control.
api_id and api_hash identify your application to Telegram. Pass them
from your environment rather than hardcoding them; new() does not read
any environment variable itself, but the examples in F<eg/> take them
from TD_API_ID and TD_API_HASH.
The login callbacks receive credentials from wherever you choose to
read them. If that is a terminal, do not echo the 2FA password: it
lands in the scrollback, in a screen recording and in whatever the
session is logged to. F<eg/01-login.pl> shows the shape -- turn echo
off through a guard object, so an interrupt cannot leave the terminal
silent for whatever runs next.
B<TDLib's log can carry credentials.> Above the lowest levels it writes
the requests it makes, and those include the ones carrying your
api_hash and bot token; logging in with verbosity raised has been
observed to put both on stderr in clear. TDLib's own default is 5, so
this module sets 1 at load, and a C<TDLIB_LOG_VERBOSITY> it cannot read
as a level falls back to 1 rather than leaving TDLib at 5. Raise it to
debug something and whatever collects your stderr collects that too.
=head1 REQUIREMENTS
=over 4
lib/EV/Telegram/TDLib/Cookbook.pod view on Meta::CPAN
=item *
Where a recipe writes C<die> in a callback it is shorthand for "stop
here", not a way to end the program. A callback that dies is caught and
reported, and the loop keeps running, so a real script warns and calls
C<EV::break> instead. See
L<EV::Telegram::TDLib/"ERROR HANDLING">.
=back
The recipes that need no credentials (formatted text, raw methods,
multiple clients) were run for real against TDLib 1.8.66. The rest
were checked field by field against the pinned TDLib scheme
(td_api.tl at commit 022d60202e446ad1287b9fb68e687c8a0760788b).
=head1 RECIPES
=head2 Logging in as a user, and reusing the session
Problem: authorize a user account with phone number, SMS code and
optional 2FA password -- and do it only once.
lib/EV/Telegram/TDLib/Cookbook.pod view on Meta::CPAN
print "TDLib $version->{value}\n"; # an optionValueString reply
my $extra;
$extra = $td->send({ '@type' => 'getOption', name => 'version' }, sub {
my ($res, $err) = @_;
die "getOption failed: $err->{message}\n" if $err;
print "async reply: $res->{value} (\@extra $extra)\n";
$td->close(sub { EV::break });
});
This recipe runs with C<auto_auth =E<gt> 0> and no credentials at
all; F<eg/06-raw-method.pl> is exactly that.
=head2 Timing out a request
Problem: do not wait forever for a reply that may never come.
C<send> accepts a C<timeout> in seconds. On expiry the callback
fails with a synthetic error -- code -1, message C<timeout>:
$td->send({ '@type' => 'getMe' }, sub {
lib/EV/Telegram/TDLib/Schema.pm view on Meta::CPAN
sendEmailAddressVerificationCode => 'email_address',
sendEphemeralMessage => 'chat_id topic_id receiver_user_id callback_query_id reply_to sending_id only_preview reply_markup input_message_content',
sendGift => 'gift_id owner_id text is_private pay_for_upgrade',
sendGiftPurchaseOffer => 'owner_id gift_name price duration paid_message_star_count',
sendGroupCallMessage => 'group_call_id text paid_message_star_count',
sendInlineQueryResultMessage => 'chat_id topic_id reply_to options query_id result_id hide_via_bot',
sendMessage => 'chat_id topic_id reply_to options reply_markup input_message_content',
sendMessageAlbum => 'chat_id topic_id reply_to options input_message_contents',
sendMessageViewMetrics => 'chat_id message_id time_in_view_ms active_time_in_view_ms height_to_viewport_ratio_per_mille seen_range_ratio_per_mille',
sendPassportAuthorizationForm => 'authorization_form_id types',
sendPaymentForm => 'input_invoice payment_form_id order_info_id shipping_option_id credentials tip_amount',
sendPhoneNumberCode => 'phone_number settings type',
sendPhoneNumberFirebaseSms => 'token',
sendQuickReplyShortcutMessages => 'chat_id shortcut_id sending_id',
sendResoldGift => 'gift_name owner_id price',
sendRichMessageDraft => 'chat_id forum_topic_id draft_id message',
sendTextMessageDraft => 'chat_id forum_topic_id draft_id text',
sendWebAppCustomRequest => 'bot_user_id method parameters',
sendWebAppData => 'bot_user_id button_text data',
setAccentColor => 'accent_color_id background_custom_emoji_id',
setAccountTtl => 'ttl',
lib/EV/Telegram/TDLib/Schema.pm view on Meta::CPAN
messageGiveaway => { parameters => 'giveawayParameters', prize => 'GiveawayPrize', sticker => 'sticker' },
messageGiveawayPrizeStars => { sticker => 'sticker' },
messageGiveawayWinners => { prize => 'GiveawayPrize' },
messageGroupCall => { other_participant_ids => 'array:MessageSender' },
messageInteractionInfo => { reactions => 'messageReactions', reply_info => 'messageReplyInfo' },
messageInvoice => { paid_media => 'PaidMedia', paid_media_caption => 'formattedText', product_info => 'productInfo' },
messageLinkInfo => { message => 'message', topic_id => 'MessageTopic' },
messageLiveLocation => { location => 'liveLocation' },
messageLocation => { location => 'location' },
messagePaidMedia => { caption => 'formattedText', media => 'array:PaidMedia' },
messagePassportDataReceived => { credentials => 'encryptedCredentials', elements => 'array:encryptedPassportElement' },
messagePassportDataSent => { types => 'array:PassportElementType' },
messagePaymentRefunded => { owner_id => 'MessageSender' },
messagePaymentSuccessfulBot => { order_info => 'orderInfo' },
messagePhoto => { caption => 'formattedText', photo => 'photo', video => 'video' },
messagePoll => { description => 'formattedText', media => 'PollMedia', poll => 'poll' },
messagePollOptionAdded => { text => 'formattedText' },
messagePollOptionDeleted => { text => 'formattedText' },
messagePositions => { positions => 'array:messagePosition' },
messagePremiumGiftCode => { creator_id => 'MessageSender', sticker => 'sticker', text => 'formattedText' },
messageProximityAlertTriggered => { traveler_id => 'MessageSender', watcher_id => 'MessageSender' },
lib/EV/Telegram/TDLib/Schema.pm view on Meta::CPAN
passportElementPersonalDetails => { personal_details => 'personalDetails' },
passportElementRentalAgreement => { rental_agreement => 'personalDocument' },
passportElementTemporaryRegistration => { temporary_registration => 'personalDocument' },
passportElementUtilityBill => { utility_bill => 'personalDocument' },
passportElements => { elements => 'array:PassportElement' },
passportElementsWithErrors => { elements => 'array:PassportElement', errors => 'array:passportElementError' },
passportRequiredElement => { suitable_elements => 'array:passportSuitableElement' },
passportSuitableElement => { type => 'PassportElementType' },
passwordState => { recovery_email_address_code_info => 'emailAddressAuthenticationCodeInfo' },
paymentForm => { product_info => 'productInfo', type => 'PaymentFormType' },
paymentFormTypeRegular => { additional_payment_options => 'array:paymentOption', invoice => 'invoice', payment_provider => 'PaymentProvider', saved_credentials => 'array:savedCredentials', saved_order_info => 'orderInfo' }...
paymentFormTypeStarSubscription => { pricing => 'starSubscriptionPricing' },
paymentReceipt => { product_info => 'productInfo', type => 'PaymentReceiptType' },
paymentReceiptTypeRegular => { invoice => 'invoice', order_info => 'orderInfo', shipping_option => 'shippingOption' },
personalDetails => { birthdate => 'date' },
personalDocument => { files => 'array:datedFile', translation => 'array:datedFile' },
phoneNumberAuthenticationSettings => { firebase_authentication_settings => 'FirebaseAuthenticationSettings' },
phoneNumberInfo => { country => 'countryInfo' },
photo => { minithumbnail => 'minithumbnail', sizes => 'array:photoSize' },
photoSize => { photo => 'file' },
pingProxy => { proxy => 'proxy' },
lib/EV/Telegram/TDLib/Schema.pm view on Meta::CPAN
sendCallRating => { call_id => 'InputCall', problems => 'array:CallProblem' },
sendChatAction => { action => 'ChatAction', topic_id => 'MessageTopic' },
sendEphemeralMessage => { input_message_content => 'InputMessageContent', reply_markup => 'ReplyMarkup', reply_to => 'InputMessageReplyTo', topic_id => 'MessageTopic' },
sendGift => { owner_id => 'MessageSender', text => 'formattedText' },
sendGiftPurchaseOffer => { owner_id => 'MessageSender', price => 'GiftResalePrice' },
sendGroupCallMessage => { text => 'formattedText' },
sendInlineQueryResultMessage => { options => 'messageSendOptions', reply_to => 'InputMessageReplyTo', topic_id => 'MessageTopic' },
sendMessage => { input_message_content => 'InputMessageContent', options => 'messageSendOptions', reply_markup => 'ReplyMarkup', reply_to => 'InputMessageReplyTo', topic_id => 'MessageTopic' },
sendMessageAlbum => { input_message_contents => 'array:InputMessageContent', options => 'messageSendOptions', reply_to => 'InputMessageReplyTo', topic_id => 'MessageTopic' },
sendPassportAuthorizationForm => { types => 'array:PassportElementType' },
sendPaymentForm => { credentials => 'InputCredentials', input_invoice => 'InputInvoice' },
sendPhoneNumberCode => { settings => 'phoneNumberAuthenticationSettings', type => 'PhoneNumberCodeType' },
sendResoldGift => { owner_id => 'MessageSender', price => 'GiftResalePrice' },
sendRichMessageDraft => { message => 'inputRichMessage' },
sendTextMessageDraft => { text => 'formattedText' },
sentGiftRegular => { gift => 'gift' },
sentGiftUpgraded => { gift => 'upgradedGift' },
session => { device_type => 'SessionDeviceType' },
sessions => { sessions => 'array:session' },
setAccountTtl => { ttl => 'accountTtl' },
setArchiveChatListSettings => { settings => 'archiveChatListSettings' },
t/45_required_args.t view on Meta::CPAN
like $sent[-1], qr/"text":"42"/, 'a numeric-looking string sends as a String';
if ($text > 10) { } # nothing but a comparison
@sent = ();
$td->send_message(-100, $text, sub {});
like $sent[-1], qr/"text":"42"/,
'and still does after the caller compares it numerically';
unlike $sent[-1], qr/"text":42\b/, 'never as a bare JSON Number';
}
# --- the login credentials are string slots too. A phone number written
# without quotes is the obvious way to hit this.
{
my $wait_phone = q({"@type":"updateAuthorizationState",)
. q("authorization_state":{"@type":"authorizationStateWaitPhoneNumber"}});
@sent = ();
my $c = EV::Telegram::TDLib->new(
api_id => 1, api_hash => 'x', phone_number => 79991234567,
database_directory => 't/tmp-authstr', on_error => sub {});
$c->inject_raw($wait_phone);
my ($req) = grep { /setAuthenticationPhoneNumber/ } @sent;
xt/argument_poison.t view on Meta::CPAN
qw(stringified address base64 passed-through followup execute);
cmp_ok $n{stringified}, '>=', 100, "leaky plain_text is caught as a stringified ref at $n{stringified} sites";
cmp_ok $n{address}, '>=', 150, "leaky num is caught as an address at $n{address} sites";
cmp_ok $n{base64}, '>=', 2, "leaky tl_bytes is caught as base64 at $n{base64} sites";
cmp_ok $n{'passed-through'}, '>=', 20, "leaky input_file is caught passing the ref through at $n{'passed-through'} sites";
cmp_ok $n{execute}, '>=', 1, "a leak in a synchronous execute() request is caught at $n{execute} sites";
cmp_ok $n{followup}, '>=', 1, "a leak in a request sent only after a reply is caught at $n{followup} sites";
cmp_ok scalar(keys %$cdiverge), '>=', 50, 'leaky plain_text is caught by the overload rerun at '
. scalar(keys %$cdiverge) . ' sites';
my %nc = map { $_ => scalar keys %{ $ctor->{classes}{$_} || {} } } qw(stringified base64);
cmp_ok $nc{stringified}, '>=', 5, "and in the constructor options and credentials at $nc{stringified} sites";
cmp_ok $nc{base64}, '>=', 1, "including the base64 encryption key at $nc{base64} sites";
cmp_ok scalar(keys %{ $ctor->{diverge} }), '>=', 5, 'and the constructor overload rerun at '
. scalar(keys %{ $ctor->{diverge} }) . ' sites';
diag sprintf 'positive control: %d poisoned and %d overload calls; %d distinct leaks (%s); %d divergences',
$ccount{calls} // 0, $cocount{calls} // 0, scalar(keys %$control),
join(', ', map { "$_ $n{$_}" } sort keys %n), scalar keys %$cdiverge;
}
phase('control');
xt/eg_run.t view on Meta::CPAN
use strict;
use warnings;
use Test::More;
plan skip_all => 'author test: set AUTHOR_TESTING=1' unless $ENV{AUTHOR_TESTING};
plan tests => 5;
# eg/06 needs no credentials and no network: execute() is local and the
# async getOption is answered by TDLib itself, so it can run for real.
# Compiling alone did not catch a wrong reply deref; running does.
my ($out, $rc);
eval {
local $SIG{ALRM} = sub { die "timeout\n" };
alarm 30;
$out = `"$^X" -Mblib eg/06-raw-method.pl 2>&1`;
$rc = $?;
alarm 0;
};