#include "search.h" #include #include #include #include #include using json = nlohmann::json; Searchflags operator|=(Searchflags& lhs, const Searchflags sf) { lhs = (Searchflags) ((uint32_t) lhs | (uint32_t) sf); return lhs; } bool operator&(Searchflags& lhs, const Searchflags sf) { return (bool) ((uint32_t) lhs & (uint32_t) sf); } static bool matches(const std::string& msg, const std::string& text) { //simpler contains check return (msg.find(text) != std::string::npos); } static bool matchesIC(const std::string& msg, const std::string& text) { //turn compare string to lower std::string lower; mrbesen::util::toLower(msg, lower); return (lower.find(text) != std::string::npos); } // matches reges is inline, so i need a wrapper static bool matchesRegex(const std::string& msg, const std::regex& reg) { return std::regex_search(msg, reg); } Search::Search() {} Search::Search(const Search& orig, std::list* list) { //copy messages if(list) { for(const Message* m : *list) { msgs.push_back(*m); } } else { msgs = orig.msgs; } //copy chatnames chatnames = orig.chatnames; } Search::~Search() {} Searchflags Search::fromString(const std::string& str) { Searchflags f = Searchflags::NONE; for(char c : str) { switch(c) { case 'R': case 'r': f |= Searchflags::REGEX; break; case 'i': case 'I': f |= Searchflags::IGNORECASE; break; default: break; } } return f; } void Search::addFile(const std::string& file) { //laden den datei try { std::ifstream fstream(file); json j; fstream >> j; //single chat export if(j.contains("messages")) { chatnames.insert({j["id"], j["name"].get()}); loadMessages(j["messages"], j["id"]); return; } //multi chat export if(j.contains("chats")) { const json& chatlist = j["chats"]["list"]; if(chatlist.is_null()) { Log::error << "File does not contain a chatlist"; return; } for(const json& chat : chatlist) { uint64_t id = chat["id"]; std::string name = ""; if(chat.contains("name") && !chat["name"].is_null()) name = chat.value("name", ""); chatnames.insert({id, name}); loadMessages(chat["messages"], id); Log::note << "Loaded Chat: " << name << " (" << id << ")"; } return; } Log::warn << "File " << file << " could not be parsed! Export as json!"; } catch (nlohmann::detail::parse_error& e) { Log::error << "Could not load File: " << e.what(); } } std::list Search::search(std::string text, Searchflags flags) const { std::list out; if(flags & Searchflags::REGEX) { searchRegex(text, flags & Searchflags::IGNORECASE, out); return out; } if(flags & Searchflags::IGNORECASE) { //turn search to lower mrbesen::util::toLower(text); runsearch(text, matchesIC, out); } else { runsearch(text, matches, out); } return out; } const std::string& Search::getChatname(uint64_t id) const { static const std::string UNKOWNCHAT = ""; auto it = chatnames.find(id); if(it == chatnames.end()) return UNKOWNCHAT; if(it->second.empty()) return UNKOWNCHAT; return it->second; } std::string Search::getShortChatname(uint64_t id) const { std::string chatname = getChatname(id); if(chatname.size() > 14) { return chatname.substr(0, 14); } return chatname; } uint32_t Search::getChatCount() const { return chatnames.size(); } uint64_t Search::getMessageCount() const { return msgs.size(); } void Search::searchRegex(const std::string& text, bool ignoreCase, std::list& out) const { //build regex pattern const std::regex pattern(text, (ignoreCase ? std::regex::icase : (std::regex::flag_type) 0)); runsearch(pattern, matchesRegex, out); } template void Search::runsearch(T st, bool (*checker)(const std::string& msg, T text), std::list& out) const { for(const Message& m : msgs) { if(checker(m.text, st)) { out.push_back(&m); } } } static void readText(const json& t, std::string& out) { if(t.is_null()) return; if(t.is_string()) out = t; if(t.is_array()) { std::ostringstream buff; for(const json& entr : t) { if(entr.is_string()) buff << (const std::string&) entr; else if(entr.contains("text")) buff << (const std::string&) entr["text"]; } out = buff.str(); } } void Search::loadMessages(const json& j, uint64_t chatid) { uint32_t failed = 0; for(const json& m : j) { try { if(m.contains("text")) { std::string text; readText(m["text"], text); msgs.push_back({text, chatid, m["id"]}); } else { Log::warn << "text less message: " << m; } } catch(const nlohmann::detail::exception& e) { Log::warn << "Parse error: " << e.id << " " << e.what(); } catch(...) { failed ++; } } if(failed != 0) { Log::warn << failed << " Messages failed to load"; } }