2021-05-02 18:22:38 +00:00
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
2023-01-02 19:30:11 +00:00
|
|
|
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
2021-05-02 18:22:38 +00:00
|
|
|
|
|
|
|
#include "WebsocketNotifier.h"
|
2021-07-06 19:36:27 +00:00
|
|
|
#include "Utils.h"
|
2021-05-25 13:06:38 +00:00
|
|
|
#include "utils/os/tails.h"
|
|
|
|
#include "utils/os/whonix.h"
|
2021-05-02 18:22:38 +00:00
|
|
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
|
|
|
WebsocketNotifier::WebsocketNotifier(QObject *parent)
|
|
|
|
: QObject(parent)
|
|
|
|
, websocketClient(new WebsocketClient(this))
|
|
|
|
{
|
2023-03-01 12:28:01 +00:00
|
|
|
connect(websocketClient, &WebsocketClient::WSMessage, this, &WebsocketNotifier::onWSMessage);
|
2021-05-02 18:22:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QPointer<WebsocketNotifier> WebsocketNotifier::m_instance(nullptr);
|
|
|
|
|
|
|
|
void WebsocketNotifier::onWSMessage(const QJsonObject &msg) {
|
|
|
|
QString cmd = msg.value("cmd").toString();
|
|
|
|
|
2022-02-23 22:27:20 +00:00
|
|
|
m_lastMessageReceived = QDateTime::currentDateTimeUtc();
|
2021-05-18 15:59:18 +00:00
|
|
|
m_cache[cmd] = msg;
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
if (cmd == "blockheights") {
|
|
|
|
QJsonObject data = msg.value("data").toObject();
|
|
|
|
int mainnet = data.value("mainnet").toInt();
|
|
|
|
int stagenet = data.value("stagenet").toInt();
|
|
|
|
|
|
|
|
emit BlockHeightsReceived(mainnet, stagenet);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(cmd == "nodes") {
|
|
|
|
this->onWSNodes(msg.value("data").toArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(cmd == "crypto_rates") {
|
|
|
|
QJsonArray crypto_rates = msg.value("data").toArray();
|
|
|
|
emit CryptoRatesReceived(crypto_rates);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(cmd == "fiat_rates") {
|
|
|
|
QJsonObject fiat_rates = msg.value("data").toObject();
|
|
|
|
emit FiatRatesReceived(fiat_rates);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(cmd == "reddit") {
|
|
|
|
QJsonArray reddit_data = msg.value("data").toArray();
|
|
|
|
this->onWSReddit(reddit_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if(cmd == "ccs") {
|
|
|
|
auto ccs_data = msg.value("data").toArray();
|
|
|
|
this->onWSCCS(ccs_data);
|
|
|
|
}
|
|
|
|
|
2022-06-23 17:48:30 +00:00
|
|
|
else if(cmd == "bounties") {
|
|
|
|
auto data = msg.value("data").toArray();
|
|
|
|
this->onWSBounties(data);
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
else if(cmd == "txFiatHistory") {
|
|
|
|
auto txFiatHistory_data = msg.value("data").toObject();
|
|
|
|
emit TxFiatHistoryReceived(txFiatHistory_data);
|
|
|
|
}
|
|
|
|
|
2022-05-25 20:40:56 +00:00
|
|
|
else if(cmd == "revuo") {
|
|
|
|
auto revuo_data = msg.value("data").toArray();
|
|
|
|
this->onWSRevuo(revuo_data);
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
#if defined(CHECK_UPDATES)
|
|
|
|
else if (cmd == "updates") {
|
|
|
|
this->onWSUpdates(msg.value("data").toObject());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(HAS_XMRIG)
|
|
|
|
else if(cmd == "xmrig") {
|
|
|
|
this->onWSXMRigDownloads(msg.value("data").toObject());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(HAS_LOCALMONERO)
|
|
|
|
else if (cmd == "localmonero_countries") {
|
|
|
|
emit LocalMoneroCountriesReceived(msg.value("data").toArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (cmd == "localmonero_currencies") {
|
|
|
|
emit LocalMoneroCurrenciesReceived(msg.value("data").toArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (cmd == "localmonero_payment_methods") {
|
|
|
|
emit LocalMoneroPaymentMethodsReceived(msg.value("data").toObject());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-05-18 15:59:18 +00:00
|
|
|
void WebsocketNotifier::emitCache() {
|
|
|
|
for (const auto &msg : m_cache) {
|
|
|
|
this->onWSMessage(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-23 22:27:20 +00:00
|
|
|
bool WebsocketNotifier::stale(int minutes) {
|
|
|
|
return m_lastMessageReceived < QDateTime::currentDateTimeUtc().addSecs(-(minutes*60));
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
void WebsocketNotifier::onWSNodes(const QJsonArray &nodes) {
|
|
|
|
// TODO: Refactor, should be filtered client side
|
|
|
|
|
|
|
|
QList<FeatherNode> l;
|
|
|
|
for (auto &&entry: nodes) {
|
|
|
|
auto obj = entry.toObject();
|
|
|
|
auto nettype = obj.value("nettype");
|
|
|
|
auto type = obj.value("type");
|
|
|
|
|
2021-05-18 15:59:18 +00:00
|
|
|
auto networkType = constants::networkType;
|
2021-05-02 18:22:38 +00:00
|
|
|
|
|
|
|
// filter remote node network types
|
|
|
|
if(nettype == "mainnet" && networkType != NetworkType::MAINNET)
|
|
|
|
continue;
|
|
|
|
if(nettype == "stagenet" && networkType != NetworkType::STAGENET)
|
|
|
|
continue;
|
|
|
|
if(nettype == "testnet" && networkType != NetworkType::TESTNET)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
FeatherNode node{obj.value("address").toString(),
|
|
|
|
obj.value("height").toInt(),
|
|
|
|
obj.value("target_height").toInt(),
|
|
|
|
obj.value("online").toBool()};
|
|
|
|
l.append(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit NodesReceived(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebsocketNotifier::onWSReddit(const QJsonArray& reddit_data) {
|
|
|
|
QList<QSharedPointer<RedditPost>> l;
|
|
|
|
|
|
|
|
for (auto &&entry: reddit_data) {
|
|
|
|
auto obj = entry.toObject();
|
|
|
|
auto redditPost = new RedditPost(
|
|
|
|
obj.value("title").toString(),
|
|
|
|
obj.value("author").toString(),
|
|
|
|
obj.value("permalink").toString(),
|
|
|
|
obj.value("comments").toInt());
|
|
|
|
QSharedPointer<RedditPost> r = QSharedPointer<RedditPost>(redditPost);
|
|
|
|
l.append(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit RedditReceived(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebsocketNotifier::onWSCCS(const QJsonArray &ccs_data) {
|
|
|
|
QList<QSharedPointer<CCSEntry>> l;
|
|
|
|
|
2023-11-25 13:27:22 +00:00
|
|
|
for (const auto& entry: ccs_data) {
|
2021-05-02 18:22:38 +00:00
|
|
|
auto obj = entry.toObject();
|
|
|
|
auto c = QSharedPointer<CCSEntry>(new CCSEntry());
|
|
|
|
|
|
|
|
if (obj.value("state").toString() != "FUNDING-REQUIRED")
|
|
|
|
continue;
|
|
|
|
|
|
|
|
c->state = obj.value("state").toString();
|
|
|
|
c->address = obj.value("address").toString();
|
|
|
|
c->author = obj.value("author").toString();
|
|
|
|
c->date = obj.value("date").toString();
|
|
|
|
c->title = obj.value("title").toString();
|
|
|
|
c->target_amount = obj.value("target_amount").toDouble();
|
|
|
|
c->raised_amount = obj.value("raised_amount").toDouble();
|
|
|
|
c->percentage_funded = obj.value("percentage_funded").toDouble();
|
|
|
|
c->contributions = obj.value("contributions").toInt();
|
2023-11-25 13:27:22 +00:00
|
|
|
c->organizer = obj.value("organizer").toString();
|
|
|
|
c->currency = obj.value("currency").toString();
|
|
|
|
|
|
|
|
QString urlpath = obj.value("urlpath").toString();
|
|
|
|
if (c->organizer == "CCS") {
|
|
|
|
c->url = QString("https://ccs.getmonero.org/%1").arg(urlpath);
|
|
|
|
}
|
|
|
|
else if (c->organizer == "MAGIC") {
|
|
|
|
c->url = QString("https://monerofund.org/%1").arg(urlpath);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
l.append(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit CCSReceived(l);
|
|
|
|
}
|
|
|
|
|
2022-05-25 20:40:56 +00:00
|
|
|
void WebsocketNotifier::onWSRevuo(const QJsonArray &revuo_data) {
|
|
|
|
QList<QSharedPointer<RevuoItem>> l;
|
|
|
|
|
|
|
|
for (auto &&entry: revuo_data) {
|
|
|
|
auto obj = entry.toObject();
|
|
|
|
|
|
|
|
QStringList newsbytes;
|
|
|
|
for (const auto &n : obj.value("newsbytes").toArray()) {
|
|
|
|
newsbytes.append(n.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
auto revuoItem = new RevuoItem(
|
|
|
|
obj.value("title").toString(),
|
|
|
|
obj.value("url").toString(),
|
|
|
|
newsbytes);
|
|
|
|
|
|
|
|
QSharedPointer<RevuoItem> r = QSharedPointer<RevuoItem>(revuoItem);
|
|
|
|
l.append(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit RevuoReceived(l);
|
|
|
|
}
|
|
|
|
|
2022-06-23 17:48:30 +00:00
|
|
|
void WebsocketNotifier::onWSBounties(const QJsonArray &bounties_data) {
|
|
|
|
QList<QSharedPointer<BountyEntry>> l;
|
|
|
|
|
|
|
|
for (const auto& entry : bounties_data) {
|
|
|
|
QJsonObject obj = entry.toObject();
|
|
|
|
auto bounty = new BountyEntry(obj.value("votes").toInt(),
|
|
|
|
obj.value("title").toString(),
|
|
|
|
obj.value("amount").toDouble(),
|
|
|
|
obj.value("link").toString(),
|
|
|
|
obj.value("address").toString(),
|
|
|
|
obj.value("status").toString());
|
|
|
|
QSharedPointer<BountyEntry> b = QSharedPointer<BountyEntry>(bounty);
|
|
|
|
l.append(b);
|
|
|
|
}
|
|
|
|
|
|
|
|
emit BountyReceived(l);
|
|
|
|
}
|
|
|
|
|
2021-05-02 18:22:38 +00:00
|
|
|
void WebsocketNotifier::onWSUpdates(const QJsonObject &updates) {
|
|
|
|
emit UpdatesReceived(updates);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WebsocketNotifier::onWSXMRigDownloads(const QJsonObject &downloads) {
|
|
|
|
emit XMRigDownloadsReceived(downloads);
|
|
|
|
}
|
|
|
|
|
|
|
|
WebsocketNotifier* WebsocketNotifier::instance()
|
|
|
|
{
|
|
|
|
if (!m_instance) {
|
|
|
|
m_instance = new WebsocketNotifier(QCoreApplication::instance());
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_instance;
|
|
|
|
}
|