From 74bfcc461c975ceeba1382ccefd2405d0479bf86 Mon Sep 17 00:00:00 2001
From: Kevin Lyda <kevin@lyda.ie>
Date: Wed, 21 May 2025 11:58:55 +0100
Subject: [PATCH] Lunch hacking

Incomplete - but updated notes and started implementing CHANGE.
---
 NOTES.md         | 13 ++++++-
 repl/messages.go | 98 +++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 107 insertions(+), 4 deletions(-)

diff --git a/NOTES.md b/NOTES.md
index f6ce8a1..cf4dc6f 100644
--- a/NOTES.md
+++ b/NOTES.md
@@ -21,7 +21,7 @@ Switch between MAIL and BULLETIN modes? MAIL commands are documented
 ## Things to do
 
   * Run [godoc](http://localhost:6060/) and then review where the help text is lacking.
-  * Missing [RESPOND] [MAIL] [SET PROMPT_EXPIRE] [SET NOREADNEW] [SET NOSHOWNEW] [SET EXPIRE_LIMIT] [SET NOPROMPT_EXPIRE] [SET READNEW] [SET SHOWNEW] [SET DEFAULT_EXPIRE] [SHOW NEW]
+  * Missing [RESPOND] [MAIL] [SET PROMPT_EXPIRE] [SET NOREADNEW] [SET NOSHOWNEW] [SET NOPROMPT_EXPIRE] [SET READNEW] [SET SHOWNEW] [SHOW NEW]
   * Run this.Skew.Safe() before... each command?  each write?
   * Handle broadcast messages - create a broadcast table and add an expiration column.
   * Database
@@ -31,8 +31,17 @@ Switch between MAIL and BULLETIN modes? MAIL commands are documented
     * Commands to connect to Mattermost or mastodon?
   * Make a spreadsheet for signups.
   * Pager:
-    * Make / work for search.
+    * Make "/" work for search.
   * Run [VACUUM](https://www.sqlite.org/lang_vacuum.html) on the expire run.
+  * Notifications:
+    * Figure out how SHOWNEW, NOTIFY, READNEW and BRIEF work.
+  * Permissions:
+    * Review permission requirements for each command.
+  * Expire.  This was created due to file storage limits.
+  * Code cleanup:
+    * Review sql queries and clean out the ones not used.
+    * Review sql queries and find duplicates.
+    * Use [dupl](https://github.com/mibk/dupl) to find things to generalise.
 
 Polishing.  Review each command and put a + next to each as it is
 fully done.
diff --git a/repl/messages.go b/repl/messages.go
index a0ffe01..1a7dde0 100644
--- a/repl/messages.go
+++ b/repl/messages.go
@@ -190,9 +190,103 @@ func ActionBack(_ *dclish.Command) error {
 	return nil
 }
 
-// ActionChange handles the `CHANGE` command.
+// ActionChange handles the `CHANGE` command.  This replaces or modifies
+// an existing message.
 func ActionChange(cmd *dclish.Command) error {
-	fmt.Printf("TODO: unimplemented...\n%s\n", cmd.Description)
+	var err error
+	optAll := false
+	if cmd.Flags["/ALL"].Value == "true" {
+		optAll = true
+	}
+
+	optExpiration := &time.Time{}
+	if cmd.Flags["/EXPIRATION"].Value != "" {
+		// dd-mmm-yyyy, or delta time: dddd
+		exp, err := time.Parse("2006-01-02", cmd.Flags["/EXPIRATION"].Value)
+		if err != nil {
+			days, err := strconv.Atoi(cmd.Flags["/EXPIRATION"].Value)
+			if err != nil {
+				optExpiration = nil
+			}
+			exp := time.Now().AddDate(0, 0, days)
+			optExpiration = &exp
+		} else {
+			optExpiration = &exp
+		}
+	}
+
+	optGeneral := false
+	if cmd.Flags["/GENERAL"].Set && this.Folder.Name != "GENERAL" {
+		return errors.New("Can only be used in the GENERAL folder")
+	}
+	if cmd.Flags["/GENERAL"].Value == "true" {
+		optGeneral = true
+	}
+	if cmd.Flags["/GENERAL"].Set && !optGeneral {
+		return errors.New("Can't specify /NOGENERAL - see /SYSTEM")
+	}
+
+	optNew := false
+	if cmd.Flags["/NEW"].Value == "true" {
+		optNew = true
+	}
+
+	optNumber := []int64{this.MsgID}
+	if cmd.Flags["/NUMBER"].Set && cmd.Flags["/NUMBER"].Value == "" {
+		return errors.New("Must supply message number(s) if /NUMBER is set")
+	}
+	if cmd.Flags["/NUMBER"].Value != "" {
+		optNumber, err = ParseNumberList(cmd.Flags["/NUMBER"].Value)
+		if err != nil {
+			return err
+		}
+	}
+
+	optPermanent := false
+	if cmd.Flags["/PERMANENT"].Value == "true" {
+		optPermanent = true
+	}
+
+	optShutdown := false
+	if cmd.Flags["/SHUTDOWN"].Value == "true" {
+		optShutdown = true
+	}
+
+	optSubject := ""
+	if cmd.Flags["/SUBJECT"].Set && cmd.Flags["/SUBJECT"].Value == "" {
+		return errors.New("Must supply subject text if /SUBJECT is set")
+	}
+	if cmd.Flags["/SUBJECT"].Value != "" {
+		optSubject = cmd.Flags["/SUBJECT"].Value
+	}
+
+	optSystem := false
+	if cmd.Flags["/SYSTEM"].Set {
+		if this.User.Admin == 0 {
+			return errors.New("Must be admin")
+		}
+		if this.Folder.Name != "GENERAL" {
+			return errors.New("Can only be used in the GENERAL folder")
+		}
+	}
+	if cmd.Flags["/SYSTEM"].Value == "true" {
+		optSystem = true
+	}
+	if cmd.Flags["/SYSTEM"].Set && !optSystem {
+		return errors.New("Can't specify /NOSYSTEM - see /GENERAL")
+	}
+
+	optText := false
+	if cmd.Flags["/TEXT"].Value == "true" {
+		optText = true
+	}
+
+	// Sanity checking.
+	if optSystem && optGeneral {
+		return errors.New("Can't specify /SYSTEM and /GENERAL")
+	}
+
+	fmt.Println("TODO: flags parsed, now need to do something.")
 	return nil
 }
 
-- 
GitLab