1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/GameManager.h"
8
9 #include "td/telegram/AccessRights.h"
10 #include "td/telegram/AuthManager.h"
11 #include "td/telegram/ContactsManager.h"
12 #include "td/telegram/DialogId.h"
13 #include "td/telegram/Global.h"
14 #include "td/telegram/InlineQueriesManager.h"
15 #include "td/telegram/MessageContentType.h"
16 #include "td/telegram/MessageId.h"
17 #include "td/telegram/MessagesManager.h"
18 #include "td/telegram/net/DcId.h"
19 #include "td/telegram/net/NetActor.h"
20 #include "td/telegram/net/NetQueryCreator.h"
21 #include "td/telegram/SequenceDispatcher.h"
22 #include "td/telegram/Td.h"
23 #include "td/telegram/UpdatesManager.h"
24
25 #include "td/utils/buffer.h"
26 #include "td/utils/logging.h"
27 #include "td/utils/Status.h"
28
29 namespace td {
30
31 class SetGameScoreActor final : public NetActorOnce {
32 Promise<Unit> promise_;
33 DialogId dialog_id_;
34
35 public:
SetGameScoreActor(Promise<Unit> && promise)36 explicit SetGameScoreActor(Promise<Unit> &&promise) : promise_(std::move(promise)) {
37 }
38
send(DialogId dialog_id,MessageId message_id,bool edit_message,tl_object_ptr<telegram_api::InputUser> input_user,int32 score,bool force,uint64 sequence_dispatcher_id)39 void send(DialogId dialog_id, MessageId message_id, bool edit_message,
40 tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force, uint64 sequence_dispatcher_id) {
41 int32 flags = 0;
42 if (edit_message) {
43 flags |= telegram_api::messages_setGameScore::EDIT_MESSAGE_MASK;
44 }
45 if (force) {
46 flags |= telegram_api::messages_setGameScore::FORCE_MASK;
47 }
48
49 dialog_id_ = dialog_id;
50
51 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Edit);
52 if (input_peer == nullptr) {
53 on_error(Status::Error(400, "Can't access the chat"));
54 stop();
55 return;
56 }
57
58 CHECK(input_user != nullptr);
59 auto query = G()->net_query_creator().create(
60 telegram_api::messages_setGameScore(flags, false /*ignored*/, false /*ignored*/, std::move(input_peer),
61 message_id.get_server_message_id().get(), std::move(input_user), score));
62
63 query->debug("send to MultiSequenceDispatcher");
64 send_closure(td_->messages_manager_->sequence_dispatcher_, &MultiSequenceDispatcher::send_with_callback,
65 std::move(query), actor_shared(this), sequence_dispatcher_id);
66 }
67
on_result(BufferSlice packet)68 void on_result(BufferSlice packet) final {
69 auto result_ptr = fetch_result<telegram_api::messages_setGameScore>(packet);
70 if (result_ptr.is_error()) {
71 return on_error(result_ptr.move_as_error());
72 }
73
74 auto ptr = result_ptr.move_as_ok();
75 LOG(INFO) << "Receive result for SetGameScore: " << to_string(ptr);
76 td_->updates_manager_->on_get_updates(std::move(ptr), std::move(promise_));
77 }
78
on_error(Status status)79 void on_error(Status status) final {
80 LOG(INFO) << "Receive error for SetGameScore: " << status;
81 td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "SetGameScoreActor");
82 promise_.set_error(std::move(status));
83 }
84 };
85
86 class SetInlineGameScoreQuery final : public Td::ResultHandler {
87 Promise<Unit> promise_;
88
89 public:
SetInlineGameScoreQuery(Promise<Unit> && promise)90 explicit SetInlineGameScoreQuery(Promise<Unit> &&promise) : promise_(std::move(promise)) {
91 }
92
send(tl_object_ptr<telegram_api::InputBotInlineMessageID> input_bot_inline_message_id,bool edit_message,tl_object_ptr<telegram_api::InputUser> input_user,int32 score,bool force)93 void send(tl_object_ptr<telegram_api::InputBotInlineMessageID> input_bot_inline_message_id, bool edit_message,
94 tl_object_ptr<telegram_api::InputUser> input_user, int32 score, bool force) {
95 CHECK(input_bot_inline_message_id != nullptr);
96 CHECK(input_user != nullptr);
97
98 int32 flags = 0;
99 if (edit_message) {
100 flags |= telegram_api::messages_setInlineGameScore::EDIT_MESSAGE_MASK;
101 }
102 if (force) {
103 flags |= telegram_api::messages_setInlineGameScore::FORCE_MASK;
104 }
105
106 auto dc_id = DcId::internal(InlineQueriesManager::get_inline_message_dc_id(input_bot_inline_message_id));
107 send_query(G()->net_query_creator().create(
108 telegram_api::messages_setInlineGameScore(flags, false /*ignored*/, false /*ignored*/,
109 std::move(input_bot_inline_message_id), std::move(input_user), score),
110 dc_id));
111 }
112
on_result(BufferSlice packet)113 void on_result(BufferSlice packet) final {
114 auto result_ptr = fetch_result<telegram_api::messages_setInlineGameScore>(packet);
115 if (result_ptr.is_error()) {
116 return on_error(result_ptr.move_as_error());
117 }
118
119 LOG_IF(ERROR, !result_ptr.ok()) << "Receive false in result of setInlineGameScore";
120
121 promise_.set_value(Unit());
122 }
123
on_error(Status status)124 void on_error(Status status) final {
125 LOG(INFO) << "Receive error for SetInlineGameScoreQuery: " << status;
126 promise_.set_error(std::move(status));
127 }
128 };
129
130 class GetGameHighScoresQuery final : public Td::ResultHandler {
131 Promise<td_api::object_ptr<td_api::gameHighScores>> promise_;
132 DialogId dialog_id_;
133
134 public:
GetGameHighScoresQuery(Promise<td_api::object_ptr<td_api::gameHighScores>> && promise)135 explicit GetGameHighScoresQuery(Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise)
136 : promise_(std::move(promise)) {
137 }
138
send(DialogId dialog_id,MessageId message_id,tl_object_ptr<telegram_api::InputUser> input_user)139 void send(DialogId dialog_id, MessageId message_id, tl_object_ptr<telegram_api::InputUser> input_user) {
140 dialog_id_ = dialog_id;
141
142 auto input_peer = td_->messages_manager_->get_input_peer(dialog_id, AccessRights::Read);
143 CHECK(input_peer != nullptr);
144
145 CHECK(input_user != nullptr);
146 send_query(G()->net_query_creator().create(telegram_api::messages_getGameHighScores(
147 std::move(input_peer), message_id.get_server_message_id().get(), std::move(input_user))));
148 }
149
on_result(BufferSlice packet)150 void on_result(BufferSlice packet) final {
151 auto result_ptr = fetch_result<telegram_api::messages_getGameHighScores>(packet);
152 if (result_ptr.is_error()) {
153 return on_error(result_ptr.move_as_error());
154 }
155
156 promise_.set_value(td_->game_manager_->get_game_high_scores_object(result_ptr.move_as_ok()));
157 }
158
on_error(Status status)159 void on_error(Status status) final {
160 td_->messages_manager_->on_get_dialog_error(dialog_id_, status, "GetGameHighScoresQuery");
161 promise_.set_error(std::move(status));
162 }
163 };
164
165 class GetInlineGameHighScoresQuery final : public Td::ResultHandler {
166 Promise<td_api::object_ptr<td_api::gameHighScores>> promise_;
167
168 public:
GetInlineGameHighScoresQuery(Promise<td_api::object_ptr<td_api::gameHighScores>> && promise)169 explicit GetInlineGameHighScoresQuery(Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise)
170 : promise_(std::move(promise)) {
171 }
172
send(tl_object_ptr<telegram_api::InputBotInlineMessageID> input_bot_inline_message_id,tl_object_ptr<telegram_api::InputUser> input_user)173 void send(tl_object_ptr<telegram_api::InputBotInlineMessageID> input_bot_inline_message_id,
174 tl_object_ptr<telegram_api::InputUser> input_user) {
175 CHECK(input_bot_inline_message_id != nullptr);
176 CHECK(input_user != nullptr);
177
178 auto dc_id = DcId::internal(InlineQueriesManager::get_inline_message_dc_id(input_bot_inline_message_id));
179 send_query(G()->net_query_creator().create(
180 telegram_api::messages_getInlineGameHighScores(std::move(input_bot_inline_message_id), std::move(input_user)),
181 dc_id));
182 }
183
on_result(BufferSlice packet)184 void on_result(BufferSlice packet) final {
185 auto result_ptr = fetch_result<telegram_api::messages_getInlineGameHighScores>(packet);
186 if (result_ptr.is_error()) {
187 return on_error(result_ptr.move_as_error());
188 }
189
190 promise_.set_value(td_->game_manager_->get_game_high_scores_object(result_ptr.move_as_ok()));
191 }
192
on_error(Status status)193 void on_error(Status status) final {
194 promise_.set_error(std::move(status));
195 }
196 };
197
GameManager(Td * td,ActorShared<> parent)198 GameManager::GameManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) {
199 }
200
201 GameManager::~GameManager() = default;
202
tear_down()203 void GameManager::tear_down() {
204 parent_.reset();
205 }
206
set_game_score(FullMessageId full_message_id,bool edit_message,UserId user_id,int32 score,bool force,Promise<td_api::object_ptr<td_api::message>> && promise)207 void GameManager::set_game_score(FullMessageId full_message_id, bool edit_message, UserId user_id, int32 score,
208 bool force, Promise<td_api::object_ptr<td_api::message>> &&promise) {
209 CHECK(td_->auth_manager_->is_bot());
210
211 if (!td_->messages_manager_->have_message_force(full_message_id, "set_game_score")) {
212 return promise.set_error(Status::Error(400, "Message not found"));
213 }
214
215 auto dialog_id = full_message_id.get_dialog_id();
216 if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Edit)) {
217 return promise.set_error(Status::Error(400, "Can't access the chat"));
218 }
219
220 auto input_user = td_->contacts_manager_->get_input_user(user_id);
221 if (input_user == nullptr) {
222 return promise.set_error(Status::Error(400, "Invalid user identifier specified"));
223 }
224
225 if (!td_->messages_manager_->can_set_game_score(full_message_id)) {
226 return promise.set_error(Status::Error(400, "Game score can't be set"));
227 }
228
229 auto query_promise = PromiseCreator::lambda(
230 [actor_id = actor_id(this), full_message_id, promise = std::move(promise)](Result<Unit> &&result) mutable {
231 if (result.is_error()) {
232 return promise.set_error(result.move_as_error());
233 }
234 send_closure(actor_id, &GameManager::on_set_game_score, full_message_id, std::move(promise));
235 });
236 send_closure(td_->create_net_actor<SetGameScoreActor>(std::move(query_promise)), &SetGameScoreActor::send, dialog_id,
237 full_message_id.get_message_id(), edit_message, std::move(input_user), score, force,
238 MessagesManager::get_sequence_dispatcher_id(dialog_id, MessageContentType::None));
239 }
240
on_set_game_score(FullMessageId full_message_id,Promise<td_api::object_ptr<td_api::message>> && promise)241 void GameManager::on_set_game_score(FullMessageId full_message_id,
242 Promise<td_api::object_ptr<td_api::message>> &&promise) {
243 promise.set_value(td_->messages_manager_->get_message_object(full_message_id, "on_set_game_score"));
244 }
245
set_inline_game_score(const string & inline_message_id,bool edit_message,UserId user_id,int32 score,bool force,Promise<Unit> && promise)246 void GameManager::set_inline_game_score(const string &inline_message_id, bool edit_message, UserId user_id, int32 score,
247 bool force, Promise<Unit> &&promise) {
248 CHECK(td_->auth_manager_->is_bot());
249
250 auto input_bot_inline_message_id = td_->inline_queries_manager_->get_input_bot_inline_message_id(inline_message_id);
251 if (input_bot_inline_message_id == nullptr) {
252 return promise.set_error(Status::Error(400, "Invalid inline message identifier specified"));
253 }
254
255 auto input_user = td_->contacts_manager_->get_input_user(user_id);
256 if (input_user == nullptr) {
257 return promise.set_error(Status::Error(400, "Wrong user identifier specified"));
258 }
259
260 td_->create_handler<SetInlineGameScoreQuery>(std::move(promise))
261 ->send(std::move(input_bot_inline_message_id), edit_message, std::move(input_user), score, force);
262 }
263
get_game_high_scores(FullMessageId full_message_id,UserId user_id,Promise<td_api::object_ptr<td_api::gameHighScores>> && promise)264 void GameManager::get_game_high_scores(FullMessageId full_message_id, UserId user_id,
265 Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise) {
266 CHECK(td_->auth_manager_->is_bot());
267
268 if (!td_->messages_manager_->have_message_force(full_message_id, "get_game_high_scores")) {
269 return promise.set_error(Status::Error(400, "Message not found"));
270 }
271
272 auto dialog_id = full_message_id.get_dialog_id();
273 if (!td_->messages_manager_->have_input_peer(dialog_id, AccessRights::Read)) {
274 return promise.set_error(Status::Error(400, "Can't access the chat"));
275 }
276 auto message_id = full_message_id.get_message_id();
277 if (message_id.is_scheduled() || !message_id.is_server()) {
278 return promise.set_error(Status::Error(400, "Wrong message identifier specified"));
279 }
280
281 auto input_user = td_->contacts_manager_->get_input_user(user_id);
282 if (input_user == nullptr) {
283 return promise.set_error(Status::Error(400, "Wrong user identifier specified"));
284 }
285
286 td_->create_handler<GetGameHighScoresQuery>(std::move(promise))->send(dialog_id, message_id, std::move(input_user));
287 }
288
get_inline_game_high_scores(const string & inline_message_id,UserId user_id,Promise<td_api::object_ptr<td_api::gameHighScores>> && promise)289 void GameManager::get_inline_game_high_scores(const string &inline_message_id, UserId user_id,
290 Promise<td_api::object_ptr<td_api::gameHighScores>> &&promise) {
291 CHECK(td_->auth_manager_->is_bot());
292
293 auto input_bot_inline_message_id = td_->inline_queries_manager_->get_input_bot_inline_message_id(inline_message_id);
294 if (input_bot_inline_message_id == nullptr) {
295 return promise.set_error(Status::Error(400, "Invalid inline message identifier specified"));
296 }
297
298 auto input_user = td_->contacts_manager_->get_input_user(user_id);
299 if (input_user == nullptr) {
300 return promise.set_error(Status::Error(400, "Wrong user identifier specified"));
301 }
302
303 td_->create_handler<GetInlineGameHighScoresQuery>(std::move(promise))
304 ->send(std::move(input_bot_inline_message_id), std::move(input_user));
305 }
306
get_game_high_scores_object(telegram_api::object_ptr<telegram_api::messages_highScores> && high_scores)307 td_api::object_ptr<td_api::gameHighScores> GameManager::get_game_high_scores_object(
308 telegram_api::object_ptr<telegram_api::messages_highScores> &&high_scores) {
309 td_->contacts_manager_->on_get_users(std::move(high_scores->users_), "get_game_high_scores_object");
310
311 auto result = td_api::make_object<td_api::gameHighScores>();
312 for (const auto &high_score : high_scores->scores_) {
313 int32 position = high_score->pos_;
314 UserId user_id(high_score->user_id_);
315 int32 score = high_score->score_;
316 if (position <= 0 || !user_id.is_valid() || score < 0) {
317 LOG(ERROR) << "Receive wrong " << to_string(high_score);
318 continue;
319 }
320 result->scores_.push_back(make_tl_object<td_api::gameHighScore>(
321 position, td_->contacts_manager_->get_user_id_object(user_id, "get_game_high_scores_object"), score));
322 }
323 return result;
324 }
325
326 } // namespace td
327