mirror of
https://github.com/la5nta/pat
synced 2026-08-07 22:32:06 -04:00
This change refactors the command line message composer to unify the various composition methods (interactive, non-interactive, reply, forward, and template-based) into a single function. The detection of non-interactive sessions was also changed to use a more reliable check on `os.Stdin` instead of inspecting arguments. A message preview and confirmation prompt has been added to the interactive mode, allowing the user to review or edit the message before it's posted to the outbox.
176 lines
4.1 KiB
Go
176 lines
4.1 KiB
Go
// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
|
|
// Use of this source code is governed by the MIT-license that can be
|
|
// found in the LICENSE file.
|
|
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/bndr/gotabulate"
|
|
|
|
"github.com/la5nta/pat/app"
|
|
"github.com/la5nta/wl2k-go/fbb"
|
|
"github.com/la5nta/wl2k-go/mailbox"
|
|
)
|
|
|
|
var mailboxes = []string{"in", "out", "sent", "archive"}
|
|
|
|
func ReadHandle(ctx context.Context, app *app.App, _ []string) {
|
|
cancel := exitOnContextCancellation(ctx)
|
|
defer cancel()
|
|
|
|
w := os.Stdout
|
|
|
|
for {
|
|
// Query user for mailbox to list
|
|
printMailboxes(w)
|
|
fmt.Fprintf(w, "\nChoose mailbox [n]: ")
|
|
mailboxIdx, ok := readInt()
|
|
if !ok {
|
|
break
|
|
} else if mailboxIdx+1 > len(mailboxes) {
|
|
fmt.Fprintln(w, "Invalid mailbox number")
|
|
continue
|
|
}
|
|
|
|
for {
|
|
// Fetch messages
|
|
msgs, err := mailbox.LoadMessageDir(filepath.Join(app.Mailbox().MBoxPath, mailboxes[mailboxIdx]))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
} else if len(msgs) == 0 {
|
|
fmt.Fprintf(w, "(empty)\n")
|
|
break
|
|
}
|
|
|
|
// Print messages (sorted by date)
|
|
sort.Sort(fbb.ByDate(msgs))
|
|
printMessages(w, msgs)
|
|
|
|
// Query user for message to print
|
|
fmt.Fprintf(w, "Choose message [n]: ")
|
|
msgIdx, ok := readInt()
|
|
if !ok {
|
|
break
|
|
} else if msgIdx+1 > len(msgs) {
|
|
fmt.Fprintf(w, "invalid message number\n")
|
|
continue
|
|
}
|
|
printMsg(w, msgs[msgIdx])
|
|
|
|
// Mark as read?
|
|
if mailbox.IsUnread(msgs[msgIdx]) {
|
|
fmt.Fprintf(w, "Mark as read? [Y/n]: ")
|
|
ans := readLine()
|
|
if ans == "" || strings.EqualFold(ans, "y") {
|
|
mailbox.SetUnread(msgs[msgIdx], false)
|
|
}
|
|
}
|
|
|
|
L:
|
|
for {
|
|
fmt.Fprintf(w, "Action [C,r,ra,f,e,d,q,?]: ")
|
|
switch ans := readLine(); ans {
|
|
case "C", "c", "":
|
|
break L
|
|
case "d":
|
|
fmt.Fprint(w, "Delete message? [y/N]: ")
|
|
if ans := readLine(); strings.EqualFold(ans, "y") {
|
|
msg := msgs[msgIdx]
|
|
mbox := mailboxes[mailboxIdx]
|
|
path := filepath.Join(app.Mailbox().MBoxPath, mbox, msg.MID()+mailbox.Ext)
|
|
if err := os.Remove(path); err != nil {
|
|
log.Printf("Failed to delete message %s from %s: %v", msg.MID(), mbox, err)
|
|
} else {
|
|
fmt.Fprintln(w, "Message deleted.")
|
|
}
|
|
break L
|
|
}
|
|
case "r":
|
|
composeMessage(app, composerFlags{from: app.Options().MyCall, inReplyTo: msgs[msgIdx].MID()}, true)
|
|
case "ra":
|
|
composeMessage(app, composerFlags{from: app.Options().MyCall, inReplyTo: msgs[msgIdx].MID(), replyAll: true}, true)
|
|
case "f":
|
|
composeMessage(app, composerFlags{from: app.Options().MyCall, forward: msgs[msgIdx].MID()}, true)
|
|
case "e":
|
|
ExtractMessageHandle(ctx, app, []string{msgs[msgIdx].MID()})
|
|
case "q":
|
|
return
|
|
case "?":
|
|
fallthrough
|
|
default:
|
|
fmt.Fprintln(w, "c - continue")
|
|
fmt.Fprintln(w, "r - reply")
|
|
fmt.Fprintln(w, "ra - reply all")
|
|
fmt.Fprintln(w, "f - forward")
|
|
fmt.Fprintln(w, "e - extract (attachments)")
|
|
fmt.Fprintln(w, "d - delete")
|
|
fmt.Fprintln(w, "q - quit")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func readInt() (int, bool) {
|
|
str := readLine()
|
|
if str == "" {
|
|
return 0, false
|
|
}
|
|
i, _ := strconv.Atoi(str)
|
|
return i, true
|
|
}
|
|
|
|
func printMsg(w io.Writer, msg *fbb.Message) {
|
|
fmt.Fprintf(w, "========================================\n")
|
|
fmt.Fprintln(w, msg)
|
|
fmt.Fprintf(w, "========================================\n\n")
|
|
}
|
|
|
|
func printMailboxes(w io.Writer) {
|
|
for i, mbox := range mailboxes {
|
|
fmt.Fprintf(w, "%d:%s\t", i, mbox)
|
|
}
|
|
}
|
|
|
|
func printMessages(w io.Writer, msgs []*fbb.Message) {
|
|
rows := make([][]string, len(msgs))
|
|
for i, msg := range msgs {
|
|
var to string
|
|
if len(msg.To()) > 0 {
|
|
to = msg.To()[0].Addr
|
|
}
|
|
if len(msg.To()) > 1 {
|
|
to += ", ..."
|
|
}
|
|
|
|
var flags string
|
|
if mailbox.IsUnread(msg) {
|
|
flags += "N" // New
|
|
}
|
|
|
|
rows[i] = []string{
|
|
fmt.Sprintf("%2d", i),
|
|
flags,
|
|
msg.Subject(),
|
|
msg.From().Addr,
|
|
msg.Date().String(),
|
|
to,
|
|
}
|
|
}
|
|
t := gotabulate.Create(rows)
|
|
t.SetHeaders([]string{"i", "Flags", "Subject", "From", "Date", "To"})
|
|
t.SetAlign("left")
|
|
t.SetWrapStrings(true)
|
|
t.SetMaxCellSize(60)
|
|
fmt.Fprintln(w, t.Render("simple"))
|
|
}
|