feat(conversations): align transcript delivery
This commit is contained in:
@@ -20,6 +20,7 @@ type Account struct {
|
||||
CustomAttributes datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"custom_attributes,omitempty"`
|
||||
FeatureFlags string `gorm:"type:text" json:"feature_flags,omitempty"` // JSON-encoded feature flags
|
||||
AutoResolveDuration int `gorm:"default:0" json:"auto_resolve_duration,omitempty"` // days
|
||||
Limits datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"limits,omitempty"` // Chatwoot account limits, e.g. emails
|
||||
AgentLimit int `gorm:"default:0" json:"agent_limit,omitempty"` // max agents allowed (0 = unlimited), Chatwoot usage_limits[:agents]
|
||||
InboxLimit int `gorm:"default:0" json:"inbox_limit,omitempty"` // max inboxes allowed (0 = unlimited), Chatwoot usage_limits[:inboxes]
|
||||
CaptainModels datatypes.JSON `gorm:"type:jsonb;default:'{}'" json:"captain_models,omitempty"`
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
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 ""
|
||||
}
|
||||
Reference in New Issue
Block a user