Support Terms of Service.

GitOrigin-RevId: 389c4ba590a4feb34ac6d801f9097291e87423d8
This commit is contained in:
levlam 2018-06-07 21:42:17 +03:00
parent 1ee7ea7acc
commit a72494d721
13 changed files with 234 additions and 38 deletions

View File

@ -409,6 +409,7 @@ set(TDLIB_SOURCE
td/telegram/StorageManager.cpp
td/telegram/Td.cpp
td/telegram/TdDb.cpp
td/telegram/TermsOfService.cpp
td/telegram/TopDialogManager.cpp
td/telegram/UpdatesManager.cpp
td/telegram/VideoNotesManager.cpp
@ -534,6 +535,7 @@ set(TDLIB_SOURCE
td/telegram/TdCallback.h
td/telegram/TdDb.h
td/telegram/TdParameters.h
td/telegram/TermsOfService.h
td/telegram/TopDialogManager.h
td/telegram/UniqueId.h
td/telegram/UpdatesManager.h

View File

@ -63,6 +63,20 @@ authenticationCodeInfo phone_number:string type:AuthenticationCodeType next_type
emailAddressAuthenticationCodeInfo email_address_pattern:string length:int32 = EmailAddressAuthenticationCodeInfo;
//@description Represents a part of the text that needs to be formatted in some unusual way @offset Offset of the entity in UTF-16 code points @length Length of the entity, in UTF-16 code points @type Type of the entity
textEntity offset:int32 length:int32 type:TextEntityType = TextEntity;
//@description Contains a list of text entities @entities List of text entities
textEntities entities:vector<textEntity> = TextEntities;
//@description A text with some entities @text The text @entities Entities contained in the text
formattedText text:string entities:vector<textEntity> = FormattedText;
//@description Contains Telegram terms of service @text Text of the terms of service @min_user_age Mininum age of a user which can accept the terms; 0 if any @show_popup True, if a blocking popup with terms of services should be shown to the user
termsOfService text:formattedText min_user_age:int32 show_popup:Bool = TermsOfService;
//@class AuthorizationState @description Represents the current authorization state of the client
//@description TDLib needs TdlibParameters for initialization
@ -74,8 +88,8 @@ authorizationStateWaitEncryptionKey is_encrypted:Bool = AuthorizationState;
//@description TDLib needs the user's phone number to authorize
authorizationStateWaitPhoneNumber = AuthorizationState;
//@description TDLib needs the user's authentication code to finalize authorization @is_registered True, if the user is already registered @code_info Information about the authorization code that was sent
authorizationStateWaitCode is_registered:Bool code_info:authenticationCodeInfo = AuthorizationState;
//@description TDLib needs the user's authentication code to finalize authorization @is_registered True, if the user is already registered @terms_of_service Telegram terms of service, which should be accepted before user can continue registration; may be null @code_info Information about the authorization code that was sent
authorizationStateWaitCode is_registered:Bool terms_of_service:termsOfService code_info:authenticationCodeInfo = AuthorizationState;
//@description The user has been authorized, but needs to enter a password to start using the application @password_hint Hint for the password; can be empty @has_recovery_email_address True if a recovery email address has been set up
//@recovery_email_address_pattern Pattern of the email address to which the recovery email was sent; empty until a recovery email has been sent
@ -173,16 +187,6 @@ maskPointChin = MaskPoint;
maskPosition point:MaskPoint x_shift:double y_shift:double scale:double = MaskPosition;
//@description Represents a part of the text that needs to be formatted in some unusual way @offset Offset of the entity in UTF-16 code points @length Length of the entity, in UTF-16 code points @type Type of the entity
textEntity offset:int32 length:int32 type:TextEntityType = TextEntity;
//@description Contains a list of text entities @entities List of text entities
textEntities entities:vector<textEntity> = TextEntities;
//@description A text with some entities @text The text @entities Entities contained in the text
formattedText text:string entities:vector<textEntity> = FormattedText;
//@description Describes an animation file. The animation must be encoded in GIF or MPEG4 format @duration Duration of the animation, in seconds; as defined by the sender @width Width of the animation @height Height of the animation
//@file_name Original name of the file; as defined by the sender @mime_type MIME type of the file, usually "image/gif" or "video/mp4" @thumbnail Animation thumbnail; may be null @animation File containing the animation
animation duration:int32 width:int32 height:int32 file_name:string mime_type:string thumbnail:photoSize animation:file = Animation;
@ -2176,6 +2180,9 @@ updateSavedAnimations animation_ids:vector<int32> = Update;
//@description The connection state has changed @state The new connection state
updateConnectionState state:ConnectionState = Update;
//@description A new terms of service should be accepted by the user @terms_of_service_id Terms of service identifier @terms_of_service The new terms of service
updateTermsOfService terms_of_service_id:string terms_of_service:termsOfService = Update;
//@description A new incoming inline query; for bots only @id Unique query identifier @sender_user_id Identifier of the user who sent the query @user_location User location, provided by the client; may be null @query Text of the query @offset Offset of the first entry to return
updateNewInlineQuery id:int64 sender_user_id:int32 user_location:location query:string offset:string = Update;
@ -3138,6 +3145,10 @@ setStickerPositionInSet sticker:InputFile position:int32 = Ok;
removeStickerFromSet sticker:InputFile = Ok;
//@description Accepts Telegram terms of services @terms_of_service_id Terms of service identifier
acceptTermsOfService terms_of_service_id:string = Ok;
//@description Sends a custom request; for bots only @method The method name @parameters JSON-serialized method parameters
sendCustomRequest method:string parameters:string = CustomRequestResult;
@ -3155,9 +3166,6 @@ getCountryCode = Text;
//@description Returns the default text for invitation messages to be used as a placeholder when the current user invites friends to Telegram
getInviteText = Text;
//@description Returns the terms of service. Can be called before authorization @country_code A two-letter ISO 3166-1 alpha-2 country code of user's country
getTermsOfService country_code:string = Text;
//@description Returns information about a tg:// deep link. Use "tg://need_update_for_some_feature" or "tg:some_unsupported_feature" for testing. Returns a 404 error for unknown links. Can be called before authorization @link The link
getDeepLinkInfo link:string = DeepLinkInfo;

Binary file not shown.

View File

@ -41,8 +41,10 @@ void SendCodeHelper::on_sent_code(telegram_api::object_ptr<telegram_api::auth_se
next_code_timestamp_ = Timestamp::in((sent_code->flags_ & SENT_CODE_FLAG_HAS_TIMEOUT) != 0 ? sent_code->timeout_ : 0);
}
td_api::object_ptr<td_api::authorizationStateWaitCode> SendCodeHelper::get_authorization_state_wait_code() const {
return make_tl_object<td_api::authorizationStateWaitCode>(phone_registered_, get_authentication_code_info_object());
td_api::object_ptr<td_api::authorizationStateWaitCode> SendCodeHelper::get_authorization_state_wait_code(
const TermsOfService &terms_of_service) const {
return make_tl_object<td_api::authorizationStateWaitCode>(
phone_registered_, terms_of_service.get_terms_of_service_object(), get_authentication_code_info_object());
}
td_api::object_ptr<td_api::authenticationCodeInfo> SendCodeHelper::get_authentication_code_info_object() const {
@ -448,7 +450,7 @@ tl_object_ptr<td_api::AuthorizationState> AuthManager::get_authorization_state_o
case State::Ok:
return make_tl_object<td_api::authorizationStateReady>();
case State::WaitCode:
return send_code_helper_.get_authorization_state_wait_code();
return send_code_helper_.get_authorization_state_wait_code(terms_of_service_);
case State::WaitPhoneNumber:
return make_tl_object<td_api::authorizationStateWaitPhoneNumber>();
case State::WaitPassword:
@ -527,6 +529,7 @@ void AuthManager::set_phone_number(uint64 query_id, string phone_number, bool al
send_code_helper_.send_code(phone_number, allow_flash_call, is_current_phone_number, api_id_, api_hash_);
if (r_send_code.is_error()) {
send_code_helper_ = SendCodeHelper();
terms_of_service_ = TermsOfService();
r_send_code =
send_code_helper_.send_code(phone_number, allow_flash_call, is_current_phone_number, api_id_, api_hash_);
if (r_send_code.is_error()) {
@ -562,10 +565,6 @@ void AuthManager::check_code(uint64 query_id, string code, string first_name, st
if (state_ != State::WaitCode) {
return on_query_error(query_id, Status::Error(8, "checkAuthenticationCode unexpected"));
}
first_name = clean_name(first_name, MAX_NAME_LENGTH);
if (!send_code_helper_.phone_registered() && first_name.empty()) {
return on_query_error(query_id, Status::Error(8, "First name can't be empty"));
}
on_new_query(query_id);
if (send_code_helper_.phone_registered() || first_name.empty()) {
@ -575,6 +574,11 @@ void AuthManager::check_code(uint64 query_id, string code, string first_name, st
send_code_helper_.phone_code_hash().str(), code)),
DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
} else {
first_name = clean_name(first_name, MAX_NAME_LENGTH);
if (first_name.empty()) {
return on_query_error(Status::Error(8, "First name can't be empty"));
}
last_name = clean_name(last_name, MAX_NAME_LENGTH);
start_net_query(
NetQueryType::SignUp,
@ -710,6 +714,8 @@ void AuthManager::on_send_code_result(NetQueryPtr &result) {
LOG(INFO) << "Receive " << to_string(sent_code);
terms_of_service_ = TermsOfService(std::move(sent_code->terms_of_service_));
send_code_helper_.on_sent_code(std::move(sent_code));
update_state(State::WaitCode, true);
@ -864,6 +870,7 @@ void AuthManager::on_result(NetQueryPtr result) {
if (query_id_ != 0) {
if (state_ == State::WaitPhoneNumber) {
send_code_helper_ = SendCodeHelper();
terms_of_service_ = TermsOfService();
}
on_query_error(std::move(result->error()));
}
@ -951,6 +958,7 @@ bool AuthManager::load_state() {
LOG(INFO) << "Load auth_state from db: " << tag("state", static_cast<int32>(db_state.state_));
if (db_state.state_ == State::WaitCode) {
send_code_helper_ = std::move(db_state.send_code_helper_);
terms_of_service_ = std::move(db_state.terms_of_service_);
} else if (db_state.state_ == State::WaitPassword) {
wait_password_state_ = std::move(db_state.wait_password_state_);
} else {
@ -970,7 +978,7 @@ void AuthManager::save_state() {
DbState db_state;
if (state_ == State::WaitCode) {
db_state = DbState::wait_code(api_id_, api_hash_, send_code_helper_);
db_state = DbState::wait_code(api_id_, api_hash_, send_code_helper_, terms_of_service_);
} else if (state_ == State::WaitPassword) {
db_state = DbState::wait_password(api_id_, api_hash_, wait_password_state_);
} else {

View File

@ -8,6 +8,7 @@
#include "td/telegram/net/NetActor.h"
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/TermsOfService.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
@ -22,7 +23,8 @@ namespace td {
class SendCodeHelper {
public:
void on_sent_code(telegram_api::object_ptr<telegram_api::auth_sentCode> sent_code);
td_api::object_ptr<td_api::authorizationStateWaitCode> get_authorization_state_wait_code() const;
td_api::object_ptr<td_api::authorizationStateWaitCode> get_authorization_state_wait_code(
const TermsOfService &terms_of_service) const;
td_api::object_ptr<td_api::authenticationCodeInfo> get_authentication_code_info_object() const;
Result<telegram_api::auth_resendCode> resend_code();
@ -209,16 +211,19 @@ class AuthManager : public NetActor {
// WaitCode
SendCodeHelper send_code_helper_;
TermsOfService terms_of_service_;
//WaitPassword
// WaitPassword
WaitPasswordState wait_password_state_;
static DbState wait_code(int32 api_id, string api_hash, SendCodeHelper send_code_helper) {
static DbState wait_code(int32 api_id, string api_hash, SendCodeHelper send_code_helper,
TermsOfService terms_of_service) {
DbState state;
state.state_ = State::WaitCode;
state.api_id_ = api_id;
state.api_hash_ = api_hash;
state.send_code_helper_ = std::move(send_code_helper);
state.terms_of_service_ = std::move(terms_of_service);
state.state_timestamp_ = Timestamp::now();
return state;
}
@ -251,6 +256,7 @@ class AuthManager : public NetActor {
// State::WaitCode
SendCodeHelper send_code_helper_;
TermsOfService terms_of_service_;
// for bots
string bot_token_;

View File

@ -11,6 +11,7 @@
#include "td/utils/tl_helpers.h"
namespace td {
template <class T>
void SendCodeHelper::AuthenticationCodeInfo::store(T &storer) const {
using td::store;
@ -18,6 +19,7 @@ void SendCodeHelper::AuthenticationCodeInfo::store(T &storer) const {
store(length, storer);
store(pattern, storer);
}
template <class T>
void SendCodeHelper::AuthenticationCodeInfo::parse(T &parser) {
using td::parse;
@ -47,6 +49,7 @@ void SendCodeHelper::parse(T &parser) {
parse(next_code_info_, parser);
parse(next_code_timestamp_, parser);
}
template <class T>
void AuthManager::WaitPasswordState::store(T &storer) const {
using td::store;
@ -70,11 +73,19 @@ void AuthManager::WaitPasswordState::parse(T &parser) {
template <class T>
void AuthManager::DbState::store(T &storer) const {
using td::store;
bool has_terms_of_service = !terms_of_service_.get_id().empty();
BEGIN_STORE_FLAGS();
STORE_FLAG(has_terms_of_service);
END_STORE_FLAGS();
store(state_, storer);
store(api_id_, storer);
store(api_hash_, storer);
store(state_timestamp_, storer);
if (has_terms_of_service) {
store(terms_of_service_, storer);
}
if (state_ == State::WaitCode) {
store(send_code_helper_, storer);
} else if (state_ == State::WaitPassword) {
@ -83,20 +94,35 @@ void AuthManager::DbState::store(T &storer) const {
UNREACHABLE();
}
}
template <class T>
void AuthManager::DbState::parse(T &parser) {
using td::parse;
bool has_terms_of_service = false;
if (parser.version() >= static_cast<int32>(Version::AddTermsOfService)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_terms_of_service);
END_PARSE_FLAGS();
}
parse(state_, parser);
parse(api_id_, parser);
parse(api_hash_, parser);
parse(state_timestamp_, parser);
if (has_terms_of_service) {
parse(terms_of_service_, parser);
}
if (state_ == State::WaitCode) {
parse(send_code_helper_, parser);
if (parser.version() < static_cast<int32>(Version::AddTermsOfService)) {
parser.set_error("Have no terms of service");
}
} else if (state_ == State::WaitPassword) {
parse(wait_password_state_, parser);
} else {
parser.set_error(PSTRING() << "Unexpected " << tag("state", static_cast<int32>(state_)));
}
}
} // namespace td

View File

@ -23539,7 +23539,6 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me
new_photo->photos.push_back(old_photo->photos.back());
if (need_merge_files) {
FileId old_file_id = old_photo->photos.back().file_id;
FileView old_file_view = td_->file_manager_->get_file_view(old_file_id);
FileId new_file_id = new_photo->photos[0].file_id;
FileView new_file_view = td_->file_manager_->get_file_view(new_file_id);
@ -23808,7 +23807,6 @@ bool MessagesManager::update_message_content(DialogId dialog_id, Message *old_me
}
if (is_content_changed || need_update) {
auto old_file_id = get_message_content_file_id(old_content.get());
old_content = std::move(new_content);
update_message_content_file_id_remote(old_content.get(), old_file_id);
} else {

View File

@ -3941,7 +3941,6 @@ bool Td::is_preauthentication_request(int32 id) {
case td_api::addNetworkStatistics::ID:
case td_api::resetNetworkStatistics::ID:
case td_api::getCountryCode::ID:
case td_api::getTermsOfService::ID:
case td_api::getDeepLinkInfo::ID:
case td_api::addProxy::ID:
case td_api::enableProxy::ID:
@ -6946,6 +6945,13 @@ void Td::on_request(uint64 id, td_api::removeRecentHashtag &request) {
send_closure(hashtag_hints_, &HashtagHints::remove_hashtag, std::move(request.hashtag_), std::move(promise));
}
void Td::on_request(uint64 id, td_api::acceptTermsOfService &request) {
CHECK_IS_USER();
CLEAN_INPUT_STRING(request.terms_of_service_id_);
CREATE_OK_REQUEST_PROMISE(promise);
accept_terms_of_service(this, std::move(request.terms_of_service_id_), std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getCountryCode &request) {
CREATE_NO_ARGS_REQUEST(GetCountryCodeRequest);
}
@ -6955,11 +6961,6 @@ void Td::on_request(uint64 id, const td_api::getInviteText &request) {
CREATE_NO_ARGS_REQUEST(GetInviteTextRequest);
}
void Td::on_request(uint64 id, td_api::getTermsOfService &request) {
CLEAN_INPUT_STRING(request.country_code_);
send_error_raw(id, 500, "Unsupported");
}
void Td::on_request(uint64 id, td_api::getDeepLinkInfo &request) {
CLEAN_INPUT_STRING(request.link_);
CREATE_REQUEST_PROMISE(promise);

View File

@ -807,12 +807,12 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, td_api::removeRecentHashtag &request);
void on_request(uint64 id, td_api::acceptTermsOfService &request);
void on_request(uint64 id, const td_api::getCountryCode &request);
void on_request(uint64 id, const td_api::getInviteText &request);
void on_request(uint64 id, td_api::getTermsOfService &request);
void on_request(uint64 id, td_api::getDeepLinkInfo &request);
void on_request(uint64 id, td_api::addProxy &request);

View File

@ -0,0 +1,76 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/TermsOfService.h"
#include "td/telegram/Global.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/NetQueryCreator.h"
#include "td/telegram/Td.h"
#include "td/utils/buffer.h"
#include "td/utils/logging.h"
namespace td {
class AcceptTermsOfServiceQuery : public Td::ResultHandler {
Promise<Unit> promise_;
public:
explicit AcceptTermsOfServiceQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
}
void send(string terms_of_service_id) {
send_query(G()->net_query_creator().create(create_storer(telegram_api::help_acceptTermsOfService(
telegram_api::make_object<telegram_api::dataJSON>(std::move(terms_of_service_id))))));
}
void on_result(uint64 id, BufferSlice packet) override {
auto result_ptr = fetch_result<telegram_api::help_acceptTermsOfService>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
auto result = result_ptr.ok();
if (!result) {
LOG(ERROR) << "Failed to accept terms of service";
}
promise_.set_value(Unit());
}
void on_error(uint64 id, Status status) override {
promise_.set_error(std::move(status));
}
};
TermsOfService::TermsOfService(telegram_api::object_ptr<telegram_api::help_termsOfService> terms) {
if (terms == nullptr) {
return;
}
id_ = std::move(terms->id_->data_);
auto entities = get_message_entities(nullptr, std::move(terms->entities_), "TermsOfService");
auto status = fix_formatted_text(terms->text_, entities, true, true, true, false);
if (status.is_error()) {
if (!clean_input_string(terms->text_)) {
terms->text_.clear();
}
entities.clear();
}
if (terms->text_.empty()) {
id_.clear();
}
text_ = FormattedText{std::move(terms->text_), std::move(entities)};
min_user_age_ =
((terms->flags_ & telegram_api::help_termsOfService::MIN_AGE_CONFIRM_MASK) != 0 ? terms->min_age_confirm_ : 0);
show_popup_ = (terms->flags_ & telegram_api::help_termsOfService::POPUP_MASK) != 0;
}
void accept_terms_of_service(Td *td, string &&terms_of_service_id, Promise<Unit> &&promise) {
td->create_handler<AcceptTermsOfServiceQuery>(std::move(promise))->send(std::move(terms_of_service_id));
}
} // namespace td

View File

@ -0,0 +1,70 @@
//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/telegram/MessageEntity.h"
#include "td/telegram/MessageEntity.hpp"
#include "td/actor/PromiseFuture.h"
#include "td/utils/common.h"
#include "td/utils/tl_helpers.h"
namespace td {
class Td;
class TermsOfService {
string id_;
FormattedText text_;
int32 min_user_age_ = 0;
bool show_popup_ = true;
public:
explicit TermsOfService(telegram_api::object_ptr<telegram_api::help_termsOfService> terms = nullptr);
Slice get_id() const {
return id_;
}
td_api::object_ptr<td_api::termsOfService> get_terms_of_service_object() const {
if (id_.empty()) {
return nullptr;
}
return td_api::make_object<td_api::termsOfService>(get_formatted_text_object(text_), min_user_age_, show_popup_);
}
template <class T>
void store(T &storer) const {
using td::store;
BEGIN_STORE_FLAGS();
STORE_FLAG(show_popup_);
END_STORE_FLAGS();
store(id_, storer);
store(text_, storer);
store(min_user_age_, storer);
}
template <class T>
void parse(T &parser) {
using td::parse;
BEGIN_PARSE_FLAGS();
PARSE_FLAG(show_popup_);
END_PARSE_FLAGS();
parse(id_, parser);
parse(text_, parser);
parse(min_user_age_, parser);
}
};
void accept_terms_of_service(Td *td, string &&terms_of_service_id, Promise<Unit> &&promise);
} // namespace td

View File

@ -24,6 +24,7 @@ enum class Version : int32 {
AddMessageInvoiceProviderData,
AddCaptionEntities,
AddVenueType,
AddTermsOfService,
Next
};

View File

@ -1744,8 +1744,8 @@ class CliClient final : public Actor {
send_request(make_tl_object<td_api::getCountryCode>());
} else if (op == "git") {
send_request(make_tl_object<td_api::getInviteText>());
} else if (op == "gtos") {
send_request(make_tl_object<td_api::getTermsOfService>(args));
} else if (op == "atos") {
send_request(make_tl_object<td_api::acceptTermsOfService>(args));
} else if (op == "gdli") {
send_request(make_tl_object<td_api::getDeepLinkInfo>(args));
} else if (op == "tme") {