new filter abstraction
This commit is contained in:
parent
797eecc487
commit
d46579b73f
|
@ -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');
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#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;
|
||||
};
|
59
src/main.cpp
59
src/main.cpp
|
@ -2,11 +2,13 @@
|
|||
#include <Log.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <signal.h> //signal handler
|
||||
#include <unistd.h>
|
||||
#include <ctime>
|
||||
|
||||
#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<std::string, Filter*> 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<std::streamsize>::max(), '\n');
|
||||
}
|
||||
|
||||
Filter* chooseFilter(std::ostream& o, std::istream& istr) {
|
||||
//print filter
|
||||
uint32_t i = 0;
|
||||
std::vector<Filter*> 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<const Message*> results = search->search(searchterm, parsedflags);
|
||||
std::list<const Message*> 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<std::streamsize>::max(), '\n');
|
||||
resetStream(std::cin);
|
||||
}
|
||||
|
||||
if(search != &rootsearch)
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
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;
|
||||
};
|
|
@ -8,51 +8,6 @@
|
|||
#include <nlohmann/json.hpp>
|
||||
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<const Message*>* list) {
|
||||
//copy messages
|
||||
|
@ -69,27 +24,6 @@ Search::Search(const Search& orig, std::list<const Message*>* 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<const Message*> Search::search(std::string text, Searchflags flags) const {
|
||||
std::list<const Message*> Search::search(const Filter& filter) const {
|
||||
std::list<const Message*> 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<const std::string&>(text, matchesIC, out);
|
||||
} else {
|
||||
runsearch<const std::string&>(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<const Message*>& out) const {
|
||||
//build regex pattern
|
||||
const std::regex pattern(text, (ignoreCase ? std::regex::icase : (std::regex::flag_type) 0));
|
||||
|
||||
runsearch<const std::regex&>(pattern, matchesRegex, out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Search::runsearch(T st, bool (*checker)(const std::string& msg, T text), std::list<const Message*>& out) const {
|
||||
void Search::runsearch(const Filter& filter, std::list<const Message*>& out) const {
|
||||
for(const Message& m : msgs) {
|
||||
if(checker(m.text, st)) {
|
||||
if(filter.filter(m)) {
|
||||
out.push_back(&m);
|
||||
}
|
||||
}
|
||||
|
|
34
src/search.h
34
src/search.h
|
@ -10,31 +10,8 @@
|
|||
#include <nlohmann/json_fwd.hpp>
|
||||
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<const Message*>* 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<const Message*> search(std::string text, Searchflags flags = Searchflags::NONE) const;
|
||||
std::list<const Message*> 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<const Message*>& out) const;
|
||||
|
||||
template<typename T>
|
||||
void runsearch(T st, bool (*checker)(const std::string& msg, T text), std::list<const Message*>& out) const;
|
||||
void runsearch(const Filter& filter, std::list<const Message*>& out) const;
|
||||
|
||||
void loadMessages(const json& j, int64_t chatid);
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
#include "textfilter.h"
|
||||
|
||||
#include <util.h> // 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);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <regex>
|
||||
|
||||
#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;
|
||||
};
|
Loading…
Reference in New Issue