mirror of
https://github.com/ivabus/pop
synced 2024-09-20 00:20:48 +03:00
feat: convert markdown to html
This commit is contained in:
parent
d6f2498dbd
commit
616601a34a
22
email.go
22
email.go
|
@ -1,12 +1,17 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/resendlabs/resend-go"
|
"github.com/resendlabs/resend-go"
|
||||||
|
"github.com/yuin/goldmark"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const TO_SEPARATOR = ","
|
||||||
|
|
||||||
// sendEmailSuccessMsg is the tea.Msg handled by Bubble Tea when the email has
|
// sendEmailSuccessMsg is the tea.Msg handled by Bubble Tea when the email has
|
||||||
// been sent successfully.
|
// been sent successfully.
|
||||||
type sendEmailSuccessMsg struct{}
|
type sendEmailSuccessMsg struct{}
|
||||||
|
@ -26,7 +31,7 @@ func (m Model) sendEmailCmd() tea.Cmd {
|
||||||
}
|
}
|
||||||
attachments[i] = string(at)
|
attachments[i] = string(at)
|
||||||
}
|
}
|
||||||
err := sendEmail(m.From.Value(), m.To.Value(), m.Subject.Value(), m.Body.Value(), attachments)
|
err := sendEmail(strings.Split(m.To.Value(), TO_SEPARATOR), m.From.Value(), m.Subject.Value(), m.Body.Value(), attachments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return sendEmailFailureMsg(err)
|
return sendEmailFailureMsg(err)
|
||||||
}
|
}
|
||||||
|
@ -34,17 +39,24 @@ func (m Model) sendEmailCmd() tea.Cmd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendEmail(from, to, subject, body string, attachments []string) error {
|
func sendEmail(to []string, from, subject, body string, attachments []string) error {
|
||||||
client := resend.NewClient(os.Getenv(RESEND_API_KEY))
|
client := resend.NewClient(os.Getenv(RESEND_API_KEY))
|
||||||
|
|
||||||
|
var html, text = bytes.NewBufferString(""), bytes.NewBufferString("")
|
||||||
|
err := goldmark.Convert([]byte(body), html)
|
||||||
|
if err != nil {
|
||||||
|
text.WriteString(body)
|
||||||
|
}
|
||||||
|
|
||||||
request := &resend.SendEmailRequest{
|
request := &resend.SendEmailRequest{
|
||||||
From: from,
|
From: from,
|
||||||
To: []string{to},
|
To: to,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
Html: body,
|
Html: html.String(),
|
||||||
|
Text: text.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := client.Emails.Send(request)
|
_, err = client.Emails.Send(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/charmbracelet/lipgloss v0.7.1
|
github.com/charmbracelet/lipgloss v0.7.1
|
||||||
github.com/resendlabs/resend-go v1.6.0
|
github.com/resendlabs/resend-go v1.6.0
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
|
github.com/yuin/goldmark v1.5.4
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -48,6 +48,8 @@ github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRM
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
|
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||||
|
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
|
50
main.go
50
main.go
|
@ -2,20 +2,52 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/resendlabs/resend-go"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const RESEND_API_KEY = "RESEND_API_KEY"
|
const RESEND_API_KEY = "RESEND_API_KEY"
|
||||||
|
const RESEND_FROM = "RESEND_FROM"
|
||||||
|
|
||||||
|
var (
|
||||||
|
from string
|
||||||
|
to []string
|
||||||
|
subject string
|
||||||
|
body string
|
||||||
|
attachments []string
|
||||||
|
)
|
||||||
|
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "email",
|
Use: "email",
|
||||||
Short: "email is a command line interface for sending emails.",
|
Short: "email is a command line interface for sending emails.",
|
||||||
Long: `email is a command line interface for sending emails.`,
|
Long: `email is a command line interface for sending emails.`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
p := tea.NewProgram(NewModel())
|
if hasStdin() {
|
||||||
|
b, err := io.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
body = string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(to) > 0 && from != "" && subject != "" && body != "" {
|
||||||
|
err := sendEmail(to, from, subject, body, attachments)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
p := tea.NewProgram(NewModel(resend.SendEmailRequest{
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
Subject: subject,
|
||||||
|
Text: body,
|
||||||
|
}))
|
||||||
_, err := p.Run()
|
_, err := p.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -24,6 +56,22 @@ var rootCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasStdin returns whether there is data in stdin.
|
||||||
|
func hasStdin() bool {
|
||||||
|
stat, err := os.Stdin.Stat()
|
||||||
|
return err == nil && (stat.Mode()&os.ModeCharDevice) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.Flags().StringSliceVar(&to, "bcc", []string{}, "Blind carbon copy recipients")
|
||||||
|
rootCmd.Flags().StringSliceVar(&to, "cc", []string{}, "Carbon copy recipients")
|
||||||
|
rootCmd.Flags().StringSliceVarP(&attachments, "attach", "a", []string{}, "Email's attachments")
|
||||||
|
rootCmd.Flags().StringSliceVarP(&to, "to", "t", []string{}, "Recipient emails")
|
||||||
|
rootCmd.Flags().StringVarP(&body, "body", "b", "", "Email's body (markdown)")
|
||||||
|
rootCmd.Flags().StringVarP(&from, "from", "f", os.Getenv(RESEND_FROM), "Email's sender ($RESEND_FROM)")
|
||||||
|
rootCmd.Flags().StringVarP(&subject, "subject", "s", "", "Email's subject")
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
key := os.Getenv(RESEND_API_KEY)
|
key := os.Getenv(RESEND_API_KEY)
|
||||||
if key == "" {
|
if key == "" {
|
||||||
|
|
10
model.go
10
model.go
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/charmbracelet/bubbles/textarea"
|
"github.com/charmbracelet/bubbles/textarea"
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/resendlabs/resend-go"
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ type Model struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModel() Model {
|
func NewModel(defaults resend.SendEmailRequest) Model {
|
||||||
from := textinput.New()
|
from := textinput.New()
|
||||||
from.Prompt = "From "
|
from.Prompt = "From "
|
||||||
from.Placeholder = "me@example.com"
|
from.Placeholder = "me@example.com"
|
||||||
|
@ -63,6 +64,7 @@ func NewModel() Model {
|
||||||
from.TextStyle = activeTextStyle
|
from.TextStyle = activeTextStyle
|
||||||
from.Cursor.Style = cursorStyle
|
from.Cursor.Style = cursorStyle
|
||||||
from.PlaceholderStyle = placeholderStyle
|
from.PlaceholderStyle = placeholderStyle
|
||||||
|
from.SetValue(defaults.From)
|
||||||
from.Focus()
|
from.Focus()
|
||||||
|
|
||||||
to := textinput.New()
|
to := textinput.New()
|
||||||
|
@ -70,14 +72,18 @@ func NewModel() Model {
|
||||||
to.PromptStyle = labelStyle.Copy()
|
to.PromptStyle = labelStyle.Copy()
|
||||||
to.Cursor.Style = cursorStyle
|
to.Cursor.Style = cursorStyle
|
||||||
to.PlaceholderStyle = placeholderStyle
|
to.PlaceholderStyle = placeholderStyle
|
||||||
|
to.TextStyle = textStyle
|
||||||
to.Placeholder = "you@example.com"
|
to.Placeholder = "you@example.com"
|
||||||
|
to.SetValue(strings.Join(defaults.To, TO_SEPARATOR))
|
||||||
|
|
||||||
subject := textinput.New()
|
subject := textinput.New()
|
||||||
subject.Prompt = "Subject "
|
subject.Prompt = "Subject "
|
||||||
subject.PromptStyle = labelStyle.Copy()
|
subject.PromptStyle = labelStyle.Copy()
|
||||||
subject.Cursor.Style = cursorStyle
|
subject.Cursor.Style = cursorStyle
|
||||||
subject.PlaceholderStyle = placeholderStyle
|
subject.PlaceholderStyle = placeholderStyle
|
||||||
|
subject.TextStyle = textStyle
|
||||||
subject.Placeholder = "Hello!"
|
subject.Placeholder = "Hello!"
|
||||||
|
subject.SetValue(defaults.Subject)
|
||||||
|
|
||||||
body := textarea.New()
|
body := textarea.New()
|
||||||
body.Placeholder = "# Email"
|
body.Placeholder = "# Email"
|
||||||
|
@ -91,6 +97,8 @@ func NewModel() Model {
|
||||||
body.BlurredStyle.Text = textStyle
|
body.BlurredStyle.Text = textStyle
|
||||||
body.BlurredStyle.Placeholder = placeholderStyle
|
body.BlurredStyle.Placeholder = placeholderStyle
|
||||||
body.Cursor.Style = cursorStyle
|
body.Cursor.Style = cursorStyle
|
||||||
|
body.CharLimit = 4000
|
||||||
|
body.SetValue(defaults.Text)
|
||||||
body.Blur()
|
body.Blur()
|
||||||
|
|
||||||
attachments := list.New([]list.Item{}, attachmentDelegate{}, 0, 3)
|
attachments := list.New([]list.Item{}, attachmentDelegate{}, 0, 3)
|
||||||
|
|
Loading…
Reference in a new issue