diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 15f840cf2..84d2e7c34 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -4226,6 +4226,24 @@ chatTheme name:string light_settings:themeSettings dark_settings:themeSettings = hashtags hashtags:vector = Hashtags; +//@class CanSendStoryResult @description Represents result of checking whether the current user can send a story + +//@description A story can be sent +canSendStoryResultOk = CanSendStoryResult; + +//@description The user must subscribe to Telegram Premium to be able to post stories +canSendStoryResultPremiumNeeded = CanSendStoryResult; + +//@description The limit for the number of active stories exceeded. The user can buy Telegram Premium, delete an active story, or wait for the oldest story to expire +canSendStoryResultActiveStoryLimitExceeded = CanSendStoryResult; + +//@description The weekly limit for the number of posted stories exceeded. The user needs to buy Telegram Premium or wait specified time @retry_after Time left before the user can send the next story +canSendStoryResultWeeklyLimitExceeded retry_after:int32 = CanSendStoryResult; + +//@description The monthly limit for the number of posted stories exceeded. The user needs to buy Telegram Premium or wait specified time @retry_after Time left before the user can send the next story +canSendStoryResultMonthlyLimitExceeded retry_after:int32 = CanSendStoryResult; + + //@class CanTransferOwnershipResult @description Represents result of checking whether the current session can be used to transfer a chat ownership to another user //@description The session can be used @@ -7425,6 +7443,9 @@ readChatList chat_list:ChatList = Ok; //@only_local Pass true to get only locally available information without sending network requests getStory story_sender_chat_id:int53 story_id:int32 only_local:Bool = Story; +//@description Checks whether the current user can send a story +canSendStory = CanSendStoryResult; + //@description Sends a new story. Returns a temporary story //@content Content of the story //@areas Clickable rectangle areas to be shown on the story media; pass null if none diff --git a/td/telegram/StoryManager.cpp b/td/telegram/StoryManager.cpp index 5f7ada551..ecba69843 100644 --- a/td/telegram/StoryManager.cpp +++ b/td/telegram/StoryManager.cpp @@ -56,6 +56,39 @@ namespace td { +static td_api::object_ptr get_can_send_story_result_object(const Status &error) { + CHECK(error.is_error()); + if (error.message() == "PREMIUM_ACCOUNT_REQUIRED") { + return td_api::make_object(); + } + if (error.message() == "STORIES_TOO_MUCH") { + return td_api::make_object(); + } + if (begins_with(error.message(), "STORY_SEND_FLOOD_WEEKLY_")) { + auto r_next_date = to_integer_safe(error.message().substr(Slice("STORY_SEND_FLOOD_WEEKLY_").size())); + if (r_next_date.is_ok() && r_next_date.ok() > 0) { + auto retry_after = r_next_date.ok() - G()->unix_time(); + if (retry_after > 0) { + return td_api::make_object(retry_after); + } else { + return td_api::make_object(); + } + } + } + if (begins_with(error.message(), "STORY_SEND_FLOOD_MONTHLY_")) { + auto r_next_date = to_integer_safe(error.message().substr(Slice("STORY_SEND_FLOOD_MONTHLY_").size())); + if (r_next_date.is_ok() && r_next_date.ok() > 0) { + auto retry_after = r_next_date.ok() - G()->unix_time(); + if (retry_after > 0) { + return td_api::make_object(retry_after); + } else { + return td_api::make_object(); + } + } + } + return nullptr; +} + class GetAllStoriesQuery final : public Td::ResultHandler { Promise> promise_; @@ -652,6 +685,36 @@ class ActivateStealthModeQuery final : public Td::ResultHandler { } }; +class CanSendStoryQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit CanSendStoryQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send() { + send_query(G()->net_query_creator().create(telegram_api::stories_canSendStory())); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + promise_.set_value(td_api::make_object()); + } + + void on_error(Status status) final { + auto result = get_can_send_story_result_object(status); + if (result != nullptr) { + return promise_.set_value(std::move(result)); + } + return promise_.set_error(std::move(status)); + } +}; + class StoryManager::SendStoryQuery final : public Td::ResultHandler { FileId file_id_; unique_ptr pending_story_; @@ -3778,6 +3841,10 @@ Result StoryManager::get_next_yet_unsent_story_id(DialogId dialog_id) { return StoryId(++story_id); } +void StoryManager::can_send_story(Promise> &&promise) { + td_->create_handler(std::move(promise))->send(); +} + void StoryManager::send_story(td_api::object_ptr &&input_story_content, td_api::object_ptr &&input_areas, td_api::object_ptr &&input_caption, diff --git a/td/telegram/StoryManager.h b/td/telegram/StoryManager.h index 5601df33a..fa24fe58a 100644 --- a/td/telegram/StoryManager.h +++ b/td/telegram/StoryManager.h @@ -198,6 +198,8 @@ class StoryManager final : public Actor { void get_story(DialogId owner_dialog_id, StoryId story_id, bool only_local, Promise> &&promise); + void can_send_story(Promise> &&promise); + void send_story(td_api::object_ptr &&input_story_content, td_api::object_ptr &&input_areas, td_api::object_ptr &&input_caption, diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index 9cd713f5d..c7e5ef8c2 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -5630,6 +5630,12 @@ void Td::on_request(uint64 id, const td_api::getStory &request) { std::move(promise)); } +void Td::on_request(uint64 id, const td_api::canSendStory &request) { + CHECK_IS_USER(); + CREATE_REQUEST_PROMISE(); + story_manager_->can_send_story(std::move(promise)); +} + void Td::on_request(uint64 id, td_api::sendStory &request) { CHECK_IS_USER(); CREATE_REQUEST_PROMISE(); diff --git a/td/telegram/Td.h b/td/telegram/Td.h index 43389169d..81a6e6922 100644 --- a/td/telegram/Td.h +++ b/td/telegram/Td.h @@ -791,6 +791,8 @@ class Td final : public Actor { void on_request(uint64 id, const td_api::getStory &request); + void on_request(uint64 id, const td_api::canSendStory &request); + void on_request(uint64 id, td_api::sendStory &request); void on_request(uint64 id, td_api::editStory &request); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 0cc39c2da..975f49890 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -4040,6 +4040,8 @@ class CliClient final : public Actor { StoryId story_id; get_args(args, story_sender_chat_id, story_id); send_request(td_api::make_object(story_sender_chat_id, story_id, op == "gstl")); + } else if (op == "csst") { + send_request(td_api::make_object()); } else if (op == "ssp" || op == "sspp") { string photo; string caption; diff --git a/td/telegram/net/NetQueryDispatcher.cpp b/td/telegram/net/NetQueryDispatcher.cpp index 53fed7bbd..0d31a2574 100644 --- a/td/telegram/net/NetQueryDispatcher.cpp +++ b/td/telegram/net/NetQueryDispatcher.cpp @@ -64,17 +64,16 @@ void NetQueryDispatcher::dispatch(NetQueryPtr net_query) { return; } - if (net_query->is_ready()) { - if (net_query->is_error()) { - auto code = net_query->error().code(); - if (code == 303) { - try_fix_migrate(net_query); - } else if (code == NetQuery::Resend) { - net_query->resend(); - } else if (code < 0 || code == 500 || code == 420) { - net_query->debug("sent to NetQueryDelayer"); - return send_closure_later(delayer_, &NetQueryDelayer::delay, std::move(net_query)); - } + if (net_query->is_ready() && net_query->is_error()) { + auto code = net_query->error().code(); + if (code == 303) { + try_fix_migrate(net_query); + } else if (code == NetQuery::Resend) { + net_query->resend(); + } else if (code < 0 || code == 500 || + (code == 420 && !begins_with(net_query->error().message(), "STORY_SEND_FLOOD_"))) { + net_query->debug("sent to NetQueryDelayer"); + return send_closure_later(delayer_, &NetQueryDelayer::delay, std::move(net_query)); } }