1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "facades.h"
9
10 #include "api/api_bot.h"
11 #include "info/info_memento.h"
12 #include "core/click_handler_types.h"
13 #include "core/application.h"
14 #include "media/clip/media_clip_reader.h"
15 #include "window/window_session_controller.h"
16 #include "window/window_peer_menu.h"
17 #include "history/history_item_components.h"
18 #include "base/platform/base_platform_info.h"
19 #include "data/data_peer.h"
20 #include "data/data_user.h"
21 #include "mainwindow.h"
22 #include "mainwidget.h"
23 #include "apiwrap.h"
24 #include "main/main_session.h"
25 #include "main/main_domain.h"
26 #include "ui/boxes/confirm_box.h"
27 #include "boxes/url_auth_box.h"
28 #include "ui/layers/layer_widget.h"
29 #include "lang/lang_keys.h"
30 #include "base/observer.h"
31 #include "history/history.h"
32 #include "history/history_item.h"
33 #include "history/view/media/history_view_media.h"
34 #include "payments/payments_checkout_process.h"
35 #include "data/data_session.h"
36 #include "styles/style_chat.h"
37
38 namespace {
39
CheckMainWidget(not_null<Main::Session * > session)40 [[nodiscard]] MainWidget *CheckMainWidget(not_null<Main::Session*> session) {
41 if (const auto m = App::main()) { // multi good
42 if (&m->session() == session) {
43 return m;
44 }
45 }
46 if (&Core::App().domain().active() != &session->account()) {
47 Core::App().domain().activate(&session->account());
48 }
49 if (const auto m = App::main()) { // multi good
50 if (&m->session() == session) {
51 return m;
52 }
53 }
54 return nullptr;
55 }
56
57 } // namespace
58
59 namespace App {
60
hideSingleUseKeyboard(not_null<const HistoryItem * > message)61 void hideSingleUseKeyboard(not_null<const HistoryItem*> message) {
62 if (const auto m = CheckMainWidget(&message->history()->session())) {
63 m->hideSingleUseKeyboard(message->history()->peer, message->id);
64 }
65 }
66
insertBotCommand(const QString & cmd)67 bool insertBotCommand(const QString &cmd) {
68 if (const auto m = App::main()) { // multi good
69 return m->insertBotCommand(cmd);
70 }
71 return false;
72 }
73
activateBotCommand(Window::SessionController * sessionController,not_null<const HistoryItem * > msg,int row,int column)74 void activateBotCommand(
75 Window::SessionController *sessionController,
76 not_null<const HistoryItem*> msg,
77 int row,
78 int column) {
79 const auto button = HistoryMessageMarkupButton::Get(
80 &msg->history()->owner(),
81 msg->fullId(),
82 row,
83 column);
84 if (!button) {
85 return;
86 }
87
88 using ButtonType = HistoryMessageMarkupButton::Type;
89 switch (button->type) {
90 case ButtonType::Default: {
91 // Copy string before passing it to the sending method
92 // because the original button can be destroyed inside.
93 if (sessionController) {
94 MsgId replyTo = msg->isRegular() ? msg->id : 0;
95 sessionController->content()->sendBotCommand({
96 .peer = msg->history()->peer,
97 .command = QString(button->text),
98 .context = msg->fullId(),
99 .replyTo = replyTo,
100 });
101 }
102 } break;
103
104 case ButtonType::Callback:
105 case ButtonType::Game: {
106 Api::SendBotCallbackData(
107 const_cast<HistoryItem*>(msg.get()),
108 row,
109 column);
110 } break;
111
112 case ButtonType::CallbackWithPassword: {
113 Api::SendBotCallbackDataWithPassword(
114 const_cast<HistoryItem*>(msg.get()),
115 row,
116 column);
117 } break;
118
119 case ButtonType::Buy: {
120 Payments::CheckoutProcess::Start(
121 msg,
122 Payments::Mode::Payment,
123 crl::guard(App::wnd(), [] { App::wnd()->activate(); }));
124 } break;
125
126 case ButtonType::Url: {
127 auto url = QString::fromUtf8(button->data);
128 auto skipConfirmation = false;
129 if (const auto bot = msg->getMessageBot()) {
130 if (bot->isVerified()) {
131 skipConfirmation = true;
132 }
133 }
134 if (skipConfirmation) {
135 UrlClickHandler::Open(url);
136 } else {
137 HiddenUrlClickHandler::Open(url);
138 }
139 } break;
140
141 case ButtonType::RequestLocation: {
142 hideSingleUseKeyboard(msg);
143 Ui::show(Box<Ui::InformBox>(
144 tr::lng_bot_share_location_unavailable(tr::now)));
145 } break;
146
147 case ButtonType::RequestPhone: {
148 hideSingleUseKeyboard(msg);
149 const auto msgId = msg->id;
150 const auto history = msg->history();
151 Ui::show(Box<Ui::ConfirmBox>(
152 tr::lng_bot_share_phone(tr::now),
153 tr::lng_bot_share_phone_confirm(tr::now),
154 [=] {
155 Ui::showPeerHistory(history, ShowAtTheEndMsgId);
156 auto action = Api::SendAction(history);
157 action.clearDraft = false;
158 action.replyTo = msgId;
159 history->session().api().shareContact(
160 history->session().user(),
161 action);
162 }));
163 } break;
164
165 case ButtonType::RequestPoll: {
166 hideSingleUseKeyboard(msg);
167 auto chosen = PollData::Flags();
168 auto disabled = PollData::Flags();
169 if (!button->data.isEmpty()) {
170 disabled |= PollData::Flag::Quiz;
171 if (button->data[0]) {
172 chosen |= PollData::Flag::Quiz;
173 }
174 }
175 if (const auto m = CheckMainWidget(&msg->history()->session())) {
176 const auto replyToId = MsgId(0);
177 Window::PeerMenuCreatePoll(
178 m->controller(),
179 msg->history()->peer,
180 replyToId,
181 chosen,
182 disabled);
183 }
184 } break;
185
186 case ButtonType::SwitchInlineSame:
187 case ButtonType::SwitchInline: {
188 const auto session = &msg->history()->session();
189 if (const auto m = CheckMainWidget(session)) {
190 if (const auto bot = msg->getMessageBot()) {
191 const auto fastSwitchDone = [&] {
192 auto samePeer = (button->type == ButtonType::SwitchInlineSame);
193 if (samePeer) {
194 Notify::switchInlineBotButtonReceived(session, QString::fromUtf8(button->data), bot, msg->id);
195 return true;
196 } else if (bot->isBot() && bot->botInfo->inlineReturnTo.key) {
197 if (Notify::switchInlineBotButtonReceived(session, QString::fromUtf8(button->data))) {
198 return true;
199 }
200 }
201 return false;
202 }();
203 if (!fastSwitchDone) {
204 m->inlineSwitchLayer('@' + bot->username + ' ' + QString::fromUtf8(button->data));
205 }
206 }
207 }
208 } break;
209
210 case ButtonType::Auth:
211 UrlAuthBox::Activate(msg, row, column);
212 break;
213 }
214 }
215
216 } // namespace App
217
218 namespace Ui {
219
showPeerProfile(not_null<PeerData * > peer)220 void showPeerProfile(not_null<PeerData*> peer) {
221 if (const auto window = App::wnd()) { // multi good
222 if (const auto controller = window->sessionController()) {
223 if (&controller->session() == &peer->session()) {
224 controller->showPeerInfo(peer);
225 return;
226 }
227 }
228 if (&Core::App().domain().active() != &peer->session().account()) {
229 Core::App().domain().activate(&peer->session().account());
230 }
231 if (const auto controller = window->sessionController()) {
232 if (&controller->session() == &peer->session()) {
233 controller->showPeerInfo(peer);
234 }
235 }
236 }
237 }
238
showPeerProfile(not_null<const History * > history)239 void showPeerProfile(not_null<const History*> history) {
240 showPeerProfile(history->peer);
241 }
242
showChatsList(not_null<Main::Session * > session)243 void showChatsList(not_null<Main::Session*> session) {
244 if (const auto m = CheckMainWidget(session)) {
245 m->ui_showPeerHistory(
246 0,
247 ::Window::SectionShow::Way::ClearStack,
248 0);
249 }
250 }
251
showPeerHistory(not_null<const History * > history,MsgId msgId)252 void showPeerHistory(not_null<const History*> history, MsgId msgId) {
253 showPeerHistory(history->peer, msgId);
254 }
255
showPeerHistory(not_null<const PeerData * > peer,MsgId msgId)256 void showPeerHistory(not_null<const PeerData*> peer, MsgId msgId) {
257 if (const auto m = CheckMainWidget(&peer->session())) {
258 m->ui_showPeerHistory(
259 peer->id,
260 ::Window::SectionShow::Way::ClearStack,
261 msgId);
262 }
263 }
264
skipPaintEvent(QWidget * widget,QPaintEvent * event)265 bool skipPaintEvent(QWidget *widget, QPaintEvent *event) {
266 if (auto w = App::wnd()) {
267 if (w->contentOverlapped(widget, event)) {
268 return true;
269 }
270 }
271 return false;
272 }
273
274 } // namespace Ui
275
276 namespace Notify {
277
switchInlineBotButtonReceived(not_null<Main::Session * > session,const QString & query,UserData * samePeerBot,MsgId samePeerReplyTo)278 bool switchInlineBotButtonReceived(
279 not_null<Main::Session*> session,
280 const QString &query,
281 UserData *samePeerBot,
282 MsgId samePeerReplyTo) {
283 if (const auto m = CheckMainWidget(session)) {
284 return m->notify_switchInlineBotButtonReceived(
285 query,
286 samePeerBot,
287 samePeerReplyTo);
288 }
289 return false;
290 }
291
292 } // namespace Notify
293