gogcli/internal/cmd/gmail_sendas.go
2025-12-31 19:47:32 +01:00

298 lines
7.6 KiB
Go

package cmd
import (
"context"
"errors"
"fmt"
"os"
"strings"
"text/tabwriter"
"github.com/alecthomas/kong"
"google.golang.org/api/gmail/v1"
"github.com/steipete/gogcli/internal/outfmt"
"github.com/steipete/gogcli/internal/ui"
)
type GmailSendAsCmd struct {
List GmailSendAsListCmd `cmd:"" name:"list" help:"List send-as aliases"`
Get GmailSendAsGetCmd `cmd:"" name:"get" help:"Get details of a send-as alias"`
Create GmailSendAsCreateCmd `cmd:"" name:"create" help:"Create a new send-as alias"`
Verify GmailSendAsVerifyCmd `cmd:"" name:"verify" help:"Resend verification email for a send-as alias"`
Delete GmailSendAsDeleteCmd `cmd:"" name:"delete" help:"Delete a send-as alias"`
Update GmailSendAsUpdateCmd `cmd:"" name:"update" help:"Update a send-as alias"`
}
type GmailSendAsListCmd struct{}
const sendAsYes = "yes"
func (c *GmailSendAsListCmd) Run(ctx context.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
account, err := requireAccount(flags)
if err != nil {
return err
}
svc, err := newGmailService(ctx, account)
if err != nil {
return err
}
resp, err := svc.Users.Settings.SendAs.List("me").Do()
if err != nil {
return err
}
if outfmt.IsJSON(ctx) {
return outfmt.WriteJSON(os.Stdout, map[string]any{"sendAs": resp.SendAs})
}
if len(resp.SendAs) == 0 {
u.Err().Println("No send-as aliases")
return nil
}
tw := tabwriter.NewWriter(os.Stdout, 0, 4, 2, ' ', 0)
fmt.Fprintln(tw, "EMAIL\tDISPLAY NAME\tDEFAULT\tVERIFIED\tTREAT AS ALIAS")
for _, sa := range resp.SendAs {
isDefault := ""
if sa.IsDefault {
isDefault = sendAsYes
}
verified := "pending"
if sa.VerificationStatus == gmailVerificationAccepted {
verified = sendAsYes
}
treatAsAlias := ""
if sa.TreatAsAlias {
treatAsAlias = sendAsYes
}
fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\n",
sa.SendAsEmail, sa.DisplayName, isDefault, verified, treatAsAlias)
}
_ = tw.Flush()
return nil
}
type GmailSendAsGetCmd struct {
Email string `arg:"" name:"email" help:"Send-as email"`
}
func (c *GmailSendAsGetCmd) Run(ctx context.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
account, err := requireAccount(flags)
if err != nil {
return err
}
sendAsEmail := strings.TrimSpace(c.Email)
if sendAsEmail == "" {
return errors.New("email is required")
}
svc, err := newGmailService(ctx, account)
if err != nil {
return err
}
sa, err := svc.Users.Settings.SendAs.Get("me", sendAsEmail).Do()
if err != nil {
return err
}
if outfmt.IsJSON(ctx) {
return outfmt.WriteJSON(os.Stdout, map[string]any{"sendAs": sa})
}
u.Out().Printf("send_as_email\t%s", sa.SendAsEmail)
u.Out().Printf("display_name\t%s", sa.DisplayName)
u.Out().Printf("reply_to\t%s", sa.ReplyToAddress)
u.Out().Printf("signature\t%s", sa.Signature)
u.Out().Printf("is_primary\t%t", sa.IsPrimary)
u.Out().Printf("is_default\t%t", sa.IsDefault)
u.Out().Printf("treat_as_alias\t%t", sa.TreatAsAlias)
u.Out().Printf("verification_status\t%s", sa.VerificationStatus)
return nil
}
type GmailSendAsCreateCmd struct {
Email string `arg:"" name:"email" help:"Send-as email"`
DisplayName string `name:"display-name" help:"Name that appears in the From field"`
ReplyTo string `name:"reply-to" help:"Reply-to address (optional)"`
Signature string `name:"signature" help:"HTML signature for emails sent from this alias"`
TreatAsAlias bool `name:"treat-as-alias" help:"Treat as alias (replies sent from Gmail web)" default:"true"`
}
func (c *GmailSendAsCreateCmd) Run(ctx context.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
account, err := requireAccount(flags)
if err != nil {
return err
}
sendAsEmail := strings.TrimSpace(c.Email)
if sendAsEmail == "" {
return errors.New("email is required")
}
svc, err := newGmailService(ctx, account)
if err != nil {
return err
}
sendAs := &gmail.SendAs{
SendAsEmail: sendAsEmail,
DisplayName: c.DisplayName,
ReplyToAddress: c.ReplyTo,
Signature: c.Signature,
TreatAsAlias: c.TreatAsAlias,
}
created, err := svc.Users.Settings.SendAs.Create("me", sendAs).Do()
if err != nil {
return err
}
if outfmt.IsJSON(ctx) {
return outfmt.WriteJSON(os.Stdout, map[string]any{"sendAs": created})
}
u.Out().Printf("send_as_email\t%s", created.SendAsEmail)
u.Out().Printf("verification_status\t%s", created.VerificationStatus)
u.Err().Println("Verification email sent. Check your inbox to complete setup.")
return nil
}
type GmailSendAsVerifyCmd struct {
Email string `arg:"" name:"email" help:"Send-as email"`
}
func (c *GmailSendAsVerifyCmd) Run(ctx context.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
account, err := requireAccount(flags)
if err != nil {
return err
}
sendAsEmail := strings.TrimSpace(c.Email)
if sendAsEmail == "" {
return errors.New("email is required")
}
svc, err := newGmailService(ctx, account)
if err != nil {
return err
}
err = svc.Users.Settings.SendAs.Verify("me", sendAsEmail).Do()
if err != nil {
return err
}
if outfmt.IsJSON(ctx) {
return outfmt.WriteJSON(os.Stdout, map[string]any{
"email": sendAsEmail,
"message": "Verification email sent",
})
}
u.Out().Printf("Verification email sent to %s", sendAsEmail)
return nil
}
type GmailSendAsDeleteCmd struct {
Email string `arg:"" name:"email" help:"Send-as email"`
}
func (c *GmailSendAsDeleteCmd) Run(ctx context.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
account, err := requireAccount(flags)
if err != nil {
return err
}
sendAsEmail := strings.TrimSpace(c.Email)
if sendAsEmail == "" {
return errors.New("email is required")
}
svc, err := newGmailService(ctx, account)
if err != nil {
return err
}
err = svc.Users.Settings.SendAs.Delete("me", sendAsEmail).Do()
if err != nil {
return err
}
if outfmt.IsJSON(ctx) {
return outfmt.WriteJSON(os.Stdout, map[string]any{
"email": sendAsEmail,
"deleted": true,
})
}
u.Out().Printf("Deleted send-as alias: %s", sendAsEmail)
return nil
}
type GmailSendAsUpdateCmd struct {
Email string `arg:"" name:"email" help:"Send-as email"`
DisplayName string `name:"display-name" help:"Name that appears in the From field"`
ReplyTo string `name:"reply-to" help:"Reply-to address"`
Signature string `name:"signature" help:"HTML signature"`
TreatAsAlias bool `name:"treat-as-alias" help:"Treat as alias" default:"true"`
MakeDefault bool `name:"make-default" help:"Make this the default send-as address"`
}
func (c *GmailSendAsUpdateCmd) Run(ctx context.Context, kctx *kong.Context, flags *RootFlags) error {
u := ui.FromContext(ctx)
account, err := requireAccount(flags)
if err != nil {
return err
}
sendAsEmail := strings.TrimSpace(c.Email)
if sendAsEmail == "" {
return errors.New("email is required")
}
svc, err := newGmailService(ctx, account)
if err != nil {
return err
}
// Get current settings first
current, err := svc.Users.Settings.SendAs.Get("me", sendAsEmail).Do()
if err != nil {
return err
}
// Update only provided fields
if flagProvided(kctx, "display-name") {
current.DisplayName = c.DisplayName
}
if flagProvided(kctx, "reply-to") {
current.ReplyToAddress = c.ReplyTo
}
if flagProvided(kctx, "signature") {
current.Signature = c.Signature
}
if flagProvided(kctx, "treat-as-alias") {
current.TreatAsAlias = c.TreatAsAlias
}
if flagProvided(kctx, "make-default") {
current.IsDefault = c.MakeDefault
}
updated, err := svc.Users.Settings.SendAs.Update("me", sendAsEmail, current).Do()
if err != nil {
return err
}
if outfmt.IsJSON(ctx) {
return outfmt.WriteJSON(os.Stdout, map[string]any{"sendAs": updated})
}
u.Out().Printf("Updated send-as alias: %s", updated.SendAsEmail)
return nil
}