File indexing completed on 2024-06-16 07:47:19

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003 
0004     SPDX-FileCopyrightText: 2010-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "filtermanager.h"
0010 
0011 #include <QAction>
0012 #include <QTimer>
0013 
0014 #include <KActionCollection>
0015 #include <KLocalizedString>
0016 #include <KMessageBox>
0017 #include <KPluginFactory>
0018 
0019 #include "choqokuiglobal.h"
0020 #include "postwidget.h"
0021 #include "quickpost.h"
0022 #include "timelinewidget.h"
0023 
0024 #include "twitterapiaccount.h"
0025 
0026 #include "configurefilters.h"
0027 #include "filter.h"
0028 #include "filtersettings.h"
0029 
0030 K_PLUGIN_CLASS_WITH_JSON(FilterManager, "choqok_filter.json")
0031 
0032 FilterManager::FilterManager(QObject *parent, const QList<QVariant> &)
0033     : Choqok::Plugin(QLatin1String("choqok_filter"), parent), state(Stopped)
0034 {
0035     QAction *action = new QAction(i18n("Configure Filters..."), this);
0036     actionCollection()->addAction(QLatin1String("configureFilters"), action);
0037     connect(action, &QAction::triggered, this, &FilterManager::slotConfigureFilters);
0038     setXMLFile(QLatin1String("filterui.rc"));
0039     connect(Choqok::UI::Global::SessionManager::self(), &Choqok::UI::Global::SessionManager::newPostWidgetAdded,
0040             this, &FilterManager::slotAddNewPostWidget);
0041 
0042     hidePost = new QAction(i18n("Hide Post"), this);
0043     Choqok::UI::PostWidget::addAction(hidePost);
0044     connect(hidePost, &QAction::triggered, this, &FilterManager::slotHidePost);
0045 }
0046 
0047 FilterManager::~FilterManager()
0048 {
0049 
0050 }
0051 
0052 void FilterManager::slotAddNewPostWidget(Choqok::UI::PostWidget *newWidget)
0053 {
0054     postsQueue.enqueue(newWidget);
0055     if (state == Stopped) {
0056         state = Running;
0057         QTimer::singleShot(1000, this, SLOT(startParsing()));
0058     }
0059 }
0060 
0061 void FilterManager::startParsing()
0062 {
0063     int i = 8;
0064     while (!postsQueue.isEmpty() && i > 0) {
0065         parse(postsQueue.dequeue());
0066         --i;
0067     }
0068 
0069     if (postsQueue.isEmpty()) {
0070         state = Stopped;
0071     } else {
0072         QTimer::singleShot(500, this, SLOT(startParsing()));
0073     }
0074 }
0075 
0076 void FilterManager::parse(Choqok::UI::PostWidget *postToParse)
0077 {
0078     if (!postToParse ||
0079             postToParse->currentPost()->author.userName == postToParse->currentAccount()->username() ||
0080             postToParse->isRead()) {
0081         return;
0082     }
0083 
0084     if (parseSpecialRules(postToParse)) {
0085         return;
0086     }
0087 
0088     if (!postToParse) {
0089         return;
0090     }
0091     //qDebug() << "Processing: "<<postToParse->content();
0092     for (Filter *filter: FilterSettings::self()->filters()) {
0093         if (filter->filterText().isEmpty()) {
0094             return;
0095         }
0096         if (filter->filterAction() == Filter::Remove && filter->dontHideReplies() &&
0097                 (postToParse->currentPost()->replyToUser.userName.compare(postToParse->currentAccount()->username(),
0098                         Qt::CaseInsensitive) == 0 ||
0099                  postToParse->currentPost()->content.contains(QStringLiteral("@%1").arg(postToParse->currentAccount()->username())))
0100            ) {
0101             continue;
0102         }
0103         switch (filter->filterField()) {
0104         case Filter::Content:
0105             doFiltering(postToParse, filterText(postToParse->currentPost()->content, filter));
0106             break;
0107         case Filter::AuthorUsername:
0108             doFiltering(postToParse, filterText(postToParse->currentPost()->author.userName, filter));
0109             break;
0110         case Filter::ReplyToUsername:
0111             doFiltering(postToParse, filterText(postToParse->currentPost()->replyToUser.userName, filter));
0112             break;
0113         case Filter::Source:
0114             doFiltering(postToParse, filterText(postToParse->currentPost()->source, filter));
0115             break;
0116         default:
0117             break;
0118         };
0119     }
0120 }
0121 
0122 Filter::FilterAction FilterManager::filterText(const QString &textToCheck, Filter *filter)
0123 {
0124     bool filtered = false;
0125     switch (filter->filterType()) {
0126     case Filter::ExactMatch:
0127         if (textToCheck.compare(filter->filterText(), Qt::CaseInsensitive) == 0) {
0128             //qDebug() << "ExactMatch:" << filter->filterText();
0129             filtered = true;
0130         }
0131         break;
0132     case Filter::RegExp:
0133         if (textToCheck.contains(QRegExp(filter->filterText()))) {
0134             //qDebug() << "RegExp:" << filter->filterText();
0135             filtered = true;
0136         }
0137         break;
0138     case Filter::Contain:
0139         if (textToCheck.contains(filter->filterText(), Qt::CaseInsensitive)) {
0140             //qDebug() << "Contain:" << filter->filterText();
0141             filtered = true;
0142         }
0143         break;
0144     case Filter::DoesNotContain:
0145         if (!textToCheck.contains(filter->filterText(), Qt::CaseInsensitive)) {
0146             //qDebug() << "DoesNotContain:" << filter->filterText();
0147             filtered = true;
0148         }
0149         break;
0150     default:
0151         break;
0152     }
0153     if (filtered) {
0154         return filter->filterAction();
0155     } else {
0156         return Filter::None;
0157     }
0158 }
0159 
0160 void FilterManager::doFiltering(Choqok::UI::PostWidget *postToFilter, Filter::FilterAction action)
0161 {
0162     QString css;
0163     switch (action) {
0164     case Filter::Remove:
0165         //qDebug() << "Post removed:" << postToFilter->currentPost()->content;
0166         postToFilter->close();
0167         break;
0168     case Filter::Highlight:
0169         css = postToFilter->styleSheet();
0170         css.replace(QLatin1String("border: 1px solid rgb(150,150,150)"), QLatin1String("border: 2px solid rgb(255,0,0)"));
0171         postToFilter->setStyleSheet(css);
0172         break;
0173     case Filter::None:
0174     default:
0175         //Do nothing
0176         break;
0177     }
0178 }
0179 
0180 void FilterManager::slotConfigureFilters()
0181 {
0182     QPointer<ConfigureFilters> dlg = new ConfigureFilters(Choqok::UI::Global::mainWindow());
0183     dlg->show();
0184 }
0185 
0186 bool FilterManager::parseSpecialRules(Choqok::UI::PostWidget *postToParse)
0187 {
0188     if (FilterSettings::hideRepliesNotRelatedToMe()) {
0189         if (!postToParse->currentPost()->replyToUser.userName.isEmpty() &&
0190                 postToParse->currentPost()->replyToUser.userName != postToParse->currentAccount()->username()) {
0191             if (!postToParse->currentPost()->content.contains(postToParse->currentAccount()->username())) {
0192                 postToParse->close();
0193 //                qDebug() << "NOT RELATE TO ME FILTERING......";
0194                 return true;
0195             }
0196         }
0197     }
0198 
0199     if (FilterSettings::hideNoneFriendsReplies()) {
0200         TwitterApiAccount *acc = qobject_cast<TwitterApiAccount *>(postToParse->currentAccount());
0201         if (!acc) {
0202             return false;
0203         }
0204         if (!postToParse->currentPost()->replyToUser.userName.isEmpty() &&
0205                 !acc->friendsList().contains(postToParse->currentPost()->replyToUser.userName)) {
0206             if (!postToParse->currentPost()->content.contains(postToParse->currentAccount()->username())) {
0207                 postToParse->close();
0208 //                qDebug() << "NONE FRIEND FILTERING......";
0209                 return true;
0210             }
0211         }
0212     }
0213 
0214     return false;
0215 }
0216 
0217 void FilterManager::slotHidePost()
0218 {
0219     Choqok::UI::PostWidget *wd;
0220     wd = dynamic_cast<Choqok::UI::PostWidgetUserData *>(hidePost->userData(32))->postWidget();
0221     QString username = wd->currentPost()->author.userName;
0222     int res = KMessageBox::questionYesNoCancel(choqokMainWindow, i18n("Hide all posts from <b>@%1</b>?",
0223               username));
0224     if (res == KMessageBox::Cancel) {
0225         return;
0226     } else if (res == KMessageBox::Yes) {
0227         Filter *fil = new Filter(username, Filter::AuthorUsername, Filter::ExactMatch);
0228         fil->writeConfig();
0229         QList<Filter *> filterList = FilterSettings::self()->filters();
0230         filterList.append(fil);
0231         FilterSettings::self()->setFilters(filterList);
0232         Choqok::UI::TimelineWidget *tm = wd->timelineWidget();
0233         if (tm) {
0234 //            qDebug() << "Closing all posts";
0235             for (Choqok::UI::PostWidget *pw: tm->postWidgets()) {
0236                 if (pw->currentPost()->author.userName == username) {
0237                     pw->close();
0238                 }
0239             }
0240         } else {
0241             wd->close();
0242         }
0243     } else {
0244         wd->close();
0245     }
0246 }
0247 
0248 #include "filtermanager.moc"
0249 #include "moc_filtermanager.cpp"