diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d258cd4..61b62d31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 1.8.6 +- Integrated Telegram push notifications +- Fixed multi miner editor +- Added miner offline/online status push notification +- Added 0/recovered hashrate push notification # 1.8.5 - Add remote reboot (machine) feature to Dashboard, Server & Miner - Integrated Pushover push notifications for Offline miners and periodical status notifications on iOS and Android diff --git a/index.html b/index.html index 52c00577..1ac65d7f 100644 --- a/index.html +++ b/index.html @@ -364,6 +364,7 @@ table.button(4).enable(selectedRows > 0); table.button(5).enable(selectedRows > 0); table.button(6).enable(selectedRows > 0); + table.button(7).enable(selectedRows > 0); }); table.on('deselect', function () { @@ -376,6 +377,7 @@ table.button(4).enable(selectedRows > 0); table.button(5).enable(selectedRows > 0); table.button(6).enable(selectedRows > 0); + table.button(7).enable(selectedRows > 0); }); table.buttons().container().appendTo('#clientStatusList_wrapper .col-sm-6:eq(0)'); diff --git a/src/Options.cpp b/src/Options.cpp index 71f36a46..70819225 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -115,10 +115,13 @@ Options:\n" --cc-key-file=FILE when tls is turned on, use this to point to the right key file (default: server.key) \n\ --cc-client-log-lines-history=N maximum lines of log history kept per miner (default: 100)\n\ --cc-client-config-folder=FOLDER Folder contains the client config files\n\ - --cc-pushover-user-key pushover user for push messages\n\ - --cc-pushover-api-token your user key for pushover notifications\n\ - --cc-push-miner-offline-info api token/keytoken of the application for pushover notifications\n\ - --cc-push-periodic-mining-status send periodic mining status push\n\ + --cc-pushover-user-key your user key for pushover notifications\n\ + --cc-pushover-api-token api token/keytoken of the application for pushover notifications\n\ + --cc-telegram-bot-token your bot token for telegram notifications\n\ + --cc-telegram-chat-id your chat-id for telegram notifications\n\ + --cc-push-miner-offline-info push notification for offline miners and recover push\n\ + --cc-push-miner-zero-hash-info push notification when miner reports 0 hashrate and recover push\n\ + --cc-push-periodic-mining-status push periodic status notification (every hour)\n\ --cc-custom-dashboard=FILE loads a custom dashboard and serve it to '/'\n" # endif "\ @@ -195,8 +198,11 @@ static struct option const options[] = { { "cc-reboot-cmd", 1, nullptr, 4021 }, { "cc-pushover-user-key", 1, nullptr, 4022 }, { "cc-pushover-api-token", 1, nullptr, 4023 }, + { "cc-telegram-bot-token", 1, nullptr, 4027 }, + { "cc-telegram-chat-id", 1, nullptr, 4028 }, { "cc-push-miner-offline-info", 0, nullptr, 4024 }, { "cc-push-periodic-mining-status", 0, nullptr, 4025 }, + { "cc-push-miner-zero-hash-info", 0, nullptr, 4026 }, { "daemonized", 0, nullptr, 4011 }, { "doublehash-thread-mask", 1, nullptr, 4013 }, { "multihash-thread-mask", 1, nullptr, 4013 }, @@ -279,7 +285,10 @@ static struct option const cc_server_options[] = { { "client-log-lines-history", 1, nullptr, 4018 }, { "pushover-user-key", 1, nullptr, 4022 }, { "pushover-api-token", 1, nullptr, 4023 }, + { "telegram-bot-token", 1, nullptr, 4027 }, + { "telegram-chat-id", 1, nullptr, 4028 }, { "push-miner-offline-info", 0, nullptr, 4024 }, + { "push-miner-zero-hash-info", 0, nullptr, 4026 }, { "push-periodic-mining-status", 0, nullptr, 4025 }, { nullptr, 0, nullptr, 0 } }; @@ -354,6 +363,7 @@ Options::Options(int argc, char **argv) : m_ccUploadConfigOnStartup(true), m_ccPushOfflineMiners(false), m_ccPushPeriodicStatus(false), + m_ccPushZeroHashrateMiners(false), m_fileName(Platform::defaultConfigName()), m_apiToken(nullptr), m_apiWorkerId(nullptr), @@ -371,6 +381,8 @@ Options::Options(int argc, char **argv) : m_ccRebootCmd(nullptr), m_ccPushoverUser(nullptr), m_ccPushoverToken(nullptr), + m_ccTelegramBotToken(nullptr), + m_ccTelegramChatId(nullptr), m_algo(ALGO_CRYPTONIGHT), m_algoVariant(AV0_AUTO), m_aesni(AESNI_AUTO), @@ -572,12 +584,12 @@ bool Options::parseArg(int key, const char *arg) case 4014: /* --cc-cert-file */ free(m_ccCertFile); - m_ccCertFile = strdup(arg); + m_ccCertFile = strdup(arg); break; case 4015: /* --cc-key-file */ free(m_ccKeyFile); - m_ccKeyFile = strdup(arg); + m_ccKeyFile = strdup(arg); break; case 4011: /* --daemonized */ @@ -630,13 +642,29 @@ bool Options::parseArg(int key, const char *arg) return parseAsmOptimization(arg); case 4021: /* --cc-reboot-cmd */ + free(m_ccRebootCmd); m_ccRebootCmd = strdup(arg); + break; case 4022: /* --cc-pushover-user-key */ + free(m_ccPushoverUser); m_ccPushoverUser = strdup(arg); + break; case 4023: /* --cc-pushover-api-token */ + free(m_ccPushoverToken); m_ccPushoverToken = strdup(arg); + break; + + case 4027: /* --cc-telegram-bot-token */ + free(m_ccTelegramBotToken); + m_ccTelegramBotToken = strdup(arg); + break; + + case 4028: /* --cc-telegram-chat-id */ + free(m_ccTelegramChatId); + m_ccTelegramChatId = strdup(arg); + break; case 4024: /* --cc-push-miner-offline-info */ return parseBoolean(key, false); @@ -644,6 +672,9 @@ bool Options::parseArg(int key, const char *arg) case 4025: /* --cc-push-periodic-mining-status */ return parseBoolean(key, false); + case 4026: /* --cc-push-miner-zero-hash-info */ + return parseBoolean(key, false); + case 't': /* --threads */ if (strncmp(arg, "all", 3) == 0) { m_threads = Cpu::threads(); @@ -888,6 +919,10 @@ bool Options::parseBoolean(int key, bool enable) m_ccPushOfflineMiners = enable; break; + case 4026: /* --cc-push-miner-zero-hash-info */ + m_ccPushZeroHashrateMiners = enable; + break; + case 4025: /* --cc-push-periodic-mining-status */ m_ccPushPeriodicStatus = enable; break; diff --git a/src/Options.h b/src/Options.h index 07fccb3c..a91508f1 100644 --- a/src/Options.h +++ b/src/Options.h @@ -77,6 +77,9 @@ public: inline bool ccUploadConfigOnStartup() const { return m_ccUploadConfigOnStartup; } inline bool ccPushOfflineMiners() const { return m_ccPushOfflineMiners; } inline bool ccPushPeriodicStatus() const { return m_ccPushPeriodicStatus; } + inline bool ccPushZeroHashrateMiners() const { return m_ccPushZeroHashrateMiners; } + inline bool ccUsePushover() const { return ccPushoverUser() && ccPushoverToken(); } + inline bool ccUseTelegram() const { return ccTelegramBotToken() && ccTelegramChatId(); } inline const char *fileName() const { return m_fileName; } inline const char *apiToken() const { return m_apiToken; } inline const char *apiWorkerId() const { return m_apiWorkerId; } @@ -94,6 +97,8 @@ public: inline const char *ccRebootCmd() const { return (m_ccRebootCmd != nullptr && strlen(m_ccRebootCmd) > 0) ? m_ccRebootCmd : nullptr; } inline const char *ccPushoverUser() const { return (m_ccPushoverUser != nullptr && strlen(m_ccPushoverUser) > 0) ? m_ccPushoverUser : nullptr; } inline const char *ccPushoverToken() const { return (m_ccPushoverToken != nullptr && strlen(m_ccPushoverToken) > 0) ? m_ccPushoverToken : nullptr; } + inline const char *ccTelegramBotToken() const { return (m_ccTelegramBotToken != nullptr && strlen(m_ccTelegramBotToken) > 0) ? m_ccTelegramBotToken : nullptr; } + inline const char *ccTelegramChatId() const { return (m_ccTelegramChatId != nullptr && strlen(m_ccTelegramChatId) > 0) ? m_ccTelegramChatId : nullptr; } inline const std::vector &pools() const { return m_pools; } inline Algo algo() const { return m_algo; } inline PowVariant powVariant() const { return m_powVariant; } @@ -159,6 +164,7 @@ private: bool m_ccUploadConfigOnStartup; bool m_ccPushOfflineMiners; bool m_ccPushPeriodicStatus; + bool m_ccPushZeroHashrateMiners; const char* m_fileName; char *m_apiToken; char *m_apiWorkerId; @@ -176,6 +182,8 @@ private: char *m_ccRebootCmd; char *m_ccPushoverUser; char *m_ccPushoverToken; + char *m_ccTelegramBotToken; + char *m_ccTelegramChatId; Algo m_algo; AlgoVariant m_algoVariant; AesNi m_aesni; diff --git a/src/cc/Service.cpp b/src/cc/Service.cpp index d88fe2e6..85302be6 100644 --- a/src/cc/Service.cpp +++ b/src/cc/Service.cpp @@ -47,8 +47,10 @@ std::map Service::m_clientCommand; std::map Service::m_clientStatus; std::map> Service::m_clientLog; +std::list Service::m_offlineNotified; +std::list Service::m_zeroHashNotified; + uint64_t Service::m_currentServerTime = 0; -uint64_t Service::m_lastOfflineCheckTime = 0; uint64_t Service::m_lastStatusUpdateTime = 0; bool Service::start() @@ -56,7 +58,7 @@ bool Service::start() uv_mutex_init(&m_mutex); #ifndef XMRIG_NO_TLS - if (Options::i()->ccPushoverToken() && Options::i()->ccPushoverUser()) + if (Options::i()->ccUsePushover() || Options::i()->ccUseTelegram()) { uv_timer_init(uv_default_loop(), &m_timer); uv_timer_start(&m_timer, Service::onPushTimer, @@ -447,7 +449,10 @@ void Service::onPushTimer(uv_timer_t* handle) if (Options::i()->ccPushOfflineMiners()) { sendMinerOfflinePush(now); - m_lastOfflineCheckTime = now; + } + + if (Options::i()->ccPushZeroHashrateMiners()) { + sendMinerZeroHashratePush(now); } if (Options::i()->ccPushPeriodicStatus()) { @@ -460,18 +465,63 @@ void Service::onPushTimer(uv_timer_t* handle) void Service::sendMinerOfflinePush(uint64_t now) { + uint64_t offlineThreshold = now - OFFLINE_TRESHOLD_IN_MS; + for (auto clientStatus : m_clientStatus) { uint64_t lastStatus = clientStatus.second.getLastStatusUpdate() * 1000; - uint64_t offlineThreshold = now - OFFLINE_TRESHOLD_IN_MS; if (lastStatus < offlineThreshold) { - offlineThreshold = now - (OFFLINE_TRESHOLD_IN_MS + (now - m_lastOfflineCheckTime)); - if (lastStatus > offlineThreshold) { + if (std::find(m_offlineNotified.begin(), m_offlineNotified.end(), clientStatus.first) == m_offlineNotified.end()) { std::stringstream message; message << "Miner: " << clientStatus.first << " just went offline!"; - LOG_WARN("Send miner went offline push", clientStatus.first.c_str()); - triggerPush(APP_NAME " Offline Monitor", message.str()); + LOG_WARN("Send miner %s went offline push", clientStatus.first.c_str()); + triggerPush(APP_NAME " Onlinestatus Monitor", message.str()); + + m_offlineNotified.push_back(clientStatus.first); + } + } else { + if (std::find(m_offlineNotified.begin(), m_offlineNotified.end(), clientStatus.first) != m_offlineNotified.end()) { + std::stringstream message; + message << "Miner: " << clientStatus.first << " is back online!"; + + LOG_WARN("Send miner %s back online push", clientStatus.first.c_str()); + triggerPush(APP_NAME " Onlinestatus Monitor", message.str()); + + m_offlineNotified.remove(clientStatus.first); + } + } + } +} + +void Service::sendMinerZeroHashratePush(uint64_t now) +{ + uint64_t offlineThreshold = now - OFFLINE_TRESHOLD_IN_MS; + + for (auto clientStatus : m_clientStatus) { + if (offlineThreshold < clientStatus.second.getLastStatusUpdate() * 1000) { + if (clientStatus.second.getHashrateShort() == 0 && clientStatus.second.getHashrateMedium() == 0) { + if (std::find(m_zeroHashNotified.begin(), m_zeroHashNotified.end(), clientStatus.first) == m_zeroHashNotified.end()) { + std::stringstream message; + message << "Miner: " << clientStatus.first << " reported 0 h/s for the last minute!"; + + LOG_WARN("Send miner %s 0 hashrate push", clientStatus.first.c_str()); + triggerPush(APP_NAME " Hashrate Monitor", message.str()); + + m_zeroHashNotified.push_back(clientStatus.first); + } + } else if (clientStatus.second.getHashrateMedium() > 0) { + if (std::find(m_zeroHashNotified.begin(), m_zeroHashNotified.end(), clientStatus.first) != m_zeroHashNotified.end()) { + std::stringstream message; + message << "Miner: " << clientStatus.first << " hashrate recovered. Reported " + << clientStatus.second.getHashrateMedium() + << " h/s for the last minute!"; + + LOG_WARN("Send miner %s hashrate recovered push", clientStatus.first.c_str()); + triggerPush(APP_NAME " Hashrate Monitor", message.str()); + + m_zeroHashNotified.remove(clientStatus.first); + } } } } @@ -493,16 +543,16 @@ void Service::sendServerStatusPush(uint64_t now) for (auto clientStatus : m_clientStatus) { if (offlineThreshold < clientStatus.second.getLastStatusUpdate() * 1000) { onlineMiner++; + + hashrateMedium += clientStatus.second.getHashrateMedium(); + hashrateLong += clientStatus.second.getHashrateLong(); + + sharesGood += clientStatus.second.getSharesGood(); + sharesTotal += clientStatus.second.getSharesTotal(); + avgTime += clientStatus.second.getAvgTime(); } else { offlineMiner++; } - - hashrateMedium += clientStatus.second.getHashrateMedium(); - hashrateLong += clientStatus.second.getHashrateLong(); - - sharesGood += clientStatus.second.getSharesGood(); - sharesTotal += clientStatus.second.getSharesTotal(); - avgTime += clientStatus.second.getAvgTime(); } if (!m_clientStatus.empty()) { @@ -521,7 +571,17 @@ void Service::sendServerStatusPush(uint64_t now) void Service::triggerPush(const std::string& title, const std::string& message) { -#ifndef XMRIG_NO_TLS + if (Options::i()->ccUsePushover()) { + sendViaPushover(title, message); + } + + if (Options::i()->ccUseTelegram()) { + sendViaTelegram(title, message); + } +} + +void Service::sendViaPushover(const std::string &title, const std::string &message) +{ std::shared_ptr cli = std::make_shared("api.pushover.net", 443); httplib::Params params; @@ -532,7 +592,24 @@ void Service::triggerPush(const std::string& title, const std::string& message) auto res = cli->Post("/1/messages.json", params); if (res) { - LOG_WARN("Push response: %s", res->body.c_str()); + LOG_WARN("Pushover response: %s", res->body.c_str()); + } +} + +void Service::sendViaTelegram(const std::string &title, const std::string &message) +{ + std::shared_ptr cli = std::make_shared("api.telegram.org", 443); + + std::string text = "" + title + "\n\n" + message; + std::string path = std::string("/bot") + Options::i()->ccTelegramBotToken() + std::string("/sendMessage"); + + httplib::Params params; + params.emplace("chat_id", Options::i()->ccTelegramChatId()); + params.emplace("text", httplib::detail::encode_url(text)); + params.emplace("parse_mode", "HTML"); + + auto res = cli->Post(path.c_str(), params); + if (res) { + LOG_WARN("Telegram response: %s", res->body.c_str()); } -#endif } \ No newline at end of file diff --git a/src/cc/Service.h b/src/cc/Service.h index adffcc10..2512e5d9 100644 --- a/src/cc/Service.h +++ b/src/cc/Service.h @@ -68,19 +68,26 @@ private: static void onPushTimer(uv_timer_t* handle); static void sendServerStatusPush(uint64_t now); static void sendMinerOfflinePush(uint64_t now); + static void sendMinerZeroHashratePush(uint64_t now); static void triggerPush(const std::string& title, const std::string& message); private: static uint64_t m_currentServerTime; - static uint64_t m_lastOfflineCheckTime; static uint64_t m_lastStatusUpdateTime; static std::map m_clientStatus; static std::map m_clientCommand; static std::map> m_clientLog; + static std::list m_offlineNotified; + static std::list m_zeroHashNotified; + static uv_mutex_t m_mutex; static uv_timer_t m_timer; + + static void sendViaPushover(const std::string &title, const std::string &message); + + static void sendViaTelegram(const std::string &title, const std::string &message); }; #endif /* __SERVICE_H__ */ diff --git a/src/cc/Summary.cpp b/src/cc/Summary.cpp index 011c60e9..938f66dd 100644 --- a/src/cc/Summary.cpp +++ b/src/cc/Summary.cpp @@ -60,14 +60,20 @@ static void print_commands() } void Summary::print_pushinfo() { - if (Options::i()->ccPushoverToken() && Options::i()->ccPushoverUser()) + if (Options::i()->ccUsePushover() || Options::i()->ccUseTelegram()) { #ifndef XMRIG_NO_TLS if (Options::i()->colors()) { - Log::i()->text("\x1B[01;32m * \x1B[01;37mPUSHSERVICE: \x1B[01;32mEnabled"); + Log::i()->text("\x1B[01;32m * \x1B[01;37mPUSHSERVICE: \x1B[01;32m%s%s%s", + Options::i()->ccUsePushover() ? "Pushover" : "", + Options::i()->ccUsePushover() && Options::i()->ccUseTelegram() ? ", " : "", + Options::i()->ccUseTelegram() ? "Telegram" : ""); } else { - Log::i()->text(" * PUSHSERVICE: Enabled"); + Log::i()->text(" * PUSHSERVICE: %s%s%s", + Options::i()->ccUsePushover() ? "Pushover" : "", + Options::i()->ccUsePushover() && Options::i()->ccUseTelegram() ? ", " : "", + Options::i()->ccUseTelegram() ? "Telegram" : ""); } #else if (Options::i()->colors()) { diff --git a/src/config_cc.json b/src/config_cc.json index ced0782a..8ec4ea65 100644 --- a/src/config_cc.json +++ b/src/config_cc.json @@ -18,6 +18,7 @@ "pushover-user-key" : "", // your user key for pushover notifications "pushover-api-token" : "", // api token/keytoken of the application for pushover notifications "push-miner-offline-info" : true, // push notification for offline miners + "push-miner-zero-hash-info" : true, // push notification when miner reports 0 hashrate "push-periodic-mining-status" : true // push periodic status notification (every hour) } } diff --git a/src/version.h b/src/version.h index 5571593f..8cc71e44 100644 --- a/src/version.h +++ b/src/version.h @@ -36,14 +36,14 @@ #define APP_DESC "XMRigCC CPU miner" #define APP_COPYRIGHT "Copyright (C) 2017- BenDr0id" #endif -#define APP_VERSION "1.8.5 (based on XMRig)" +#define APP_VERSION "1.8.6 (based on XMRig)" #define APP_DOMAIN "" #define APP_SITE "https://github.com/Bendr0id/xmrigCC" #define APP_KIND "cpu" #define APP_VER_MAJOR 1 #define APP_VER_MINOR 8 -#define APP_VER_BUILD 5 +#define APP_VER_BUILD 6 #define APP_VER_REV 0 #ifndef NDEBUG