diff --git a/src/filter.cpp b/src/filter.cpp new file mode 100644 index 0000000..8721bcb --- /dev/null +++ b/src/filter.cpp @@ -0,0 +1,9 @@ +#include "filter.h" + +void Filter::setup(std::ostream& o, std::istream& str) { + o << "ignoreCase? [y/n]"; + char c; + str >> c; + ignoreCase = (c == 'y' || c == 'Y'); +} + diff --git a/src/filter.h b/src/filter.h new file mode 100644 index 0000000..89ceeb0 --- /dev/null +++ b/src/filter.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include "message.h" + +//ideen: nach sender filtern, nur nachrichten mit medien, nur nachrichten ohne medien, medien Dateinamen, nach datum filtern, service messages? (joined, kicked, invited,...), inverted (not conatain) + +struct Filter { + virtual ~Filter() {} + virtual void setup(std::ostream& o, std::istream& str); + virtual bool filter(const Message& m) const = 0; +protected: + bool ignoreCase; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 62c820c..a1dc83c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,11 +2,13 @@ #include #include +#include #include //signal handler #include #include #include "search.h" +#include "textfilter.h" static bool run = true; @@ -17,6 +19,45 @@ void sig_handler(int sig_num) { (void) sig_num; } +std::map filterlist; + +void loadFilter() { + filterlist.insert({"TextSearch", new TextFilter()}); + filterlist.insert({"RegexSearch", new RegexFilter()}); +} + +void removeFilter() { + for(auto it : filterlist) { + delete it.second; + } + filterlist.clear(); +} + +inline void resetStream(std::istream& i = std::cin) { + i.ignore(std::numeric_limits::max(), '\n'); +} + +Filter* chooseFilter(std::ostream& o, std::istream& istr) { + //print filter + uint32_t i = 0; + std::vector templookup; + templookup.reserve(filterlist.size()); + for(auto it : filterlist) { + o << "[" << std::setw(2) << std::setfill('0') << i << "] " << it.first << std::endl; + templookup.push_back(it.second); + ++i; + } + + uint32_t choise = 0; + do { + o << "Pick one" << std::endl; + istr >> choise; + } while(choise > templookup.size()); + + resetStream(istr); + return templookup.at(choise); +} + int main(int argc, const char** argv) { Log::init(); Log::setConsoleLogLevel(Log::Level::TRACE); @@ -41,6 +82,8 @@ int main(int argc, const char** argv) { time_t end = time(nullptr); Log::info << rootsearch.getChatCount() << " Chats with " << rootsearch.getMessageCount() << " Messages loaded in " << end-start << "s"; + loadFilter(); + signal(SIGINT, sig_handler); Search* search = nullptr; @@ -49,22 +92,16 @@ int main(int argc, const char** argv) { if(!search) search = &rootsearch; - Log::info << search->getMessageCount() << " Messages. Enter searchterm: "; - std::string searchterm; - std::getline(std::cin, searchterm); - Log::trace << "searchterm: \"" << searchterm << "\""; + Log::info << search->getMessageCount() << " Messages. Configure Search: "; - if(!run) break; + Filter* filter = chooseFilter(std::cout, std::cin); - Log::info << "Enter Flags: [RI]"; - std::string flags; - std::cin >> flags; - Searchflags parsedflags = Search::fromString(flags); + filter->setup(std::cout, std::cin); if(!run) break; time_t start = time(nullptr); - std::list results = search->search(searchterm, parsedflags); + std::list results = search->search(*filter); time_t end = time(nullptr); Log::info << results.size() << " results found in " << end-start << "s"; if(results.size()) { @@ -105,7 +142,7 @@ int main(int argc, const char** argv) { } } - std::cin.ignore(std::numeric_limits::max(), '\n'); + resetStream(std::cin); } if(search != &rootsearch) diff --git a/src/message.cpp b/src/message.cpp new file mode 100644 index 0000000..1487a1b --- /dev/null +++ b/src/message.cpp @@ -0,0 +1,20 @@ +#include "message.h" + +bool Message::operator==(const Message& m) const { + return (m.chatid == chatid) && (m.messageid == messageid); +} + +bool Message::operator!=(const Message& m) const { + return (m.chatid != chatid) || (m.messageid != messageid); +} + +bool Message::operator<(const Message& m) const { + if (chatid < m.chatid) return true; + if (chatid > m.chatid) return false; + // chatid == m.chatid + return (messageid < m.messageid); +} + +bool Message::hasFile() const { + return !filename.empty(); +} \ No newline at end of file diff --git a/src/message.h b/src/message.h new file mode 100644 index 0000000..658b61f --- /dev/null +++ b/src/message.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include + +struct Message { + int64_t chatid; + int64_t messageid; + int64_t senderid; + std::string text; + std::string filename = ""; //empty filename = no file + bool isreply = false; + + bool operator==(const Message& m) const; + bool operator!=(const Message& m) const; + bool operator<(const Message& m) const; + bool hasFile() const; +}; \ No newline at end of file diff --git a/src/search.cpp b/src/search.cpp index 226c03d..b3029ef 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -8,51 +8,6 @@ #include using json = nlohmann::json; -bool Message::operator==(const Message& m) const { - return (m.chatid == chatid) && (m.messageid == messageid); -} - -bool Message::operator!=(const Message& m) const { - return (m.chatid != chatid) || (m.messageid != messageid); -} - -bool Message::operator<(const Message& m) const { - if (chatid < m.chatid) return true; - if (chatid > m.chatid) return false; - // chatid == m.chatid - return (messageid < m.messageid); -} - -bool Message::hasFile() const { - return !filename.empty(); -} - -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 @@ -69,27 +24,6 @@ Search::Search(const Search& orig, std::list* list) { } 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 { @@ -151,22 +85,10 @@ void Search::finalize() { deduplicate.clear(); } -std::list Search::search(std::string text, Searchflags flags) const { +std::list Search::search(const Filter& filter) 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); - } + runsearch(filter, out); return out; } @@ -195,17 +117,9 @@ 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 { +void Search::runsearch(const Filter& filter, std::list& out) const { for(const Message& m : msgs) { - if(checker(m.text, st)) { + if(filter.filter(m)) { out.push_back(&m); } } diff --git a/src/search.h b/src/search.h index 328132d..0d05289 100644 --- a/src/search.h +++ b/src/search.h @@ -10,31 +10,8 @@ #include using json = nlohmann::json; -struct Message { - int64_t chatid; - int64_t messageid; - int64_t senderid; - std::string text; - std::string filename = ""; //empty filename = no file - bool isreply = false; - - bool operator==(const Message& m) const; - bool operator!=(const Message& m) const; - bool operator<(const Message& m) const; - bool hasFile() const; -}; - -enum class Searchflags { - NONE = 0, - IGNORECASE = 1, - REGEX = 2, - - //ideen: nach sender filtern, nur nachrichten mit medien, nur nachrichten ohne medien, medien Dateinamen, nach datum filtern, service messages? (joined, kicked, invited,...), inverted (not conatain) -}; - -Searchflags operator|=(Searchflags& lhs, const Searchflags sf); - -bool operator&(Searchflags& lhs, const Searchflags sf); +#include "filter.h" +#include "message.h" class Search { public: @@ -43,12 +20,10 @@ public: Search(const Search& orig, std::list* list = nullptr); ~Search(); - static Searchflags fromString(const std::string&); - void addFile(const std::string& file); void finalize(); //stop adding files and finalize deduplication, could be called twice, but then a deduplication is not guaranteed - std::list search(std::string text, Searchflags flags = Searchflags::NONE) const; + std::list search(const Filter& filter) const; const std::string& getChatname(int64_t id) const; std::string getShortChatname(int64_t id) const; @@ -57,8 +32,7 @@ public: private: void searchRegex(const std::string& text, bool ignoreCase, std::list& out) const; - template - void runsearch(T st, bool (*checker)(const std::string& msg, T text), std::list& out) const; + void runsearch(const Filter& filter, std::list& out) const; void loadMessages(const json& j, int64_t chatid); diff --git a/src/textfilter.cpp b/src/textfilter.cpp new file mode 100644 index 0000000..45791ca --- /dev/null +++ b/src/textfilter.cpp @@ -0,0 +1,46 @@ +#include "textfilter.h" + +#include // mrbesen/util.h + +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); +} + +void TextFilter::setup(std::ostream& o, std::istream& str) { + o << "Such text:"; + + std::getline(str, text); + + Filter::setup(o, str); + + if(ignoreCase) { + mrbesen::util::toLower(text); + } +} + +bool TextFilter::filter(const Message& m) const { + if(ignoreCase) + return matchesIC(m.text, text); + else + return matches(m.text, text); +} + +void RegexFilter::setup(std::ostream& o, std::istream& str) { + //build regex pattern + std::string text = ""; //TODO + pattern = std::regex(text, (ignoreCase ? std::regex::icase : (std::regex::flag_type) 0)); + + Filter::setup(o, str); +} + +bool RegexFilter::filter(const Message& m) const { + return std::regex_search(m.text, pattern); +} \ No newline at end of file diff --git a/src/textfilter.h b/src/textfilter.h new file mode 100644 index 0000000..708b528 --- /dev/null +++ b/src/textfilter.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "filter.h" + +struct TextFilter : public Filter { + + virtual void setup(std::ostream& o, std::istream& str) override; + virtual bool filter(const Message& m) const override; +protected: + std::string text; +}; + +struct RegexFilter : public Filter { + virtual void setup(std::ostream& o, std::istream& str) override; + virtual bool filter(const Message& m) const override; +protected: + std::regex pattern; +}; \ No newline at end of file