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