2018-08-10 00:04:50 +02:00
|
|
|
#include "controllers/notifications/NotificationController.hpp"
|
2018-08-09 15:41:03 +02:00
|
|
|
|
|
|
|
#include "Application.hpp"
|
2020-11-21 16:20:10 +01:00
|
|
|
#include "common/QLogging.hpp"
|
2018-08-09 15:41:03 +02:00
|
|
|
#include "controllers/notifications/NotificationModel.hpp"
|
2023-01-29 10:36:25 +01:00
|
|
|
#include "controllers/sound/SoundController.hpp"
|
2022-12-31 15:41:01 +01:00
|
|
|
#include "messages/Message.hpp"
|
Sort and force grouping of includes (#4172)
This change enforces strict include grouping using IncludeCategories
In addition to adding this to the .clang-format file and applying it in the tests/src and src directories, I also did the following small changes:
In ChatterSet.hpp, I changed lrucache to a <>include
In Irc2.hpp, I change common/SignalVector.hpp to a "project-include"
In AttachedWindow.cpp, NativeMessaging.cpp, WindowsHelper.hpp, BaseWindow.cpp, and StreamerMode.cpp, I disabled clang-format for the windows-includes
In WindowDescriptors.hpp, I added the missing vector include. It was previously not needed because the include was handled by another file that was previously included first.
clang-format minimum version has been bumped, so Ubuntu version used in the check-formatting job has been bumped to 22.04 (which is the latest LTS)
2022-11-27 19:32:53 +01:00
|
|
|
#include "providers/twitch/api/Helix.hpp"
|
2019-09-15 13:02:02 +02:00
|
|
|
#include "providers/twitch/TwitchIrcServer.hpp"
|
2021-05-09 16:17:04 +02:00
|
|
|
#include "providers/twitch/TwitchMessageBuilder.hpp"
|
2022-12-31 15:41:01 +01:00
|
|
|
#include "singletons/Settings.hpp"
|
2018-08-14 16:29:52 +02:00
|
|
|
#include "singletons/Toasts.hpp"
|
2018-08-25 14:13:26 +02:00
|
|
|
#include "singletons/WindowManager.hpp"
|
2022-06-17 20:52:20 +02:00
|
|
|
#include "util/Helpers.hpp"
|
2018-08-29 19:25:37 +02:00
|
|
|
#include "widgets/Window.hpp"
|
2018-08-09 15:41:03 +02:00
|
|
|
|
2018-09-01 13:01:54 +02:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
# include <wintoastlib.h>
|
|
|
|
#endif
|
2018-08-11 12:47:03 +02:00
|
|
|
|
|
|
|
#include <QDesktopServices>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QUrl>
|
Sort and force grouping of includes (#4172)
This change enforces strict include grouping using IncludeCategories
In addition to adding this to the .clang-format file and applying it in the tests/src and src directories, I also did the following small changes:
In ChatterSet.hpp, I changed lrucache to a <>include
In Irc2.hpp, I change common/SignalVector.hpp to a "project-include"
In AttachedWindow.cpp, NativeMessaging.cpp, WindowsHelper.hpp, BaseWindow.cpp, and StreamerMode.cpp, I disabled clang-format for the windows-includes
In WindowDescriptors.hpp, I added the missing vector include. It was previously not needed because the include was handled by another file that was previously included first.
clang-format minimum version has been bumped, so Ubuntu version used in the check-formatting job has been bumped to 22.04 (which is the latest LTS)
2022-11-27 19:32:53 +01:00
|
|
|
|
2022-01-01 18:06:54 +01:00
|
|
|
#include <unordered_set>
|
2018-08-11 12:47:03 +02:00
|
|
|
|
2018-08-09 15:41:03 +02:00
|
|
|
namespace chatterino {
|
|
|
|
|
|
|
|
void NotificationController::initialize(Settings &settings, Paths &paths)
|
|
|
|
{
|
|
|
|
this->initialized_ = true;
|
2018-08-12 15:29:40 +02:00
|
|
|
for (const QString &channelName : this->twitchSetting_.getValue())
|
|
|
|
{
|
2020-02-23 19:44:13 +01:00
|
|
|
this->channelMap[Platform::Twitch].append(channelName);
|
2018-08-09 15:41:03 +02:00
|
|
|
}
|
|
|
|
|
2023-09-16 13:52:51 +02:00
|
|
|
// We can safely ignore this signal connection since channelMap will always be destroyed
|
|
|
|
// before the NotificationController
|
|
|
|
std::ignore =
|
|
|
|
this->channelMap[Platform::Twitch].delayedItemsChanged.connect([this] {
|
|
|
|
this->twitchSetting_.setValue(
|
|
|
|
this->channelMap[Platform::Twitch].raw());
|
|
|
|
});
|
2018-08-25 14:13:26 +02:00
|
|
|
|
|
|
|
liveStatusTimer_ = new QTimer();
|
2018-08-26 13:19:09 +02:00
|
|
|
|
|
|
|
this->fetchFakeChannels();
|
|
|
|
|
2022-12-24 12:56:11 +01:00
|
|
|
QObject::connect(this->liveStatusTimer_, &QTimer::timeout, [this] {
|
2020-11-08 12:02:19 +01:00
|
|
|
this->fetchFakeChannels();
|
|
|
|
});
|
2018-08-26 13:19:09 +02:00
|
|
|
this->liveStatusTimer_->start(60 * 1000);
|
2018-08-09 15:41:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void NotificationController::updateChannelNotification(
|
2018-08-12 18:54:32 +02:00
|
|
|
const QString &channelName, Platform p)
|
2018-08-09 15:41:03 +02:00
|
|
|
{
|
2018-08-12 20:21:21 +02:00
|
|
|
if (isChannelNotified(channelName, p))
|
|
|
|
{
|
|
|
|
removeChannelNotification(channelName, p);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
addChannelNotification(channelName, p);
|
2018-08-09 15:41:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-12 15:29:40 +02:00
|
|
|
bool NotificationController::isChannelNotified(const QString &channelName,
|
2018-08-12 18:54:32 +02:00
|
|
|
Platform p)
|
2018-08-09 15:41:03 +02:00
|
|
|
{
|
2019-07-31 22:29:07 +02:00
|
|
|
for (const auto &channel : this->channelMap[p])
|
2018-08-12 20:21:21 +02:00
|
|
|
{
|
|
|
|
if (channelName.toLower() == channel.toLower())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2018-08-11 16:11:51 +02:00
|
|
|
}
|
2018-08-10 00:04:50 +02:00
|
|
|
return false;
|
2018-08-09 15:41:03 +02:00
|
|
|
}
|
2018-08-11 12:47:03 +02:00
|
|
|
|
2018-08-12 20:21:21 +02:00
|
|
|
void NotificationController::addChannelNotification(const QString &channelName,
|
|
|
|
Platform p)
|
2018-08-09 15:41:03 +02:00
|
|
|
{
|
2020-02-23 19:44:13 +01:00
|
|
|
channelMap[p].append(channelName);
|
2018-08-09 15:41:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void NotificationController::removeChannelNotification(
|
2018-08-12 20:21:21 +02:00
|
|
|
const QString &channelName, Platform p)
|
2018-08-09 15:41:03 +02:00
|
|
|
{
|
2020-02-23 19:44:13 +01:00
|
|
|
for (std::vector<int>::size_type i = 0; i != channelMap[p].raw().size();
|
|
|
|
i++)
|
2018-08-12 20:21:21 +02:00
|
|
|
{
|
2020-02-23 17:45:59 +01:00
|
|
|
if (channelMap[p].raw()[i].toLower() == channelName.toLower())
|
2018-08-12 21:05:12 +02:00
|
|
|
{
|
2020-02-23 19:44:13 +01:00
|
|
|
channelMap[p].removeAt(i);
|
2018-08-10 00:04:50 +02:00
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
2018-08-09 15:41:03 +02:00
|
|
|
}
|
2018-08-12 20:21:21 +02:00
|
|
|
void NotificationController::playSound()
|
|
|
|
{
|
2019-05-10 23:31:10 +02:00
|
|
|
QUrl highlightSoundUrl =
|
|
|
|
getSettings()->notificationCustomSound
|
|
|
|
? QUrl::fromLocalFile(
|
|
|
|
getSettings()->notificationPathSound.getValue())
|
|
|
|
: QUrl("qrc:/sounds/ping2.wav");
|
2019-05-08 08:51:14 +02:00
|
|
|
|
2023-01-29 10:36:25 +01:00
|
|
|
getApp()->sound->play(highlightSoundUrl);
|
2018-08-12 20:21:21 +02:00
|
|
|
}
|
2018-08-24 18:38:27 +02:00
|
|
|
|
2018-08-12 18:54:32 +02:00
|
|
|
NotificationModel *NotificationController::createModel(QObject *parent,
|
|
|
|
Platform p)
|
2018-08-09 15:41:03 +02:00
|
|
|
{
|
|
|
|
NotificationModel *model = new NotificationModel(parent);
|
2020-02-23 19:31:43 +01:00
|
|
|
model->initialize(&this->channelMap[p]);
|
2018-08-09 15:41:03 +02:00
|
|
|
return model;
|
|
|
|
}
|
|
|
|
|
2018-08-25 14:13:26 +02:00
|
|
|
void NotificationController::fetchFakeChannels()
|
|
|
|
{
|
2022-01-01 18:06:54 +01:00
|
|
|
qCDebug(chatterinoNotification) << "fetching fake channels";
|
|
|
|
QStringList channels;
|
2018-08-25 14:13:26 +02:00
|
|
|
for (std::vector<int>::size_type i = 0;
|
2022-06-17 20:52:20 +02:00
|
|
|
i < channelMap[Platform::Twitch].raw().size(); i++)
|
2018-08-25 14:13:26 +02:00
|
|
|
{
|
2022-03-19 12:02:29 +01:00
|
|
|
auto chan = getApp()->twitch->getChannelOrEmpty(
|
2020-02-23 17:45:59 +01:00
|
|
|
channelMap[Platform::Twitch].raw()[i]);
|
2018-08-26 13:19:09 +02:00
|
|
|
if (chan->isEmpty())
|
|
|
|
{
|
2022-01-01 18:06:54 +01:00
|
|
|
channels.push_back(channelMap[Platform::Twitch].raw()[i]);
|
2018-08-25 14:13:26 +02:00
|
|
|
}
|
|
|
|
}
|
2022-06-17 20:52:20 +02:00
|
|
|
|
|
|
|
for (const auto &batch : splitListIntoBatches(channels))
|
2022-01-01 18:06:54 +01:00
|
|
|
{
|
|
|
|
getHelix()->fetchStreams(
|
2022-06-17 20:52:20 +02:00
|
|
|
{}, batch,
|
2022-01-01 18:06:54 +01:00
|
|
|
[batch, this](std::vector<HelixStream> streams) {
|
|
|
|
std::unordered_set<QString> liveStreams;
|
|
|
|
for (const auto &stream : streams)
|
|
|
|
{
|
2022-04-06 23:10:22 +02:00
|
|
|
liveStreams.insert(stream.userLogin);
|
2022-01-01 18:06:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &name : batch)
|
|
|
|
{
|
2022-04-06 23:10:22 +02:00
|
|
|
auto it = liveStreams.find(name.toLower());
|
2022-01-01 18:06:54 +01:00
|
|
|
this->checkStream(it != liveStreams.end(), name);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[batch]() {
|
|
|
|
// we done fucked up.
|
|
|
|
qCWarning(chatterinoNotification)
|
|
|
|
<< "Failed to fetch live status for " << batch;
|
2022-05-28 20:10:10 +02:00
|
|
|
},
|
|
|
|
[]() {
|
|
|
|
// finally
|
2022-01-01 18:06:54 +01:00
|
|
|
});
|
|
|
|
}
|
2018-08-25 14:13:26 +02:00
|
|
|
}
|
2022-01-01 18:06:54 +01:00
|
|
|
void NotificationController::checkStream(bool live, QString channelName)
|
2018-08-25 14:13:26 +02:00
|
|
|
{
|
2022-01-01 18:06:54 +01:00
|
|
|
qCDebug(chatterinoNotification)
|
|
|
|
<< "[TwitchChannel" << channelName << "] Refreshing live status";
|
2020-03-14 12:13:57 +01:00
|
|
|
|
2022-01-01 18:06:54 +01:00
|
|
|
if (!live)
|
|
|
|
{
|
|
|
|
// Stream is offline
|
|
|
|
this->removeFakeChannel(channelName);
|
|
|
|
return;
|
|
|
|
}
|
2020-03-14 12:13:57 +01:00
|
|
|
|
2022-01-01 18:06:54 +01:00
|
|
|
// Stream is online
|
|
|
|
auto i = std::find(fakeTwitchChannels.begin(), fakeTwitchChannels.end(),
|
|
|
|
channelName);
|
2020-03-14 12:13:57 +01:00
|
|
|
|
2022-01-01 18:06:54 +01:00
|
|
|
if (i != fakeTwitchChannels.end())
|
|
|
|
{
|
|
|
|
// We have already pushed the live state of this stream
|
|
|
|
// Could not find stream in fake Twitch channels!
|
|
|
|
return;
|
|
|
|
}
|
2020-03-14 12:13:57 +01:00
|
|
|
|
2022-01-01 18:06:54 +01:00
|
|
|
if (Toasts::isEnabled())
|
|
|
|
{
|
2022-10-30 13:29:43 +01:00
|
|
|
getApp()->toasts->sendChannelNotification(channelName, QString(),
|
2022-01-01 18:06:54 +01:00
|
|
|
Platform::Twitch);
|
|
|
|
}
|
|
|
|
if (getSettings()->notificationPlaySound &&
|
|
|
|
!(isInStreamerMode() &&
|
|
|
|
getSettings()->streamerModeSuppressLiveNotifications))
|
|
|
|
{
|
|
|
|
getApp()->notifications->playSound();
|
|
|
|
}
|
|
|
|
if (getSettings()->notificationFlashTaskbar &&
|
|
|
|
!(isInStreamerMode() &&
|
|
|
|
getSettings()->streamerModeSuppressLiveNotifications))
|
|
|
|
{
|
|
|
|
getApp()->windows->sendAlert();
|
|
|
|
}
|
|
|
|
MessageBuilder builder;
|
|
|
|
TwitchMessageBuilder::liveMessage(channelName, &builder);
|
2022-03-19 12:02:29 +01:00
|
|
|
getApp()->twitch->liveChannel->addMessage(builder.release());
|
2022-01-01 18:06:54 +01:00
|
|
|
|
|
|
|
// Indicate that we have pushed notifications for this stream
|
|
|
|
fakeTwitchChannels.push_back(channelName);
|
2018-08-25 14:13:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void NotificationController::removeFakeChannel(const QString channelName)
|
|
|
|
{
|
|
|
|
auto i = std::find(fakeTwitchChannels.begin(), fakeTwitchChannels.end(),
|
|
|
|
channelName);
|
|
|
|
if (i != fakeTwitchChannels.end())
|
|
|
|
{
|
|
|
|
fakeTwitchChannels.erase(i);
|
2022-04-30 13:37:24 +02:00
|
|
|
// "delete" old 'CHANNEL is live' message
|
|
|
|
LimitedQueueSnapshot<MessagePtr> snapshot =
|
|
|
|
getApp()->twitch->liveChannel->getMessageSnapshot();
|
|
|
|
int snapshotLength = snapshot.size();
|
|
|
|
|
|
|
|
// MSVC hates this code if the parens are not there
|
|
|
|
int end = (std::max)(0, snapshotLength - 200);
|
|
|
|
// this assumes that channelName is a login name therefore will only delete messages from fake channels
|
|
|
|
auto liveMessageSearchText = QString("%1 is live!").arg(channelName);
|
|
|
|
|
|
|
|
for (int i = snapshotLength - 1; i >= end; --i)
|
|
|
|
{
|
|
|
|
auto &s = snapshot[i];
|
|
|
|
|
|
|
|
if (s->messageText == liveMessageSearchText)
|
|
|
|
{
|
|
|
|
s->flags.set(MessageFlag::Disabled);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-08-25 14:13:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 15:41:03 +02:00
|
|
|
} // namespace chatterino
|