199 lines
4.2 KiB
Go
199 lines
4.2 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"os"
|
||
|
"path"
|
||
|
|
||
|
"github.com/godbus/dbus"
|
||
|
"github.com/gorilla/websocket"
|
||
|
)
|
||
|
|
||
|
type messageType struct {
|
||
|
ID int `json:"id"`
|
||
|
AppID int `json:"appid"`
|
||
|
Message string `json:"message"`
|
||
|
Title string `json:"title"`
|
||
|
Priority int `json:"priority"`
|
||
|
// Date time.Time `json:"date"` // Ignore that for now
|
||
|
}
|
||
|
|
||
|
type applicationType struct {
|
||
|
Description string `json:"description"`
|
||
|
ID int `json:"id"`
|
||
|
Image string `json:"image"`
|
||
|
Name string `json:"name"`
|
||
|
Token string `json:"token"`
|
||
|
}
|
||
|
|
||
|
func listenToPushEvents(url string, c chan messageType) {
|
||
|
defer close(c)
|
||
|
connection, _, err := websocket.DefaultDialer.Dial(url, nil)
|
||
|
if err != nil {
|
||
|
log.Printf("Dial error: %s", err.Error())
|
||
|
}
|
||
|
defer connection.Close()
|
||
|
|
||
|
for {
|
||
|
var result messageType
|
||
|
err := connection.ReadJSON(&result)
|
||
|
if err != nil {
|
||
|
log.Printf("Connection error: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
log.Printf("Message: %s %s\n", result.Title, result.Message)
|
||
|
c <- result
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func sendNotification(dbusConn *dbus.Conn, message messageType, hostname, token string) error {
|
||
|
|
||
|
image := getAppImage(hostname, token, message.AppID)
|
||
|
|
||
|
// https://specifications.freedesktop.org/notification-spec/notification-spec-latest.html#urgency-levels
|
||
|
var urgencyByte int
|
||
|
|
||
|
if message.Priority <= 3 {
|
||
|
urgencyByte = 0
|
||
|
} else if message.Priority <= 7 {
|
||
|
urgencyByte = 1
|
||
|
} else {
|
||
|
urgencyByte = 2
|
||
|
}
|
||
|
|
||
|
obj := dbusConn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
|
||
|
call := obj.Call("org.freedesktop.Notifications.Notify", 0, "gotify", uint32(0), "", message.Title, message.Message, []string{},
|
||
|
map[string]dbus.Variant{"urgency": dbus.MakeVariant(byte(urgencyByte)), "image-path": dbus.MakeVariant(image)}, int32(-1))
|
||
|
|
||
|
return call.Err
|
||
|
}
|
||
|
|
||
|
func getAppImage(hostname, token string, appID int) string {
|
||
|
|
||
|
cacheDir := getImageDir()
|
||
|
image := path.Join(cacheDir, fmt.Sprint(appID))
|
||
|
|
||
|
// Check if you need to download the image
|
||
|
_, err := os.Stat(image)
|
||
|
if os.IsNotExist(err) {
|
||
|
// File does not exist
|
||
|
err := downloadAppImage(hostname, token, cacheDir, appID)
|
||
|
if err != nil {
|
||
|
return ""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return image
|
||
|
}
|
||
|
|
||
|
func getImageDir() string {
|
||
|
cacheDir := os.Getenv("XDG_CACHE_HOME")
|
||
|
|
||
|
if cacheDir == "" {
|
||
|
cacheDir = path.Join(os.Getenv("HOME"), ".cache/gotify-desktop")
|
||
|
} else {
|
||
|
cacheDir = path.Join(cacheDir, "gotify-desktop")
|
||
|
}
|
||
|
|
||
|
return cacheDir
|
||
|
}
|
||
|
|
||
|
func downloadAppImage(hostname string, token string, cacheDir string, appID int) error {
|
||
|
u, err := getAppImageURL(hostname, token, appID)
|
||
|
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
os.MkdirAll(cacheDir, 0755) // rwx r-x r-x
|
||
|
|
||
|
resp, err := http.Get(u)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
out, err := os.Create(path.Join(cacheDir, fmt.Sprint(appID)))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer out.Close()
|
||
|
|
||
|
_, err = io.Copy(out, resp.Body)
|
||
|
return err
|
||
|
|
||
|
}
|
||
|
|
||
|
func getAppImageURL(hostname string, token string, appID int) (string, error) {
|
||
|
u := url.URL{Scheme: "https", Host: hostname, Path: "/application"}
|
||
|
param := url.Values{}
|
||
|
param.Add("token", token)
|
||
|
|
||
|
resp, err := http.Get(u.String() + "?" + param.Encode())
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
defer resp.Body.Close()
|
||
|
|
||
|
body, err := ioutil.ReadAll(resp.Body)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
var result []applicationType
|
||
|
json.Unmarshal(body, &result)
|
||
|
|
||
|
for i := 0; i < len(result); i++ {
|
||
|
if result[i].ID == appID {
|
||
|
u := url.URL{Scheme: "https", Host: hostname, Path: result[i].Image}
|
||
|
|
||
|
return u.String() + "?" + param.Encode(), nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return "", errors.New("")
|
||
|
}
|
||
|
|
||
|
func parseArgs() (string, string) {
|
||
|
|
||
|
if len(os.Args) < 2 {
|
||
|
fmt.Println("Usage: gotify-dektop <hostname> <token>")
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
return os.Args[1], os.Args[2]
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
|
||
|
hostname, token := parseArgs()
|
||
|
|
||
|
u := url.URL{Scheme: "wss", Host: hostname, Path: "/stream"}
|
||
|
|
||
|
param := url.Values{}
|
||
|
param.Add("token", token)
|
||
|
|
||
|
dbusConn, err := dbus.ConnectSessionBus()
|
||
|
if err != nil {
|
||
|
log.Printf("Dbus error: %s", err.Error())
|
||
|
os.Exit(1)
|
||
|
}
|
||
|
|
||
|
for {
|
||
|
push := make(chan messageType)
|
||
|
go listenToPushEvents(u.String()+"?"+param.Encode(), push)
|
||
|
|
||
|
for pushMessage := range push {
|
||
|
go sendNotification(dbusConn, pushMessage, hostname, token)
|
||
|
}
|
||
|
}
|
||
|
}
|