Documentation
¶
Index ¶
- Constants
- Variables
- func BuildCustomFieldsJSON(fields map[string]string) string
- func BuildDiscordWebhookPayload(event DiscordNotificationEvent) discordWebhookPayload
- func BuildRFCMessage(p EmailParams) string
- func BuildRawMessage(p EmailParams) string
- func CampaignStateTransition(db *sql.DB, name, action, fromStatus, toStatus string) error
- func CheckGWSInstalled() error
- func ConfigPath() string
- func DBPath() string
- func DataDir() string
- func DatabaseDisplayTarget() string
- func DeleteCampaign(db *sql.DB, name string) (int64, error)
- func EnsureDataDir() error
- func EnsureDiscordNotifyCursor(db *sql.DB) error
- func ExtractDomain(email string) string
- func ExtractPlaceholders(s string) []string
- func FormatSendDays(s string) string
- func FormatTickResult(r *TickResult) string
- func GWSAuthLogin(configDir string) error
- func GWSConfigDirForAccount(email string) string
- func GenerateRFCMessageID(fromEmail string) string
- func GetLastPollAt(db *sql.DB) time.Time
- func IsUnsubscribeRequest(subject, snippet string) bool
- func LoadEnvFile(path string) error
- func NormalizeWorkspaceID(workspaceID string) string
- func OpenDB(path string) (*sql.DB, error)
- func ParseSendDays(s string) ([]time.Weekday, error)
- func PrepareFollowUp(p *EmailParams, parentMessageID, threadID, originalSubject string)
- func ProcessBounces(db *sql.DB, gws GWSClient, accounts []Account) (int, error)
- func ProcessDiscordNotifications(ctx context.Context, db *sql.DB, notifier DiscordNotifier, ...) (int, error)
- func ProcessDiscordOperationalNotifications(ctx context.Context, db *sql.DB, notifier DiscordNotifier, ...) (int, error)
- func ProcessIMAPBounces(db *sql.DB, imap IMAPMessageLister, accounts []Account) (int, error)
- func ProcessIMAPReplies(db *sql.DB, imap IMAPMessageLister, accounts []Account) (replies int, unsubscribes int, err error)
- func ProcessReplies(db *sql.DB, gws GWSClient, accounts []Account) (replies int, unsubscribes int, err error)
- func RebalancePendingSchedules(db *sql.DB, accountIDs []int64) error
- func RenderTemplate(tmpl string, fields map[string]string) string
- func ResolveAlias(name string) string
- func ResolveCampaignName(db *sql.DB, nameOrID string) (string, error)
- func ResolveCampaignNameInWorkspace(db *sql.DB, workspaceID, nameOrID string) (string, error)
- func ResolveLeadScheduleTimezone(fields map[string]string, defaultTZ *time.Location) (*time.Location, error)
- func ResolveSecretRef(ref string) (string, error)
- func SetLastPollAt(db *sql.DB, t time.Time)
- func StripUnresolved(s string) (string, []string)
- func SuggestField(name string, available []string) string
- func UpdateAccount(db *sql.DB, email string, opts UpdateAccountOpts) error
- func UpdateCampaign(db *sql.DB, name string, opts UpdateCampaignOpts) error
- func ValidateLeadFields(leads []LeadRecord, placeholders []string) ([]string, error)
- func ValidateLeadScheduleOverrides(leads []LeadRecord) error
- func ValidateSecretRef(ref string) error
- func WorkspaceIDFromEnv() string
- func WriteDefaultConfig(path string) error
- type Account
- type AccountVerifyResult
- type AddAccountResult
- type AddLeadsResult
- type AddSMTPIMAPAccountOpts
- type AddSMTPIMAPAccountResult
- type BackfillEmailMessagesConfig
- type BackfillEmailMessagesResult
- type BlacklistResult
- type Campaign
- type CampaignLead
- type CampaignListRow
- type CampaignStats
- type CampaignStatusInfo
- type CloneCampaignOpts
- type Config
- type CreateCampaignOpts
- type CreateCampaignResult
- type CreateDraftCampaignOpts
- type DailyLimitWarning
- type Dialect
- type DiscordNotificationEvent
- type DiscordNotifier
- type DiscordNotifyOptions
- type DiscordOperationalNotifyOptions
- type DiscordWebhookNotifier
- type DomainCheck
- type DomainDiagnostic
- type EmailMessage
- type EmailParams
- type EmailValidationPolicy
- type EnvSecretResolver
- type Event
- type EventLogRow
- type FailureReason
- type GWSCLI
- func (g *GWSCLI) GetMessage(account, msgID string) (*GWSMessage, error)
- func (g *GWSCLI) GetThreadMessages(account, threadID string) ([]GWSMessage, error)
- func (g *GWSCLI) ListMessages(account, query string, includeSpamTrash ...bool) ([]GWSMessage, error)
- func (g *GWSCLI) SendEmail(account, to, rawMsg, threadID string) (string, string, error)
- func (g *GWSCLI) SetConfigDir(account, configDir string)
- type GWSClient
- type GWSMessage
- type IMAPAccountVerifier
- type IMAPMessageLister
- type IMAPTransport
- type Lead
- type LeadEmailValidationResult
- type LeadEmailValidationRow
- type LeadForSchedule
- type LeadListRow
- type LeadRecord
- type LeadStatsRow
- type ListAccountsRow
- type ListEmailThreadMessagesOpts
- type PauseAccountResult
- type PauseLeadResult
- type PreviewRow
- type RecipientVerificationResult
- type RecipientVerifier
- type RemoveLeadResult
- type RenderedEmail
- type ResumeAccountResult
- type ResumeLeadResult
- type RetryCampaignResult
- type SMTPAccountVerifier
- type SMTPEmailSender
- type SMTPIMAPAccountSecrets
- type SMTPRecipientVerifier
- type SMTPTransport
- type ScheduleConfig
- type ScheduledSend
- type ScheduledSendRow
- type SecretResolver
- type SecretResolverFunc
- type SendInboxReplyConfig
- type SendInboxReplyResult
- type SendNowResult
- type SendSMTPTestEmailOpts
- type SendSMTPTestEmailResult
- type Sequence
- type SequenceDefaults
- type SequenceStep
- type SequenceVariant
- type StepStats
- type Store
- func (s *Store) AcquireTickLock(ctx context.Context) (TickLock, error)
- func (s *Store) Begin() (*Tx, error)
- func (s *Store) Close() error
- func (s *Store) DisplayTarget() string
- func (s *Store) Exec(query string, args ...any) (sql.Result, error)
- func (s *Store) Query(query string, args ...any) (*sql.Rows, error)
- func (s *Store) QueryRow(query string, args ...any) *sql.Row
- func (s *Store) Rebind(query string) string
- type TickConfig
- type TickLock
- type TickResult
- type Tx
- type UpdateAccountOpts
- type UpdateCampaignOpts
- type UpdateSMTPIMAPAccountOpts
- type VariantStats
Constants ¶
const ( DiscordEventCampaignCompleted = "campaign_completed" DiscordEventCampaignCompletedWithFailures = "campaign_completed_with_failures" DiscordEventSenderIdle = "sender_idle" )
const ( EmailValidationPass = "pass" EmailValidationManualReview = "manual_review" EmailValidationFail = "fail" RecipientStatusVerified = "verified" RecipientStatusRejected = "rejected" RecipientStatusCatchAll = "catch_all" RecipientStatusUnknown = "unknown" RecipientStatusNoMX = "no_mx" RecipientStatusFreeMail = "free_email" )
const ( AccountProviderGWS = "gws" AccountProviderSMTPIMAP = "smtp_imap" CampaignStatusCompletedWithFailures = "completed_with_failures" )
const ( EmailMessageDirectionOutbound = "outbound" EmailMessageDirectionInbound = "inbound" EmailMessageTypeSent = "sent" EmailMessageTypeReply = "reply" EmailMessageTypeBounce = "bounce" EmailMessageTypeAutoReply = "auto_reply" EmailMessageTypeUnsubscribe = "unsubscribe" EmailMessageTypeManualReply = "manual_reply" )
const ( DefaultWorkspaceID = "default" WorkspaceEnvVar = "COLD_CLI_WORKSPACE_ID" )
const ScheduleTimezoneField = "schedule_timezone"
Variables ¶
var BuiltinFields = []string{"email", "first_name", "last_name", "company", "domain"}
BuiltinFields are fields always available for template rendering from the leads table.
var DefaultConfig = Config{
DefaultTimezone: "America/New_York",
DefaultDailyLimit: 50,
MinGapSeconds: 90,
MaxGapSeconds: 140,
SendWindowStart: "09:00",
SendWindowEnd: "17:00",
SendDays: "1,2,3,4,5",
UnsubscribeSubject: "Unsubscribe",
}
Functions ¶
func BuildCustomFieldsJSON ¶
BuildCustomFieldsJSON extracts non-builtin fields from a LeadRecord and returns them as JSON.
func BuildDiscordWebhookPayload ¶
func BuildDiscordWebhookPayload(event DiscordNotificationEvent) discordWebhookPayload
func BuildRFCMessage ¶
func BuildRFCMessage(p EmailParams) string
BuildRFCMessage constructs an RFC 2822 message.
func BuildRawMessage ¶
func BuildRawMessage(p EmailParams) string
BuildRawMessage constructs an RFC 2822 message and returns it as a base64url-encoded string.
func CampaignStateTransition ¶
CampaignStateTransition changes a campaign's status with validation.
func CheckGWSInstalled ¶
func CheckGWSInstalled() error
CheckGWSInstalled verifies gws binary is available on PATH.
func ConfigPath ¶
func ConfigPath() string
func DataDir ¶
func DataDir() string
DataDir returns the cold-cli data directory path. Respects COLD_CLI_DATA_DIR env var for testing.
func DatabaseDisplayTarget ¶
func DatabaseDisplayTarget() string
DatabaseDisplayTarget returns a user-facing database target description.
func DeleteCampaign ¶
DeleteCampaign deletes a campaign and all associated data.
func EnsureDataDir ¶
func EnsureDataDir() error
EnsureDataDir creates the data directory if it doesn't exist.
func EnsureDiscordNotifyCursor ¶
EnsureDiscordNotifyCursor initializes the Discord cursor to the current event high-water mark. Tick calls this before polling so first deploys do not alert on old historical replies, while replies found during that tick still notify.
func ExtractDomain ¶
ExtractDomain returns the domain part of an email address.
func ExtractPlaceholders ¶
ExtractPlaceholders returns all unique {{placeholder}} names from a string.
func FormatSendDays ¶
FormatSendDays converts "1,2,3,4,5" to "Mon-Fri" or similar human-readable format.
func FormatTickResult ¶
func FormatTickResult(r *TickResult) string
FormatTickResult returns a human-readable summary of a tick result.
func GWSAuthLogin ¶
GWSAuthLogin runs 'gws auth login' for a specific account config dir.
func GWSConfigDirForAccount ¶
GWSConfigDirForAccount returns the config dir path for an account. Creates the directory and copies client_secret.json from the default gws config.
func GenerateRFCMessageID ¶
GenerateRFCMessageID creates a Message-ID scoped to the sender domain.
func GetLastPollAt ¶
GetLastPollAt returns the last poll timestamp, or a default (24h ago).
func IsUnsubscribeRequest ¶
IsUnsubscribeRequest checks if a message appears to be an unsubscribe request based on its subject and snippet text.
func LoadEnvFile ¶
LoadEnvFile loads KEY=VALUE pairs from an explicit env file into the process environment. It intentionally does not auto-discover .env files.
func NormalizeWorkspaceID ¶
func ParseSendDays ¶
ParseSendDays converts a comma-separated day string to weekday slice. Accepts numbers (0=Sun,1=Mon,...,6=Sat) or names (sun,mon,...,sat).
func PrepareFollowUp ¶
func PrepareFollowUp(p *EmailParams, parentMessageID, threadID, originalSubject string)
PrepareFollowUp adds threading headers to an EmailParams for step 2+.
func ProcessBounces ¶
ProcessBounces checks inbox messages for bounce NDRs. Uses two strategies:
- Thread matching — if the NDR shares a thread_id with a sent email, we know the lead
- Snippet/header parsing — fallback: extract bounced email from NDR text
Returns the number of new bounces detected.
func ProcessDiscordNotifications ¶
func ProcessDiscordNotifications(ctx context.Context, db *sql.DB, notifier DiscordNotifier, opts DiscordNotifyOptions) (int, error)
ProcessDiscordNotifications sends unnotified reply/unsubscribe events to Discord.
func ProcessDiscordOperationalNotifications ¶
func ProcessDiscordOperationalNotifications(ctx context.Context, db *sql.DB, notifier DiscordNotifier, opts DiscordOperationalNotifyOptions) (int, error)
ProcessDiscordOperationalNotifications sends durable, workspace-scoped campaign completion and sender-idle alerts. Completion alerts include any campaign inboxes that became idle, and suppress a duplicate immediate idle alert for those inboxes. Idle reminders repeat at most once per interval.
func ProcessIMAPBounces ¶
ProcessIMAPBounces checks IMAP messages for bounce NDRs.
func ProcessIMAPReplies ¶
func ProcessIMAPReplies(db *sql.DB, imap IMAPMessageLister, accounts []Account) (replies int, unsubscribes int, err error)
ProcessIMAPReplies checks IMAP inbox messages for replies to sent SMTP/IMAP emails.
func ProcessReplies ¶
func ProcessReplies(db *sql.DB, gws GWSClient, accounts []Account) (replies int, unsubscribes int, err error)
ProcessReplies checks inbox messages for replies to our sent emails. Returns the number of new replies and unsubscribes detected.
func RebalancePendingSchedules ¶
RebalancePendingSchedules rewrites pending send_at timestamps for the affected accounts so daily limits are respected across all active and draft campaigns.
func RenderTemplate ¶
RenderTemplate replaces all {{placeholder}} occurrences with values from the fields map. It also resolves known aliases (e.g., {{name}} → first_name value).
func ResolveAlias ¶
ResolveAlias returns the canonical field name for a known alias, or the name unchanged.
func ResolveCampaignName ¶
ResolveCampaignName accepts a campaign name or numeric ID and returns the campaign name. This lets users reference campaigns by either their name or the ID shown in "campaign list".
func ResolveCampaignNameInWorkspace ¶
ResolveCampaignNameInWorkspace accepts a campaign name or numeric ID and returns the campaign name for one workspace.
func ResolveLeadScheduleTimezone ¶
func ResolveLeadScheduleTimezone(fields map[string]string, defaultTZ *time.Location) (*time.Location, error)
ResolveLeadScheduleTimezone returns the lead's effective scheduling timezone.
func ResolveSecretRef ¶
ResolveSecretRef resolves a secret reference without exposing the value in errors.
func SetLastPollAt ¶
SetLastPollAt updates the last poll timestamp.
func StripUnresolved ¶
StripUnresolved removes any remaining {{...}} placeholders from a string, collapses double spaces left behind, and returns the names of stripped variables.
func SuggestField ¶
SuggestField returns the closest matching field name if within Levenshtein distance 3, or "".
func UpdateAccount ¶
func UpdateAccount(db *sql.DB, email string, opts UpdateAccountOpts) error
UpdateAccount modifies account settings.
func UpdateCampaign ¶
func UpdateCampaign(db *sql.DB, name string, opts UpdateCampaignOpts) error
UpdateCampaign updates campaign settings with validation.
func ValidateLeadFields ¶
func ValidateLeadFields(leads []LeadRecord, placeholders []string) ([]string, error)
ValidateLeadFields checks that every placeholder in the sequence maps to a known field and that every lead has non-empty values for all required placeholders. Returns alias-mapping warnings (if any) and an error if validation fails.
func ValidateLeadScheduleOverrides ¶
func ValidateLeadScheduleOverrides(leads []LeadRecord) error
ValidateLeadScheduleOverrides checks optional per-lead scheduling override fields.
func ValidateSecretRef ¶
ValidateSecretRef verifies that a secret reference uses a supported scheme.
The env: scheme is resolved by the local CLI. The secret: scheme is an opaque hosted-product reference resolved by callers that provide their own SecretResolver.
func WorkspaceIDFromEnv ¶
func WorkspaceIDFromEnv() string
func WriteDefaultConfig ¶
Types ¶
type Account ¶
type Account struct {
ID int64 `json:"id"`
WorkspaceID string `json:"workspace_id"`
Email string `json:"email"`
DailyLimit int `json:"daily_limit"`
LastSendAt *time.Time `json:"last_send_at,omitempty"`
Status string `json:"status"`
Provider string `json:"provider"`
GWSConfigDir string `json:"gws_config_dir,omitempty"`
SMTPHost string `json:"smtp_host,omitempty"`
SMTPPort int `json:"smtp_port,omitempty"`
SMTPUsername string `json:"smtp_username,omitempty"`
SMTPPasswordRef string `json:"smtp_password_ref,omitempty"`
SMTPTLSMode string `json:"smtp_tls_mode,omitempty"`
IMAPHost string `json:"imap_host,omitempty"`
IMAPPort int `json:"imap_port,omitempty"`
IMAPUsername string `json:"imap_username,omitempty"`
IMAPPasswordRef string `json:"imap_password_ref,omitempty"`
IMAPTLSMode string `json:"imap_tls_mode,omitempty"`
}
type AccountVerifyResult ¶
type AccountVerifyResult struct {
Email string `json:"email"`
Provider string `json:"provider"`
SMTPOK bool `json:"smtp_ok,omitempty"`
IMAPOK bool `json:"imap_ok,omitempty"`
SMTPError string `json:"smtp_error,omitempty"`
IMAPError string `json:"imap_error,omitempty"`
}
func VerifySMTPIMAPAccount ¶
func VerifySMTPIMAPAccount(account Account, smtpVerifier SMTPAccountVerifier, imapVerifier IMAPAccountVerifier) (*AccountVerifyResult, error)
type AddAccountResult ¶
type AddAccountResult struct {
ID int64 `json:"id"`
WorkspaceID string `json:"workspace_id"`
Email string `json:"email"`
DailyLimit int `json:"daily_limit"`
Status string `json:"status"`
Provider string `json:"provider"`
GWSConfigDir string `json:"gws_config_dir"`
}
AddAccountResult is returned by AddAccount.
func AddAccount ¶
func AddAccount(db *sql.DB, email string, dailyLimit int, configDir string) (*AddAccountResult, error)
AddAccount inserts a new sending account into the database. If the account was previously removed, it is reactivated with the new settings.
func AddAccountInWorkspace ¶
func AddAccountInWorkspace(db *sql.DB, workspaceID, email string, dailyLimit int, configDir string) (*AddAccountResult, error)
AddAccountInWorkspace inserts a new sending account into a workspace. If the account was previously removed, it is reactivated with the new settings.
type AddLeadsResult ¶
type AddLeadsResult struct {
Campaign string `json:"campaign"`
LeadsAdded int `json:"leads_added"`
LeadsSkipped int `json:"leads_skipped"`
ScheduledSends int `json:"scheduled_sends"`
Warnings []string `json:"warnings,omitempty"`
}
AddLeadsResult is returned by AddLeadsToCampaign.
func AddLeadsToCampaign ¶
func AddLeadsToCampaign(db *sql.DB, campaignName, leadsFile, leadsInline string) (*AddLeadsResult, error)
AddLeadsToCampaign adds new leads to an existing campaign and schedules their sends. Pass leadsFile for file path, or leadsInline for inline CSV content (one should be non-empty).
type AddSMTPIMAPAccountOpts ¶
type AddSMTPIMAPAccountOpts struct {
WorkspaceID string
Email string
DailyLimit int
SMTPHost string
SMTPPort int
SMTPUsername string
SMTPPasswordRef string
SMTPTLSMode string
IMAPHost string
IMAPPort int
IMAPUsername string
IMAPPasswordRef string
IMAPTLSMode string
}
AddSMTPIMAPAccountOpts holds settings for a generic SMTP/IMAP account.
type AddSMTPIMAPAccountResult ¶
type AddSMTPIMAPAccountResult struct {
ID int64 `json:"id"`
WorkspaceID string `json:"workspace_id"`
Email string `json:"email"`
DailyLimit int `json:"daily_limit"`
Status string `json:"status"`
Provider string `json:"provider"`
SMTPHost string `json:"smtp_host"`
SMTPPort int `json:"smtp_port"`
SMTPUsername string `json:"smtp_username"`
SMTPTLSMode string `json:"smtp_tls_mode"`
IMAPHost string `json:"imap_host"`
IMAPPort int `json:"imap_port"`
IMAPUsername string `json:"imap_username"`
IMAPTLSMode string `json:"imap_tls_mode"`
}
AddSMTPIMAPAccountResult is returned by AddSMTPIMAPAccount.
func AddSMTPIMAPAccount ¶
func AddSMTPIMAPAccount(db *sql.DB, opts AddSMTPIMAPAccountOpts) (*AddSMTPIMAPAccountResult, error)
AddSMTPIMAPAccount inserts a generic SMTP/IMAP sending account. Password fields are stored as references, not raw secret values.
func UpdateSMTPIMAPAccount ¶
func UpdateSMTPIMAPAccount(db *sql.DB, email string, opts UpdateSMTPIMAPAccountOpts) (*AddSMTPIMAPAccountResult, error)
UpdateSMTPIMAPAccount modifies provider settings on a generic SMTP/IMAP account.
type BackfillEmailMessagesResult ¶
type BackfillEmailMessagesResult struct {
Scanned int `json:"scanned"`
Backfilled int `json:"backfilled"`
Sent int `json:"sent"`
Inbound int `json:"inbound"`
Skipped int `json:"skipped"`
Unsupported int `json:"unsupported"`
Failed int `json:"failed"`
DryRun bool `json:"dry_run"`
}
func BackfillEmailMessages ¶
func BackfillEmailMessages(cfg BackfillEmailMessagesConfig) (*BackfillEmailMessagesResult, error)
type BlacklistResult ¶
type BlacklistResult struct {
Target string `json:"target"`
IsDomain bool `json:"is_domain"`
BlacklistedLeads int64 `json:"blacklisted_leads"`
CancelledSends int64 `json:"cancelled_sends"`
}
BlacklistResult is returned by BlacklistLead.
func BlacklistLead ¶
func BlacklistLead(db *sql.DB, target string) (*BlacklistResult, error)
BlacklistLead blacklists a lead by email or all leads on a domain.
type Campaign ¶
type Campaign struct {
ID int64 `json:"id"`
WorkspaceID string `json:"workspace_id"`
Name string `json:"name"`
Status string `json:"status"`
SequenceFile string `json:"sequence_file"`
StopOnReply bool `json:"stop_on_reply"`
StopOnDomainReply bool `json:"stop_on_domain_reply"`
SendWindowStart string `json:"send_window_start"`
SendWindowEnd string `json:"send_window_end"`
SendDays string `json:"send_days"`
Timezone string `json:"timezone"`
MinGapSeconds int `json:"min_gap_seconds"`
MaxGapSeconds int `json:"max_gap_seconds"`
CreatedAt time.Time `json:"created_at"`
}
type CampaignLead ¶
type CampaignListRow ¶
type CampaignListRow struct {
ID int64 `json:"id"`
WorkspaceID string `json:"workspace_id"`
Name string `json:"name"`
Status string `json:"status"`
Leads int `json:"leads"`
Sends int `json:"sends"`
SendWindow string `json:"send_window"`
SendDays string `json:"send_days"`
}
CampaignListRow is a row from ListCampaigns.
func ListCampaigns ¶
func ListCampaigns(db *sql.DB) ([]CampaignListRow, error)
ListCampaigns returns campaigns in the default workspace with lead and send counts.
func ListCampaignsForWorkspace ¶
func ListCampaignsForWorkspace(db *sql.DB, workspaceID string) ([]CampaignListRow, error)
ListCampaignsForWorkspace returns campaigns in one workspace with lead and send counts.
type CampaignStats ¶
type CampaignStats struct {
Name string `json:"name"`
Status string `json:"status"`
Sent int `json:"sent"`
Replies int `json:"replies"`
Unsubscribes int `json:"unsubscribes"`
Bounces int `json:"bounces"`
}
CampaignStats is a row from GetAllCampaignStats.
func GetAllCampaignStats ¶
func GetAllCampaignStats(db *sql.DB) ([]CampaignStats, error)
GetAllCampaignStats returns sent/replied/bounced counts per campaign.
type CampaignStatusInfo ¶
type CampaignStatusInfo struct {
Name string `json:"name"`
Status string `json:"status"`
Sequence string `json:"sequence"`
Timezone string `json:"timezone"`
SendWindow string `json:"send_window"`
SendDays string `json:"send_days"`
Leads int `json:"leads"`
Accounts int `json:"accounts"`
TotalSends int `json:"total_sends"`
SendCounts map[string]int `json:"send_counts"`
CreatedAt string `json:"created_at"`
ReplyRate *float64 `json:"reply_rate,omitempty"`
NextSendAt *string `json:"next_send_at,omitempty"`
LastSendAt *string `json:"last_send_at,omitempty"`
FailureReasons []FailureReason `json:"failure_reasons,omitempty"`
}
CampaignStatusInfo is returned by GetCampaignStatus.
func GetCampaignStatus ¶
func GetCampaignStatus(db *sql.DB, name string) (*CampaignStatusInfo, error)
GetCampaignStatus returns campaign details and send counts.
type CloneCampaignOpts ¶
type CloneCampaignOpts struct {
WorkspaceID string
SourceName string
NewName string
LeadsFile string
LeadsInline string // inline CSV content (alternative to LeadsFile)
Accounts []string // optional: override accounts; empty = reuse source accounts
}
CloneCampaignOpts holds options for CloneCampaign.
type Config ¶
type Config struct {
DefaultTimezone string `yaml:"default_timezone"`
DefaultDailyLimit int `yaml:"default_daily_limit"`
MinGapSeconds int `yaml:"min_gap_seconds"`
MaxGapSeconds int `yaml:"max_gap_seconds"`
SendWindowStart string `yaml:"send_window_start"`
SendWindowEnd string `yaml:"send_window_end"`
SendDays string `yaml:"send_days"`
UnsubscribeHeader bool `yaml:"unsubscribe_header"`
UnsubscribeSubject string `yaml:"unsubscribe_subject"`
}
func LoadConfig ¶
type CreateCampaignOpts ¶
type CreateCampaignOpts struct {
WorkspaceID string
Name string
SequenceFile string
SequenceInline string // inline YAML content (alternative to SequenceFile)
LeadsFile string
LeadsInline string // inline CSV content (alternative to LeadsFile)
AccountEmails []string
StartDate string // optional "YYYY-MM-DD"; empty = now
SendWindowStart string // optional HH:MM override; empty = config default
SendWindowEnd string // optional HH:MM override; empty = config default
SendDays string // optional send days override for this campaign
Timezone string // optional IANA timezone override; empty = config default
}
CreateCampaignOpts holds options for CreateCampaign.
type CreateCampaignResult ¶
type CreateCampaignResult struct {
ID int64 `json:"id"`
WorkspaceID string `json:"workspace_id"`
Name string `json:"name"`
Status string `json:"status"`
Leads int `json:"leads"`
ScheduledSends int `json:"scheduled_sends"`
Accounts int `json:"accounts"`
Warnings []string `json:"warnings,omitempty"`
}
CreateCampaignResult is returned by CreateCampaign.
func CloneCampaign ¶
func CloneCampaign(db *sql.DB, opts CloneCampaignOpts) (*CreateCampaignResult, error)
CloneCampaign creates a new campaign by copying settings from an existing one with new leads.
func CreateCampaign ¶
func CreateCampaign(db *sql.DB, opts CreateCampaignOpts) (*CreateCampaignResult, error)
CreateCampaign parses sequence+CSV, validates, computes schedule, and inserts everything.
func CreateDraftCampaign ¶
func CreateDraftCampaign(db *sql.DB, opts CreateDraftCampaignOpts) (*CreateCampaignResult, error)
CreateDraftCampaign inserts a draft campaign shell without sequence steps or leads.
type CreateDraftCampaignOpts ¶
type CreateDraftCampaignOpts struct {
WorkspaceID string
Name string
AccountEmails []string
SendWindowStart string
SendWindowEnd string
SendDays string
Timezone string
}
CreateDraftCampaignOpts holds options for CreateDraftCampaign.
type DailyLimitWarning ¶
type DailyLimitWarning struct {
Date string `json:"date"`
Account string `json:"account"`
Scheduled int `json:"scheduled"`
Limit int `json:"limit"`
Overflow int `json:"overflow"`
}
DailyLimitWarning describes a day where scheduled sends exceed an account's daily limit.
func GetDailyLimitWarnings ¶
func GetDailyLimitWarnings(db *sql.DB) ([]DailyLimitWarning, error)
GetDailyLimitWarnings checks all pending sends across all active campaigns for each account and returns warnings for days that exceed the account's daily limit.
type Dialect ¶
type Dialect string
func CurrentDialect ¶
func CurrentDialect() Dialect
CurrentDialect returns the active database dialect from environment.
type DiscordNotificationEvent ¶
type DiscordNotificationEvent struct {
EventID int64
EventType string
Timestamp string
WorkspaceID string
CampaignID int64
CampaignName string
CampaignStatus string
LeadEmail string
LeadCompany string
AccountEmail string
AccountEmails []string
IdleAccountEmails []string
FromEmail string
Subject string
Snippet string
MessageID string
LeadsContacted int
SentCount int
ReplyCount int
UnsubscribeCount int
BounceCount int
FailedCount int
SkippedCount int
CancelledCount int
PendingCount int
IdleSince string
Reminder bool
}
DiscordNotificationEvent is the compact event shape used for Discord alerts.
type DiscordNotifier ¶
type DiscordNotifier interface {
NotifyDiscord(context.Context, DiscordNotificationEvent) error
}
DiscordNotifier sends a single cold-cli event to Discord.
type DiscordNotifyOptions ¶
DiscordNotifyOptions configures one notification processing pass.
type DiscordOperationalNotifyOptions ¶
type DiscordOperationalNotifyOptions struct {
Workspaces []string
Now time.Time
IdleReminderInterval time.Duration
}
DiscordOperationalNotifyOptions configures campaign completion and sender-idle alerts. Operational alerts are disabled when Workspaces is empty.
type DiscordWebhookNotifier ¶
type DiscordWebhookNotifier struct {
WebhookURL string
Username string
AvatarURL string
HTTPClient *http.Client
}
DiscordWebhookNotifier posts cold-cli notifications to a Discord webhook URL.
func (DiscordWebhookNotifier) NotifyDiscord ¶
func (n DiscordWebhookNotifier) NotifyDiscord(ctx context.Context, event DiscordNotificationEvent) error
type DomainCheck ¶
type DomainCheck struct {
Name string `json:"name"`
Passed bool `json:"passed"`
Detail string `json:"detail"`
Fix string `json:"fix,omitempty"`
}
DomainCheck is the result of checking one DNS aspect.
type DomainDiagnostic ¶
type DomainDiagnostic struct {
Domain string `json:"domain"`
Checks []DomainCheck `json:"checks"`
Score int `json:"score"`
MaxScore int `json:"max_score"`
}
DomainDiagnostic is the full result of CheckDomain.
func CheckDomain ¶
func CheckDomain(domain string) (*DomainDiagnostic, error)
CheckDomain runs DNS diagnostics for email deliverability.
type EmailMessage ¶
type EmailMessage struct {
ID int64 `json:"id"`
CampaignID int64 `json:"campaign_id"`
LeadID int64 `json:"lead_id"`
AccountID int64 `json:"account_id"`
Direction string `json:"direction"`
Type string `json:"type"`
StepNumber int `json:"step_number"`
ScheduledSendID *int64 `json:"scheduled_send_id,omitempty"`
EventID *int64 `json:"event_id,omitempty"`
MessageID string `json:"message_id"`
ThreadID string `json:"thread_id"`
InReplyTo string `json:"in_reply_to,omitempty"`
FromEmail string `json:"from_email"`
ToEmails string `json:"to_emails"`
CcEmails string `json:"cc_emails,omitempty"`
BccEmails string `json:"bcc_emails,omitempty"`
ReplyToEmails string `json:"reply_to_emails,omitempty"`
Subject string `json:"subject"`
TextBody string `json:"text_body"`
DisplayBody string `json:"display_body"`
DisplayHTML string `json:"display_html"`
HTMLBody string `json:"html_body"`
Snippet string `json:"snippet"`
RawHeaders string `json:"raw_headers,omitempty"`
OccurredAt time.Time `json:"occurred_at"`
CreatedAt time.Time `json:"created_at"`
}
func ListEmailThreadMessages ¶
func ListEmailThreadMessages(db *sql.DB, opts ListEmailThreadMessagesOpts) ([]EmailMessage, error)
type EmailParams ¶
type EmailParams struct {
FromName string
FromEmail string
ToEmail string
Subject string
Body string
// For follow-ups (step 2+)
InReplyTo string // Message-ID of the previous step
References string // same as InReplyTo for simple chains
ThreadID string // Gmail thread ID for threading
MessageID string // RFC Message-ID to set before sending
// Unsubscribe
UnsubscribeEmail string // mailto address for List-Unsubscribe header
UnsubscribeSubject string // subject for the mailto unsubscribe
// Optional Date header. If zero, no Date header is added.
Date time.Time
// Stripped unresolved template variables (for logging)
StrippedVars []string
}
EmailParams holds everything needed to construct and send an email.
func BuildEmailForSend ¶
func BuildEmailForSend( seq *Sequence, stepNumber int, variantIndex int, lead map[string]string, fromEmail string, ) EmailParams
BuildEmailForSend constructs the full email for a scheduled send, applying template rendering and selecting the correct variant.
type EmailValidationPolicy ¶
type EnvSecretResolver ¶
type EnvSecretResolver struct{}
EnvSecretResolver resolves env:NAME references from the process environment.
func (EnvSecretResolver) ResolveSecret ¶
func (EnvSecretResolver) ResolveSecret(ref string) (string, error)
type Event ¶
type Event struct {
ID int64 `json:"id"`
CampaignID int64 `json:"campaign_id"`
LeadID int64 `json:"lead_id"`
AccountID int64 `json:"account_id"`
Type string `json:"type"`
StepNumber int `json:"step_number"`
MessageID string `json:"message_id"`
ThreadID string `json:"thread_id"`
Timestamp time.Time `json:"timestamp"`
Metadata string `json:"metadata,omitempty"`
}
type EventLogRow ¶
type EventLogRow struct {
Timestamp string `json:"timestamp"`
Type string `json:"type"`
Campaign string `json:"campaign"`
LeadEmail string `json:"lead_email"`
AccountEmail string `json:"account_email"`
StepNumber int `json:"step_number"`
MessageID string `json:"message_id,omitempty"`
}
EventLogRow is a row from GetEventLog.
func GetEventLog ¶
GetEventLog returns the most recent events, optionally filtered by campaign.
type FailureReason ¶
FailureReason is an error message and its count from failed sends.
type GWSCLI ¶
type GWSCLI struct {
Timeout time.Duration
ConfigDirs map[string]string // account email → gws config dir
}
GWSCLI is the real implementation that calls gws as a subprocess.
func (*GWSCLI) GetMessage ¶
func (g *GWSCLI) GetMessage(account, msgID string) (*GWSMessage, error)
GetMessage retrieves a single message with full payload (headers).
func (*GWSCLI) GetThreadMessages ¶
func (g *GWSCLI) GetThreadMessages(account, threadID string) ([]GWSMessage, error)
func (*GWSCLI) ListMessages ¶
func (g *GWSCLI) ListMessages(account, query string, includeSpamTrash ...bool) ([]GWSMessage, error)
ListMessages lists messages matching a Gmail search query.
func (*GWSCLI) SendEmail ¶
SendEmail sends an email via gws and returns the Gmail message ID and thread ID.
func (*GWSCLI) SetConfigDir ¶
SetConfigDir registers a gws config directory for a specific account.
type GWSClient ¶
type GWSClient interface {
SendEmail(account, to, rawMsg, threadID string) (msgID, sentThreadID string, err error)
ListMessages(account, query string, includeSpamTrash ...bool) ([]GWSMessage, error)
GetMessage(account, msgID string) (*GWSMessage, error)
GetThreadMessages(account, threadID string) ([]GWSMessage, error)
}
GWSClient is the interface for Gmail operations via gws CLI.
type GWSMessage ¶
type GWSMessage struct {
ID string `json:"id"`
ThreadID string `json:"threadId"`
Snippet string `json:"snippet"`
TextBody string
HTMLBody string
MimeType string
PartMimeTypes []string
LabelIDs []string `json:"labelIds"`
Headers map[string]string // parsed from payload.headers
From string
To string
Subject string
InReplyTo string
Date time.Time
}
GWSMessage represents a parsed Gmail message from gws output.
func ParseIMAPRawMessage ¶
type IMAPAccountVerifier ¶
IMAPAccountVerifier verifies IMAP connectivity and authentication.
type IMAPMessageLister ¶
type IMAPMessageLister interface {
ListMessages(account Account, since time.Time, includeSpamTrash bool) ([]GWSMessage, error)
}
IMAPMessageLister lists mailbox messages for reply and bounce polling.
type IMAPTransport ¶
type IMAPTransport struct {
Resolver SecretResolver
Timeout time.Duration
Mailboxes []string
SpamTrashBoxes []string
MaxBodyBytes int64
// contains filtered or unexported fields
}
IMAPTransport is the production IMAP polling transport.
func NewIMAPTransport ¶
func NewIMAPTransport(resolver SecretResolver) *IMAPTransport
func (*IMAPTransport) ListMessages ¶
func (t *IMAPTransport) ListMessages(account Account, since time.Time, includeSpamTrash bool) ([]GWSMessage, error)
func (*IMAPTransport) VerifyAccount ¶
func (t *IMAPTransport) VerifyAccount(account Account) error
type Lead ¶
type Lead struct {
ID int64 `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Company string `json:"company"`
Domain string `json:"domain"`
CustomFields string `json:"custom_fields,omitempty"`
GlobalStatus string `json:"global_status"`
CreatedAt time.Time `json:"created_at"`
}
type LeadEmailValidationResult ¶
type LeadEmailValidationResult struct {
Rows []LeadEmailValidationRow `json:"rows"`
Checked int `json:"checked"`
Pass int `json:"pass"`
ManualReview int `json:"manual_review"`
Fail int `json:"fail"`
}
func ValidateLeadEmails ¶
func ValidateLeadEmails(records []LeadRecord, verifier RecipientVerifier, policy EmailValidationPolicy) (*LeadEmailValidationResult, error)
func (LeadEmailValidationResult) HasBlockingRows ¶
func (r LeadEmailValidationResult) HasBlockingRows() bool
type LeadEmailValidationRow ¶
type LeadForSchedule ¶
LeadForSchedule is the minimal lead info needed for scheduling.
type LeadListRow ¶
type LeadListRow struct {
ID int64 `json:"id"`
Email string `json:"email"`
FirstName string `json:"first_name"`
Company string `json:"company"`
Domain string `json:"domain"`
GlobalStatus string `json:"global_status"`
Campaigns int `json:"campaigns"`
}
LeadListRow is a row from ListLeads.
type LeadRecord ¶
LeadRecord represents a parsed CSV row with all fields available by header name.
func ParseLeadsCSV ¶
func ParseLeadsCSV(path string) ([]LeadRecord, []string, error)
ParseLeadsCSV reads a CSV file and returns parsed lead records. Headers are normalized to lowercase with spaces replaced by underscores. Strips UTF-8 BOM if present.
func ParseLeadsCSVFromReader ¶
func ParseLeadsCSVFromReader(r io.Reader) ([]LeadRecord, []string, error)
ParseLeadsCSVFromReader parses leads CSV from a reader. Returns the lead records and the normalized header names.
type LeadStatsRow ¶
type LeadStatsRow struct {
Email string `json:"email"`
Status string `json:"status"`
StepsSent int `json:"steps_sent"`
ReplyAt *string `json:"reply_at,omitempty"`
}
LeadStatsRow is a row from GetCampaignLeadStats.
func GetCampaignLeadStats ¶
func GetCampaignLeadStats(db *sql.DB, campaignID int64) ([]LeadStatsRow, error)
GetCampaignLeadStats returns per-lead stats for a campaign.
type ListAccountsRow ¶
type ListAccountsRow struct {
ID int64 `json:"id"`
WorkspaceID string `json:"workspace_id"`
Email string `json:"email"`
DailyLimit int `json:"daily_limit"`
Status string `json:"status"`
Provider string `json:"provider"`
}
ListAccountsRow is a row from ListAccounts.
func ListAccounts ¶
func ListAccounts(db *sql.DB) ([]ListAccountsRow, error)
ListAccounts returns accounts in the default workspace ordered by ID.
func ListAccountsForWorkspace ¶
func ListAccountsForWorkspace(db *sql.DB, workspaceID string) ([]ListAccountsRow, error)
ListAccountsForWorkspace returns accounts in one workspace ordered by ID.
func ListAllAccounts ¶
func ListAllAccounts(db *sql.DB) ([]ListAccountsRow, error)
ListAllAccounts returns all accounts ordered by workspace then ID.
type PauseAccountResult ¶
type PauseAccountResult struct {
Email string `json:"email"`
CancelledSends int64 `json:"cancelled_sends"`
}
PauseAccountResult is returned by PauseAccount.
func PauseAccount ¶
func PauseAccount(db *sql.DB, email string) (*PauseAccountResult, error)
PauseAccount deactivates an account and cancels its pending sends.
func RemoveAccount ¶
func RemoveAccount(db *sql.DB, email string) (*PauseAccountResult, error)
RemoveAccount deactivates an account permanently and cancels its pending sends. The account row is kept (status='removed') because historical sends/events reference it.
type PauseLeadResult ¶
type PauseLeadResult struct {
Email string `json:"email"`
PausedCampaigns int64 `json:"paused_campaigns"`
CancelledSends int64 `json:"cancelled_sends"`
}
PauseLeadResult is returned by PauseLead.
type PreviewRow ¶
type PreviewRow struct {
StepNumber int `json:"step_number"`
VariantIndex int `json:"variant_index"`
SendAt string `json:"send_at"`
Status string `json:"status"`
LeadEmail string `json:"lead_email"`
AccountEmail string `json:"account_email"`
ErrorMessage string `json:"error_message,omitempty"`
}
PreviewRow is a row from GetCampaignPreview.
func GetCampaignPreview ¶
func GetCampaignPreview(db *sql.DB, name string) (campaignID int64, status string, preview []PreviewRow, err error)
GetCampaignPreview returns the full scheduled send list for a campaign.
type RecipientVerifier ¶
type RemoveLeadResult ¶
type RemoveLeadResult struct {
Email string `json:"email"`
Campaign string `json:"campaign"`
CancelledSends int64 `json:"cancelled_sends"`
}
RemoveLeadResult is returned by RemoveLeadFromCampaign.
func RemoveLeadFromCampaign ¶
func RemoveLeadFromCampaign(db *sql.DB, campaignName, email string) (*RemoveLeadResult, error)
RemoveLeadFromCampaign removes a single lead from a specific campaign.
type RenderedEmail ¶
type RenderedEmail struct {
StepNumber int `json:"step_number"`
VariantIndex int `json:"variant_index"`
LeadEmail string `json:"lead_email"`
AccountEmail string `json:"account_email"`
Subject string `json:"subject"`
Body string `json:"body"`
StrippedVars []string `json:"stripped_vars,omitempty"`
}
RenderedEmail is a preview of an actual email with templates filled in.
func GetCampaignRenderedPreview ¶
GetCampaignRenderedPreview returns rendered emails for a specific lead (or the first lead) in a campaign.
type ResumeAccountResult ¶
type ResumeAccountResult struct {
Email string `json:"email"`
RestoredSends int64 `json:"restored_sends"`
}
ResumeAccountResult is returned by ResumeAccount.
func ResumeAccount ¶
func ResumeAccount(db *sql.DB, email string) (*ResumeAccountResult, error)
ResumeAccount reactivates a paused account and restores its eligible sends.
type ResumeLeadResult ¶
type ResumeLeadResult struct {
Email string `json:"email"`
ResumedCampaigns int64 `json:"resumed_campaigns"`
RestoredSends int64 `json:"restored_sends"`
}
ResumeLeadResult is returned by ResumeLead.
func ResumeLead ¶
func ResumeLead(db *sql.DB, email string) (*ResumeLeadResult, error)
ResumeLead resumes a paused lead: reactivates campaign_leads and restores cancelled sends.
type RetryCampaignResult ¶
RetryCampaignResult is returned by RetryCampaign.
func RetryCampaign ¶
RetryCampaign resets failed sends back to pending with send_at = now. If step is non-nil, only retry failed sends for that specific step.
type SMTPAccountVerifier ¶
SMTPAccountVerifier verifies SMTP connectivity and authentication.
type SMTPEmailSender ¶
type SMTPEmailSender interface {
SendEmail(account Account, params EmailParams) (messageID string, threadID string, err error)
}
SMTPEmailSender sends rendered emails through generic SMTP accounts.
type SMTPIMAPAccountSecrets ¶
func ResolveSMTPIMAPAccountSecrets ¶
func ResolveSMTPIMAPAccountSecrets(account Account, resolver SecretResolver) (*SMTPIMAPAccountSecrets, error)
ResolveSMTPIMAPAccountSecrets resolves the password references for an SMTP/IMAP account.
type SMTPRecipientVerifier ¶
func (SMTPRecipientVerifier) VerifyRecipients ¶
func (v SMTPRecipientVerifier) VerifyRecipients(ctx context.Context, domain string, emails []string) (*RecipientVerificationResult, error)
type SMTPTransport ¶
type SMTPTransport struct {
Resolver SecretResolver
Timeout time.Duration
Now func() time.Time
// contains filtered or unexported fields
}
SMTPTransport is the production SMTP sender.
func NewSMTPTransport ¶
func NewSMTPTransport(resolver SecretResolver) *SMTPTransport
func (*SMTPTransport) SendEmail ¶
func (s *SMTPTransport) SendEmail(account Account, params EmailParams) (string, string, error)
func (*SMTPTransport) VerifyAccount ¶
func (s *SMTPTransport) VerifyAccount(account Account) error
type ScheduleConfig ¶
type ScheduleConfig struct {
CampaignID int64
AccountIDs []int64
Leads []LeadForSchedule
Sequence *Sequence
StartDate string
SendWindowStart string // "09:00"
SendWindowEnd string // "17:00"
SendDays []time.Weekday
Timezone *time.Location
MinGapSeconds int
MaxGapSeconds int
StartTime time.Time // when the campaign starts (typically now)
}
ScheduleConfig holds parameters for schedule computation.
type ScheduledSend ¶
type ScheduledSend struct {
ID int64 `json:"id"`
CampaignID int64 `json:"campaign_id"`
LeadID int64 `json:"lead_id"`
AccountID int64 `json:"account_id"`
StepNumber int `json:"step_number"`
VariantIndex int `json:"variant_index"`
SendAt time.Time `json:"send_at"`
Status string `json:"status"`
ThreadID string `json:"thread_id,omitempty"`
ParentMessageID string `json:"parent_message_id,omitempty"`
MessageID string `json:"message_id,omitempty"`
SentAt *time.Time `json:"sent_at,omitempty"`
ErrorMessage string `json:"error_message,omitempty"`
}
type ScheduledSendRow ¶
type ScheduledSendRow struct {
CampaignID int64
LeadID int64
AccountID int64
StepNumber int
VariantIndex int
SendAt time.Time
}
ScheduledSendRow is a row to insert into scheduled_sends.
func ComputeSchedule ¶
func ComputeSchedule(cfg ScheduleConfig) ([]ScheduledSendRow, error)
ComputeSchedule generates all scheduled_sends rows for a campaign.
type SecretResolver ¶
SecretResolver resolves stored secret references into runtime secret values.
type SecretResolverFunc ¶
SecretResolverFunc adapts a function to SecretResolver.
func (SecretResolverFunc) ResolveSecret ¶
func (fn SecretResolverFunc) ResolveSecret(ref string) (string, error)
type SendInboxReplyConfig ¶
type SendInboxReplyResult ¶
type SendInboxReplyResult struct {
CampaignID int64 `json:"campaign_id"`
LeadID int64 `json:"lead_id"`
AccountID int64 `json:"account_id"`
FromEmail string `json:"from_email"`
ToEmail string `json:"to_email"`
Subject string `json:"subject"`
MessageID string `json:"message_id"`
ThreadID string `json:"thread_id"`
}
func SendInboxReply ¶
func SendInboxReply(cfg SendInboxReplyConfig) (*SendInboxReplyResult, error)
type SendNowResult ¶
SendNowResult is returned by SendNowCampaign.
func SendNowCampaign ¶
func SendNowCampaign(db *sql.DB, name string) (*SendNowResult, error)
SendNowCampaign sets send_at to now for all pending sends in a campaign, so the next tick picks them up immediately.
type SendSMTPTestEmailOpts ¶
type SendSMTPTestEmailOpts struct {
RecipientEmail string `json:"recipient_email"`
Subject string `json:"subject"`
Body string `json:"body"`
}
SendSMTPTestEmailOpts holds a one-off SMTP test email request.
type SendSMTPTestEmailResult ¶
type SendSMTPTestEmailResult struct {
Email string `json:"email"`
RecipientEmail string `json:"recipient_email"`
MessageID string `json:"message_id"`
ThreadID string `json:"thread_id"`
}
SendSMTPTestEmailResult is returned after a test email is accepted by SMTP.
func SendSMTPTestEmail ¶
func SendSMTPTestEmail(account Account, opts SendSMTPTestEmailOpts, resolver SecretResolver) (*SendSMTPTestEmailResult, error)
SendSMTPTestEmail sends a one-off test email through an SMTP/IMAP account.
type Sequence ¶
type Sequence struct {
Name string `yaml:"name"`
Defaults SequenceDefaults `yaml:"defaults"`
Steps []SequenceStep `yaml:"steps"`
}
Sequence represents a parsed sequence YAML file.
func ParseSequence ¶
ParseSequence reads and parses a sequence YAML file.
func ParseSequenceFromBytes ¶
ParseSequenceFromBytes parses sequence YAML from bytes.
func (*Sequence) CollectPlaceholders ¶
CollectPlaceholders returns all unique placeholders used across all steps and variants.
type SequenceDefaults ¶
type SequenceDefaults struct {
FromName string `yaml:"from_name"`
}
type SequenceStep ¶
type SequenceStep struct {
Step int `yaml:"step"`
Delay int `yaml:"delay"` // days after previous step
Subject string `yaml:"subject"`
Body string `yaml:"body"`
Variants []SequenceVariant `yaml:"variants"`
}
type SequenceVariant ¶
type StepStats ¶
type StepStats struct {
Step int `json:"step"`
Sent int `json:"sent"`
Replies int `json:"replies"`
Unsubscribes int `json:"unsubscribes"`
Bounces int `json:"bounces"`
}
StepStats is a row from GetCampaignStepStats.
type Store ¶
Store is a thin wrapper around the database handle plus dialect metadata and dialect-specific tick locking.
func (*Store) AcquireTickLock ¶
AcquireTickLock acquires the dialect-specific tick lock and returns a handle that must be closed to release it.
func (*Store) DisplayTarget ¶
DisplayTarget returns a user-facing database target description.
type TickConfig ¶
type TickConfig struct {
DB *sql.DB
GWS GWSClient
DryRun bool
SendNow bool // ignore send_at timestamps, send all pending
Now time.Time // injectable for testing
NoSleep bool // skip inter-send sleep (for testing)
Timezone *time.Location // for daily limit day boundary; defaults to UTC
UnsubscribeHeader bool // add List-Unsubscribe header (off by default for cold email)
UnsubscribeSubject string // subject for List-Unsubscribe mailto header
SecretResolver SecretResolver // optional resolver for SMTP/IMAP password refs
SMTPSender SMTPEmailSender
IMAP IMAPMessageLister
DiscordNotifier DiscordNotifier
DiscordNotifyLimit int
DiscordProviders []string
DiscordOperationalWorkspaces []string
}
TickConfig holds configuration for a tick invocation.
type TickResult ¶
type TickResult struct {
RepliesDetected int `json:"replies_detected"`
UnsubscribesDetected int `json:"unsubscribes_detected"`
BouncesDetected int `json:"bounces_detected"`
DiscordNotificationsSent int `json:"discord_notifications_sent"`
Sent int `json:"sent"`
Failed int `json:"failed"`
Skipped int `json:"skipped"`
DryRun bool `json:"dry_run"`
}
TickResult holds the summary of a tick invocation.
func Tick ¶
func Tick(cfg TickConfig) (*TickResult, error)
Tick runs one tick cycle: poll replies, poll bounces, send due emails.
type UpdateAccountOpts ¶
type UpdateAccountOpts struct {
DailyLimit *int
}
UpdateAccountOpts holds fields to update on an account.
type UpdateCampaignOpts ¶
type UpdateCampaignOpts struct {
SendWindowStart *string
SendWindowEnd *string
SendDays *string
Timezone *string
MinGapSeconds *int
MaxGapSeconds *int
SequenceFile *string // path to new sequence YAML
}
UpdateCampaignOpts holds fields to update. Zero values are ignored.
type UpdateSMTPIMAPAccountOpts ¶
type UpdateSMTPIMAPAccountOpts struct {
DailyLimit *int
SMTPHost *string
SMTPPort *int
SMTPUsername *string
SMTPPasswordRef *string
SMTPTLSMode *string
IMAPHost *string
IMAPPort *int
IMAPUsername *string
IMAPPasswordRef *string
IMAPTLSMode *string
}
UpdateSMTPIMAPAccountOpts holds fields to update on a generic SMTP/IMAP account.
type VariantStats ¶
type VariantStats struct {
Step int `json:"step"`
Variant int `json:"variant"`
Sent int `json:"sent"`
Replies int `json:"replies"`
ReplyRate float64 `json:"reply_rate"`
Unsubscribes int `json:"unsubscribes"`
Bounces int `json:"bounces"`
}
VariantStats is a row from GetCampaignVariantStats.
func GetCampaignVariantStats ¶
func GetCampaignVariantStats(db *sql.DB, campaignID int64) ([]VariantStats, error)
GetCampaignVariantStats returns per-step, per-variant stats for a campaign.
Source Files
¶
- account.go
- account_verify.go
- campaign.go
- config.go
- csv.go
- db.go
- discord_notify.go
- email_body.go
- email_message.go
- email_message_backfill.go
- email_validation.go
- env_file.go
- gws.go
- imap.go
- lead.go
- lead_fields.go
- models.go
- reply.go
- schedule_overrides.go
- scheduler.go
- secrets.go
- send.go
- smtp.go
- sql_runner.go
- stats.go
- store.go
- template.go
- test_email.go
- tick.go
- workspace.go