github.com/wtfutil/wtf@v0.43.0/modules/uptimerobot/widget.go (about)

     1  package uptimerobot
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"strings"
    11  
    12  	"github.com/rivo/tview"
    13  	"github.com/wtfutil/wtf/view"
    14  )
    15  
    16  type Widget struct {
    17  	view.ScrollableWidget
    18  
    19  	monitors []Monitor
    20  	settings *Settings
    21  	err      error
    22  }
    23  
    24  func NewWidget(tviewApp *tview.Application, redrawChan chan bool, pages *tview.Pages, settings *Settings) *Widget {
    25  	widget := &Widget{
    26  		ScrollableWidget: view.NewScrollableWidget(tviewApp, redrawChan, pages, settings.Common),
    27  
    28  		settings: settings,
    29  	}
    30  
    31  	widget.SetRenderFunction(widget.Render)
    32  	widget.initializeKeyboardControls()
    33  
    34  	return widget
    35  }
    36  
    37  /* -------------------- Exported Functions -------------------- */
    38  
    39  func (widget *Widget) Refresh() {
    40  	if widget.Disabled() {
    41  		return
    42  	}
    43  
    44  	monitors, err := widget.getMonitors()
    45  
    46  	if widget.settings.offlineFirst {
    47  		var tmp Monitor
    48  		var next int
    49  		for i := 0; i < len(monitors); i++ {
    50  			if monitors[i].State != 2 {
    51  				tmp = monitors[i]
    52  				for j := i; j > next; j-- {
    53  					monitors[j] = monitors[j-1]
    54  				}
    55  				monitors[next] = tmp
    56  				next++
    57  			}
    58  		}
    59  	}
    60  
    61  	widget.monitors = monitors
    62  	widget.err = err
    63  	widget.SetItemCount(len(monitors))
    64  
    65  	widget.Render()
    66  }
    67  
    68  // Render sets up the widget data for redrawing to the screen
    69  func (widget *Widget) Render() {
    70  	widget.Redraw(widget.content)
    71  }
    72  
    73  /* -------------------- Unexported Functions -------------------- */
    74  
    75  func (widget *Widget) content() (string, string, bool) {
    76  	numUp := 0
    77  	for _, monitor := range widget.monitors {
    78  		if monitor.State == 2 {
    79  			numUp++
    80  		}
    81  	}
    82  
    83  	title := fmt.Sprintf("%s (%d/%d)", widget.CommonSettings().Title, numUp, len(widget.monitors))
    84  
    85  	if widget.err != nil {
    86  		return title, widget.err.Error(), true
    87  	}
    88  
    89  	if widget.monitors == nil {
    90  		return title, "No monitors to display", false
    91  	}
    92  
    93  	str := widget.contentFrom(widget.monitors)
    94  
    95  	return title, str, false
    96  }
    97  
    98  func (widget *Widget) contentFrom(monitors []Monitor) string {
    99  	var str string
   100  
   101  	for _, monitor := range monitors {
   102  		prefix := ""
   103  
   104  		switch monitor.State {
   105  		case 2:
   106  			prefix += "[green] + "
   107  		case 8:
   108  		case 9:
   109  			prefix += "[red] - "
   110  		default:
   111  			prefix += "[yellow] ~ "
   112  		}
   113  
   114  		str += fmt.Sprintf(`%s%s [gray](%s)[white]
   115  `,
   116  			prefix,
   117  			monitor.Name,
   118  			formatUptimes(monitor.Uptime),
   119  		)
   120  	}
   121  
   122  	return str
   123  }
   124  
   125  func formatUptimes(str string) string {
   126  	splits := strings.Split(str, "-")
   127  	str = ""
   128  	for i, s := range splits {
   129  		if i != 0 {
   130  			str += "|"
   131  		}
   132  		s = s[:5]
   133  		s = strings.TrimRight(s, "0")
   134  		s = strings.TrimRight(s, ".") + "%"
   135  		str += s
   136  	}
   137  	return str
   138  }
   139  
   140  type Monitor struct {
   141  	Name string `json:"friendly_name"`
   142  	// Monitor state, see: https://uptimerobot.com/api/#parameters
   143  	State int8 `json:"status"`
   144  	// Uptime ratio, preformatted, e.g.: 100.000-97.233-96.975
   145  	Uptime string `json:"custom_uptime_ratio"`
   146  }
   147  
   148  func (widget *Widget) getMonitors() ([]Monitor, error) {
   149  	// See: https://uptimerobot.com/api/#getMonitorsWrap
   150  	resp, errh := http.PostForm("https://api.uptimerobot.com/v2/getMonitors",
   151  		url.Values{
   152  			"api_key":              {widget.settings.apiKey},
   153  			"format":               {"json"},
   154  			"custom_uptime_ratios": {widget.settings.uptimePeriods},
   155  		},
   156  	)
   157  
   158  	if errh != nil {
   159  		return nil, errh
   160  	}
   161  	defer func() { _ = resp.Body.Close() }()
   162  
   163  	body, _ := io.ReadAll(resp.Body)
   164  
   165  	// First pass to read the status
   166  	c := make(map[string]json.RawMessage)
   167  	errj1 := json.Unmarshal(body, &c)
   168  
   169  	if errj1 != nil {
   170  		return nil, errj1
   171  	}
   172  
   173  	if string(c["stat"]) != `"ok"` {
   174  		return nil, errors.New(string(body))
   175  	}
   176  
   177  	// Second pass to get the actual info
   178  	var monitors []Monitor
   179  	errj2 := json.Unmarshal(c["monitors"], &monitors)
   180  
   181  	if errj2 != nil {
   182  		return nil, errj2
   183  	}
   184  
   185  	return monitors, nil
   186  }