Add Calendar Events widget, integrate ICS calendar parsing, and refine related UI components
This commit is contained in:
parent
a29beeda43
commit
dfbc6066c9
89
apis/calendar/main.go
Normal file
89
apis/calendar/main.go
Normal file
@ -0,0 +1,89 @@
|
||||
package calendar
|
||||
|
||||
import (
|
||||
"ArinDash/config"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/arran4/golang-ical"
|
||||
)
|
||||
|
||||
type configFile struct {
|
||||
Calendar calendarConfig
|
||||
}
|
||||
|
||||
type calendarConfig struct {
|
||||
ICS []ICS
|
||||
Icon string
|
||||
}
|
||||
|
||||
type ICS struct {
|
||||
Icon string
|
||||
URL string
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
UID string
|
||||
Summary string
|
||||
Description string
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Location string
|
||||
Icon string
|
||||
}
|
||||
|
||||
func FetchEvents() []Event {
|
||||
cfg := &configFile{}
|
||||
config.LoadConfig(cfg)
|
||||
|
||||
events := make([]Event, 0)
|
||||
for _, ic := range cfg.Calendar.ICS {
|
||||
cal, err := ics.ParseCalendarFromUrl(ic.URL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, event := range cal.Events() {
|
||||
startAt, err := event.GetStartAt()
|
||||
if err != nil {
|
||||
startAt = time.Now()
|
||||
}
|
||||
endAt, err := event.GetEndAt()
|
||||
if err != nil {
|
||||
endAt = startAt
|
||||
}
|
||||
events = append(events, Event{
|
||||
UID: getValue(event, ics.ComponentPropertyUniqueId),
|
||||
Icon: ic.Icon,
|
||||
Summary: getValue(event, ics.ComponentPropertySummary),
|
||||
Description: getValue(event, ics.ComponentPropertyDescription),
|
||||
Start: startAt,
|
||||
End: endAt,
|
||||
Location: getValue(event, ics.ComponentPropertyLocation),
|
||||
})
|
||||
}
|
||||
}
|
||||
result := filter(events, func(i Event) bool {
|
||||
return i.End.After(time.Now())
|
||||
})
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return result[i].Start.Before(result[j].Start)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func getValue(event *ics.VEvent, property ics.ComponentProperty) string {
|
||||
ianaProperty := event.GetProperty(property)
|
||||
if ianaProperty == nil {
|
||||
return ""
|
||||
}
|
||||
return ianaProperty.Value
|
||||
}
|
||||
|
||||
func filter[T any](ss []T, test func(T) bool) (ret []T) {
|
||||
for _, s := range ss {
|
||||
if test(s) {
|
||||
ret = append(ret, s)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -84,6 +84,7 @@ func knownDevices() map[string]Device {
|
||||
}
|
||||
|
||||
func arpDevices() map[string]Device {
|
||||
exec.Command("ip", "n", "show")
|
||||
arp := exec.Command("arp", "-a")
|
||||
var out bytes.Buffer
|
||||
arp.Stdout = &out
|
||||
|
||||
@ -47,9 +47,17 @@ func FetchNews() News {
|
||||
config.LoadConfig(cfg)
|
||||
client := &http.Client{}
|
||||
|
||||
if cfg.News.ApiKey == "" {
|
||||
return News{
|
||||
Status: "No API key provided",
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", apiBaseURL+"?sources="+cfg.News.Sources+"&pageSize=100&apiKey="+cfg.News.ApiKey, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return News{
|
||||
Status: err.Error(),
|
||||
}
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
|
||||
@ -23,6 +23,11 @@ func FetchCurrentWeather() CurrentWeather {
|
||||
config.LoadConfig(cfg)
|
||||
client := &http.Client{}
|
||||
currentWeather := &CurrentWeather{}
|
||||
if cfg.OpenWeatherMap.ApiKey == "" {
|
||||
return CurrentWeather{
|
||||
ErrorMessage: "No API key provided",
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", apiBaseURL+"?id="+cfg.OpenWeatherMap.LocationId+"&units=metric&lang=en&APPID="+cfg.OpenWeatherMap.ApiKey, nil)
|
||||
if err != nil {
|
||||
|
||||
@ -78,6 +78,9 @@ func Connect() PiHConnector {
|
||||
cfg := &configFile{}
|
||||
config.LoadConfig(cfg)
|
||||
client := &http.Client{}
|
||||
if cfg.Pihole.Host == "" {
|
||||
return PiHConnector{}
|
||||
}
|
||||
req, err := http.NewRequest("POST", "http://"+cfg.Pihole.Host+"/api/auth", strings.NewReader("{\"password\": \""+cfg.Pihole.Password+"\"}"))
|
||||
|
||||
if err != nil {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
@ -8,6 +9,7 @@ import (
|
||||
|
||||
func LoadConfig(config interface{}) {
|
||||
if err := toml.Unmarshal(readFile("config.toml"), config); err != nil {
|
||||
fmt.Println(err)
|
||||
config = nil
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,3 +22,11 @@ gebietsCode = "000000000000"
|
||||
#apiKey from newsapi.org
|
||||
ApiKey = "ApiKey"
|
||||
Sources = "comma,sepparated,sources,from,newsapi"
|
||||
|
||||
[[calendar.ics]]
|
||||
Icon = ""
|
||||
Url = "https://url.to.ics"
|
||||
|
||||
[[calendar.ics]]
|
||||
Icon = ""
|
||||
Url = "https://url.to.second.ics"
|
||||
|
||||
1
go.mod
1
go.mod
@ -3,6 +3,7 @@ module ArinDash
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
github.com/arran4/golang-ical v0.3.2
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/mum4k/termdash v0.20.0
|
||||
github.com/pelletier/go-toml/v2 v2.2.4
|
||||
|
||||
2
go.sum
2
go.sum
@ -2,6 +2,8 @@ github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEK
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/arran4/golang-ical v0.3.2 h1:MGNjcXJFSuCXmYX/RpZhR2HDCYoFuK8vTPFLEdFC3JY=
|
||||
github.com/arran4/golang-ical v0.3.2/go.mod h1:xblDGxxIUMWwFZk9dlECUlc1iXNV65LJZOTHLVwu8bo=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
|
||||
59
main.go
59
main.go
@ -35,6 +35,7 @@ func initWidgets(ctx context.Context, term *tcell.Terminal) {
|
||||
widgets.New("Clock", widgets.Clock()),
|
||||
widgets.New("Date", widgets.Date()),
|
||||
widgets.New("Calendar", widgets.Calendar()),
|
||||
widgets.New("CalendarEvents", widgets.CalendarEvents()),
|
||||
widgets.New("Weather", widgets.Weather()),
|
||||
widgets.New("ChangeTitle", widgets.TextInput(titleUpdate, textinput.Label("Update Title: "), textinput.PlaceHolder("New Title"))),
|
||||
widgets.New("Wifi", widgets.WifiQRCode()),
|
||||
@ -63,10 +64,10 @@ func layout() []container.Option {
|
||||
grid.Widget(widgets.Get["NetworkDevices"],
|
||||
container.BorderTitle("Network Devices"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite)),
|
||||
),
|
||||
grid.RowHeightFixed(1,
|
||||
grid.Widget(widgets.Get["empty"]),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -86,6 +87,8 @@ func layout() []container.Option {
|
||||
container.BorderTitle("pi-hole"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
grid.RowHeightFixed(40,
|
||||
@ -95,9 +98,6 @@ func layout() []container.Option {
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
),
|
||||
),
|
||||
grid.RowHeightFixed(85,
|
||||
grid.Widget(widgets.Get["empty"]),
|
||||
),
|
||||
),
|
||||
|
||||
grid.ColWidthFixed(26,
|
||||
@ -105,23 +105,42 @@ func layout() []container.Option {
|
||||
grid.Widget(widgets.Get["Calendar"],
|
||||
container.BorderTitle("Calendar"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite)),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
grid.RowHeightFixed(25,
|
||||
grid.Widget(widgets.Get["empty"]),
|
||||
grid.Widget(widgets.Get["empty"],
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorGreen),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
grid.ColWidthFixed(25,
|
||||
grid.RowHeightFixed(20,
|
||||
grid.Widget(widgets.Get["CalendarEvents"],
|
||||
container.BorderTitle("Events"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
grid.RowHeightFixed(13,
|
||||
grid.Widget(widgets.Get["Weather"],
|
||||
container.BorderTitle("Weather"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
),
|
||||
),
|
||||
grid.RowHeightFixed(85,
|
||||
grid.Widget(widgets.Get["empty"]),
|
||||
grid.Widget(widgets.Get["empty"],
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -131,6 +150,8 @@ func layout() []container.Option {
|
||||
container.BorderTitle("News"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
grid.RowHeightFixed(20,
|
||||
@ -138,6 +159,8 @@ func layout() []container.Option {
|
||||
container.BorderTitle("BBK Warnings"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -145,17 +168,27 @@ func layout() []container.Option {
|
||||
|
||||
grid.ColWidthPerc(35,
|
||||
grid.RowHeightFixed(20,
|
||||
grid.Widget(widgets.Get["HTTPProber"],
|
||||
container.BorderTitle("Website Status"),
|
||||
grid.Widget(widgets.Get["empty"],
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
),
|
||||
),
|
||||
grid.RowHeightPerc(25,
|
||||
grid.RowHeightFixed(24,
|
||||
grid.Widget(widgets.Get["HTTPProber"],
|
||||
container.BorderTitle("Website Status"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
grid.RowHeightFixed(40,
|
||||
grid.Widget(widgets.Get["Docker"],
|
||||
container.BorderTitle("Docker"),
|
||||
container.Border(linestyle.Light),
|
||||
container.BorderColor(cell.ColorWhite),
|
||||
container.PaddingLeft(1),
|
||||
container.PaddingTop(1),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -78,7 +78,7 @@ func createTableForMonth(widget *text.Text) error {
|
||||
}
|
||||
}
|
||||
var str string
|
||||
if err := widget.Write("│"); err != nil {
|
||||
if err := widget.Write("│", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if field == 0 {
|
||||
@ -96,7 +96,7 @@ func createTableForMonth(widget *text.Text) error {
|
||||
return err
|
||||
}
|
||||
if index%7 == 6 {
|
||||
if err := widget.Write("│\n"); err != nil {
|
||||
if err := widget.Write("│\n", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
89
widgets/calendarEvents.go
Normal file
89
widgets/calendarEvents.go
Normal file
@ -0,0 +1,89 @@
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"ArinDash/apis/calendar"
|
||||
"ArinDash/util"
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mum4k/termdash/cell"
|
||||
"github.com/mum4k/termdash/terminal/terminalapi"
|
||||
"github.com/mum4k/termdash/widgetapi"
|
||||
"github.com/mum4k/termdash/widgets/text"
|
||||
)
|
||||
|
||||
type CalendarEventsOptions struct {
|
||||
}
|
||||
|
||||
func CalendarEvents() CalendarEventsOptions {
|
||||
widgetOptions["CalendarEventsOptions"] = createCalendarEventsOptions
|
||||
return CalendarEventsOptions{}
|
||||
}
|
||||
|
||||
func createCalendarEventsOptions(ctx context.Context, _ terminalapi.Terminal, _ interface{}) widgetapi.Widget {
|
||||
widget := util.PanicOnErrorWithResult(text.New(text.WrapAtWords()))
|
||||
go util.Periodic(ctx, 1*time.Hour, func() error {
|
||||
widget.Reset()
|
||||
calendarEvents := calendar.FetchEvents()
|
||||
for _, event := range calendarEvents {
|
||||
var color cell.Color
|
||||
onlySummary := false
|
||||
if isToday(event.Start) {
|
||||
color = cell.ColorGreen
|
||||
} else if event.Start.After(time.Now().AddDate(0, 0, 7)) {
|
||||
color = cell.ColorGray
|
||||
onlySummary = true
|
||||
} else {
|
||||
color = cell.ColorWhite
|
||||
}
|
||||
summary := event.Summary
|
||||
if onlySummary {
|
||||
summary = fmt.Sprintf("%s %s", event.Start.Format(time.DateOnly), event.Summary)
|
||||
}
|
||||
if err := widget.Write(fmt.Sprintf("%s %s\n", event.Icon, summary), text.WriteCellOpts(cell.FgColor(color), cell.Bold())); err != nil {
|
||||
return err
|
||||
}
|
||||
if !onlySummary {
|
||||
var date string
|
||||
if event.Start == event.End {
|
||||
date = formatDate(event.Start)
|
||||
} else {
|
||||
date = fmt.Sprintf("%s - %s", formatDate(event.Start), formatDate(event.End))
|
||||
}
|
||||
if err := widget.Write(fmt.Sprintf("%s\n", date), text.WriteCellOpts(cell.FgColor(color), cell.Dim())); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := widget.Write(fmt.Sprintf("%s\n", event.Description), text.WriteCellOpts(cell.FgColor(color))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := widget.Write(fmt.Sprintf("%s\n\n", event.Location), text.WriteCellOpts(cell.FgColor(color))); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return widget
|
||||
}
|
||||
|
||||
func isToday(date time.Time) bool {
|
||||
year, month, day := date.Date()
|
||||
return year == time.Now().Year() && month == time.Now().Month() && day == time.Now().Day()
|
||||
}
|
||||
|
||||
func isTomorrow(date time.Time) bool {
|
||||
year, month, day := date.Date()
|
||||
return year == time.Now().Year() && month == time.Now().Month() && day == time.Now().AddDate(0, 0, 1).Day()
|
||||
}
|
||||
|
||||
func formatDate(date time.Time) string {
|
||||
if isToday(date) {
|
||||
return fmt.Sprintf("Today at %s", date.Format(time.TimeOnly))
|
||||
} else if isTomorrow(date) {
|
||||
return fmt.Sprintf("Tomorrow at %s", date.Format(time.TimeOnly))
|
||||
}
|
||||
return date.Format(time.DateTime)
|
||||
}
|
||||
@ -33,10 +33,10 @@ func createDockerList(ctx context.Context, _ terminalapi.Terminal, _ interface{}
|
||||
}
|
||||
|
||||
sort.Slice(ms, func(i, j int) bool { return ms[i].CPUPercent > ms[j].CPUPercent })
|
||||
if err := list.Write(fmt.Sprintf("%-40s | %-6s | %-6s | %s\n", "Name", "CPU", "MEM", "Status"), text.WriteReplace()); err != nil {
|
||||
if err := list.Write(fmt.Sprintf("%-30s | %-6s | %-6s | %s\n", "Name", "CPU", "MEM", "Status"), text.WriteReplace(), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprintf("─────────────────────────────────────────┼────────┼────────┼─────────────────────\n")); err != nil {
|
||||
if err := list.Write(fmt.Sprintf("───────────────────────────────┼────────┼────────┼─────────────────────\n"), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, m := range ms {
|
||||
@ -53,22 +53,28 @@ func createDockerList(ctx context.Context, _ terminalapi.Terminal, _ interface{}
|
||||
if strings.Contains(m.Status, "Paused") {
|
||||
status = cell.ColorBlue
|
||||
}
|
||||
if err := list.Write(fmt.Sprintf("%-40s", m.Name), text.WriteCellOpts(cell.FgColor(status))); err != nil {
|
||||
var name string
|
||||
if len(m.Name) > 30 {
|
||||
name = m.Name[:27] + "..."
|
||||
} else {
|
||||
name = m.Name
|
||||
}
|
||||
if err := list.Write(fmt.Sprintf("%-30s", name), text.WriteCellOpts(cell.FgColor(status))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprint(" | ")); err != nil {
|
||||
if err := list.Write(fmt.Sprint(" │ "), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writePercent(m.CPUPercent, list); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprint(" | ")); err != nil {
|
||||
if err := list.Write(fmt.Sprint(" │ "), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writePercent(m.MemPercent, list); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprint(" | ")); err != nil {
|
||||
if err := list.Write(fmt.Sprint(" │ "), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprintf("%s\n", m.Status), text.WriteCellOpts(cell.FgColor(status))); err != nil {
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/mum4k/termdash/cell"
|
||||
"github.com/mum4k/termdash/terminal/terminalapi"
|
||||
"github.com/mum4k/termdash/widgetapi"
|
||||
"github.com/mum4k/termdash/widgets/text"
|
||||
@ -26,19 +27,19 @@ func createPiholeStats(ctx context.Context, _ terminalapi.Terminal, _ interface{
|
||||
ph := pihole.Connect()
|
||||
go util.Periodic(ctx, 1*time.Minute, func() error {
|
||||
summary := ph.Summary()
|
||||
if err := list.Write(fmt.Sprintf("Blocked Domains: %d\n", summary.Gravity.DomainsBeingBlocked), text.WriteReplace()); err != nil {
|
||||
if err := list.Write(fmt.Sprintf("Blocked Domains: %d\n", summary.Gravity.DomainsBeingBlocked), text.WriteReplace(), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprint("---------------------------\n")); err != nil {
|
||||
if err := list.Write(fmt.Sprint("───────────────────────────\n"), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprintf("Total Queries: %d\n", summary.Queries.Total)); err != nil {
|
||||
if err := list.Write(fmt.Sprintf("Total Queries: %d\n", summary.Queries.Total), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprintf("Blocked Queries: %d\n", summary.Queries.Blocked)); err != nil {
|
||||
if err := list.Write(fmt.Sprintf("Blocked Queries: %d\n", summary.Queries.Blocked), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := list.Write(fmt.Sprintf("Unique Domains: %d\n", summary.Queries.UniqueDomains)); err != nil {
|
||||
if err := list.Write(fmt.Sprintf("Unique Domains: %d\n", summary.Queries.UniqueDomains), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user