diff --git a/.gitignore b/.gitignore index f2ada31..f47a2c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build chatz +*.exe coverage.out diff --git a/README.md b/README.md index 41ab7dd..9898798 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ With chatz, you can streamline your notification processes across multiple platf - Discord: [Read documentation](docs/discord.md) - Redis: [Read documentation](docs/redis.md) - SMTP: [Read documentation](docs/smtp.md) +- Gotify: [Read documentation](docs/gotify.md) ## Installation Download and install executable binary from GitHub releases page. diff --git a/chatz.ini b/chatz.ini index 85e3724..aaa91c5 100644 --- a/chatz.ini +++ b/chatz.ini @@ -36,3 +36,10 @@ SMTP_PASSWORD= SMTP_SUBJECT= SMTP_FROM= SMTP_TO= + +[gotify] +PROVIDER=gotify +GOTIFY_URL= +GOTIFY_TOKEN= +GOTIFY_TITLE="Chatz" +GOTIFY_PRIORITY=5 diff --git a/config/config.go b/config/config.go index 7cef296..3d821fb 100644 --- a/config/config.go +++ b/config/config.go @@ -2,19 +2,23 @@ package config // Environment type Config struct { - Provider string - WebHookURL string - Token string - ChannelId string - ChatId string - ConnectionURL string - SMTPHost string - SMTPPort string - UseTLS bool - UseSTARTTLS bool - SMTPUser string - SMTPPassword string - SMTPSubject string - SMTPFrom string - SMTPTo string + Provider string + WebHookURL string + Token string + ChannelId string + ChatId string + ConnectionURL string + SMTPHost string + SMTPPort string + UseTLS bool + UseSTARTTLS bool + SMTPUser string + SMTPPassword string + SMTPSubject string + SMTPFrom string + SMTPTo string + GotifyURL string + GotifyToken string + GotifyTitle string + GotifyPriority int } diff --git a/constants/common.go b/constants/common.go index fff8268..a1dcbe1 100644 --- a/constants/common.go +++ b/constants/common.go @@ -7,4 +7,5 @@ const ( PROVIDER_GOOGLE = "google" PROVIDER_REDIS = "redis" PROVIDER_SMTP = "smtp" + PROVIDER_GOTIFY = "gotify" ) diff --git a/docs/gotify.md b/docs/gotify.md new file mode 100644 index 0000000..96bfa0e --- /dev/null +++ b/docs/gotify.md @@ -0,0 +1,59 @@ +# Gotify Notification Setup + +This guide will walk you through setting up Gotify notifications. You will learn how to obtain the necessary credentials and configure them in your environment. + +## 1. Get Gotify URL and App Token + +First, you need to get the Gotify server URL and an app token. + +1. **Gotify Server URL**: This is the URL of your Gotify server. +2. **App Token**: You can create a new app in the Gotify web UI and get the token. + +## 2. Configure Chatz + +Next, you need to configure Chatz to use the Gotify provider. You can do this by editing the `~/.chatz.ini` file or by setting environment variables. + +### Using `~/.chatz.ini` + +Add the following section to your `~/.chatz.ini` file: + +```ini +[gotify] +PROVIDER=gotify +GOTIFY_URL= +GOTIFY_TOKEN= +GOTIFY_TITLE= +GOTIFY_PRIORITY= +``` + +### Using Environment Variables + +Set the following environment variables: + +```sh +export PROVIDER=gotify +export GOTIFY_URL= +export GOTIFY_TOKEN= +export GOTIFY_TITLE= +export GOTIFY_PRIORITY= +``` + +## 3. Send a Test Notification + +Now you can send a test notification to your Gotify server. + +```sh +chatz --profile=gotify "Hello from Chatz!" +``` + +You should receive a notification on your Gotify server. + +### Using CLI Flags + +You can also override the title and priority using CLI flags: + +```sh +chatz --profile=gotify --subject "Custom Title" --priority 7 "Hello from Chatz!" +``` + +This will send a notification with "Custom Title" as the title and a priority of 7. diff --git a/docs/smtp.md b/docs/smtp.md index 8998533..ee614c8 100644 --- a/docs/smtp.md +++ b/docs/smtp.md @@ -2,41 +2,34 @@ This document explains how to configure SMTP with Gmail, including creating an App Password, and understanding TLS/STARTTLS and encryption options. - ## 1. Creating an App Password -Gmail requires an "App Password" for less secure apps accessing your account. This is a more secure alternative to using your regular Gmail password directly. +Gmail requires an "App Password" for less secure apps accessing your account. This is a more secure alternative to using your regular Gmail password directly. 1. Go to your [Google account security settings](https://myaccount.google.com/security). 2. Scroll down to "Signing in to Google" and click "App Passwords". -3. Select "Mail" as the app and "Other (Custom name)" as the device. Give it a descriptive name (e.g., "My SMTP App"). -4. Click "Generate". A new password will be displayed. **Copy this password immediately; you won't be able to see it again.** +3. Select "Mail" as the app and "Other (Custom name)" as the device. Give it a descriptive name (e.g., "My SMTP App"). +4. Click "Generate". A new password will be displayed. **Copy this password immediately; you won't be able to see it again.** ## 2. SMTP Server Settings -* **Server:** `smtp.gmail.com` -* **Port:** - * **STARTTLS:** 587 (Recommended) - * **TLS:** 465 - * **No Encryption (Insecure - Avoid):** 25 -* **Username:** Your full Gmail address (e.g., `yourname@gmail.com`) -* **Password:** The App Password you generated. - +- **Server:** `smtp.gmail.com` +- **Port:** + - **STARTTLS:** 587 (Recommended) + - **TLS:** 465 + - **No Encryption (Insecure - Avoid):** 25 +- **Username:** Your full Gmail address (e.g., `yourname@gmail.com`) +- **Password:** The App Password you generated. ## 3. TLS/STARTTLS and Encryption -* **TLS (Transport Layer Security) / STARTTLS:** These are encryption protocols that secure the connection between your email client and the SMTP server. They encrypt your email messages and prevent eavesdropping. **Using TLS/STARTTLS is strongly recommended.** Many email clients support STARTTLS, initiating encryption during the connection process. - - -* **No Encryption:** Sending emails without encryption is highly discouraged. Your email, including the subject, body, and any attachments, could be intercepted by malicious actors. Avoid this unless absolutely necessary, and only with trusted parties. - +- **TLS (Transport Layer Security) / STARTTLS:** These are encryption protocols that secure the connection between your email client and the SMTP server. They encrypt your email messages and prevent eavesdropping. **Using TLS/STARTTLS is strongly recommended.** Many email clients support STARTTLS, initiating encryption during the connection process. +- **No Encryption:** Sending emails without encryption is highly discouraged. Your email, including the subject, body, and any attachments, could be intercepted by malicious actors. Avoid this unless absolutely necessary, and only with trusted parties. ## 4. Example Configuration (Illustrative - adapt to your email client) - -This is a generic example; the exact settings will depend on your email client (e.g., Outlook, Thunderbird). Refer to your email client's documentation for specific instructions. - +This is a generic example; the exact settings will depend on your email client (e.g., Outlook, Thunderbird). Refer to your email client's documentation for specific instructions. ``` SMTP_HOST=smtp.gmail.com @@ -46,14 +39,26 @@ SMTP_USE_STARTTLS=true Remember to replace placeholders with your actual information. -## 5. Troubleshooting +### Using CLI Flags +You can also override the subject using the CLI flag: + +```sh +chatz --profile=smtp --subject "Custom Subject" "Hello from Chatz!" +``` + +This will send an email with "Custom Subject" as the subject. + +## 5. Troubleshooting If you encounter issues, check: -* **Correct App Password:** Verify you copied the correct App Password. -* **Firewall Settings:** Ensure your firewall isn't blocking outgoing connections on port 587 (or 465 if using no encryption). -* **Email Client Configuration:** Double-check all server settings in your email client. +- **Correct App Password:** Verify you copied the correct App Password. +- **Firewall Settings:** Ensure your firewall isn't blocking outgoing connections on port 587 (or 465 if using no encryption). +- **Email Client Configuration:** Double-check all server settings in your email client. Using an App Password is crucial for securing your Gmail account when using SMTP. Always prioritize using TLS/STARTTLS encryption for the security of your emails. + +``` + ``` diff --git a/main.go b/main.go index bc06edb..96ffe51 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "fmt" "os" + "github.com/tech-thinker/chatz/models" "github.com/tech-thinker/chatz/providers" "github.com/tech-thinker/chatz/utils" "github.com/urfave/cli/v2" @@ -22,6 +23,8 @@ func main() { var threadId string var output bool var fromEnv bool + var subject string + var priority int app := cli.NewApp() app.Name = "chatz" @@ -59,6 +62,18 @@ func main() { Usage: "To use config from environment variables", Destination: &fromEnv, }, + &cli.StringFlag{ + Name: "subject", + Aliases: []string{"s"}, + Usage: "Subject for provider which supports subject or title", + Destination: &subject, + }, + &cli.IntFlag{ + Name: "priority", + Aliases: []string{"pr"}, + Usage: "Priority for gotify notification", + Destination: &priority, + }, } app.Action = func(ctx *cli.Context) error { if version { @@ -92,14 +107,21 @@ func main() { return nil } + option := models.Option{ + Title: &subject, + Subject: &subject, + Priority: &priority, + } + if len(threadId) > 0 { - res, _ := provider.Reply(threadId, message) + res, _ := provider.Reply(threadId, message, option) if output { fmt.Println(res) } return nil } - res, err := provider.Post(message) + + res, err := provider.Post(message, option) if output { fmt.Println(res) } diff --git a/man/chatz.1 b/man/chatz.1 index 4b0177b..9b1b41a 100644 --- a/man/chatz.1 +++ b/man/chatz.1 @@ -25,6 +25,9 @@ Print output to stdout. .B \-t, \-\-thread-id Thread ID for replies to a message. .TP +.B \-s, \-\-subject +Subject for provider which supports subject or title. +.TP .B \-e, \-\-from-env Use system environment variables. .TP diff --git a/models/base.go b/models/base.go new file mode 100644 index 0000000..c3f7804 --- /dev/null +++ b/models/base.go @@ -0,0 +1,7 @@ +package models + +type Option struct { + Priority *int `json:"priority"` + Title *string `json:"title"` + Subject *string `json:"subject"` +} diff --git a/models/slack.go b/models/slack.go index 46bf6cc..0b51a0e 100644 --- a/models/slack.go +++ b/models/slack.go @@ -2,36 +2,36 @@ package models // SlackRes represents the main structure of the JSON response. type SlackRes struct { - Ok bool `json:"ok"` - Channel string `json:"channel"` - Ts string `json:"ts"` + Ok bool `json:"ok"` + Channel string `json:"channel"` + Ts string `json:"ts"` Message SlackMessage `json:"message"` - Warning string `json:"warning"` + Warning string `json:"warning"` ResponseMetadata SlackResponseMetadata `json:"response_metadata"` } // SlackMessage represents the message object within the main response. type SlackMessage struct { - User string `json:"user"` - Type string `json:"type"` - Ts string `json:"ts"` - BotID string `json:"bot_id"` - AppID string `json:"app_id"` - Text string `json:"text"` - Team string `json:"team"` + User string `json:"user"` + Type string `json:"type"` + Ts string `json:"ts"` + BotID string `json:"bot_id"` + AppID string `json:"app_id"` + Text string `json:"text"` + Team string `json:"team"` BotProfile SlackBotProfile `json:"bot_profile"` Blocks []SlackBlock `json:"blocks"` } // SlackBotProfile represents the profile of the bot that sent the message. type SlackBotProfile struct { - ID string `json:"id"` - AppID string `json:"app_id"` - Name string `json:"name"` - Icons SlackIcons `json:"icons"` - Deleted bool `json:"deleted"` - Updated int64 `json:"updated"` - TeamID string `json:"team_id"` + ID string `json:"id"` + AppID string `json:"app_id"` + Name string `json:"name"` + Icons SlackIcons `json:"icons"` + Deleted bool `json:"deleted"` + Updated int64 `json:"updated"` + TeamID string `json:"team_id"` } // SlackIcons represents the bot's profile images of different sizes. @@ -43,14 +43,14 @@ type SlackIcons struct { // Block represents the block object in the message. type SlackBlock struct { - Type string `json:"type"` - BlockID string `json:"block_id"` + Type string `json:"type"` + BlockID string `json:"block_id"` Elements []SlackElement `json:"elements"` } // SlackElement represents a section within the block. type SlackElement struct { - Type string `json:"type"` + Type string `json:"type"` Elements []SlackInnerElement `json:"elements"` } diff --git a/providers/agent.go b/providers/agent.go index b6b4ea5..0ce7102 100644 --- a/providers/agent.go +++ b/providers/agent.go @@ -5,11 +5,12 @@ import ( "github.com/tech-thinker/chatz/config" "github.com/tech-thinker/chatz/constants" + "github.com/tech-thinker/chatz/models" ) type Provider interface { - Post(message string) (interface{}, error) - Reply(threadId string, message string) (interface{}, error) + Post(message string, option models.Option) (any, error) + Reply(threadId string, message string, option models.Option) (any, error) } func NewProvider(env *config.Config) (Provider, error) { @@ -26,6 +27,8 @@ func NewProvider(env *config.Config) (Provider, error) { return &RedisProvider{config: env}, nil case constants.PROVIDER_SMTP: return &SMTPProvider{config: env}, nil + case constants.PROVIDER_GOTIFY: + return &GotifyProvider{config: env}, nil default: return nil, errors.New("Invalid provider config in ~/.chatz.ini") } diff --git a/providers/discord.go b/providers/discord.go index 59e9e85..57c85b9 100644 --- a/providers/discord.go +++ b/providers/discord.go @@ -8,21 +8,22 @@ import ( "strings" "github.com/tech-thinker/chatz/config" + "github.com/tech-thinker/chatz/models" ) type DiscordProvider struct { - config *config.Config + config *config.Config } -func (agent *DiscordProvider) Post(message string) (interface{}, error) { - url := agent.config.WebHookURL +func (agent *DiscordProvider) Post(message string, option models.Option) (any, error) { + url := agent.config.WebHookURL payloadStr := fmt.Sprintf( - `{"content": "%s"}`, - message, - ) + `{"content": "%s"}`, + message, + ) - payload := strings.NewReader(payloadStr) + payload := strings.NewReader(payloadStr) req, _ := http.NewRequest("POST", url, payload) @@ -30,17 +31,16 @@ func (agent *DiscordProvider) Post(message string) (interface{}, error) { req.Header.Add("User-Agent", "tech-thinker/chatz") res, err := http.DefaultClient.Do(req) - if err!=nil { - return nil, err - } + if err != nil { + return nil, err + } defer res.Body.Close() body, err := io.ReadAll(res.Body) - return string(body), err + return string(body), err } -func (agent *DiscordProvider) Reply(threadId string, message string) (interface{}, error) { - fmt.Println("Reply to discord not supported yet.") - return nil, errors.New("Reply to discord not supported yet.") +func (agent *DiscordProvider) Reply(threadId string, message string, option models.Option) (any, error) { + fmt.Println("Reply to discord not supported yet.") + return nil, errors.New("Reply to discord not supported yet.") } - diff --git a/providers/google.go b/providers/google.go index bf7babe..67bfadc 100644 --- a/providers/google.go +++ b/providers/google.go @@ -7,21 +7,22 @@ import ( "strings" "github.com/tech-thinker/chatz/config" + "github.com/tech-thinker/chatz/models" ) type GoogleProvider struct { - config *config.Config + config *config.Config } -func (agent *GoogleProvider) Post(message string) (interface{}, error) { - url := agent.config.WebHookURL +func (agent *GoogleProvider) Post(message string, option models.Option) (any, error) { + url := agent.config.WebHookURL payloadStr := fmt.Sprintf( - `{"text": "%s"}`, - message, - ) + `{"text": "%s"}`, + message, + ) - payload := strings.NewReader(payloadStr) + payload := strings.NewReader(payloadStr) req, _ := http.NewRequest("POST", url, payload) @@ -29,24 +30,24 @@ func (agent *GoogleProvider) Post(message string) (interface{}, error) { req.Header.Add("User-Agent", "tech-thinker/chatz") res, err := http.DefaultClient.Do(req) - if err!=nil { - return nil, err - } + if err != nil { + return nil, err + } defer res.Body.Close() body, err := io.ReadAll(res.Body) - return string(body), err + return string(body), err } -func (agent *GoogleProvider) Reply(threadId string, message string) (interface{}, error) { - url := fmt.Sprintf("%s&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD",agent.config.WebHookURL) +func (agent *GoogleProvider) Reply(threadId string, message string, option models.Option) (any, error) { + url := fmt.Sprintf("%s&messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD", agent.config.WebHookURL) payloadStr := fmt.Sprintf( - `{"text": "%s", "thread": {"name": "%s"}}`, - message, threadId, - ) + `{"text": "%s", "thread": {"name": "%s"}}`, + message, threadId, + ) - payload := strings.NewReader(payloadStr) + payload := strings.NewReader(payloadStr) req, _ := http.NewRequest("POST", url, payload) @@ -54,12 +55,11 @@ func (agent *GoogleProvider) Reply(threadId string, message string) (interface{} req.Header.Add("User-Agent", "tech-thinker/chatz") res, err := http.DefaultClient.Do(req) - if err!=nil { - return nil, err - } + if err != nil { + return nil, err + } defer res.Body.Close() body, err := io.ReadAll(res.Body) - return string(body), err + return string(body), err } - diff --git a/providers/gotify.go b/providers/gotify.go new file mode 100644 index 0000000..1be2019 --- /dev/null +++ b/providers/gotify.go @@ -0,0 +1,66 @@ +package providers + +import ( + "errors" + "fmt" + "io" + "net/http" + "strings" + + "github.com/tech-thinker/chatz/config" + "github.com/tech-thinker/chatz/models" + "github.com/tech-thinker/chatz/utils" +) + +type GotifyProvider struct { + config *config.Config +} + +func (agent *GotifyProvider) Post(message string, option models.Option) (any, error) { + if option.Title == nil { + if len(agent.config.GotifyTitle) > 0 { + option.Title = &agent.config.GotifyTitle + } else { + option.Title = utils.NewString("Chatz Notification") + } + } + + if option.Priority == nil { + if agent.config.GotifyPriority > 0 { + option.Priority = utils.NewInt(agent.config.GotifyPriority) + } else { + option.Priority = utils.NewInt(5) + } + } + + url := fmt.Sprintf("%s/message", agent.config.GotifyURL) + + payloadStr := fmt.Sprintf( + `{"message": "%s", "priority": %d, "title": "%s"}`, + message, + *option.Priority, + *option.Title, + ) + + payload := strings.NewReader(payloadStr) + + req, _ := http.NewRequest("POST", url, payload) + + req.Header.Add("Content-Type", "application/json") + req.Header.Add("User-Agent", "tech-thinker/chatz") + req.Header.Add("X-Gotify-Key", agent.config.GotifyToken) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + defer res.Body.Close() + body, err := io.ReadAll(res.Body) + return string(body), err +} + +func (agent *GotifyProvider) Reply(threadId string, message string, option models.Option) (any, error) { + fmt.Println("Reply to gotify not supported yet.") + return nil, errors.New("reply to gotify not supported yet") +} diff --git a/providers/redis.go b/providers/redis.go index 2ae9522..4dc094e 100644 --- a/providers/redis.go +++ b/providers/redis.go @@ -7,36 +7,36 @@ import ( "github.com/redis/go-redis/v9" "github.com/tech-thinker/chatz/config" + "github.com/tech-thinker/chatz/models" ) type RedisProvider struct { - config *config.Config + config *config.Config } -func (agent *RedisProvider) Post(message string) (interface{}, error) { - options, err := redis.ParseURL(agent.config.ConnectionURL) +func (agent *RedisProvider) Post(message string, option models.Option) (any, error) { + options, err := redis.ParseURL(agent.config.ConnectionURL) if err != nil { return nil, err } - - rdb := redis.NewClient(options) - ctx := context.Background() - - _, err = rdb.Ping(ctx).Result() + + rdb := redis.NewClient(options) + ctx := context.Background() + + _, err = rdb.Ping(ctx).Result() if err != nil { - return nil, err + return nil, err } - err = rdb.Publish(ctx, agent.config.ChannelId, message).Err() + err = rdb.Publish(ctx, agent.config.ChannelId, message).Err() if err != nil { - return nil, err + return nil, err } - - return "Published", nil -} -func (agent *RedisProvider) Reply(threadId string, message string) (interface{}, error) { - fmt.Println("Reply to redis not supported yet.") - return nil, errors.New("Reply to redis not supported yet.") + return "Published", nil } +func (agent *RedisProvider) Reply(threadId string, message string, option models.Option) (any, error) { + fmt.Println("Reply to redis not supported yet.") + return nil, errors.New("Reply to redis not supported yet.") +} diff --git a/providers/slack.go b/providers/slack.go index 3bf24bd..ccb0622 100644 --- a/providers/slack.go +++ b/providers/slack.go @@ -7,21 +7,22 @@ import ( "strings" "github.com/tech-thinker/chatz/config" + "github.com/tech-thinker/chatz/models" ) type SlackProvider struct { - config *config.Config + config *config.Config } -func (agent *SlackProvider) Post(message string) (interface{}, error) { - url := "https://slack.com/api/chat.postMessage" +func (agent *SlackProvider) Post(message string, option models.Option) (any, error) { + url := "https://slack.com/api/chat.postMessage" payloadStr := fmt.Sprintf( - `{"channel": "%s","text": "%s"}`, - agent.config.ChannelId, message, - ) + `{"channel": "%s","text": "%s"}`, + agent.config.ChannelId, message, + ) - payload := strings.NewReader(payloadStr) + payload := strings.NewReader(payloadStr) req, _ := http.NewRequest("POST", url, payload) @@ -30,24 +31,24 @@ func (agent *SlackProvider) Post(message string) (interface{}, error) { req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", agent.config.Token)) res, err := http.DefaultClient.Do(req) - if err!=nil { - return nil, err - } + if err != nil { + return nil, err + } defer res.Body.Close() body, err := io.ReadAll(res.Body) - return string(body), err + return string(body), err } -func (agent *SlackProvider) Reply(threadId string, message string) (interface{}, error) { - url := "https://slack.com/api/chat.postMessage" +func (agent *SlackProvider) Reply(threadId string, message string, option models.Option) (any, error) { + url := "https://slack.com/api/chat.postMessage" payloadStr := fmt.Sprintf( - `{"channel": "%s", "text": "%s", "thread_ts": "%s"}`, - agent.config.ChannelId, message, threadId, - ) + `{"channel": "%s", "text": "%s", "thread_ts": "%s"}`, + agent.config.ChannelId, message, threadId, + ) - payload := strings.NewReader(payloadStr) + payload := strings.NewReader(payloadStr) req, _ := http.NewRequest("POST", url, payload) @@ -56,12 +57,11 @@ func (agent *SlackProvider) Reply(threadId string, message string) (interface{}, req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", agent.config.Token)) res, err := http.DefaultClient.Do(req) - if err!=nil { - return nil, err - } + if err != nil { + return nil, err + } defer res.Body.Close() body, err := io.ReadAll(res.Body) - return string(body), err + return string(body), err } - diff --git a/providers/smtp.go b/providers/smtp.go index d5bed09..a6eab96 100644 --- a/providers/smtp.go +++ b/providers/smtp.go @@ -9,20 +9,29 @@ import ( "strings" "github.com/tech-thinker/chatz/config" + "github.com/tech-thinker/chatz/models" + "github.com/tech-thinker/chatz/utils" ) type SMTPProvider struct { config *config.Config } -func (agent *SMTPProvider) Post(message string) (interface{}, error) { +func (agent *SMTPProvider) Post(message string, option models.Option) (any, error) { host := agent.config.SMTPHost port := agent.config.SMTPPort smtpServer := fmt.Sprintf("%s:%s", host, port) user := agent.config.SMTPUser password := agent.config.SMTPPassword - subject := agent.config.SMTPSubject + if option.Subject == nil { + if len(agent.config.SMTPSubject) > 0 { + option.Subject = &agent.config.SMTPSubject + } else { + option.Subject = utils.NewString("Chatz Notification") + } + } + from := agent.config.SMTPFrom recipients := agent.config.SMTPTo @@ -30,14 +39,10 @@ func (agent *SMTPProvider) Post(message string) (interface{}, error) { from = user } - if len(subject) == 0 { - subject = "Chatz Notification" - } - // Create the message with proper headers msg := []byte(fmt.Sprintf( "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s", - from, recipients, subject, message, + from, recipients, *option.Subject, message, )) // Split recipients into a slice @@ -108,7 +113,7 @@ func (agent *SMTPProvider) Post(message string) (interface{}, error) { } // Helper function to send email -func sendEmail(client *smtp.Client, from string, to []string, msg []byte) (interface{}, error) { +func sendEmail(client *smtp.Client, from string, to []string, msg []byte) (any, error) { // Set the sender and recipients if err := client.Mail(from); err != nil { return nil, fmt.Errorf("failed to set sender: %w", err) @@ -136,7 +141,7 @@ func sendEmail(client *smtp.Client, from string, to []string, msg []byte) (inter return `{"status": "success"}`, nil } -func (agent *SMTPProvider) Reply(threadId string, message string) (interface{}, error) { +func (agent *SMTPProvider) Reply(threadId string, message string, option models.Option) (any, error) { fmt.Println("Reply to SMTP not supported yet.") return nil, errors.New("Reply to SMTP not supported yet.") } diff --git a/providers/telegram.go b/providers/telegram.go index d1da8a0..989f76b 100644 --- a/providers/telegram.go +++ b/providers/telegram.go @@ -7,24 +7,25 @@ import ( "strings" "github.com/tech-thinker/chatz/config" + "github.com/tech-thinker/chatz/models" ) type TelegramProvider struct { - config *config.Config + config *config.Config } -func (agent *TelegramProvider) Post(message string) (interface{}, error) { - url := fmt.Sprintf( - `https://api.telegram.org/bot%s/sendMessage`, - agent.config.Token, - ) +func (agent *TelegramProvider) Post(message string, option models.Option) (any, error) { + url := fmt.Sprintf( + `https://api.telegram.org/bot%s/sendMessage`, + agent.config.Token, + ) payloadStr := fmt.Sprintf( - `{"chat_id": "%s","text": "%s"}`, - agent.config.ChatId, message, - ) + `{"chat_id": "%s","text": "%s"}`, + agent.config.ChatId, message, + ) - payload := strings.NewReader(payloadStr) + payload := strings.NewReader(payloadStr) req, _ := http.NewRequest("POST", url, payload) @@ -32,27 +33,27 @@ func (agent *TelegramProvider) Post(message string) (interface{}, error) { req.Header.Add("User-Agent", "tech-thinker/chatz") res, err := http.DefaultClient.Do(req) - if err!=nil { - return nil, err - } + if err != nil { + return nil, err + } defer res.Body.Close() body, err := io.ReadAll(res.Body) - return string(body), err + return string(body), err } -func (agent *TelegramProvider) Reply(threadId string, message string) (interface{}, error) { - url := fmt.Sprintf( - `https://api.telegram.org/bot%s/sendMessage`, - agent.config.Token, - ) +func (agent *TelegramProvider) Reply(threadId string, message string, option models.Option) (any, error) { + url := fmt.Sprintf( + `https://api.telegram.org/bot%s/sendMessage`, + agent.config.Token, + ) payloadStr := fmt.Sprintf( - `{"chat_id": "%s", "text": "%s", "reply_to_message_id": "%s"}`, - agent.config.ChatId, message, threadId, - ) + `{"chat_id": "%s", "text": "%s", "reply_to_message_id": "%s"}`, + agent.config.ChatId, message, threadId, + ) - payload := strings.NewReader(payloadStr) + payload := strings.NewReader(payloadStr) req, _ := http.NewRequest("POST", url, payload) @@ -60,11 +61,11 @@ func (agent *TelegramProvider) Reply(threadId string, message string) (interface req.Header.Add("User-Agent", "tech-thinker/chatz") res, err := http.DefaultClient.Do(req) - if err!=nil { - return nil, err - } + if err != nil { + return nil, err + } defer res.Body.Close() body, err := io.ReadAll(res.Body) - return string(body), err + return string(body), err } diff --git a/utils/common.go b/utils/common.go index 8dbfb5d..e15c5ae 100644 --- a/utils/common.go +++ b/utils/common.go @@ -37,6 +37,10 @@ func loadEnvFromSystemEnv() (*config.Config, error) { smtpSubject := v.GetString("SMTP_SUBJECT") smtpFrom := v.GetString("SMTP_FROM") smtpTo := v.GetString("SMTP_TO") + gotifyURL := v.GetString("GOTIFY_URL") + gotifyToken := v.GetString("GOTIFY_TOKEN") + gotifyTitle := v.GetString("GOTIFY_TITLE") + gotifyPriority := v.GetInt("GOTIFY_PRIORITY") var env config.Config @@ -55,6 +59,10 @@ func loadEnvFromSystemEnv() (*config.Config, error) { env.SMTPSubject = smtpSubject env.SMTPFrom = smtpFrom env.SMTPTo = smtpTo + env.GotifyURL = gotifyURL + env.GotifyToken = gotifyToken + env.GotifyTitle = gotifyTitle + env.GotifyPriority = gotifyPriority return &env, nil } @@ -99,6 +107,11 @@ func loadEnvFromFile(profile string) (*config.Config, error) { smtpFrom := viper.GetString(fmt.Sprintf("%s.SMTP_FROM", profile)) smtpTo := viper.GetString(fmt.Sprintf("%s.SMTP_TO", profile)) + gotifyURL := viper.GetString(fmt.Sprintf("%s.GOTIFY_URL", profile)) + gotifyToken := viper.GetString(fmt.Sprintf("%s.GOTIFY_TOKEN", profile)) + gotifyTitle := viper.GetString(fmt.Sprintf("%s.GOTIFY_TITLE", profile)) + gotifyPriority := viper.GetInt(fmt.Sprintf("%s.GOTIFY_PRIORITY", profile)) + var env config.Config env.Provider = provider env.WebHookURL = webHookURL @@ -115,6 +128,10 @@ func loadEnvFromFile(profile string) (*config.Config, error) { env.SMTPSubject = smtpSubject env.SMTPFrom = smtpFrom env.SMTPTo = smtpTo + env.GotifyURL = gotifyURL + env.GotifyToken = gotifyToken + env.GotifyTitle = gotifyTitle + env.GotifyPriority = gotifyPriority return &env, nil } diff --git a/utils/pointers.go b/utils/pointers.go new file mode 100644 index 0000000..3eb0748 --- /dev/null +++ b/utils/pointers.go @@ -0,0 +1,9 @@ +package utils + +func NewInt(value int) *int { + return &value +} + +func NewString(value string) *string { + return &value +}