<commit_before>#include <bts/client/notifier.hpp>
#include <bts/blockchain/config.hpp>
#include <bts/blockchain/time.hpp>

#include <fc/network/gntp.hpp>

#include <sstream>

namespace bts { namespace client { 
  namespace detail 
  {
    class bts_gntp_notifier_impl
    {
    public:
      fc::gntp_notifier _notifier;
      fc::gntp_icon_ptr _bitshares_icon;

      uint32_t _last_reported_connection_count;
      uint32_t _connection_count_notification_threshold;
      bool     _shutting_down;
      fc::time_point _last_head_block_too_old_notification_time;
      fc::microseconds _last_head_block_too_old_notification_interval;
      uint32_t _missed_block_count_threshold;

      bts_gntp_notifier_impl();
      void register_notification_types();
    };
    extern unsigned char bitshares_icon_png[];
    extern unsigned bitshares_icon_png_len;

    bts_gntp_notifier_impl::bts_gntp_notifier_impl() :
      _last_reported_connection_count(0),
      _connection_count_notification_threshold(5),
      _shutting_down(false),
      _last_head_block_too_old_notification_interval(fc::seconds(300)),
      _missed_block_count_threshold(3),
      _bitshares_icon(std::make_shared<fc::gntp_icon>((const char*)bitshares_icon_png, bitshares_icon_png_len))
    {
    }

    void bts_gntp_notifier_impl::register_notification_types()
    {
      fc::gntp_notification_type notification_type;

      notification_type.name = "connection_count_below_threshold";
      notification_type.display_name = "Connection Count Below Threshold";
      notification_type.enabled = true;
      notification_type.icon = _bitshares_icon;
      _notifier.add_notification_type(notification_type);

      notification_type.name = "client_exiting_unexpectedly";
      notification_type.display_name = "Client Exiting Unexpectedly";
      notification_type.enabled = true;
      notification_type.icon = _bitshares_icon;
      _notifier.add_notification_type(notification_type);

      notification_type.name = "head_block_too_old";
      notification_type.display_name = "Head Block is Too Old";
      notification_type.enabled = true;
      notification_type.icon = _bitshares_icon;
      _notifier.add_notification_type(notification_type);

      _notifier.register_notifications();
    }
  }

  bts_gntp_notifier::bts_gntp_notifier() : 
    my(new detail::bts_gntp_notifier_impl)
  {
    my->_notifier.set_application_name("BitShares");
    my->_notifier.set_application_icon(my->_bitshares_icon);
    my->register_notification_types();
  }

  bts_gntp_notifier::~bts_gntp_notifier()
  {
  }

  void bts_gntp_notifier::client_is_shutting_down()
  {
    my->_shutting_down = true;
  }

  void bts_gntp_notifier::notify_connection_count_changed(uint32_t new_connection_count)
  {
    if (new_connection_count < my->_connection_count_notification_threshold && 
        my->_last_reported_connection_count >= my->_connection_count_notification_threshold)
      {
      std::ostringstream message;
      message << "The BitShares client's peer connection count dropped to " << new_connection_count << 
                 ", which is below the warning threshold of " << my->_connection_count_notification_threshold;
      my->_notifier.send_notification("connection_count_below_threshold", "Connection Count Below Threshold", message.str(), my->_bitshares_icon);
      my->_last_reported_connection_count = new_connection_count;
      }
  }

  void bts_gntp_notifier::notify_client_exiting_unexpectedly()
  {
    std::ostringstream message;
    message << "The BitShares client is exiting due to an unhandled exception";
    my->_notifier.send_notification("client_exiting_unexpectedly", "Client Exiting Unexpectedly", message.str(), my->_bitshares_icon);
  }

  void bts_gntp_notifier::notify_head_block_too_old(const fc::time_point_sec& head_block_age)
  {
    fc::time_point block_age_cutoff = fc::time_point::now() - fc::seconds(BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC * my->_missed_block_count_threshold);
    if (head_block_age < block_age_cutoff)
    {
      fc::time_point notification_time_cutoff = fc::time_point::now() - my->_last_head_block_too_old_notification_interval;
      if (my->_last_head_block_too_old_notification_time < notification_time_cutoff)
      {
        std::ostringstream message;
        uint32_t age_in_sec = bts::blockchain::now().sec_since_epoch() - head_block_age.sec_since_epoch();
        uint32_t missed_block_count = age_in_sec / BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC;
        message << "The last block on our blockchain is " << fc::get_approximate_relative_time_string(head_block_age, bts::blockchain::now(), " old") << 
                   " seconds old, meaning we've missed " << missed_block_count << " blocks";
        my->_notifier.send_notification("head_block_too_old", "Head Block is Too Old", message.str(), my->_bitshares_icon);
        my->_last_head_block_too_old_notification_time = fc::time_point::now();    
      }
    }
  }


  namespace detail
  {
    unsigned char bitshares_icon_png[] = {
      0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
      0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
      0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00,
      0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
      0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x0a, 0x4f, 0x69, 0x43,
      0x43, 0x50, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20,<commit_msg>Add a timeout on connection change notifications<commit_after>#include <bts/client/notifier.hpp>
#include <bts/blockchain/config.hpp>
#include <bts/blockchain/time.hpp>

#include <fc/network/gntp.hpp>

#include <sstream>

namespace bts { namespace client { 
  namespace detail 
  {
    class bts_gntp_notifier_impl
    {
    public:
      fc::gntp_notifier _notifier;
      fc::gntp_icon_ptr _bitshares_icon;

      bool     _shutting_down;

      uint32_t _last_reported_connection_count;
      uint32_t _connection_count_notification_threshold;
      fc::time_point _last_connection_count_notification_time;
      fc::microseconds _connection_count_notification_interval;
      fc::time_point _last_head_block_too_old_notification_time;
      fc::microseconds _head_block_too_old_notification_interval;
      uint32_t _missed_block_count_threshold;

      bts_gntp_notifier_impl();
      void register_notification_types();
    };
    extern unsigned char bitshares_icon_png[];
    extern unsigned bitshares_icon_png_len;

    bts_gntp_notifier_impl::bts_gntp_notifier_impl() :
      _bitshares_icon(std::make_shared<fc::gntp_icon>((const char*)bitshares_icon_png, bitshares_icon_png_len)),
      _shutting_down(false),
      _last_reported_connection_count(0),
      _connection_count_notification_interval(fc::seconds(300)),
      _connection_count_notification_threshold(5),
      _head_block_too_old_notification_interval(fc::seconds(300)),
      _missed_block_count_threshold(3)
    {
    }

    void bts_gntp_notifier_impl::register_notification_types()
    {
      fc::gntp_notification_type notification_type;

      notification_type.name = "connection_count_below_threshold";
      notification_type.display_name = "Connection Count Below Threshold";
      notification_type.enabled = true;
      notification_type.icon = _bitshares_icon;
      _notifier.add_notification_type(notification_type);

      notification_type.name = "client_exiting_unexpectedly";
      notification_type.display_name = "Client Exiting Unexpectedly";
      notification_type.enabled = true;
      notification_type.icon = _bitshares_icon;
      _notifier.add_notification_type(notification_type);

      notification_type.name = "head_block_too_old";
      notification_type.display_name = "Head Block is Too Old";
      notification_type.enabled = true;
      notification_type.icon = _bitshares_icon;
      _notifier.add_notification_type(notification_type);

      _notifier.register_notifications();
    }
  }

  bts_gntp_notifier::bts_gntp_notifier() : 
    my(new detail::bts_gntp_notifier_impl)
  {
    my->_notifier.set_application_name("BitShares");
    my->_notifier.set_application_icon(my->_bitshares_icon);
    my->register_notification_types();
  }

  bts_gntp_notifier::~bts_gntp_notifier()
  {
  }

  void bts_gntp_notifier::client_is_shutting_down()
  {
    my->_shutting_down = true;
  }

  void bts_gntp_notifier::notify_connection_count_changed(uint32_t new_connection_count)
  {
    if (new_connection_count < my->_connection_count_notification_threshold && 
        my->_last_reported_connection_count >= my->_connection_count_notification_threshold)
    {
      fc::time_point notification_time_cutoff = fc::time_point::now() - my->_connection_count_notification_interval;
      if (my->_last_connection_count_notification_time < notification_time_cutoff)
      {
        std::ostringstream message;
        message << "The BitShares client's peer connection count dropped to " << new_connection_count << 
                   ", which is below the warning threshold of " << my->_connection_count_notification_threshold;
        my->_notifier.send_notification("connection_count_below_threshold", "Connection Count Below Threshold", message.str(), my->_bitshares_icon);
        my->_last_reported_connection_count = new_connection_count;
      }
    }
  }

  void bts_gntp_notifier::notify_client_exiting_unexpectedly()
  {
    std::ostringstream message;
    message << "The BitShares client is exiting due to an unhandled exception";
    my->_notifier.send_notification("client_exiting_unexpectedly", "Client Exiting Unexpectedly", message.str(), my->_bitshares_icon);
  }

  void bts_gntp_notifier::notify_head_block_too_old(const fc::time_point_sec& head_block_age)
  {
    fc::time_point block_age_cutoff = fc::time_point::now() - fc::seconds(BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC * my->_missed_block_count_threshold);
    if (head_block_age < block_age_cutoff)
    {
      fc::time_point notification_time_cutoff = fc::time_point::now() - my->_head_block_too_old_notification_interval;
      if (my->_last_head_block_too_old_notification_time < notification_time_cutoff)
      {
        std::ostringstream message;
        uint32_t age_in_sec = bts::blockchain::now().sec_since_epoch() - head_block_age.sec_since_epoch();
        uint32_t missed_block_count = age_in_sec / BTS_BLOCKCHAIN_BLOCK_INTERVAL_SEC;
        message << "The last block on our blockchain is " << fc::get_approximate_relative_time_string(head_block_age, bts::blockchain::now(), " old") << 
                   " seconds old, meaning we've missed " << missed_block_count << " blocks";
        my->_notifier.send_notification("head_block_too_old", "Head Block is Too Old", message.str(), my->_bitshares_icon);
        my->_last_head_block_too_old_notification_time = fc::time_point::now();    
      }
    }
  }


  namespace detail
  {
    unsigned char bitshares_icon_png[] = {
      0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
      0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40,
      0x08, 0x02, 0x00, 0x00, 0x00, 0x25, 0x0b, 0xe6, 0x89, 0x00, 0x00, 0x00,
      0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, 0x00, 0x0b,
      0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x0a, 0x4f, 0x69, 0x43,
      0x43, 0x50, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20,
{'fraction_non_alphanumeric': 0.09601750547045952, 'fraction_numerical': 0.03824945295404814, 'mean_word_length': 4.461759082217974, 'pattern_counts': {'":': 0, '<': 62, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2203593', 'n_tokens_mistral': 4331, 'n_tokens_neox': 3981, 'n_words': 613}