Files
gochat/internal/model/account_email.go
T

134 lines
3.0 KiB
Go

package model
import (
"encoding/json"
"strconv"
"strings"
"time"
"gorm.io/datatypes"
)
const accountOutboundEmailCountKey = "_outbound_email_count"
// LimitsMap returns the Chatwoot-style account limits JSON object.
func (a *Account) LimitsMap() map[string]any {
return jsonMap(a.Limits)
}
// EmailTranscriptEnabled mirrors Account#email_transcript_enabled?. Community
// accounts allow transcripts by default; enterprise tests may disable it via
// limits or custom attributes.
func (a *Account) EmailTranscriptEnabled() bool {
if a == nil {
return true
}
for _, attrs := range []map[string]any{a.LimitsMap(), a.CustomAttributesMap()} {
if value, ok := firstPresent(attrs, "email_transcript_enabled", "email_transcript"); ok {
return truthy(value, true)
}
}
return true
}
// EmailRateLimit returns the configured daily transcript/outbound email limit.
// A zero value means unlimited, matching non-cloud Chatwoot behavior.
func (a *Account) EmailRateLimit() int {
if a == nil {
return 0
}
for _, attrs := range []map[string]any{a.LimitsMap(), a.CustomAttributesMap()} {
if value, ok := firstPresent(attrs, "emails", "email_rate_limit", "outbound_email_limit"); ok {
if n := intValue(value); n > 0 {
return n
}
}
}
return 0
}
func (a *Account) EmailsSentToday(now time.Time) int {
attrs := a.CustomAttributesMap()
value, ok := attrs[accountOutboundEmailCountKey]
if !ok {
return intValue(attrs["emails_sent_today"])
}
record, ok := value.(map[string]any)
if !ok || strings.TrimSpace(stringValue(record["date"])) != now.Format("2006-01-02") {
return 0
}
return intValue(record["count"])
}
func (a *Account) IncrementEmailSentCount(now time.Time) error {
attrs := a.CustomAttributesMap()
count := a.EmailsSentToday(now) + 1
attrs[accountOutboundEmailCountKey] = map[string]any{
"date": now.Format("2006-01-02"),
"count": count,
}
return a.SetCustomAttributesMap(attrs)
}
func jsonMap(raw datatypes.JSON) map[string]any {
attrs := map[string]any{}
if len(raw) > 0 {
_ = json.Unmarshal(raw, &attrs)
}
return attrs
}
func firstPresent(attrs map[string]any, keys ...string) (any, bool) {
for _, key := range keys {
if value, ok := attrs[key]; ok {
return value, true
}
}
return nil, false
}
func truthy(value any, defaultValue bool) bool {
switch v := value.(type) {
case bool:
return v
case string:
s := strings.TrimSpace(strings.ToLower(v))
if s == "" {
return defaultValue
}
return s == "true" || s == "1" || s == "yes" || s == "enabled"
case float64:
return v != 0
case int:
return v != 0
default:
return defaultValue
}
}
func intValue(value any) int {
switch v := value.(type) {
case int:
return v
case int64:
return int(v)
case float64:
return int(v)
case json.Number:
n, _ := v.Int64()
return int(n)
case string:
n, _ := strconv.Atoi(strings.TrimSpace(v))
return n
default:
return 0
}
}
func stringValue(value any) string {
if s, ok := value.(string); ok {
return s
}
return ""
}