mirror of
https://github.com/EnumeratedDev/stormfetch.git
synced 2026-09-15 23:46:12 +00:00
465 lines
12 KiB
Go
465 lines
12 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Used to declare modules in config files
|
|
type stormfetchModuleConfig struct {
|
|
Name string `yaml:"name"`
|
|
Format string `yaml:"format"`
|
|
Data map[string]any `yaml:"data"`
|
|
}
|
|
|
|
type StormfetchModule struct {
|
|
Execute func(StormfetchModule) string
|
|
stormfetchModuleConfig
|
|
}
|
|
|
|
var Modules map[string]StormfetchModule = make(map[string]StormfetchModule)
|
|
|
|
func (sm StormfetchModule) GetData(key string, defaultValue any) (any, bool) {
|
|
if sm.Data == nil {
|
|
return defaultValue, false
|
|
}
|
|
|
|
data, ok := sm.Data[key]
|
|
if !ok {
|
|
return defaultValue, false
|
|
}
|
|
|
|
if reflect.ValueOf(data).Kind() != reflect.ValueOf(defaultValue).Kind() {
|
|
return defaultValue, false
|
|
}
|
|
|
|
return data, true
|
|
}
|
|
|
|
func RegisterModule(module StormfetchModule) bool {
|
|
if _, ok := Modules[module.Name]; ok {
|
|
return false
|
|
}
|
|
|
|
Modules[module.Name] = module
|
|
return true
|
|
}
|
|
|
|
func initializeModuleMap() {
|
|
// Distribution Module
|
|
distributionModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "distribution", Format: "%3Distribution: %4$DISTRO_SHORT ($ARCH)"}, Execute: func(sm StormfetchModule) string {
|
|
distroInfo := GetDistroInfo()
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "DISTRO_ID":
|
|
return distroInfo.ID
|
|
case "DISTRO_SHORT":
|
|
return distroInfo.ShortName
|
|
case "DISTRO_LONG":
|
|
return distroInfo.LongName
|
|
case "ARCH":
|
|
return GetArch()
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(distributionModule)
|
|
|
|
// Hostname module
|
|
hostnameModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "hostname", Format: "%3Hostname: %4$HOSTNAME"}, Execute: func(sm StormfetchModule) string {
|
|
hostname, _ := os.Hostname()
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "HOSTNAME":
|
|
return hostname
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(hostnameModule)
|
|
|
|
// Kernel module
|
|
kernelModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "kernel", Format: "%3Kernel: %4$KERNEL_NAME $KERNEL_RELEASE"}, Execute: func(sm StormfetchModule) string {
|
|
kernelName, kernelRelease := GetKernel()
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "KERNEL_NAME":
|
|
return kernelName
|
|
case "KERNEL_RELEASE":
|
|
return kernelRelease
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(kernelModule)
|
|
|
|
// Packages module
|
|
packagesModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "packages", Format: "%3Packages: %4$PACKAGES"}, Execute: func(sm StormfetchModule) string {
|
|
packages := GetInstalledPackages()
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "PACKAGES":
|
|
return packages
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(packagesModule)
|
|
|
|
// Shell module
|
|
shellModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "shell", Format: "%3Shell: %4$SHELL"}, Execute: func(sm StormfetchModule) string {
|
|
shell := GetShell()
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "SHELL":
|
|
return shell
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(shellModule)
|
|
|
|
// Init system module
|
|
initSystemModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "init_system", Format: "%3Init: %4$INIT"}, Execute: func(sm StormfetchModule) string {
|
|
initSystem := GetInitSystem()
|
|
|
|
if initSystem == "" {
|
|
return ""
|
|
}
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "INIT":
|
|
return initSystem
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(initSystemModule)
|
|
|
|
// Libc module
|
|
libcModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "libc", Format: "%3Libc: %4$LIBC"}, Execute: func(sm StormfetchModule) string {
|
|
libc := GetLibc()
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "LIBC":
|
|
return libc
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(libcModule)
|
|
|
|
// Motherboard module
|
|
MotherboardModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "motherboard", Format: "%3Motherboard: %4$MOTHERBOARD"}, Execute: func(sm StormfetchModule) string {
|
|
motherboard := GetMotherboardModel()
|
|
|
|
// Return empty string if can't detect motherboard model
|
|
if motherboard == "" {
|
|
return ""
|
|
}
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "MOTHERBOARD":
|
|
return motherboard
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(MotherboardModule)
|
|
|
|
// Motherboard module
|
|
cpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "cpus", Format: "%3CPU: %4$CPU_MODEL ($CPU_THREADS threads)"}, Execute: func(sm StormfetchModule) string {
|
|
hiddenCPUsInterface, _ := sm.GetData("hidden_cpus", make([]any, 0))
|
|
|
|
// Convert interface slices to string slices
|
|
hiddenCPUs := make([]int, 0)
|
|
for _, value := range hiddenCPUsInterface.([]any) {
|
|
hiddenCPUs = append(hiddenCPUs, value.(int))
|
|
}
|
|
|
|
builder := strings.Builder{}
|
|
cpus := GetCPUs(hiddenCPUs)
|
|
|
|
for i, cpu := range cpus {
|
|
expanded := os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "CPU_NUM":
|
|
return strconv.Itoa(i + 1)
|
|
case "CPU_MODEL":
|
|
return cpu.Model
|
|
case "CPU_CORES":
|
|
return strconv.Itoa(cpu.Cores)
|
|
case "CPU_THREADS":
|
|
return strconv.Itoa(cpu.Threads)
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
|
|
builder.WriteString(expanded + "\n")
|
|
}
|
|
|
|
return builder.String()
|
|
}}
|
|
RegisterModule(cpusModule)
|
|
|
|
// GPUs module
|
|
gpusModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "gpus", Format: "%3GPU: %4$GPU_VENDOR $GPU_NAME"}, Execute: func(sm StormfetchModule) string {
|
|
hiddenGPUsInterface, _ := sm.GetData("hidden_gpus", make([]any, 0))
|
|
|
|
// Convert interface slices to string slices
|
|
hiddenGPUs := make([]int, 0)
|
|
for _, value := range hiddenGPUsInterface.([]any) {
|
|
hiddenGPUs = append(hiddenGPUs, value.(int))
|
|
}
|
|
|
|
builder := strings.Builder{}
|
|
gpus := GetGPUModels(hiddenGPUs)
|
|
|
|
for i, gpu := range gpus {
|
|
vram := strconv.FormatInt(gpu.VRAM, 10)
|
|
|
|
expanded := os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "GPU_NUM":
|
|
return strconv.Itoa(i + 1)
|
|
case "GPU_VENDOR":
|
|
return gpu.Vendor
|
|
case "GPU_NAME":
|
|
return gpu.Name
|
|
case "GPU_PRODUCT":
|
|
return gpu.Product
|
|
case "GPU_SUBSYSTEM":
|
|
return gpu.Subsystem
|
|
case "GPU_DRIVER":
|
|
return gpu.Driver
|
|
case "GPU_VRAM":
|
|
return vram
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
|
|
builder.WriteString(expanded + "\n")
|
|
}
|
|
|
|
return builder.String()
|
|
}}
|
|
RegisterModule(gpusModule)
|
|
|
|
// Memory module
|
|
memoryModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "memory", Format: "%3Memory: %4$MEM_USED MiB / $MEM_TOTAL MiB"}, Execute: func(sm StormfetchModule) string {
|
|
memoryInfo := GetMemoryInfo()
|
|
|
|
if memoryInfo == nil {
|
|
return ""
|
|
}
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "MEM_TOTAL":
|
|
return strconv.Itoa(memoryInfo.MemTotal)
|
|
case "MEM_AVAILABLE":
|
|
return strconv.Itoa(memoryInfo.MemAvailable)
|
|
case "MEM_FREE":
|
|
return strconv.Itoa(memoryInfo.MemFree)
|
|
case "MEM_USED":
|
|
return strconv.Itoa(memoryInfo.MemTotal - memoryInfo.MemAvailable)
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(memoryModule)
|
|
|
|
// Partitions module
|
|
partitionsModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "partitions", Format: "%3Partition ${PART_AUTONAME} (${PART_FS}): %4${PART_USED} / ${PART_TOTAL}"}, Execute: func(sm StormfetchModule) string {
|
|
hiddenPartitionsInterface, _ := sm.GetData("hidden_partitions", make([]any, 0))
|
|
hiddenFilesystemsInterface, _ := sm.GetData("hidden_filesystems", make([]any, 0))
|
|
alternativeNamesInterface, _ := sm.GetData("alternative_names", make(map[string]any, 0))
|
|
|
|
// Convert interface slices to string slices
|
|
hiddenPartitions := make([]string, 0)
|
|
for _, value := range hiddenPartitionsInterface.([]any) {
|
|
hiddenPartitions = append(hiddenPartitions, value.(string))
|
|
}
|
|
hiddenFilesystems := make([]string, 0)
|
|
for _, value := range hiddenFilesystemsInterface.([]any) {
|
|
hiddenFilesystems = append(hiddenFilesystems, value.(string))
|
|
}
|
|
|
|
// Convert interface map to map[string]string
|
|
alternativeNames := make(map[string]string)
|
|
for key, value := range alternativeNamesInterface.(map[string]any) {
|
|
alternativeNames[key] = value.(string)
|
|
}
|
|
|
|
builder := strings.Builder{}
|
|
partitions := GetMountedPartitions(hiddenPartitions, hiddenFilesystems)
|
|
|
|
for i, partition := range partitions {
|
|
partitionAutoname := ""
|
|
if altName, ok := alternativeNames[partition.Device]; ok {
|
|
partitionAutoname = altName
|
|
} else if partition.Label != "" {
|
|
partitionAutoname = partition.Label
|
|
} else {
|
|
partitionAutoname = partition.MountPoint
|
|
}
|
|
|
|
expanded := os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "PART_NUM":
|
|
return strconv.Itoa(i + 1)
|
|
case "PART_FS":
|
|
return partition.FileystemType
|
|
case "PART_DEVICE":
|
|
return partition.Device
|
|
case "PART_AUTONAME":
|
|
return partitionAutoname
|
|
case "PART_LABEL":
|
|
return partition.Label
|
|
case "PART_MOUNTPOINT":
|
|
return partition.MountPoint
|
|
case "PART_FREE":
|
|
return FormatBytes(partition.FreeSize)
|
|
case "PART_USED":
|
|
return FormatBytes(partition.UsedSize)
|
|
case "PART_TOTAL":
|
|
return FormatBytes(partition.TotalSize)
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
|
|
builder.WriteString(expanded + "\n")
|
|
}
|
|
|
|
return builder.String()
|
|
}}
|
|
RegisterModule(partitionsModule)
|
|
|
|
// Local IP module
|
|
localIpModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "local_ip", Format: "%3Local IP: %4$LOCAL_IP"}, Execute: func(sm StormfetchModule) string {
|
|
localIP := GetLocalIP()
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "LOCAL_IP":
|
|
return localIP
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(localIpModule)
|
|
|
|
// DEWM module
|
|
dewmModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "de_wm", Format: "%3${DEWM_TYPE}: %4${DEWM_NAME} ${DEWM_VERSION} ($DISPLAY_PROTOCOL)"}, Execute: func(sm StormfetchModule) string {
|
|
// Return empty string if currently in TTY
|
|
if os.Getenv("XDG_SESSION_TYPE") == "" || os.Getenv("XDG_SESSION_TYPE") == "tty" {
|
|
return ""
|
|
}
|
|
|
|
dewm := GetDEWM()
|
|
displayProtocol := GetDisplayProtocol()
|
|
|
|
// Return empty string if can't detect DE/WM
|
|
if dewm.Name == "Unknown" {
|
|
return ""
|
|
}
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "DEWM_NAME":
|
|
return dewm.Name
|
|
case "DEWM_TYPE":
|
|
return dewm.Type
|
|
case "DEWM_VERSION":
|
|
return dewm.Version
|
|
case "DISPLAY_PROTOCOL":
|
|
return displayProtocol
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
}}
|
|
RegisterModule(dewmModule)
|
|
|
|
// Monitors module
|
|
monitorsModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "monitors", Format: "%3Monitor: %4${MONITOR_WIDTH}x${MONITOR_HEIGHT} ${MONITOR_REFRESH_RATE}Hz"}, Execute: func(sm StormfetchModule) string {
|
|
builder := strings.Builder{}
|
|
monitors := GetMonitors()
|
|
|
|
for i, monitor := range monitors {
|
|
expanded := os.Expand(sm.Format, func(s string) string {
|
|
switch s {
|
|
case "MONITOR_NUM":
|
|
return strconv.Itoa(i + 1)
|
|
case "MONITOR_WIDTH":
|
|
return strconv.Itoa(monitor.Width)
|
|
case "MONITOR_HEIGHT":
|
|
return strconv.Itoa(monitor.Height)
|
|
case "MONITOR_REFRESH_RATE":
|
|
return strconv.Itoa(monitor.RefreshRate)
|
|
default:
|
|
return ""
|
|
}
|
|
})
|
|
|
|
builder.WriteString(expanded + "\n")
|
|
}
|
|
|
|
return builder.String()
|
|
}}
|
|
RegisterModule(monitorsModule)
|
|
|
|
// Custom module
|
|
customModule := StormfetchModule{stormfetchModuleConfig: stormfetchModuleConfig{Name: "custom"}, Execute: func(sm StormfetchModule) string {
|
|
shell, _ := sm.GetData("shell", "/bin/sh")
|
|
commandList, _ := sm.GetData("commands", make([]any, 0))
|
|
|
|
// Exeucte all commands
|
|
commandOutput := make(map[int]string)
|
|
for i, value := range commandList.([]any) {
|
|
command, ok := value.(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
commandOutput[i+1] = runCommand(command, shell.(string))
|
|
}
|
|
|
|
return os.Expand(sm.Format, func(s string) string {
|
|
if len(s) <= 4 || !strings.HasPrefix(s, "CMD_") {
|
|
return ""
|
|
}
|
|
|
|
commandIndexStr := strings.Split(s, "CMD_")[1]
|
|
|
|
commandIndex, err := strconv.Atoi(commandIndexStr)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
return commandOutput[commandIndex]
|
|
})
|
|
}}
|
|
RegisterModule(customModule)
|
|
}
|