td_example.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. //
  2. // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
  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/Client.h>
  8. #include <td/telegram/Log.h>
  9. #include <td/telegram/td_api.h>
  10. #include <td/telegram/td_api.hpp>
  11. #include <cstdint>
  12. #include <functional>
  13. #include <iostream>
  14. #include <limits>
  15. #include <map>
  16. #include <sstream>
  17. #include <string>
  18. #include <vector>
  19. #include <thread>
  20. #include <chrono>
  21. #include <algorithm>
  22. #include <stack>
  23. #include <variant>
  24. #include "simple/support/misc.hpp"
  25. #include "simple/support/function_utils.hpp"
  26. using namespace std::chrono_literals;
  27. // Simple single-threaded example of TDLib usage.
  28. // Real world programs should use separate thread for the user input.
  29. // Example includes user authentication, receiving updates, getting chat list and sending text messages.
  30. using simple::support::overloaded;
  31. namespace td_api = td::td_api;
  32. std::string trim(const std::string& view)
  33. {
  34. auto isspace = [](char c){ return std::isspace(c, std::locale{}); };
  35. auto begin = std::find_if_not(view.begin(), view.end(), isspace);
  36. auto end = std::find_if_not(view.rbegin(), view.rend(), isspace);
  37. return {begin, end.base()};
  38. }
  39. template <typename Number>
  40. class postfix_calculator
  41. {
  42. using input_stack = std::deque<std::string>;
  43. using number_stack = std::stack<Number>;
  44. input_stack stack;
  45. std::unordered_map<std::string, const char*(*)(number_stack&)> operations {
  46. { "+", +[](number_stack& s)
  47. {
  48. if(s.size() < 2)
  49. return "Addition requires 2 operands";
  50. auto b = s.top(); s.pop();
  51. auto a = s.top(); s.pop();
  52. std::cout << "got " << a << " and " << b << " from stack" << '\n';
  53. s.push(a + b);
  54. std::cout << "pushed " << a + b << " to stack" << '\n';
  55. return "";
  56. }
  57. },
  58. { "-", +[](number_stack& s)
  59. {
  60. if(s.size() < 2)
  61. return "Subtraction requires 2 operands";
  62. auto b = s.top(); s.pop();
  63. auto a = s.top(); s.pop();
  64. s.push(a - b);
  65. return "";
  66. }
  67. },
  68. { "*", +[](number_stack& s)
  69. {
  70. if(s.size() < 2)
  71. return "Multiplication requires 2 operands";
  72. auto b = s.top(); s.pop();
  73. auto a = s.top(); s.pop();
  74. s.push(a * b);
  75. return "";
  76. }
  77. },
  78. { "/", +[](number_stack& s)
  79. {
  80. if(s.size() < 2)
  81. return "Division requires 2 operands";
  82. auto b = s.top(); s.pop();
  83. auto a = s.top(); s.pop();
  84. if(b == 0)
  85. return "Division by zero!!";
  86. s.push(a / b);
  87. return "";
  88. }
  89. },
  90. { "%", +[](number_stack& s)
  91. {
  92. if(s.size() < 2)
  93. return "Modulo requires 2 operands";
  94. auto b = s.top(); s.pop();
  95. auto a = s.top(); s.pop();
  96. if(b == 0)
  97. return "Division by zero!!";
  98. s.push(a % b);
  99. return "";
  100. }
  101. },
  102. };
  103. public:
  104. std::string error;
  105. std::optional<Number> compute(std::istringstream& stream)
  106. {
  107. std::string param;
  108. while(stream >> param)
  109. stack.push_back(trim(param));
  110. return compute();
  111. }
  112. std::optional<Number> compute()
  113. {
  114. error.clear();
  115. number_stack ns;
  116. while(stack.size() > 0)
  117. {
  118. auto top = stack.front();
  119. stack.pop_front();
  120. std::cout << "processing: " << top << '\n';
  121. auto top_number = simple::support::to_<Number>(top);
  122. if(top_number)
  123. {
  124. std::cout << "it's a number! pushing to numbers as" << *top_number << '\n';
  125. ns.push(*top_number);
  126. }
  127. else {
  128. auto op = operations.find(top);
  129. if(op != operations.end())
  130. {
  131. std::cout << "it's an operator! calling it! ";
  132. std::cout << '\n';
  133. error = op->second(ns);
  134. }
  135. else
  136. error = top + " is not a number or valid operator";
  137. if(!error.empty())
  138. {
  139. stack.clear();
  140. break;
  141. }
  142. }
  143. }
  144. if (ns.size() > 0)
  145. return ns.top();
  146. else
  147. return std::nullopt;
  148. }
  149. void push(std::string a) { stack.push_back(std::move(a)); }
  150. void clear() { stack.clear(); }
  151. };
  152. class TdExample {
  153. public:
  154. TdExample() {
  155. td::Log::set_verbosity_level(1);
  156. client_ = std::make_unique<td::Client>();
  157. }
  158. void loop() {
  159. while (true) {
  160. if (need_restart_) {
  161. restart();
  162. } else if (!are_authorized_) {
  163. process_response(client_->receive(10));
  164. } else {
  165. std::cerr << "Enter action [q] quit [u] check for updates and request results [c] show chats [m <id> <text>] "
  166. "send message [l] logout "
  167. "[stalk <chat_indexes>] unleash a creepy stalker bot on the given chats: "
  168. << std::endl;
  169. std::string line;
  170. std::getline(std::cin, line);
  171. std::istringstream ss(line);
  172. std::string action;
  173. if (!(ss >> action)) {
  174. continue;
  175. }
  176. if (action == "q") {
  177. return;
  178. }
  179. if (action == "u") {
  180. std::cerr << "Checking for updates..." << std::endl;
  181. while (true) {
  182. auto response = client_->receive(0);
  183. if (response.object) {
  184. process_response(std::move(response));
  185. } else {
  186. break;
  187. }
  188. }
  189. } else if (action == "l") {
  190. std::cerr << "Logging out..." << std::endl;
  191. send_query(td_api::make_object<td_api::logOut>(), {});
  192. } else if (action == "m") {
  193. std::int64_t chat_id;
  194. ss >> chat_id;
  195. ss.get();
  196. std::string text;
  197. std::getline(ss, text);
  198. std::cerr << "Sending message to chat " << chat_id << "..." << std::endl;
  199. send_message(chat_id, text);
  200. } else if (action == "c") {
  201. std::cerr << "Loading chat list..." << std::endl;
  202. send_query(td_api::make_object<td_api::getChats>(std::numeric_limits<std::int64_t>::max(), 0, 20),
  203. [this](Object object) {
  204. if (object->get_id() == td_api::error::ID) {
  205. return;
  206. }
  207. auto chats = td::move_tl_object_as<td_api::chats>(object);
  208. for (int chat_index = 0; chat_index < chats->chat_ids_.size(); ++chat_index) {
  209. auto chat_id = chats->chat_ids_[chat_index];
  210. std::cerr << '[' << chat_index << ']' << " [id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl;
  211. }
  212. this->chat_ids_ = std::move(chats->chat_ids_);
  213. });
  214. } else if (action == "stalk") {
  215. std::size_t stalk_chat_index;
  216. while(ss >> stalk_chat_index)
  217. if(stalk_chat_index <= this->chat_ids_.size())
  218. this->stalk_chat_ids.push_back(this->chat_ids_[stalk_chat_index]);
  219. std::cerr << "Stalking chats: " << std::endl;
  220. for(auto&& id : this->stalk_chat_ids)
  221. std::cout << chat_title_[id] << '\n';
  222. while (!std::empty(this->stalk_chat_ids)) {
  223. auto response = client_->receive(0);
  224. if (response.object) {
  225. process_response(std::move(response));
  226. }
  227. std::this_thread::sleep_for(30ms);
  228. }
  229. }
  230. }
  231. }
  232. }
  233. private:
  234. using Object = td_api::object_ptr<td_api::Object>;
  235. std::unique_ptr<td::Client> client_;
  236. td_api::object_ptr<td_api::AuthorizationState> authorization_state_;
  237. bool are_authorized_{false};
  238. bool need_restart_{false};
  239. std::uint64_t current_query_id_{0};
  240. std::uint64_t authentication_query_id_{0};
  241. std::map<std::uint64_t, std::function<void(Object)>> handlers_;
  242. std::map<std::int32_t, td_api::object_ptr<td_api::user>> users_;
  243. std::map<std::int64_t, std::string> chat_title_;
  244. std::vector<std::int64_t> stalk_chat_ids;
  245. std::vector<std::int64_t> chat_ids_;
  246. std::string bot_name = ":>";
  247. postfix_calculator<long long> comp;
  248. void restart() {
  249. client_.reset();
  250. *this = TdExample();
  251. }
  252. void send_query(td_api::object_ptr<td_api::Function> f, std::function<void(Object)> handler) {
  253. auto query_id = next_query_id();
  254. if (handler) {
  255. handlers_.emplace(query_id, std::move(handler));
  256. }
  257. client_->send({query_id, std::move(f)});
  258. }
  259. void process_response(td::Client::Response response) {
  260. if (!response.object) {
  261. return;
  262. }
  263. //std::cerr << response.id << " " << to_string(response.object) << std::endl;
  264. if (response.id == 0) {
  265. return process_update(std::move(response.object));
  266. }
  267. auto it = handlers_.find(response.id);
  268. if (it != handlers_.end()) {
  269. it->second(std::move(response.object));
  270. }
  271. }
  272. std::string get_user_name(std::int32_t user_id) {
  273. auto it = users_.find(user_id);
  274. if (it == users_.end()) {
  275. return "unknown user";
  276. }
  277. return it->second->first_name_ + " " + it->second->last_name_;
  278. }
  279. void process_update(td_api::object_ptr<td_api::Object> update) {
  280. td_api::downcast_call(
  281. *update, overloaded{
  282. [this](td_api::updateAuthorizationState &update_authorization_state) {
  283. authorization_state_ = std::move(update_authorization_state.authorization_state_);
  284. on_authorization_state_update();
  285. },
  286. [this](td_api::updateNewChat &update_new_chat) {
  287. chat_title_[update_new_chat.chat_->id_] = update_new_chat.chat_->title_;
  288. },
  289. [this](td_api::updateChatTitle &update_chat_title) {
  290. chat_title_[update_chat_title.chat_id_] = update_chat_title.title_;
  291. },
  292. [this](td_api::updateUser &update_user) {
  293. auto user_id = update_user.user_->id_;
  294. users_[user_id] = std::move(update_user.user_);
  295. },
  296. [this](td_api::updateNewMessage &update_new_message) {
  297. auto chat_id = update_new_message.message_->chat_id_;
  298. auto sender_user_name = get_user_name(update_new_message.message_->sender_user_id_);
  299. std::string text;
  300. if (update_new_message.message_->content_->get_id() == td_api::messageText::ID) {
  301. text = static_cast<td_api::messageText &>(*update_new_message.message_->content_).text_->text_;
  302. }
  303. std::cerr << "Got message: [chat_id:" << chat_id << "] [from:" << sender_user_name << "] ["
  304. << text << "]" << std::endl;
  305. auto i = std::find(this->stalk_chat_ids.begin(), this->stalk_chat_ids.end(), chat_id);
  306. if(i != this->stalk_chat_ids.end())
  307. {
  308. text = trim(text);
  309. const auto mismatch = std::mismatch(text.begin(), text.end(), this->bot_name.begin(), this->bot_name.end());
  310. if(mismatch.second == this->bot_name.end())
  311. {
  312. std::cout << "wooo, they are talking to meeee!!" << '\n';
  313. const auto sentence = trim(std::string(mismatch.first, text.end()));
  314. std::cout << "they said: " << sentence << '\n';
  315. std::istringstream words(sentence);
  316. std::string word;
  317. words >> word;
  318. word = trim(word);
  319. if("stop stalking me please" == sentence)
  320. {
  321. send_message(chat_id, "ok, sorry i really didn't mean to cause any trouble, byeooo " + this->bot_name);
  322. std::cout << "No longer stalking: " << chat_title_[chat_id] << '\n';
  323. this->stalk_chat_ids.erase(i);
  324. }
  325. else if("heyo" == word)
  326. {
  327. send_message(chat_id, "heyoooo! i'm a young aspiring bot, aiming to become the ultimate birthday happiness assitant " + this->bot_name );
  328. send_message(chat_id, "my name is :> and you can address me by starting your messages with it. " + this->bot_name );
  329. send_message(chat_id, "i also sign all of my messages with it so that you can recognize me " + this->bot_name );
  330. send_message(chat_id, "i can't do much yet, but to prove you that i am a bot i can quickly compute integer arithmetic expression in postfix notation for you! " + this->bot_name );
  331. send_message(chat_id, "just ask me to compute like this `:> compute 3 4 + 4 3 + *` " + this->bot_name );
  332. }
  333. else if("compute" == word || "comp" == word)
  334. {
  335. auto result = comp.compute(words);
  336. if(!comp.error.empty())
  337. send_message(chat_id, "oh noo! " + comp.error + ' ' + this->bot_name);
  338. else if (!result)
  339. send_message(chat_id, "nothing to compute thereee" + this->bot_name);
  340. else
  341. send_message(chat_id, "the asnwer of cource is: " + std::to_string(*result) + ' ' + this->bot_name);
  342. }
  343. else
  344. {
  345. send_message(chat_id, "sorry, don't understand that " + this->bot_name);
  346. }
  347. }
  348. }
  349. },
  350. [](auto &update) {}});
  351. }
  352. auto create_authentication_query_handler() {
  353. return [this, id = authentication_query_id_](Object object) {
  354. if (id == authentication_query_id_) {
  355. check_authentication_error(std::move(object));
  356. }
  357. };
  358. }
  359. void on_authorization_state_update() {
  360. authentication_query_id_++;
  361. td_api::downcast_call(
  362. *authorization_state_,
  363. overloaded{
  364. [this](td_api::authorizationStateReady &) {
  365. are_authorized_ = true;
  366. std::cerr << "Got authorization" << std::endl;
  367. },
  368. [this](td_api::authorizationStateLoggingOut &) {
  369. are_authorized_ = false;
  370. std::cerr << "Logging out" << std::endl;
  371. },
  372. [this](td_api::authorizationStateClosing &) { std::cerr << "Closing" << std::endl; },
  373. [this](td_api::authorizationStateClosed &) {
  374. are_authorized_ = false;
  375. need_restart_ = true;
  376. std::cerr << "Terminated" << std::endl;
  377. },
  378. [this](td_api::authorizationStateWaitCode &wait_code) {
  379. std::string first_name;
  380. std::string last_name;
  381. if (!wait_code.is_registered_) {
  382. std::cerr << "Enter your first name: ";
  383. std::cin >> first_name;
  384. std::cerr << "Enter your last name: ";
  385. std::cin >> last_name;
  386. }
  387. std::cerr << "Enter authentication code: ";
  388. std::string code;
  389. std::cin >> code;
  390. send_query(td_api::make_object<td_api::checkAuthenticationCode>(code, first_name, last_name),
  391. create_authentication_query_handler());
  392. },
  393. [this](td_api::authorizationStateWaitPassword &) {
  394. std::cerr << "Enter authentication password: ";
  395. std::string password;
  396. std::cin >> password;
  397. send_query(td_api::make_object<td_api::checkAuthenticationPassword>(password),
  398. create_authentication_query_handler());
  399. },
  400. [this](td_api::authorizationStateWaitPhoneNumber &) {
  401. std::cerr << "Enter phone number: ";
  402. std::string phone_number;
  403. std::cin >> phone_number;
  404. send_query(td_api::make_object<td_api::setAuthenticationPhoneNumber>(
  405. phone_number, false /*allow_flash_calls*/, false /*is_current_phone_number*/),
  406. create_authentication_query_handler());
  407. },
  408. [this](td_api::authorizationStateWaitEncryptionKey &) {
  409. std::cerr << "Enter encryption key or DESTROY: ";
  410. std::string key;
  411. std::getline(std::cin, key);
  412. if (key == "DESTROY") {
  413. send_query(td_api::make_object<td_api::destroy>(), create_authentication_query_handler());
  414. } else {
  415. send_query(td_api::make_object<td_api::checkDatabaseEncryptionKey>(std::move(key)),
  416. create_authentication_query_handler());
  417. }
  418. },
  419. [this](td_api::authorizationStateWaitTdlibParameters &) {
  420. auto parameters = td_api::make_object<td_api::tdlibParameters>();
  421. parameters->database_directory_ = "tdlib";
  422. parameters->use_message_database_ = true;
  423. parameters->use_secret_chats_ = true;
  424. parameters->api_id_ = 94575;
  425. parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
  426. parameters->system_language_code_ = "en";
  427. parameters->device_model_ = "Desktop";
  428. parameters->system_version_ = "Unknown";
  429. parameters->application_version_ = "1.0";
  430. parameters->enable_storage_optimizer_ = true;
  431. send_query(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)),
  432. create_authentication_query_handler());
  433. }});
  434. }
  435. void check_authentication_error(Object object) {
  436. if (object->get_id() == td_api::error::ID) {
  437. auto error = td::move_tl_object_as<td_api::error>(object);
  438. std::cerr << "Error: " << to_string(error);
  439. on_authorization_state_update();
  440. }
  441. }
  442. std::uint64_t next_query_id() {
  443. return ++current_query_id_;
  444. }
  445. void send_message(std::int64_t chat, std::string message)
  446. {
  447. auto send_message = td_api::make_object<td_api::sendMessage>();
  448. send_message->chat_id_ = chat;
  449. auto message_content = td_api::make_object<td_api::inputMessageText>();
  450. message_content->text_ = td_api::make_object<td_api::formattedText>();
  451. message_content->text_->text_ = std::move(message);
  452. send_message->input_message_content_ = std::move(message_content);
  453. send_query(std::move(send_message), {});
  454. }
  455. };
  456. int main() {
  457. TdExample example;
  458. example.loop();
  459. }