123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- //
- // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- #include <td/telegram/Client.h>
- #include <td/telegram/Log.h>
- #include <td/telegram/td_api.h>
- #include <td/telegram/td_api.hpp>
- #include <cstdint>
- #include <functional>
- #include <iostream>
- #include <limits>
- #include <map>
- #include <sstream>
- #include <string>
- #include <vector>
- #include <thread>
- #include <chrono>
- #include <algorithm>
- #include <stack>
- #include <variant>
- #include "simple/support/misc.hpp"
- #include "simple/support/function_utils.hpp"
- using namespace std::chrono_literals;
- // Simple single-threaded example of TDLib usage.
- // Real world programs should use separate thread for the user input.
- // Example includes user authentication, receiving updates, getting chat list and sending text messages.
- using simple::support::overloaded;
- namespace td_api = td::td_api;
- std::string trim(const std::string& view)
- {
- auto isspace = [](char c){ return std::isspace(c, std::locale{}); };
- auto begin = std::find_if_not(view.begin(), view.end(), isspace);
- auto end = std::find_if_not(view.rbegin(), view.rend(), isspace);
- return {begin, end.base()};
- }
- template <typename Number>
- class postfix_calculator
- {
- using input_stack = std::deque<std::string>;
- using number_stack = std::stack<Number>;
- input_stack stack;
- std::unordered_map<std::string, const char*(*)(number_stack&)> operations {
- { "+", +[](number_stack& s)
- {
- if(s.size() < 2)
- return "Addition requires 2 operands";
- auto b = s.top(); s.pop();
- auto a = s.top(); s.pop();
- std::cout << "got " << a << " and " << b << " from stack" << '\n';
- s.push(a + b);
- std::cout << "pushed " << a + b << " to stack" << '\n';
- return "";
- }
- },
- { "-", +[](number_stack& s)
- {
- if(s.size() < 2)
- return "Subtraction requires 2 operands";
- auto b = s.top(); s.pop();
- auto a = s.top(); s.pop();
- s.push(a - b);
- return "";
- }
- },
- { "*", +[](number_stack& s)
- {
- if(s.size() < 2)
- return "Multiplication requires 2 operands";
- auto b = s.top(); s.pop();
- auto a = s.top(); s.pop();
- s.push(a * b);
- return "";
- }
- },
- { "/", +[](number_stack& s)
- {
- if(s.size() < 2)
- return "Division requires 2 operands";
- auto b = s.top(); s.pop();
- auto a = s.top(); s.pop();
- if(b == 0)
- return "Division by zero!!";
- s.push(a / b);
- return "";
- }
- },
- { "%", +[](number_stack& s)
- {
- if(s.size() < 2)
- return "Modulo requires 2 operands";
- auto b = s.top(); s.pop();
- auto a = s.top(); s.pop();
- if(b == 0)
- return "Division by zero!!";
- s.push(a % b);
- return "";
- }
- },
- };
- public:
- std::string error;
- std::optional<Number> compute(std::istringstream& stream)
- {
- std::string param;
- while(stream >> param)
- stack.push_back(trim(param));
- return compute();
- }
- std::optional<Number> compute()
- {
- error.clear();
- number_stack ns;
- while(stack.size() > 0)
- {
- auto top = stack.front();
- stack.pop_front();
- std::cout << "processing: " << top << '\n';
- auto top_number = simple::support::to_<Number>(top);
- if(top_number)
- {
- std::cout << "it's a number! pushing to numbers as" << *top_number << '\n';
- ns.push(*top_number);
- }
- else {
- auto op = operations.find(top);
- if(op != operations.end())
- {
- std::cout << "it's an operator! calling it! ";
- std::cout << '\n';
- error = op->second(ns);
- }
- else
- error = top + " is not a number or valid operator";
- if(!error.empty())
- {
- stack.clear();
- break;
- }
- }
- }
- if (ns.size() > 0)
- return ns.top();
- else
- return std::nullopt;
- }
- void push(std::string a) { stack.push_back(std::move(a)); }
- void clear() { stack.clear(); }
- };
- class TdExample {
- public:
- TdExample() {
- td::Log::set_verbosity_level(1);
- client_ = std::make_unique<td::Client>();
- }
- void loop() {
- while (true) {
- if (need_restart_) {
- restart();
- } else if (!are_authorized_) {
- process_response(client_->receive(10));
- } else {
- std::cerr << "Enter action [q] quit [u] check for updates and request results [c] show chats [m <id> <text>] "
- "send message [l] logout "
- "[stalk <chat_indexes>] unleash a creepy stalker bot on the given chats: "
- << std::endl;
- std::string line;
- std::getline(std::cin, line);
- std::istringstream ss(line);
- std::string action;
- if (!(ss >> action)) {
- continue;
- }
- if (action == "q") {
- return;
- }
- if (action == "u") {
- std::cerr << "Checking for updates..." << std::endl;
- while (true) {
- auto response = client_->receive(0);
- if (response.object) {
- process_response(std::move(response));
- } else {
- break;
- }
- }
- } else if (action == "l") {
- std::cerr << "Logging out..." << std::endl;
- send_query(td_api::make_object<td_api::logOut>(), {});
- } else if (action == "m") {
- std::int64_t chat_id;
- ss >> chat_id;
- ss.get();
- std::string text;
- std::getline(ss, text);
- std::cerr << "Sending message to chat " << chat_id << "..." << std::endl;
- send_message(chat_id, text);
- } else if (action == "c") {
- std::cerr << "Loading chat list..." << std::endl;
- send_query(td_api::make_object<td_api::getChats>(std::numeric_limits<std::int64_t>::max(), 0, 20),
- [this](Object object) {
- if (object->get_id() == td_api::error::ID) {
- return;
- }
- auto chats = td::move_tl_object_as<td_api::chats>(object);
- for (int chat_index = 0; chat_index < chats->chat_ids_.size(); ++chat_index) {
- auto chat_id = chats->chat_ids_[chat_index];
- std::cerr << '[' << chat_index << ']' << " [id:" << chat_id << "] [title:" << chat_title_[chat_id] << "]" << std::endl;
- }
- this->chat_ids_ = std::move(chats->chat_ids_);
- });
- } else if (action == "stalk") {
- std::size_t stalk_chat_index;
- while(ss >> stalk_chat_index)
- if(stalk_chat_index <= this->chat_ids_.size())
- this->stalk_chat_ids.push_back(this->chat_ids_[stalk_chat_index]);
- std::cerr << "Stalking chats: " << std::endl;
- for(auto&& id : this->stalk_chat_ids)
- std::cout << chat_title_[id] << '\n';
- while (!std::empty(this->stalk_chat_ids)) {
- auto response = client_->receive(0);
- if (response.object) {
- process_response(std::move(response));
- }
- std::this_thread::sleep_for(30ms);
- }
- }
- }
- }
- }
- private:
- using Object = td_api::object_ptr<td_api::Object>;
- std::unique_ptr<td::Client> client_;
- td_api::object_ptr<td_api::AuthorizationState> authorization_state_;
- bool are_authorized_{false};
- bool need_restart_{false};
- std::uint64_t current_query_id_{0};
- std::uint64_t authentication_query_id_{0};
- std::map<std::uint64_t, std::function<void(Object)>> handlers_;
- std::map<std::int32_t, td_api::object_ptr<td_api::user>> users_;
- std::map<std::int64_t, std::string> chat_title_;
- std::vector<std::int64_t> stalk_chat_ids;
- std::vector<std::int64_t> chat_ids_;
- std::string bot_name = ":>";
- postfix_calculator<long long> comp;
- void restart() {
- client_.reset();
- *this = TdExample();
- }
- void send_query(td_api::object_ptr<td_api::Function> f, std::function<void(Object)> handler) {
- auto query_id = next_query_id();
- if (handler) {
- handlers_.emplace(query_id, std::move(handler));
- }
- client_->send({query_id, std::move(f)});
- }
- void process_response(td::Client::Response response) {
- if (!response.object) {
- return;
- }
- //std::cerr << response.id << " " << to_string(response.object) << std::endl;
- if (response.id == 0) {
- return process_update(std::move(response.object));
- }
- auto it = handlers_.find(response.id);
- if (it != handlers_.end()) {
- it->second(std::move(response.object));
- }
- }
- std::string get_user_name(std::int32_t user_id) {
- auto it = users_.find(user_id);
- if (it == users_.end()) {
- return "unknown user";
- }
- return it->second->first_name_ + " " + it->second->last_name_;
- }
- void process_update(td_api::object_ptr<td_api::Object> update) {
- td_api::downcast_call(
- *update, overloaded{
- [this](td_api::updateAuthorizationState &update_authorization_state) {
- authorization_state_ = std::move(update_authorization_state.authorization_state_);
- on_authorization_state_update();
- },
- [this](td_api::updateNewChat &update_new_chat) {
- chat_title_[update_new_chat.chat_->id_] = update_new_chat.chat_->title_;
- },
- [this](td_api::updateChatTitle &update_chat_title) {
- chat_title_[update_chat_title.chat_id_] = update_chat_title.title_;
- },
- [this](td_api::updateUser &update_user) {
- auto user_id = update_user.user_->id_;
- users_[user_id] = std::move(update_user.user_);
- },
- [this](td_api::updateNewMessage &update_new_message) {
- auto chat_id = update_new_message.message_->chat_id_;
- auto sender_user_name = get_user_name(update_new_message.message_->sender_user_id_);
- std::string text;
- if (update_new_message.message_->content_->get_id() == td_api::messageText::ID) {
- text = static_cast<td_api::messageText &>(*update_new_message.message_->content_).text_->text_;
- }
- std::cerr << "Got message: [chat_id:" << chat_id << "] [from:" << sender_user_name << "] ["
- << text << "]" << std::endl;
- auto i = std::find(this->stalk_chat_ids.begin(), this->stalk_chat_ids.end(), chat_id);
- if(i != this->stalk_chat_ids.end())
- {
- text = trim(text);
- const auto mismatch = std::mismatch(text.begin(), text.end(), this->bot_name.begin(), this->bot_name.end());
- if(mismatch.second == this->bot_name.end())
- {
- std::cout << "wooo, they are talking to meeee!!" << '\n';
- const auto sentence = trim(std::string(mismatch.first, text.end()));
- std::cout << "they said: " << sentence << '\n';
- std::istringstream words(sentence);
- std::string word;
- words >> word;
- word = trim(word);
- if("stop stalking me please" == sentence)
- {
- send_message(chat_id, "ok, sorry i really didn't mean to cause any trouble, byeooo " + this->bot_name);
- std::cout << "No longer stalking: " << chat_title_[chat_id] << '\n';
- this->stalk_chat_ids.erase(i);
- }
- else if("heyo" == word)
- {
- send_message(chat_id, "heyoooo! i'm a young aspiring bot, aiming to become the ultimate birthday happiness assitant " + this->bot_name );
- send_message(chat_id, "my name is :> and you can address me by starting your messages with it. " + this->bot_name );
- send_message(chat_id, "i also sign all of my messages with it so that you can recognize me " + this->bot_name );
- 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 );
- send_message(chat_id, "just ask me to compute like this `:> compute 3 4 + 4 3 + *` " + this->bot_name );
- }
- else if("compute" == word || "comp" == word)
- {
- auto result = comp.compute(words);
- if(!comp.error.empty())
- send_message(chat_id, "oh noo! " + comp.error + ' ' + this->bot_name);
- else if (!result)
- send_message(chat_id, "nothing to compute thereee" + this->bot_name);
- else
- send_message(chat_id, "the asnwer of cource is: " + std::to_string(*result) + ' ' + this->bot_name);
- }
- else
- {
- send_message(chat_id, "sorry, don't understand that " + this->bot_name);
- }
- }
- }
- },
- [](auto &update) {}});
- }
- auto create_authentication_query_handler() {
- return [this, id = authentication_query_id_](Object object) {
- if (id == authentication_query_id_) {
- check_authentication_error(std::move(object));
- }
- };
- }
- void on_authorization_state_update() {
- authentication_query_id_++;
- td_api::downcast_call(
- *authorization_state_,
- overloaded{
- [this](td_api::authorizationStateReady &) {
- are_authorized_ = true;
- std::cerr << "Got authorization" << std::endl;
- },
- [this](td_api::authorizationStateLoggingOut &) {
- are_authorized_ = false;
- std::cerr << "Logging out" << std::endl;
- },
- [this](td_api::authorizationStateClosing &) { std::cerr << "Closing" << std::endl; },
- [this](td_api::authorizationStateClosed &) {
- are_authorized_ = false;
- need_restart_ = true;
- std::cerr << "Terminated" << std::endl;
- },
- [this](td_api::authorizationStateWaitCode &wait_code) {
- std::string first_name;
- std::string last_name;
- if (!wait_code.is_registered_) {
- std::cerr << "Enter your first name: ";
- std::cin >> first_name;
- std::cerr << "Enter your last name: ";
- std::cin >> last_name;
- }
- std::cerr << "Enter authentication code: ";
- std::string code;
- std::cin >> code;
- send_query(td_api::make_object<td_api::checkAuthenticationCode>(code, first_name, last_name),
- create_authentication_query_handler());
- },
- [this](td_api::authorizationStateWaitPassword &) {
- std::cerr << "Enter authentication password: ";
- std::string password;
- std::cin >> password;
- send_query(td_api::make_object<td_api::checkAuthenticationPassword>(password),
- create_authentication_query_handler());
- },
- [this](td_api::authorizationStateWaitPhoneNumber &) {
- std::cerr << "Enter phone number: ";
- std::string phone_number;
- std::cin >> phone_number;
- send_query(td_api::make_object<td_api::setAuthenticationPhoneNumber>(
- phone_number, false /*allow_flash_calls*/, false /*is_current_phone_number*/),
- create_authentication_query_handler());
- },
- [this](td_api::authorizationStateWaitEncryptionKey &) {
- std::cerr << "Enter encryption key or DESTROY: ";
- std::string key;
- std::getline(std::cin, key);
- if (key == "DESTROY") {
- send_query(td_api::make_object<td_api::destroy>(), create_authentication_query_handler());
- } else {
- send_query(td_api::make_object<td_api::checkDatabaseEncryptionKey>(std::move(key)),
- create_authentication_query_handler());
- }
- },
- [this](td_api::authorizationStateWaitTdlibParameters &) {
- auto parameters = td_api::make_object<td_api::tdlibParameters>();
- parameters->database_directory_ = "tdlib";
- parameters->use_message_database_ = true;
- parameters->use_secret_chats_ = true;
- parameters->api_id_ = 94575;
- parameters->api_hash_ = "a3406de8d171bb422bb6ddf3bbd800e2";
- parameters->system_language_code_ = "en";
- parameters->device_model_ = "Desktop";
- parameters->system_version_ = "Unknown";
- parameters->application_version_ = "1.0";
- parameters->enable_storage_optimizer_ = true;
- send_query(td_api::make_object<td_api::setTdlibParameters>(std::move(parameters)),
- create_authentication_query_handler());
- }});
- }
- void check_authentication_error(Object object) {
- if (object->get_id() == td_api::error::ID) {
- auto error = td::move_tl_object_as<td_api::error>(object);
- std::cerr << "Error: " << to_string(error);
- on_authorization_state_update();
- }
- }
- std::uint64_t next_query_id() {
- return ++current_query_id_;
- }
- void send_message(std::int64_t chat, std::string message)
- {
- auto send_message = td_api::make_object<td_api::sendMessage>();
- send_message->chat_id_ = chat;
- auto message_content = td_api::make_object<td_api::inputMessageText>();
- message_content->text_ = td_api::make_object<td_api::formattedText>();
- message_content->text_->text_ = std::move(message);
- send_message->input_message_content_ = std::move(message_content);
- send_query(std::move(send_message), {});
- }
- };
- int main() {
- TdExample example;
- example.loop();
- }
|