From 44dab48739e4bbe7d4612fa0698c572f173fe2ad Mon Sep 17 00:00:00 2001
From: Igor Zhukov <igor.beatle@gmail.com>
Date: Wed, 1 Jul 2015 20:05:18 +0300
Subject: [PATCH] Supported startgroup and invite to group

---
 app/js/controllers.js                | 29 ++++++++++--
 app/js/lib/utils.js                  |  4 ++
 app/js/locales/en-us.json            |  1 +
 app/js/services.js                   | 66 +++++++++++++++++++++++++---
 app/partials/desktop/user_modal.html |  4 ++
 5 files changed, 94 insertions(+), 10 deletions(-)

diff --git a/app/js/controllers.js b/app/js/controllers.js
index 765004d0..77c3f5fc 100644
--- a/app/js/controllers.js
+++ b/app/js/controllers.js
@@ -638,6 +638,9 @@ angular.module('myApp.controllers', ['myApp.i18n'])
       var topMessages = [];
       var topToDialogs = {};
       angular.forEach(dialogsUpdated, function (dialog, peerID) {
+        if ($scope.noUsers && peerID > 0) {
+          return;
+        }
         topToDialogs[dialog.top_message] = dialog;
         topMessages.push(dialog.top_message);
       });
@@ -776,7 +779,11 @@ angular.module('myApp.controllers', ['myApp.i18n'])
           return AppMessagesManager.getSearch({_: 'inputPeerEmpty'}, $scope.search.query, {_: 'inputMessagesFilterEmpty'}, maxID);
         });
       } else {
-        promise = AppMessagesManager.getDialogs($scope.search.query, maxID);
+        var query = $scope.search.query;
+        if ($scope.noUsers) {
+          query = '%pg ' + (query || '');
+        }
+        promise = AppMessagesManager.getDialogs(query, maxID);
       }
 
       return promise.then(function (result) {
@@ -861,7 +868,10 @@ angular.module('myApp.controllers', ['myApp.i18n'])
         return;
       }
 
-      if (!hasMore && !searchMessages && ($scope.search.query || !$scope.dialogs.length)) {
+      if (!hasMore &&
+          !searchMessages &&
+          !$scope.noUsers &&
+          ($scope.search.query || !$scope.dialogs.length)) {
         showMoreConversations();
         return;
       }
@@ -936,7 +946,7 @@ angular.module('myApp.controllers', ['myApp.i18n'])
         }, 500);
       }
 
-      if ($scope.search.query) {
+      if ($scope.search.query && !$scope.noMessages) {
         searchMessages = true;
         loadDialogs();
       }
@@ -2570,6 +2580,19 @@ angular.module('myApp.controllers', ['myApp.i18n'])
       });
     };
 
+    $scope.inviteToGroup = function () {
+      PeersSelectService.selectPeer({
+        confirm_type: 'INVITE_TO_GROUP',
+        noUsers: true
+      }).then(function (peerString) {
+        var peerID = AppPeersManager.getPeerID(peerString);
+        var chatID = peerID < 0 ? -peerID : 0;
+        AppMessagesManager.startBot($scope.user.id, chatID).then(function () {
+          $rootScope.$broadcast('history_focus', {peerString: peerString});
+        });
+      });
+    };
+
     $scope.toggleBlock = function (block) {
       MtpApiManager.invokeApi(block ? 'contacts.block' : 'contacts.unblock', {
         id: AppUsersManager.getUserInput($scope.userID)
diff --git a/app/js/lib/utils.js b/app/js/lib/utils.js
index fdcdc47e..daa63d25 100644
--- a/app/js/lib/utils.js
+++ b/app/js/lib/utils.js
@@ -365,11 +365,15 @@ function versionCompare (ver1, ver2) {
   }
 
   function cleanSearchText (text) {
+    var hasTag = text.charAt(0) == '%';
     text = text.replace(badCharsRe, ' ').replace(trimRe, '');
     text = text.replace(/[^A-Za-z0-9]/g, function (ch) {
       return Config.LatinizeMap[ch] || ch;
     });
     text = text.toLowerCase();
+    if (hasTag) {
+      text = '%' + text;
+    }
 
     return text;
   }
diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json
index 47ac64a1..fc481dcf 100644
--- a/app/js/locales/en-us.json
+++ b/app/js/locales/en-us.json
@@ -115,6 +115,7 @@
 	"user_modal_block_user": "Block user",
 	"user_modal_unblock_user": "Unblock user",
 	"user_modal_delete_chat": "Delete chat",
+	"user_modal_add_to_group": "Add to group",
 	"user_modal_info": "Info",
 	"user_modal_phone": "Phone",
 	"user_modal_about": "About",
diff --git a/app/js/services.js b/app/js/services.js
index 0500d00b..360a2748 100755
--- a/app/js/services.js
+++ b/app/js/services.js
@@ -795,10 +795,10 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
     getPeerSearchText: function (peerID) {
       var text;
       if (peerID > 0) {
-        text = AppUsersManager.getUserSearchText(peerID);
+        text = '%pu ' + AppUsersManager.getUserSearchText(peerID);
       } else if (peerID < 0) {
         var chat = AppChatsManager.getChat(-peerID);
-        text = chat.title || '';
+        text = '%pg ' + (chat.title || '');
       }
       return text;
     },
@@ -973,7 +973,6 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
   NotificationsManager.start();
 
   function getDialogs (query, maxID, limit) {
-
     var curDialogStorage = dialogsStorage;
 
     if (angular.isString(query) && query.length) {
@@ -2006,6 +2005,39 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
     });
   };
 
+  function startBot (botID, chatID, startParam) {
+    if (startParam) {
+      var randomID = bigint(nextRandomInt(0xFFFFFFFF)).shiftLeft(32).add(bigint(nextRandomInt(0xFFFFFFFF))).toString();
+
+      return MtpApiManager.invokeApi('messages.startBot', {
+        bot: AppUsersManager.getUserInput(botID),
+        chat_id: chatID,
+        random_id: randomID,
+        start_param: startParam
+      });
+    }
+
+    var peerID = chatID ? -chatID : botID;
+    var inputPeer = AppPeersManager.getInputPeerByID(peerID);
+
+    if (chatID) {
+      return MtpApiManager.invokeApi('messages.addChatUser', {
+        chat_id: chatID,
+        user_id: AppUsersManager.getUserInput(botID)
+      }).then(function (updates) {
+        ApiUpdatesManager.processUpdateMessage(updates);
+      }, function (error) {
+        if (error && error.type == 'USER_ALREADY_PARTICIPANT') {
+          var bot = AppUsersManager.getUser(botID);
+          sendText(-chatID, '/start@' + bot.username);
+          error.handled = true;
+        }
+      });
+    }
+
+    return sendText(botID, '/start');
+  }
+
   function cancelPendingMessage (randomID) {
     var pendingData = pendingByRandomID[randomID];
 
@@ -2909,6 +2941,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
     sendFile: sendFile,
     sendOther: sendOther,
     forwardMessages: forwardMessages,
+    startBot: startBot,
     openChatInviteLink: openChatInviteLink,
     getMessagePeer: getMessagePeer,
     wrapForDialog: wrapForDialog,
@@ -4549,7 +4582,8 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
                     url = 'tg://addstickers?set=' + path[1];
                     break;
                   default:
-                    url = 'tg://resolve?domain=' + path[0];
+                    var domainQuery = path[0].split('?');
+                    url = 'tg://resolve?domain=' + domainQuery[0] + (domainQuery[1] ? '&' + domainQuery[1] : '');
                 }
               }
             } else { // IP address
@@ -5409,6 +5443,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
   function selectPeer (options) {
     var scope = $rootScope.$new();
     scope.multiSelect = false;
+    scope.noMessages = true;
     if (options) {
       angular.extend(scope, options);
     }
@@ -5430,6 +5465,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
 
     var scope = $rootScope.$new();
     scope.multiSelect = true;
+    scope.noMessages = true;
     if (options) {
       angular.extend(scope, options);
     }
@@ -5651,7 +5687,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
 })
 
 
-.service('LocationParamsService', function ($rootScope, $routeParams, AppPeersManager, AppUsersManager, AppMessagesManager, AppStickersManager) {
+.service('LocationParamsService', function ($rootScope, $routeParams, AppPeersManager, AppUsersManager, AppMessagesManager, PeersSelectService, AppStickersManager) {
 
   var tgAddrRegExp = /^(web\+)?tg:(\/\/)?(.+)/;
 
@@ -5671,10 +5707,26 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
   function handleTgProtoAddr (url, inner) {
     var matches;
 
-    if (matches = url.match(/^resolve\?domain=(.+)$/)) {
+    if (matches = url.match(/^resolve\?domain=(.+?)(?:&(start|startgroup)=(.+))?$/)) {
       AppUsersManager.resolveUsername(matches[1]).then(function (userID) {
+
+        if (matches[2] == 'startgroup') {
+          PeersSelectService.selectPeer({
+            confirm_type: 'INVITE_TO_GROUP',
+            noUsers: true
+          }).then(function (peerString) {
+            var peerID = AppPeersManager.getPeerID(peerString);
+            var chatID = peerID < 0 ? -peerID : 0;
+            AppMessagesManager.startBot(userID, chatID, matches[3]).then(function () {
+              $rootScope.$broadcast('history_focus', {peerString: peerString});
+            });
+          });
+          return true;
+        }
+
         $rootScope.$broadcast('history_focus', {
-          peerString: AppUsersManager.getUserString(userID)
+          peerString: AppUsersManager.getUserString(userID),
+          startParam: matches[3]
         });
       });
       return true;
diff --git a/app/partials/desktop/user_modal.html b/app/partials/desktop/user_modal.html
index dd04eba5..c7a7c5ab 100644
--- a/app/partials/desktop/user_modal.html
+++ b/app/partials/desktop/user_modal.html
@@ -69,6 +69,10 @@
       <div class="md_modal_iconed_section_wrap md_modal_iconed_section_link" ng-init="f.showMoreActions = !user.phone.length">
         <i class="md_modal_section_icon md_modal_section_icon_more"></i>
 
+        <div class="md_modal_section_link_wrap" ng-if="user.pFlags.bot &amp;&amp; !user.pFlags.botNoGroups">
+          <a class="md_modal_section_link" ng-click="inviteToGroup()" my-i18n="user_modal_add_to_group"></a>
+        </div>
+
         <div class="md_modal_section_link_wrap" ng-if="user.phone.length > 0 &amp;&amp; !user.pFlags.contact &amp;&amp; !user.pFlags.self">
           <a class="md_modal_section_link" ng-click="importContact()" my-i18n="user_modal_add_contact"></a>
         </div>