Adds a cron-driven CLI command that emails the GM a player attendance summary at noon on the session date, skipping automatically if the game was canceled or today isn't game day, and tracking last-sent date so it won't double-send. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
106 lines
2.3 KiB
Go
106 lines
2.3 KiB
Go
package data
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
DATE_FORMAT string = "Monday Jan 2, 2006 3:04pm"
|
|
)
|
|
|
|
type Player struct {
|
|
Name string `json:"name"`
|
|
Attending string `json:"attending"`
|
|
ToggleUrl string `json:"-"`
|
|
Email string `json:"email"`
|
|
Gm bool `json:"gm"`
|
|
}
|
|
|
|
type Session struct {
|
|
Date string `json:"date"`
|
|
Players []Player `json:"players"`
|
|
IncrementDays int `json:"inc_days"`
|
|
Status string `json:"status"`
|
|
Password string `json:"password"`
|
|
LastNoticeSentDate string `json:"last_notice_sent_date,omitempty"`
|
|
}
|
|
|
|
func (s *Session) GMEmail() string {
|
|
for _, p := range s.Players {
|
|
if p.Gm {
|
|
return p.Email
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (s *Session) FormatStatusUpdate(playerName, newStatus, source string) (string, string) {
|
|
subject := fmt.Sprintf("Player Status Update: %s → %s", playerName, newStatus)
|
|
|
|
var yes, no, unknown int
|
|
for _, p := range s.Players {
|
|
switch p.Attending {
|
|
case "yes":
|
|
yes++
|
|
case "no":
|
|
no++
|
|
default:
|
|
unknown++
|
|
}
|
|
}
|
|
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "%s has updated their status to %q (via %s).\n\n", playerName, newStatus, source)
|
|
fmt.Fprintf(&sb, "Attending: %d | Not attending: %d | No response: %d\n\n", yes, no, unknown)
|
|
sb.WriteString("Current player status:\n")
|
|
for _, p := range s.Players {
|
|
role := ""
|
|
if p.Gm {
|
|
role = " (GM)"
|
|
}
|
|
fmt.Fprintf(&sb, " %s%s: %s\n", p.Name, role, p.Attending)
|
|
}
|
|
|
|
return subject, sb.String()
|
|
}
|
|
|
|
func (s *Session) FormatGameDayNotice() (string, string) {
|
|
subject := fmt.Sprintf("Game Day Status Check: %s", s.Date)
|
|
|
|
var yes, no, unknown int
|
|
for _, p := range s.Players {
|
|
switch p.Attending {
|
|
case "yes":
|
|
yes++
|
|
case "no":
|
|
no++
|
|
default:
|
|
unknown++
|
|
}
|
|
}
|
|
|
|
var sb strings.Builder
|
|
fmt.Fprintf(&sb, "Today is game day (%s). Here is the current player status.\n\n", s.Date)
|
|
fmt.Fprintf(&sb, "Attending: %d | Not attending: %d | No response: %d\n\n", yes, no, unknown)
|
|
sb.WriteString("Current player status:\n")
|
|
for _, p := range s.Players {
|
|
role := ""
|
|
if p.Gm {
|
|
role = " (GM)"
|
|
}
|
|
fmt.Fprintf(&sb, " %s%s: %s\n", p.Name, role, p.Attending)
|
|
}
|
|
|
|
return subject, sb.String()
|
|
}
|
|
|
|
func (s *Session) GetPlayerByEmail(addr string) *Player {
|
|
for i, p := range s.Players {
|
|
if addr == p.Email {
|
|
return &s.Players[i]
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|