add_bot_to_chat_box.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "boxes/peers/add_bot_to_chat_box.h"
  8. #include "lang/lang_keys.h"
  9. #include "data/data_user.h"
  10. #include "data/data_chat.h"
  11. #include "data/data_channel.h"
  12. #include "data/data_session.h"
  13. #include "data/data_histories.h"
  14. #include "history/history.h"
  15. #include "main/main_session.h"
  16. #include "boxes/peers/edit_participant_box.h"
  17. #include "boxes/peers/edit_participants_box.h"
  18. #include "boxes/filters/edit_filter_chats_list.h"
  19. #include "ui/boxes/confirm_box.h"
  20. #include "ui/wrap/vertical_layout.h"
  21. #include "ui/wrap/slide_wrap.h"
  22. #include "ui/ui_utility.h"
  23. #include "base/random.h"
  24. #include "base/weak_ptr.h"
  25. #include "api/api_chat_participants.h"
  26. #include "window/window_session_controller.h"
  27. #include "apiwrap.h"
  28. #include "styles/style_boxes.h"
  29. namespace {
  30. class Controller final
  31. : public PeerListController
  32. , public base::has_weak_ptr {
  33. public:
  34. Controller(
  35. not_null<Main::Session*> session,
  36. rpl::producer<not_null<PeerData*>> add,
  37. Fn<void(not_null<PeerData*> chat)> callback);
  38. Main::Session &session() const override;
  39. void prepare() override;
  40. void rowClicked(not_null<PeerListRow*> row) override;
  41. private:
  42. void addRow(not_null<PeerData*> peer);
  43. const not_null<Main::Session*> _session;
  44. Fn<void(not_null<PeerData*> chat)> _callback;
  45. std::vector<not_null<PeerData*>> _list;
  46. bool _prepared = false;
  47. bool _refreshing = false;
  48. rpl::lifetime _lifetime;
  49. };
  50. Controller::Controller(
  51. not_null<Main::Session*> session,
  52. rpl::producer<not_null<PeerData*>> add,
  53. Fn<void(not_null<PeerData*> chat)> callback)
  54. : _session(session)
  55. , _callback(std::move(callback)) {
  56. std::move(
  57. add
  58. ) | rpl::start_with_next([=](not_null<PeerData*> peer) {
  59. if (_prepared) {
  60. addRow(peer);
  61. } else {
  62. _list.push_back(peer);
  63. }
  64. }, _lifetime);
  65. }
  66. Main::Session &Controller::session() const {
  67. return *_session;
  68. }
  69. void Controller::prepare() {
  70. _prepared = true;
  71. for (const auto &peer : _list) {
  72. addRow(peer);
  73. }
  74. }
  75. void Controller::rowClicked(not_null<PeerListRow*> row) {
  76. _callback(row->peer());
  77. }
  78. void Controller::addRow(not_null<PeerData*> peer) {
  79. if (delegate()->peerListFindRow(peer->id.value)) {
  80. return;
  81. }
  82. delegate()->peerListAppendRow(std::make_unique<PeerListRow>(peer));
  83. if (!_refreshing) {
  84. _refreshing = true;
  85. Ui::PostponeCall(this, [=] {
  86. _refreshing = false;
  87. delegate()->peerListRefreshRows();
  88. });
  89. }
  90. }
  91. } // namespace
  92. void AddBotToGroupBoxController::Start(
  93. not_null<Window::SessionController*> controller,
  94. not_null<UserData*> bot,
  95. Scope scope,
  96. const QString &token,
  97. ChatAdminRights requestedRights) {
  98. auto initBox = [=](not_null<PeerListBox*> box) {
  99. box->addButton(tr::lng_cancel(), [box] { box->closeBox(); });
  100. };
  101. controller->show(Box<PeerListBox>(
  102. std::make_unique<AddBotToGroupBoxController>(
  103. controller,
  104. bot,
  105. scope,
  106. token,
  107. requestedRights),
  108. std::move(initBox)));
  109. }
  110. AddBotToGroupBoxController::AddBotToGroupBoxController(
  111. not_null<Window::SessionController*> controller,
  112. not_null<UserData*> bot,
  113. Scope scope,
  114. const QString &token,
  115. ChatAdminRights requestedRights)
  116. : ChatsListBoxController(std::unique_ptr<PeerListSearchController>())
  117. , _controller(controller)
  118. , _bot(bot)
  119. , _scope(scope)
  120. , _token(token)
  121. , _requestedRights(requestedRights)
  122. , _adminToGroup((scope == Scope::GroupAdmin)
  123. || (scope == Scope::All && _bot->botInfo->groupAdminRights != 0))
  124. , _adminToChannel((scope == Scope::ChannelAdmin)
  125. || (scope == Scope::All && _bot->botInfo->channelAdminRights != 0))
  126. , _memberToGroup(scope == Scope::All) {
  127. }
  128. Main::Session &AddBotToGroupBoxController::session() const {
  129. return _bot->session();
  130. }
  131. void AddBotToGroupBoxController::rowClicked(not_null<PeerListRow*> row) {
  132. addBotToGroup(row->peer());
  133. }
  134. void AddBotToGroupBoxController::requestExistingRights(
  135. not_null<ChannelData*> channel) {
  136. if (_existingRightsChannel == channel) {
  137. return;
  138. }
  139. _existingRightsChannel = channel;
  140. _bot->session().api().request(_existingRightsRequestId).cancel();
  141. _existingRightsRequestId = _bot->session().api().request(
  142. MTPchannels_GetParticipant(
  143. _existingRightsChannel->inputChannel,
  144. _bot->input)
  145. ).done([=](const MTPchannels_ChannelParticipant &result) {
  146. result.match([&](const MTPDchannels_channelParticipant &data) {
  147. channel->owner().processUsers(data.vusers());
  148. const auto participant = Api::ChatParticipant(
  149. data.vparticipant(),
  150. channel);
  151. _existingRights = participant.rights().flags;
  152. _existingRank = participant.rank();
  153. _promotedSince = participant.promotedSince();
  154. _promotedBy = participant.by();
  155. addBotToGroup(_existingRightsChannel);
  156. });
  157. }).fail([=] {
  158. _existingRights = ChatAdminRights();
  159. _existingRank = QString();
  160. _promotedSince = 0;
  161. _promotedBy = 0;
  162. addBotToGroup(_existingRightsChannel);
  163. }).send();
  164. }
  165. void AddBotToGroupBoxController::addBotToGroup(not_null<PeerData*> chat) {
  166. if (const auto megagroup = chat->asMegagroup()) {
  167. if (!megagroup->canAddMembers()) {
  168. _controller->show(
  169. Ui::MakeInformBox(tr::lng_error_cant_add_member()));
  170. return;
  171. }
  172. }
  173. if (_existingRightsChannel != chat) {
  174. _existingRights = {};
  175. _existingRank = QString();
  176. _existingRightsChannel = nullptr;
  177. _promotedSince = 0;
  178. _promotedBy = 0;
  179. _bot->session().api().request(_existingRightsRequestId).cancel();
  180. }
  181. const auto requestedAddAdmin = (_scope == Scope::GroupAdmin)
  182. || (_scope == Scope::ChannelAdmin);
  183. if (chat->isChannel()
  184. && requestedAddAdmin
  185. && !_existingRights.has_value()) {
  186. requestExistingRights(chat->asChannel());
  187. return;
  188. }
  189. const auto bot = _bot;
  190. const auto controller = _controller;
  191. const auto close = [=](auto&&...) {
  192. using Way = Window::SectionShow::Way;
  193. controller->hideLayer();
  194. controller->showPeerHistory(chat, Way::ClearStack, ShowAtUnreadMsgId);
  195. };
  196. const auto rights = requestedAddAdmin
  197. ? _requestedRights
  198. : (chat->isBroadcast()
  199. && chat->asBroadcast()->canAddAdmins())
  200. ? bot->botInfo->channelAdminRights
  201. : ((chat->isMegagroup() && chat->asMegagroup()->canAddAdmins())
  202. || (chat->isChat() && chat->asChat()->canAddAdmins()))
  203. ? bot->botInfo->groupAdminRights
  204. : ChatAdminRights();
  205. const auto addingAdmin = requestedAddAdmin || (rights != 0);
  206. const auto show = controller->uiShow();
  207. if (addingAdmin) {
  208. const auto scope = _scope;
  209. const auto token = _token;
  210. const auto done = [=](
  211. ChatAdminRightsInfo newRights,
  212. const QString &rank) {
  213. if (scope == Scope::GroupAdmin) {
  214. chat->session().api().sendBotStart(show, bot, chat, token);
  215. }
  216. close();
  217. };
  218. const auto saveCallback = SaveAdminCallback(
  219. show,
  220. chat,
  221. bot,
  222. done,
  223. close);
  224. auto box = Box<EditAdminBox>(
  225. chat,
  226. bot,
  227. ChatAdminRightsInfo(rights),
  228. _existingRank,
  229. _promotedSince,
  230. _promotedBy ? chat->owner().user(_promotedBy).get() : nullptr,
  231. EditAdminBotFields{
  232. _token,
  233. _existingRights.value_or(ChatAdminRights()),
  234. });
  235. box->setSaveCallback(saveCallback);
  236. controller->show(std::move(box));
  237. } else {
  238. auto callback = crl::guard(this, [=] {
  239. AddBotToGroup(show, bot, chat, _token);
  240. controller->hideLayer();
  241. });
  242. controller->show(Ui::MakeConfirmBox({
  243. tr::lng_bot_sure_invite(tr::now, lt_group, chat->name()),
  244. std::move(callback),
  245. }));
  246. }
  247. }
  248. auto AddBotToGroupBoxController::createRow(not_null<History*> history)
  249. -> std::unique_ptr<ChatsListBoxController::Row> {
  250. if (!needToCreateRow(history->peer)) {
  251. return nullptr;
  252. }
  253. return std::make_unique<Row>(history);
  254. }
  255. bool AddBotToGroupBoxController::needToCreateRow(
  256. not_null<PeerData*> peer) const {
  257. if (const auto chat = peer->asChat()) {
  258. if (onlyAdminToGroup()) {
  259. return chat->canAddAdmins();
  260. } else if (_adminToGroup && chat->canAddAdmins()) {
  261. _groups.fire_copy(peer);
  262. } else if (!onlyAdminToChannel()) {
  263. return chat->canAddMembers();
  264. }
  265. } else if (const auto group = peer->asMegagroup()) {
  266. if (onlyAdminToGroup()) {
  267. return group->canAddAdmins();
  268. } else if (_adminToGroup && group->canAddAdmins()) {
  269. _groups.fire_copy(peer);
  270. } else if (!onlyAdminToChannel()) {
  271. return group->canAddMembers();
  272. }
  273. } else if (const auto channel = peer->asBroadcast()) {
  274. if (onlyAdminToChannel()) {
  275. return channel->canAddAdmins();
  276. } else if (_adminToChannel && channel->canAddAdmins()) {
  277. _channels.fire_copy(peer);
  278. }
  279. }
  280. return false;
  281. }
  282. QString AddBotToGroupBoxController::emptyBoxText() const {
  283. return !session().data().chatsListLoaded()
  284. ? tr::lng_contacts_loading(tr::now)
  285. : _adminToChannel
  286. ? tr::lng_bot_no_chats(tr::now)
  287. : tr::lng_bot_no_groups(tr::now);
  288. }
  289. QString AddBotToGroupBoxController::noResultsText() const {
  290. return !session().data().chatsListLoaded()
  291. ? tr::lng_contacts_loading(tr::now)
  292. : _adminToChannel
  293. ? tr::lng_bot_chats_not_found(tr::now)
  294. : tr::lng_bot_groups_not_found(tr::now);
  295. }
  296. void AddBotToGroupBoxController::updateLabels() {
  297. setSearchNoResultsText(noResultsText());
  298. }
  299. object_ptr<Ui::RpWidget> AddBotToGroupBoxController::prepareAdminnedChats() {
  300. auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
  301. const auto container = result.data();
  302. const auto callback = [=](not_null<PeerData*> chat) {
  303. addBotToGroup(chat);
  304. };
  305. const auto addList = [&](
  306. tr::phrase<> subtitle,
  307. rpl::event_stream<not_null<PeerData*>> &items) {
  308. const auto wrap = container->add(
  309. object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
  310. container,
  311. object_ptr<Ui::VerticalLayout>(container)));
  312. wrap->hide(anim::type::instant);
  313. const auto inner = wrap->entity();
  314. inner->add(CreatePeerListSectionSubtitle(inner, subtitle()));
  315. const auto delegate = inner->lifetime().make_state<
  316. PeerListContentDelegateSimple
  317. >();
  318. const auto controller = inner->lifetime().make_state<Controller>(
  319. &session(),
  320. items.events(),
  321. callback);
  322. const auto content = inner->add(object_ptr<PeerListContent>(
  323. container,
  324. controller));
  325. delegate->setContent(content);
  326. controller->setDelegate(delegate);
  327. items.events() | rpl::take(1) | rpl::start_with_next([=] {
  328. wrap->show(anim::type::instant);
  329. }, inner->lifetime());
  330. };
  331. if (_adminToChannel) {
  332. addList(tr::lng_bot_channels_manage, _channels);
  333. }
  334. if (_adminToGroup) {
  335. addList(tr::lng_bot_groups_manage, _groups);
  336. }
  337. rpl::merge(
  338. _groups.events(),
  339. _channels.events()
  340. ) | rpl::take(1) | rpl::start_with_next([=] {
  341. container->add(CreatePeerListSectionSubtitle(
  342. container,
  343. tr::lng_bot_groups()));
  344. }, container->lifetime());
  345. return result;
  346. }
  347. bool AddBotToGroupBoxController::onlyAdminToGroup() const {
  348. return _adminToGroup && !_memberToGroup && !_adminToChannel;
  349. }
  350. bool AddBotToGroupBoxController::onlyAdminToChannel() const {
  351. return _adminToChannel && !_memberToGroup && !_adminToGroup;
  352. }
  353. void AddBotToGroupBoxController::prepareViewHook() {
  354. delegate()->peerListSetTitle(_adminToChannel
  355. ? tr::lng_bot_choose_chat()
  356. : tr::lng_bot_choose_group());
  357. if ((_adminToGroup && !onlyAdminToGroup())
  358. || (_adminToChannel && !onlyAdminToChannel())) {
  359. delegate()->peerListSetAboveWidget(prepareAdminnedChats());
  360. }
  361. updateLabels();
  362. session().data().chatsListLoadedEvents(
  363. ) | rpl::filter([=](Data::Folder *folder) {
  364. return !folder;
  365. }) | rpl::start_with_next([=] {
  366. updateLabels();
  367. }, lifetime());
  368. }
  369. void AddBotToGroup(
  370. std::shared_ptr<Ui::Show> show,
  371. not_null<UserData*> bot,
  372. not_null<PeerData*> chat,
  373. const QString &startToken) {
  374. if (!startToken.isEmpty()) {
  375. chat->session().api().sendBotStart(show, bot, chat, startToken);
  376. } else {
  377. chat->session().api().chatParticipants().add(show, chat, { 1, bot });
  378. }
  379. if (const auto window = chat->session().tryResolveWindow()) {
  380. window->showPeerHistory(chat);
  381. }
  382. }