Add support for LoginUrl buttons handling.

GitOrigin-RevId: 3b53d50a6619743830e065f34946869e56f94bcb
This commit is contained in:
levlam 2019-09-07 02:07:56 +03:00
parent e70d12c751
commit 1f22f9e0b7
7 changed files with 242 additions and 6 deletions

View File

@ -349,7 +349,7 @@ chatMemberStatusCreator is_member:Bool = ChatMemberStatus;
//@can_invite_users True, if the administrator can invite new users to the chat //@can_invite_users True, if the administrator can invite new users to the chat
//@can_restrict_members True, if the administrator can restrict, ban, or unban chat members //@can_restrict_members True, if the administrator can restrict, ban, or unban chat members
//@can_pin_messages True, if the administrator can pin messages; applicable to groups only //@can_pin_messages True, if the administrator can pin messages; applicable to groups only
//@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by him //@can_promote_members True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that were directly or indirectly promoted by them
chatMemberStatusAdministrator can_be_edited:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool = ChatMemberStatus; chatMemberStatusAdministrator can_be_edited:Bool can_change_info:Bool can_post_messages:Bool can_edit_messages:Bool can_delete_messages:Bool can_invite_users:Bool can_restrict_members:Bool can_pin_messages:Bool can_promote_members:Bool = ChatMemberStatus;
//@description The user is a member of a chat, without any additional privileges or restrictions //@description The user is a member of a chat, without any additional privileges or restrictions
@ -679,7 +679,7 @@ keyboardButton text:string type:KeyboardButtonType = KeyboardButton;
//@description A button that opens a specified URL @url HTTP or tg:// URL to open //@description A button that opens a specified URL @url HTTP or tg:// URL to open
inlineKeyboardButtonTypeUrl url:string = InlineKeyboardButtonType; inlineKeyboardButtonTypeUrl url:string = InlineKeyboardButtonType;
//@description A button that opens a specified URL and automatically logs in in current user if they allowed to do that @url HTTP URL to open @id Unique button identifier @forward_text If non-empty, new text of the button in forwarded messages //@description A button that opens a specified URL and automatically logs in in current user if they allowed to do that @url An HTTP URL to open @id Unique button identifier @forward_text If non-empty, new text of the button in forwarded messages
inlineKeyboardButtonTypeLoginUrl url:string id:int32 forward_text:string = InlineKeyboardButtonType; inlineKeyboardButtonTypeLoginUrl url:string id:int32 forward_text:string = InlineKeyboardButtonType;
//@description A button that sends a special callback query to a bot @data Data to be sent to the bot via a callback query //@description A button that sends a special callback query to a bot @data Data to be sent to the bot via a callback query
@ -721,6 +721,16 @@ replyMarkupShowKeyboard rows:vector<vector<keyboardButton>> resize_keyboard:Bool
replyMarkupInlineKeyboard rows:vector<vector<inlineKeyboardButton>> = ReplyMarkup; replyMarkupInlineKeyboard rows:vector<vector<inlineKeyboardButton>> = ReplyMarkup;
//@class LoginUrlInfo @description Contains information about an inline button of type inlineKeyboardButtonTypeLoginUrl returned by the method getLoginUrlInfo
//@description An HTTP url needs to be open @url The URL to open @skip_confirm True, if there is no need to show an ordinary open URL confirm
loginUrlInfoOpen url:string skip_confirm:Bool = LoginUrlInfo;
//@description An authorization confirmation dialog needs to be shown to the user @url An HTTP URL to be opened @domain A domain of the URL
//@bot_user_id User identifier of a bot linked with the website @request_write_access True, if the user needs to be requested to give the permission to the bot to send them messages
loginUrlInfoRequestConfirmation url:string domain:string bot_user_id:int32 request_write_access:Bool = LoginUrlInfo;
//@class RichText @description Describes a text object inside an instant-view web page //@class RichText @description Describes a text object inside an instant-view web page
//@description A plain text @text Text //@description A plain text @text Text
@ -2122,7 +2132,7 @@ pushMessageContentVoiceNote voice_note:voiceNote is_pinned:Bool = PushMessageCon
pushMessageContentBasicGroupChatCreate = PushMessageContent; pushMessageContentBasicGroupChatCreate = PushMessageContent;
//@description New chat members were invited to a group @member_name Name of the added member @is_current_user True, if the current user was added to the group //@description New chat members were invited to a group @member_name Name of the added member @is_current_user True, if the current user was added to the group
//@is_returned True, if the user has returned to the group himself //@is_returned True, if the user has returned to the group themself
pushMessageContentChatAddMembers member_name:string is_current_user:Bool is_returned:Bool = PushMessageContent; pushMessageContentChatAddMembers member_name:string is_current_user:Bool is_returned:Bool = PushMessageContent;
//@description A chat photo was edited //@description A chat photo was edited
@ -2132,7 +2142,7 @@ pushMessageContentChatChangePhoto = PushMessageContent;
pushMessageContentChatChangeTitle title:string = PushMessageContent; pushMessageContentChatChangeTitle title:string = PushMessageContent;
//@description A chat member was deleted @member_name Name of the deleted member @is_current_user True, if the current user was deleted from the group //@description A chat member was deleted @member_name Name of the deleted member @is_current_user True, if the current user was deleted from the group
//@is_left True, if the user has left the group himself //@is_left True, if the user has left the group themself
pushMessageContentChatDeleteMember member_name:string is_current_user:Bool is_left:Bool = PushMessageContent; pushMessageContentChatDeleteMember member_name:string is_current_user:Bool is_left:Bool = PushMessageContent;
//@description A new member joined the chat by invite link //@description A new member joined the chat by invite link
@ -3248,6 +3258,17 @@ setPollAnswer chat_id:int53 message_id:int53 option_ids:vector<int32> = Ok;
stopPoll chat_id:int53 message_id:int53 reply_markup:ReplyMarkup = Ok; stopPoll chat_id:int53 message_id:int53 reply_markup:ReplyMarkup = Ok;
//@description Returns information about a button of type inlineKeyboardButtonTypeLoginUrl. The method needs to be called when the user presses the button
//@chat_id Chat identifier of the message with the button @message_id Message identifier of the message with the button @button_id Button identifier
getLoginUrlInfo chat_id:int53 message_id:int53 button_id:int32 = LoginUrlInfo;
//@description Returns an HTTP URL which can be used to automatically authorize the user on a website after clicking an inline button of type inlineKeyboardButtonTypeLoginUrl.
//-Use the method getLoginUrlInfo to find whether a prior user confirmation is needed. If an error is returned, then the button must be handled as an ordinary URL button
//@chat_id Chat identifier of the message with the button @message_id Message identifier of the message with the button @button_id Button identifier
//@allow_write_access True, if the user allowed the bot to send them messages
getLoginUrl chat_id:int53 message_id:int53 button_id:int32 allow_write_access:Bool = HttpUrl;
//@description Sends an inline query to a bot and returns its results. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @bot_user_id The identifier of the target bot //@description Sends an inline query to a bot and returns its results. Returns an error with code 502 if the bot fails to answer the query before the query timeout expires @bot_user_id The identifier of the target bot
//@chat_id Identifier of the chat, where the query was sent @user_location Location of the user, only if needed @query Text of the query @offset Offset of the first entry to return //@chat_id Identifier of the chat, where the query was sent @user_location Location of the user, only if needed @query Text of the query @offset Offset of the first entry to return
getInlineQueryResults bot_user_id:int32 chat_id:int53 user_location:location query:string offset:string = InlineQueryResults; getInlineQueryResults bot_user_id:int32 chat_id:int53 user_location:location query:string offset:string = InlineQueryResults;

Binary file not shown.

View File

@ -3394,7 +3394,6 @@ class GetStatsUrlQuery : public Td::ResultHandler {
} }
void send(DialogId dialog_id, const string &parameters, bool is_dark) { void send(DialogId dialog_id, const string &parameters, bool is_dark) {
// TODO use parameters and is_dark
dialog_id_ = dialog_id; dialog_id_ = dialog_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read); auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
CHECK(input_peer != nullptr); CHECK(input_peer != nullptr);
@ -3422,6 +3421,120 @@ class GetStatsUrlQuery : public Td::ResultHandler {
} }
}; };
class RequestUrlAuthQuery : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> promise_;
string url_;
DialogId dialog_id_;
public:
explicit RequestUrlAuthQuery(Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise)
: promise_(std::move(promise)) {
}
void send(string url, DialogId dialog_id, MessageId message_id, int32 button_id) {
url_ = std::move(url);
dialog_id_ = dialog_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
CHECK(input_peer != nullptr);
send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_requestUrlAuth(
std::move(input_peer), message_id.get_server_message_id().get(), button_id))));
}
void on_result(uint64 id, BufferSlice packet) override {
auto result_ptr = fetch_result<telegram_api::messages_requestUrlAuth>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive " << to_string(result);
switch (result->get_id()) {
case telegram_api::urlAuthResultRequest::ID: {
auto request = telegram_api::move_object_as<telegram_api::urlAuthResultRequest>(result);
UserId bot_user_id = ContactsManager::get_user_id(request->bot_);
if (!bot_user_id.is_valid()) {
return on_error(id, Status::Error(500, "Receive invalid bot_user_id"));
}
td->contacts_manager_->on_get_user(std::move(request->bot_), "RequestUrlAuthQuery");
bool request_write_access =
(request->flags_ & telegram_api::urlAuthResultRequest::REQUEST_WRITE_ACCESS_MASK) != 0;
promise_.set_value(td_api::make_object<td_api::loginUrlInfoRequestConfirmation>(
url_, request->domain_, td->contacts_manager_->get_user_id_object(bot_user_id, "RequestUrlAuthQuery"),
request_write_access));
break;
}
case telegram_api::urlAuthResultAccepted::ID: {
auto accepted = telegram_api::move_object_as<telegram_api::urlAuthResultAccepted>(result);
promise_.set_value(td_api::make_object<td_api::loginUrlInfoOpen>(accepted->url_, true));
break;
}
case telegram_api::urlAuthResultDefault::ID:
promise_.set_value(td_api::make_object<td_api::loginUrlInfoOpen>(url_, false));
break;
}
}
void on_error(uint64 id, Status status) override {
if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "RequestUrlAuthQuery")) {
LOG(INFO) << "RequestUrlAuthQuery returned " << status;
}
promise_.set_value(td_api::make_object<td_api::loginUrlInfoOpen>(url_, false));
}
};
class AcceptUrlAuthQuery : public Td::ResultHandler {
Promise<td_api::object_ptr<td_api::httpUrl>> promise_;
string url_;
DialogId dialog_id_;
public:
explicit AcceptUrlAuthQuery(Promise<td_api::object_ptr<td_api::httpUrl>> &&promise) : promise_(std::move(promise)) {
}
void send(string url, DialogId dialog_id, MessageId message_id, int32 button_id, bool allow_write_access) {
url_ = std::move(url);
dialog_id_ = dialog_id;
auto input_peer = td->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
CHECK(input_peer != nullptr);
int32 flags = 0;
if (allow_write_access) {
flags |= telegram_api::messages_acceptUrlAuth::WRITE_ALLOWED_MASK;
}
send_query(G()->net_query_creator().create(create_storer(telegram_api::messages_acceptUrlAuth(
flags, false /*ignored*/, std::move(input_peer), message_id.get_server_message_id().get(), button_id))));
}
void on_result(uint64 id, BufferSlice packet) override {
auto result_ptr = fetch_result<telegram_api::messages_acceptUrlAuth>(packet);
if (result_ptr.is_error()) {
return on_error(id, result_ptr.move_as_error());
}
auto result = result_ptr.move_as_ok();
LOG(INFO) << "Receive " << to_string(result);
switch (result->get_id()) {
case telegram_api::urlAuthResultRequest::ID:
LOG(ERROR) << "Receive unexpected " << to_string(result);
return on_error(id, Status::Error(500, "Receive unexpected urlAuthResultRequest"));
case telegram_api::urlAuthResultAccepted::ID: {
auto accepted = telegram_api::move_object_as<telegram_api::urlAuthResultAccepted>(result);
promise_.set_value(td_api::make_object<td_api::httpUrl>(accepted->url_));
break;
}
case telegram_api::urlAuthResultDefault::ID:
promise_.set_value(td_api::make_object<td_api::httpUrl>(url_));
break;
}
}
void on_error(uint64 id, Status status) override {
if (!td->messages_manager_->on_get_dialog_error(dialog_id_, status, "AcceptUrlAuthQuery")) {
LOG(INFO) << "AcceptUrlAuthQuery returned " << status;
}
promise_.set_error(std::move(status));
}
};
class GetChannelDifferenceQuery : public Td::ResultHandler { class GetChannelDifferenceQuery : public Td::ResultHandler {
DialogId dialog_id_; DialogId dialog_id_;
int32 pts_; int32 pts_;
@ -6523,6 +6636,64 @@ void MessagesManager::get_dialog_statistics_url(DialogId dialog_id, const string
td_->create_handler<GetStatsUrlQuery>(std::move(promise))->send(dialog_id, parameters, is_dark); td_->create_handler<GetStatsUrlQuery>(std::move(promise))->send(dialog_id, parameters, is_dark);
} }
Result<string> MessagesManager::get_login_button_url(DialogId dialog_id, MessageId message_id, int32 button_id) {
Dialog *d = get_dialog_force(dialog_id);
if (d == nullptr) {
return Status::Error(3, "Chat not found");
}
if (!have_input_peer(dialog_id, AccessRights::Read)) {
return Status::Error(3, "Can't access the chat");
}
auto m = get_message_force(d, message_id, "get_login_button_url");
if (m == nullptr) {
return Status::Error(5, "Message not found");
}
if (m->reply_markup == nullptr || m->reply_markup->type != ReplyMarkup::Type::InlineKeyboard) {
return Status::Error(5, "Message has no inline keyboard");
}
if (!message_id.is_server()) {
// it shouldn't have UrlAuth buttons anyway
return Status::Error(5, "Message is not server");
}
if (dialog_id.get_type() == DialogType::SecretChat) {
// secret chat messages can't have reply markup, so this shouldn't happen now
return Status::Error(5, "Message is in a secret chat");
}
for (auto &row : m->reply_markup->inline_keyboard) {
for (auto &button : row) {
if (button.type == InlineKeyboardButton::Type::UrlAuth && button.id == button_id) {
return button.data;
}
}
}
return Status::Error(5, "Button not found");
}
void MessagesManager::get_login_url_info(DialogId dialog_id, MessageId message_id, int32 button_id,
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise) {
auto r_url = get_login_button_url(dialog_id, message_id, button_id);
if (r_url.is_error()) {
return promise.set_error(r_url.move_as_error());
}
td_->create_handler<RequestUrlAuthQuery>(std::move(promise))
->send(r_url.move_as_ok(), dialog_id, message_id, button_id);
}
void MessagesManager::get_login_url(DialogId dialog_id, MessageId message_id, int32 button_id, bool allow_write_access,
Promise<td_api::object_ptr<td_api::httpUrl>> &&promise) {
auto r_url = get_login_button_url(dialog_id, message_id, button_id);
if (r_url.is_error()) {
return promise.set_error(r_url.move_as_error());
}
td_->create_handler<AcceptUrlAuthQuery>(std::move(promise))
->send(r_url.move_as_ok(), dialog_id, message_id, button_id, allow_write_access);
}
void MessagesManager::load_secret_thumbnail(FileId thumbnail_file_id) { void MessagesManager::load_secret_thumbnail(FileId thumbnail_file_id) {
class Callback : public FileManager::DownloadCallback { class Callback : public FileManager::DownloadCallback {
public: public:

View File

@ -665,10 +665,16 @@ class MessagesManager : public Actor {
void report_dialog(DialogId dialog_id, const tl_object_ptr<td_api::ChatReportReason> &reason, void report_dialog(DialogId dialog_id, const tl_object_ptr<td_api::ChatReportReason> &reason,
const vector<MessageId> &message_ids, Promise<Unit> &&promise); const vector<MessageId> &message_ids, Promise<Unit> &&promise);
void on_get_peer_settings(DialogId dialog_id, tl_object_ptr<telegram_api::peerSettings> &&peer_settings);
void get_dialog_statistics_url(DialogId dialog_id, const string &parameters, bool is_dark, void get_dialog_statistics_url(DialogId dialog_id, const string &parameters, bool is_dark,
Promise<td_api::object_ptr<td_api::httpUrl>> &&promise); Promise<td_api::object_ptr<td_api::httpUrl>> &&promise);
void on_get_peer_settings(DialogId dialog_id, tl_object_ptr<telegram_api::peerSettings> &&peer_settings); void get_login_url_info(DialogId dialog_id, MessageId message_id, int32 button_id,
Promise<td_api::object_ptr<td_api::LoginUrlInfo>> &&promise);
void get_login_url(DialogId dialog_id, MessageId message_id, int32 button_id, bool allow_write_access,
Promise<td_api::object_ptr<td_api::httpUrl>> &&promise);
void before_get_difference(); void before_get_difference();
@ -2263,6 +2269,8 @@ class MessagesManager : public Actor {
void suffix_load_till_date(Dialog *d, int32 date, Promise<> promise); void suffix_load_till_date(Dialog *d, int32 date, Promise<> promise);
void suffix_load_till_message_id(Dialog *d, MessageId message_id, Promise<> promise); void suffix_load_till_message_id(Dialog *d, MessageId message_id, Promise<> promise);
Result<string> get_login_button_url(DialogId dialog_id, MessageId message_id, int32 button_id);
Result<ServerMessageId> get_invoice_message_id(FullMessageId full_message_id); Result<ServerMessageId> get_invoice_message_id(FullMessageId full_message_id);
bool is_broadcast_channel(DialogId dialog_id) const; bool is_broadcast_channel(DialogId dialog_id) const;

View File

@ -7054,6 +7054,20 @@ void Td::on_request(uint64 id, td_api::stopPoll &request) {
std::move(request.reply_markup_), std::move(promise)); std::move(request.reply_markup_), std::move(promise));
} }
void Td::on_request(uint64 id, const td_api::getLoginUrlInfo &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
messages_manager_->get_login_url_info(DialogId(request.chat_id_), MessageId(request.message_id_), request.button_id_,
std::move(promise));
}
void Td::on_request(uint64 id, const td_api::getLoginUrl &request) {
CHECK_IS_USER();
CREATE_REQUEST_PROMISE();
messages_manager_->get_login_url(DialogId(request.chat_id_), MessageId(request.message_id_), request.button_id_,
request.allow_write_access_, std::move(promise));
}
void Td::on_request(uint64 id, td_api::getInlineQueryResults &request) { void Td::on_request(uint64 id, td_api::getInlineQueryResults &request) {
CHECK_IS_USER(); CHECK_IS_USER();
CLEAN_INPUT_STRING(request.query_); CLEAN_INPUT_STRING(request.query_);

View File

@ -874,6 +874,10 @@ class Td final : public NetQueryCallback {
void on_request(uint64 id, td_api::stopPoll &request); void on_request(uint64 id, td_api::stopPoll &request);
void on_request(uint64 id, const td_api::getLoginUrlInfo &request);
void on_request(uint64 id, const td_api::getLoginUrl &request);
void on_request(uint64 id, td_api::getInlineQueryResults &request); void on_request(uint64 id, td_api::getInlineQueryResults &request);
void on_request(uint64 id, td_api::answerInlineQuery &request); void on_request(uint64 id, td_api::answerInlineQuery &request);

View File

@ -475,6 +475,10 @@ class CliClient final : public Actor {
return transform(full_split(trim(message_ids), delimiter), as_message_id); return transform(full_split(trim(message_ids), delimiter), as_message_id);
} }
static int32 as_button_id(Slice str) {
return to_integer<int32>(trim(str));
}
int32 as_user_id(Slice str) const { int32 as_user_id(Slice str) const {
str = trim(str); str = trim(str);
if (str[0] == '@') { if (str[0] == '@') {
@ -3637,6 +3641,20 @@ class CliClient final : public Actor {
std::tie(parameters, is_dark) = split(args); std::tie(parameters, is_dark) = split(args);
send_request(td_api::make_object<td_api::getChatStatisticsUrl>(as_chat_id(args), parameters, as_bool(is_dark))); send_request(td_api::make_object<td_api::getChatStatisticsUrl>(as_chat_id(args), parameters, as_bool(is_dark)));
} else if (op == "glui" || op == "glu" || op == "glua") {
string chat_id;
string message_id;
string button_id;
std::tie(chat_id, args) = split(args);
std::tie(message_id, button_id) = split(args);
if (op == "glui") {
send_request(td_api::make_object<td_api::getLoginUrlInfo>(as_chat_id(chat_id), as_message_id(message_id),
as_button_id(button_id)));
} else {
send_request(td_api::make_object<td_api::getLoginUrl>(as_chat_id(chat_id), as_message_id(message_id),
as_button_id(button_id), op == "glua"));
}
} else if (op == "rsgs" || op == "rchs") { } else if (op == "rsgs" || op == "rchs") {
string supergroup_id; string supergroup_id;
string user_id; string user_id;