Notifications system. Mainly to be used in case the updater breaks.
This commit is contained in:
		| @@ -144,6 +144,8 @@ SET(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.") | ||||
| # Updater enabled? | ||||
| SET(MultiMC_UPDATER false CACHE BOOL "Whether or not the update system is enabled. If this is enabled, you must also set MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL in order for it to work properly.") | ||||
|  | ||||
| # Notification URL | ||||
| SET(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.") | ||||
|  | ||||
| # Build a version string to display in the configure logs. | ||||
| SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}") | ||||
| @@ -337,6 +339,8 @@ logic/updater/UpdateChecker.h | ||||
| logic/updater/UpdateChecker.cpp | ||||
| logic/updater/DownloadUpdateTask.h | ||||
| logic/updater/DownloadUpdateTask.cpp | ||||
| logic/updater/NotificationChecker.h | ||||
| logic/updater/NotificationChecker.cpp | ||||
|  | ||||
| # News System | ||||
| logic/news/NewsChecker.h | ||||
|   | ||||
| @@ -26,6 +26,7 @@ | ||||
| #include "logic/JavaUtils.h" | ||||
|  | ||||
| #include "logic/updater/UpdateChecker.h" | ||||
| #include "logic/updater/NotificationChecker.h" | ||||
|  | ||||
| #include "pathutils.h" | ||||
| #include "cmdutils.h" | ||||
| @@ -182,6 +183,9 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &data_dir_override) | ||||
| 	// initialize the updater | ||||
| 	m_updateChecker.reset(new UpdateChecker()); | ||||
|  | ||||
| 	// initialize the notification checker | ||||
| 	m_notificationChecker.reset(new NotificationChecker()); | ||||
|  | ||||
| 	// initialize the news checker | ||||
| 	m_newsChecker.reset(new NewsChecker(NEWS_RSS_URL)); | ||||
|  | ||||
| @@ -350,6 +354,7 @@ void MultiMC::initGlobalSettings() | ||||
| 	// Updates | ||||
| 	m_settings->registerSetting("UseDevBuilds", false); | ||||
| 	m_settings->registerSetting("AutoUpdate", true); | ||||
| 	m_settings->registerSetting("ShownNotifications", QString()); | ||||
|  | ||||
| 	// FTB | ||||
| 	m_settings->registerSetting("TrackFTBInstances", false); | ||||
|   | ||||
| @@ -17,6 +17,7 @@ class QNetworkAccessManager; | ||||
| class ForgeVersionList; | ||||
| class JavaVersionList; | ||||
| class UpdateChecker; | ||||
| class NotificationChecker; | ||||
| class NewsChecker; | ||||
|  | ||||
| #if defined(MMC) | ||||
| @@ -90,6 +91,11 @@ public: | ||||
| 		return m_updateChecker; | ||||
| 	} | ||||
|  | ||||
| 	std::shared_ptr<NotificationChecker> notificationChecker() | ||||
| 	{ | ||||
| 		return m_notificationChecker; | ||||
| 	} | ||||
|  | ||||
| 	std::shared_ptr<NewsChecker> newsChecker() | ||||
| 	{ | ||||
| 		return m_newsChecker; | ||||
| @@ -166,6 +172,7 @@ private: | ||||
| 	std::shared_ptr<SettingsObject> m_settings; | ||||
| 	std::shared_ptr<InstanceList> m_instances; | ||||
| 	std::shared_ptr<UpdateChecker> m_updateChecker; | ||||
| 	std::shared_ptr<NotificationChecker> m_notificationChecker; | ||||
| 	std::shared_ptr<NewsChecker> m_newsChecker; | ||||
| 	std::shared_ptr<MojangAccountList> m_accounts; | ||||
| 	std::shared_ptr<IconList> m_icons; | ||||
|   | ||||
| @@ -10,6 +10,12 @@ | ||||
| // URL for the updater's channel | ||||
| #define CHANLIST_URL       "@MultiMC_CHANLIST_URL@" | ||||
|  | ||||
| // URL for notifications | ||||
| #define NOTIFICATION_URL "@MultiMC_NOTIFICATION_URL@" | ||||
|  | ||||
| // Used for matching notifications | ||||
| #define FULL_VERSION_STR "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_BUILD@" | ||||
|  | ||||
| // The commit hash of this build | ||||
| #define GIT_COMMIT		   "@MultiMC_GIT_COMMIT@" | ||||
|  | ||||
|   | ||||
| @@ -92,6 +92,7 @@ | ||||
| #include "logic/assets/AssetsUtils.h" | ||||
| #include "logic/assets/AssetsMigrateTask.h" | ||||
| #include <logic/updater/UpdateChecker.h> | ||||
| #include <logic/updater/NotificationChecker.h> | ||||
| #include <logic/tasks/ThreadTask.h> | ||||
|  | ||||
| MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) | ||||
| @@ -279,6 +280,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi | ||||
| 		// if automatic update checks are allowed, start one. | ||||
| 		if (MMC->settings()->get("AutoUpdate").toBool()) | ||||
| 			on_actionCheckUpdate_triggered(); | ||||
|  | ||||
| 		connect(MMC->notificationChecker().get(), &NotificationChecker::notificationCheckFinished, | ||||
| 				this, &MainWindow::notificationsChanged); | ||||
| 	} | ||||
|  | ||||
| 	const QString currentInstanceId = MMC->settings()->get("SelectedInstance").toString(); | ||||
| @@ -495,6 +499,58 @@ void MainWindow::updateAvailable(QString repo, QString versionName, int versionI | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QList<int> stringToIntList(const QString &string) | ||||
| { | ||||
| 	QStringList split = string.split(',', QString::SkipEmptyParts); | ||||
| 	QList<int> out; | ||||
| 	for (int i = 0; i < split.size(); ++i) | ||||
| 	{ | ||||
| 		out.append(split.at(i).toInt()); | ||||
| 	} | ||||
| 	return out; | ||||
| } | ||||
| QString intListToString(const QList<int> &list) | ||||
| { | ||||
| 	QStringList slist; | ||||
| 	for (int i = 0; i < list.size(); ++i) | ||||
| 	{ | ||||
| 		slist.append(QString::number(list.at(i))); | ||||
| 	} | ||||
| 	return slist.join(','); | ||||
| } | ||||
| void MainWindow::notificationsChanged() | ||||
| { | ||||
| 	QList<NotificationChecker::NotificationEntry> entries = | ||||
| 		MMC->notificationChecker()->notificationEntries(); | ||||
| 	QList<int> shownNotifications = | ||||
| 		stringToIntList(MMC->settings()->get("ShownNotifications").toString()); | ||||
| 	for (auto it = entries.begin(); it != entries.end(); ++it) | ||||
| 	{ | ||||
| 		NotificationChecker::NotificationEntry entry = *it; | ||||
| 		if (!shownNotifications.contains(entry.id) && entry.applies()) | ||||
| 		{ | ||||
| 			QMessageBox::Icon icon; | ||||
| 			switch (entry.type) | ||||
| 			{ | ||||
| 			case NotificationChecker::NotificationEntry::Critical: | ||||
| 				icon = QMessageBox::Critical; | ||||
| 				break; | ||||
| 			case NotificationChecker::NotificationEntry::Warning: | ||||
| 				icon = QMessageBox::Warning; | ||||
| 				break; | ||||
| 			case NotificationChecker::NotificationEntry::Information: | ||||
| 				icon = QMessageBox::Information; | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			QMessageBox box(icon, tr("Notification"), entry.message, QMessageBox::Ok, this); | ||||
| 			box.exec(); | ||||
| 			shownNotifications.append(entry.id); | ||||
| 		} | ||||
| 	} | ||||
| 	MMC->settings()->set("ShownNotifications", intListToString(shownNotifications)); | ||||
| } | ||||
|  | ||||
| void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit) | ||||
| { | ||||
| 	QLOG_INFO() << "Downloading updates."; | ||||
|   | ||||
| @@ -157,6 +157,8 @@ slots: | ||||
|  | ||||
| 	void updateAvailable(QString repo, QString versionName, int versionId); | ||||
|  | ||||
| 	void notificationsChanged(); | ||||
|  | ||||
| 	void activeAccountChanged(); | ||||
|  | ||||
| 	void changeActiveAccount(); | ||||
|   | ||||
							
								
								
									
										113
									
								
								logic/updater/NotificationChecker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								logic/updater/NotificationChecker.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | ||||
| #include "NotificationChecker.h" | ||||
|  | ||||
| #include <QJsonDocument> | ||||
| #include <QJsonObject> | ||||
| #include <QJsonArray> | ||||
|  | ||||
| #include "MultiMC.h" | ||||
| #include "logic/net/CacheDownload.h" | ||||
| #include "config.h" | ||||
|  | ||||
| NotificationChecker::NotificationChecker(QObject *parent) | ||||
| 	: QObject(parent), m_notificationsUrl(QUrl(NOTIFICATION_URL)) | ||||
| { | ||||
| 	// this will call checkForNotifications once the event loop is running | ||||
| 	QMetaObject::invokeMethod(this, "checkForNotifications", Qt::QueuedConnection); | ||||
| } | ||||
|  | ||||
| QUrl NotificationChecker::notificationsUrl() const | ||||
| { | ||||
| 	return m_notificationsUrl; | ||||
| } | ||||
| void NotificationChecker::setNotificationsUrl(const QUrl ¬ificationsUrl) | ||||
| { | ||||
| 	m_notificationsUrl = notificationsUrl; | ||||
| } | ||||
|  | ||||
| QList<NotificationChecker::NotificationEntry> NotificationChecker::notificationEntries() const | ||||
| { | ||||
| 	return m_entries; | ||||
| } | ||||
|  | ||||
| void NotificationChecker::checkForNotifications() | ||||
| { | ||||
| 	if (m_checkJob) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	m_checkJob.reset(new NetJob("Checking for notifications")); | ||||
| 	auto entry = MMC->metacache()->resolveEntry("root", "notifications.json"); | ||||
| 	entry->stale = true; | ||||
| 	m_checkJob->addNetAction(m_download = CacheDownload::make(m_notificationsUrl, entry)); | ||||
| 	connect(m_download.get(), &CacheDownload::succeeded, this, | ||||
| 			&NotificationChecker::downloadSucceeded); | ||||
| 	m_checkJob->start(); | ||||
| } | ||||
|  | ||||
| void NotificationChecker::downloadSucceeded(int) | ||||
| { | ||||
| 	m_entries.clear(); | ||||
|  | ||||
| 	QFile file(m_download->m_output_file.fileName()); | ||||
| 	if (file.open(QFile::ReadOnly)) | ||||
| 	{ | ||||
| 		QJsonArray root = QJsonDocument::fromJson(file.readAll()).array(); | ||||
| 		for (auto it = root.begin(); it != root.end(); ++it) | ||||
| 		{ | ||||
| 			QJsonObject obj = (*it).toObject(); | ||||
| 			NotificationEntry entry; | ||||
| 			entry.id = obj.value("id").toInt(); | ||||
| 			entry.message = obj.value("message").toString(); | ||||
| 			entry.channel = obj.value("channel").toString(); | ||||
| 			entry.buildtype = obj.value("buildtype").toString(); | ||||
| 			entry.from = obj.value("from").toString(); | ||||
| 			entry.to = obj.value("to").toString(); | ||||
| 			const QString type = obj.value("type").toString("critical"); | ||||
| 			if (type == "critical") | ||||
| 			{ | ||||
| 				entry.type = NotificationEntry::Critical; | ||||
| 			} | ||||
| 			else if (type == "warning") | ||||
| 			{ | ||||
| 				entry.type = NotificationEntry::Warning; | ||||
| 			} | ||||
| 			else if (type == "information") | ||||
| 			{ | ||||
| 				entry.type = NotificationEntry::Information; | ||||
| 			} | ||||
| 			m_entries.append(entry); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	m_checkJob.reset(); | ||||
|  | ||||
| 	emit notificationCheckFinished(); | ||||
| } | ||||
|  | ||||
| bool NotificationChecker::NotificationEntry::applies() const | ||||
| { | ||||
| 	bool channelApplies = channel.isEmpty() || channel == VERSION_CHANNEL; | ||||
| 	bool buildtypeApplies = buildtype.isEmpty() || buildtype == VERSION_BUILD_TYPE; | ||||
| 	bool fromApplies = | ||||
| 		from.isEmpty() || from == FULL_VERSION_STR || !versionLessThan(FULL_VERSION_STR, from); | ||||
| 	bool toApplies = | ||||
| 		to.isEmpty() || to == FULL_VERSION_STR || !versionLessThan(to, FULL_VERSION_STR); | ||||
| 	return channelApplies && buildtypeApplies && fromApplies && toApplies; | ||||
| } | ||||
|  | ||||
| bool NotificationChecker::NotificationEntry::versionLessThan(const QString &v1, | ||||
| 															 const QString &v2) | ||||
| { | ||||
| 	QStringList l1 = v1.split('.'); | ||||
| 	QStringList l2 = v2.split('.'); | ||||
| 	while (!l1.isEmpty() && !l2.isEmpty()) | ||||
| 	{ | ||||
| 		int one = l1.isEmpty() ? 0 : l1.takeFirst().toInt(); | ||||
| 		int two = l2.isEmpty() ? 0 : l2.takeFirst().toInt(); | ||||
| 		if (one != two) | ||||
| 		{ | ||||
| 			return one < two; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
							
								
								
									
										54
									
								
								logic/updater/NotificationChecker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								logic/updater/NotificationChecker.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QObject> | ||||
|  | ||||
| #include "logic/net/NetJob.h" | ||||
| #include "logic/net/CacheDownload.h" | ||||
|  | ||||
| class NotificationChecker : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
|  | ||||
| public: | ||||
| 	explicit NotificationChecker(QObject *parent = 0); | ||||
|  | ||||
| 	QUrl notificationsUrl() const; | ||||
| 	void setNotificationsUrl(const QUrl ¬ificationsUrl); | ||||
|  | ||||
| 	struct NotificationEntry | ||||
| 	{ | ||||
| 		int id; | ||||
| 		QString message; | ||||
| 		enum | ||||
| 		{ | ||||
| 			Critical, | ||||
| 			Warning, | ||||
| 			Information | ||||
| 		} type; | ||||
| 		QString channel; | ||||
| 		QString buildtype; | ||||
| 		QString from; | ||||
| 		QString to; | ||||
| 		bool applies() const; | ||||
| 		static bool versionLessThan(const QString &v1, const QString &v2); | ||||
| 	}; | ||||
|  | ||||
| 	QList<NotificationEntry> notificationEntries() const; | ||||
|  | ||||
| public | ||||
| slots: | ||||
| 	void checkForNotifications(); | ||||
|  | ||||
| private | ||||
| slots: | ||||
| 	void downloadSucceeded(int); | ||||
|  | ||||
| signals: | ||||
| 	void notificationCheckFinished(); | ||||
|  | ||||
| private: | ||||
| 	QList<NotificationEntry> m_entries; | ||||
| 	QUrl m_notificationsUrl; | ||||
| 	NetJobPtr m_checkJob; | ||||
| 	CacheDownloadPtr m_download; | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user