mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-06-12 09:04:53 -07:00
Automatic Spoiler Season (#2991)
* oracle now can be run in spoiler or normal mode * tests for travis * only run on relaunch * spoilers in client (not oracle now) and tray icon shows when done * spoiler status will be checked before downloading spoiler file * only download if they care about spoilers * reload db on spoiler download * manual update button, code cleanup, and fix enabling sets when new * cleanup, nullchecks, and fixes to spoiler * reload DB even if not in spoiler season; necessary as we have a check elsewhere to prevent the reload if spoiler check happens * Implement changes from 2991#issuecomment-356169374 * Change implicit nullptrs, alert on file deletion, minor changes * make reload thread safe and minor changes from 2991#issuecomment-356450302 * Fix locking * Disable update now button while process running
This commit is contained in:
parent
51ec593759
commit
d19744236e
23 changed files with 2106 additions and 913 deletions
251
cockatrice/src/spoilerbackgroundupdater.cpp
Normal file
251
cockatrice/src/spoilerbackgroundupdater.cpp
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QNetworkReply>
|
||||
#include <QMessageBox>
|
||||
#include <QFile>
|
||||
#include <QApplication>
|
||||
#include <QtConcurrent>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include "spoilerbackgroundupdater.h"
|
||||
#include "settingscache.h"
|
||||
#include "carddatabase.h"
|
||||
#include "main.h"
|
||||
#include "window_main.h"
|
||||
|
||||
#define SPOILERS_STATUS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/SpoilerSeasonEnabled"
|
||||
#define SPOILERS_URL "https://raw.githubusercontent.com/Cockatrice/Magic-Spoiler/files/spoiler.xml"
|
||||
|
||||
SpoilerBackgroundUpdater::SpoilerBackgroundUpdater(QObject *apParent) : QObject(apParent), cardUpdateProcess(nullptr)
|
||||
{
|
||||
isSpoilerDownloadEnabled = settingsCache->getDownloadSpoilersStatus();
|
||||
if (isSpoilerDownloadEnabled)
|
||||
{
|
||||
// Start the process of checking if we're in spoiler season
|
||||
// File exists means we're in spoiler season
|
||||
// We will load the database before attempting to download spoilers, incase they fail
|
||||
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
|
||||
startSpoilerDownloadProcess(SPOILERS_STATUS_URL, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SpoilerBackgroundUpdater::startSpoilerDownloadProcess(QString url, bool saveResults)
|
||||
{
|
||||
auto spoilerURL = QUrl(url);
|
||||
downloadFromURL(spoilerURL, saveResults);
|
||||
}
|
||||
|
||||
void SpoilerBackgroundUpdater::downloadFromURL(QUrl url, bool saveResults)
|
||||
{
|
||||
auto *nam = new QNetworkAccessManager(this);
|
||||
QNetworkReply *reply = nam->get(QNetworkRequest(url));
|
||||
|
||||
if (saveResults)
|
||||
{
|
||||
// This will write out to the file (used for spoiler.xml)
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(actDownloadFinishedSpoilersFile()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will check the status (used to see if we're in spoiler season or not)
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(actCheckIfSpoilerSeasonEnabled()));
|
||||
}
|
||||
}
|
||||
|
||||
void SpoilerBackgroundUpdater::actDownloadFinishedSpoilersFile()
|
||||
{
|
||||
// Check for server reply
|
||||
auto *reply = dynamic_cast<QNetworkReply *>(sender());
|
||||
QNetworkReply::NetworkError errorCode = reply->error();
|
||||
|
||||
if (errorCode == QNetworkReply::NoError)
|
||||
{
|
||||
spoilerData = reply->readAll();
|
||||
|
||||
// Save the spoiler.xml file to the disk
|
||||
saveDownloadedFile(spoilerData);
|
||||
|
||||
reply->deleteLater();
|
||||
emit spoilerCheckerDone();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Error downloading spoilers file" << errorCode;
|
||||
emit spoilerCheckerDone();
|
||||
}
|
||||
}
|
||||
|
||||
bool SpoilerBackgroundUpdater::deleteSpoilerFile()
|
||||
{
|
||||
QString fileName = settingsCache->getSpoilerCardDatabasePath();
|
||||
QFileInfo fi(fileName);
|
||||
QDir fileDir(fi.path());
|
||||
QFile file(fileName);
|
||||
|
||||
// Delete the spoiler.xml file
|
||||
if (file.exists() && file.remove())
|
||||
{
|
||||
qDebug() << "Deleting spoiler.xml";
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
qDebug() << "Error: Spoiler.xml not found or not deleted";
|
||||
return false;
|
||||
}
|
||||
|
||||
void SpoilerBackgroundUpdater::actCheckIfSpoilerSeasonEnabled()
|
||||
{
|
||||
auto *response = dynamic_cast<QNetworkReply *>(sender());
|
||||
QNetworkReply::NetworkError errorCode = response->error();
|
||||
|
||||
if (errorCode == QNetworkReply::ContentNotFoundError)
|
||||
{
|
||||
// Spoiler season is offline at this point, so the spoiler.xml file can be safely deleted
|
||||
// The user should run Oracle to get the latest card information
|
||||
if (deleteSpoilerFile() && trayIcon)
|
||||
{
|
||||
trayIcon->showMessage(tr("Spoilers season has ended"), tr("Deleting spoiler.xml. Please run Oracle"));
|
||||
}
|
||||
|
||||
qDebug() << "Spoiler Season Offline";
|
||||
emit spoilerCheckerDone();
|
||||
}
|
||||
else if (errorCode == QNetworkReply::NoError)
|
||||
{
|
||||
qDebug() << "Spoiler Service Online";
|
||||
startSpoilerDownloadProcess(SPOILERS_URL, true);
|
||||
}
|
||||
else if (errorCode == QNetworkReply::HostNotFoundError)
|
||||
{
|
||||
if (trayIcon)
|
||||
{
|
||||
trayIcon->showMessage(tr("Spoilers download failed"), tr("No internet connection"));
|
||||
}
|
||||
|
||||
qDebug() << "Spoiler download failed due to no internet connection";
|
||||
emit spoilerCheckerDone();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (trayIcon)
|
||||
{
|
||||
trayIcon->showMessage(tr("Spoilers download failed"), tr("Error") + " " + errorCode);
|
||||
}
|
||||
|
||||
qDebug() << "Spoiler download failed with reason" << errorCode;
|
||||
emit spoilerCheckerDone();
|
||||
}
|
||||
}
|
||||
|
||||
bool SpoilerBackgroundUpdater::saveDownloadedFile(QByteArray data)
|
||||
{
|
||||
QString fileName = settingsCache->getSpoilerCardDatabasePath();
|
||||
QFileInfo fi(fileName);
|
||||
QDir fileDir(fi.path());
|
||||
|
||||
if (!fileDir.exists() && !fileDir.mkpath(fileDir.absolutePath()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the data matches. If it does, then spoilers are up to date.
|
||||
if (getHash(fileName) == getHash(data))
|
||||
{
|
||||
if (trayIcon)
|
||||
{
|
||||
trayIcon->showMessage(tr("Spoilers already up to date"), tr("No new spoilers added"));
|
||||
}
|
||||
|
||||
qDebug() << "Spoilers Up to Date";
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qDebug() << "Spoiler Service Error: File open (w) failed for" << fileName;
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.write(data) == -1)
|
||||
{
|
||||
qDebug() << "Spoiler Service Error: File write (w) failed for" << fileName;
|
||||
file.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
|
||||
// Data written, so reload the card database
|
||||
qDebug() << "Spoiler Service Data Written";
|
||||
QtConcurrent::run(db, &CardDatabase::loadCardDatabases);
|
||||
|
||||
// If the user has notifications enabled, let them know
|
||||
// when the database was last updated
|
||||
if (trayIcon)
|
||||
{
|
||||
QList<QByteArray> lines = data.split('\n');
|
||||
|
||||
foreach (QByteArray line, lines)
|
||||
{
|
||||
if (line.indexOf("created:") > -1)
|
||||
{
|
||||
QString timeStamp = QString(line).replace("created:", "").trimmed();
|
||||
timeStamp.chop(6); // Remove " (UTC)"
|
||||
|
||||
auto utcTime = QDateTime::fromString(timeStamp, QString("ddd, MMM dd yyyy, hh:mm:ss"));
|
||||
utcTime.setTimeSpec(Qt::UTC);
|
||||
|
||||
QString localTime = utcTime.toLocalTime().toString("MMM d, hh:mm");
|
||||
|
||||
trayIcon->showMessage(tr("Spoilers have been updated!"), tr("Last change:") + " " + localTime);
|
||||
emit spoilersUpdatedSuccessfully();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QByteArray SpoilerBackgroundUpdater::getHash(const QString fileName)
|
||||
{
|
||||
QFile file(fileName);
|
||||
|
||||
if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
// Only read the first 512 bytes (enough to get the "created" tag)
|
||||
const QByteArray bytes = file.read(512);
|
||||
|
||||
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
|
||||
hash.addData(bytes);
|
||||
|
||||
qDebug() << "File Hash =" << hash.result();
|
||||
|
||||
file.close();
|
||||
return hash.result();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "getHash ReadOnly failed!";
|
||||
file.close();
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray SpoilerBackgroundUpdater::getHash(QByteArray data)
|
||||
{
|
||||
// Only read the first 512 bytes (enough to get the "created" tag)
|
||||
const QByteArray bytes = data.left(512);
|
||||
|
||||
QCryptographicHash hash(QCryptographicHash::Algorithm::Md5);
|
||||
hash.addData(bytes);
|
||||
|
||||
qDebug() << "Data Hash =" << hash.result();
|
||||
|
||||
return hash.result();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue