view release on metacpan or search on metacpan
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/ (from the distribution root: "perl -Mblib
eg/NAME.pl"; credentials come from the environment, see "ENVIRONMENT"):
eg/01-login.pl
user login with phone, SMS code and 2FA; creates the session
database
eg/02-bot-echo.pl
bot login via token; echoes incoming text messages
eg/03-list-chats.pl
loads the chat list and prints id and title per chat
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 -Mblib eg/01-login.pl
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 -Mblib eg/02-bot-echo.pl
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
eg/03-list-chats.pl view on Meta::CPAN
# 03-list-chats.pl - load the chat list and print id and title of each chat
#
# 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 (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 -Mblib eg/03-list-chats.pl
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 -Mblib eg/04-send-message.pl CHAT_ID [TEXT]
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
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 -Mblib eg/05-download-file.pl FILE_ID
use strict;
use warnings;
use EV;
use EV::Telegram::TDLib;
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 -Mblib eg/06-raw-method.pl
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 -Mblib eg/10-webapp-bot.pl
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/> (from the distribution root:
C<perl -Mblib eg/NAME.pl>; credentials come from the environment, see
L</ENVIRONMENT>):
=over 4
=item F<eg/01-login.pl>
user login with phone, SMS code and 2FA; creates the session database
=item F<eg/02-bot-echo.pl>
lib/EV/Telegram/TDLib/Cookbook.pod view on Meta::CPAN
(TD_API_ID, TD_API_HASH, TD_PHONE, TD_BOT_TOKEN) -- a convention of
these recipes and F<eg/>, not the module's API.
=item *
Every snippet ends up inside a running EV loop; stand-alone recipes
finish with C<EV::run>.
=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' },
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;
};