-
Notifications
You must be signed in to change notification settings - Fork 10
/
post.go
178 lines (155 loc) · 5.18 KB
/
post.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package common
import (
"errors"
"regexp"
"strconv"
"strings"
"time"
)
const (
// Максимальное количество попыток подключения к почтовику за отправку письма
MaxTryConnectionCount int = 30
AllDomains string = "*"
EmptyStr string = ""
)
var (
// Регулярка для проверки адреса почты, сразу компилируем, чтобы при отправке не терять на этом время
EmailRegexp = regexp.MustCompile(`^[\w\d\.\_\%\+\-]+@([\w\d\.\-]+\.\w{2,5})$`)
HostnameRegex = regexp.MustCompile(`^[\w\d\.\-]+\.\w{2,5}$`)
EmptyStrSlice = []string{}
)
// таймауты приложения
type Timeout struct {
Sleep time.Duration `yaml:"sleep"`
Waiting time.Duration `yaml:"waiting"`
Connection time.Duration `yaml:"connection"`
Hello time.Duration `yaml:"hello"`
Mail time.Duration `yaml:"mail"`
Rcpt time.Duration `yaml:"rcpt"`
Data time.Duration `yaml:"data"`
}
// инициализирует значения таймаутов по умолчанию
func (t *Timeout) Init() {
if t.Sleep == 0 {
t.Sleep = time.Second
}
if t.Waiting == 0 {
t.Waiting = 30 * time.Second
}
if t.Connection == 0 {
t.Connection = 5 * time.Minute
}
if t.Hello == 0 {
t.Hello = 5 * time.Minute
}
if t.Mail == 0 {
t.Mail = 5 * time.Minute
}
if t.Rcpt == 0 {
t.Rcpt = 5 * time.Minute
}
if t.Data == 0 {
t.Data = 10 * time.Minute
}
}
// тип отложенной очереди
type DelayedBindingType int
const (
UnknownDelayedBinding DelayedBindingType = iota
SecondDelayedBinding
ThirtySecondDelayedBinding
MinuteDelayedBinding
FiveMinutesDelayedBinding
TenMinutesDelayedBinding
TwentyMinutesDelayedBinding
ThirtyMinutesDelayedBinding
FortyMinutesDelayedBinding
FiftyMinutesDelayedBinding
HourDelayedBinding
SixHoursDelayedBinding
DayDelayedBinding
NotSendDelayedBinding
)
// ошибка во время отпрвки письма
type MailError struct {
// сообщение
Message string `json:"message"`
// код ошибки
Code int `json:"code"`
}
// письмо
type MailMessage struct {
// идентификатор для логов
Id int64 `json:"-"`
// отправитель
Envelope string `json:"envelope"`
// получатель
Recipient string `json:"recipient"`
// тело письма
Body string `json:"body"`
// домен отправителя, удобно сразу получить и использовать далее
HostnameFrom string `json:"-"`
// Домен получателя, удобно сразу получить и использовать далее
HostnameTo string `json:"-"`
// дата создания, используется в основном сервисом ограничений
CreatedDate time.Time `json:"-"`
// тип очереди, в которою письмо уже было отправлено после неудачной отправки, ипользуется для цепочки очередей
BindingType DelayedBindingType `json:"bindingType"`
// ошибка отправки
Error *MailError `json:"error"`
}
// инициализирует письмо
func (m *MailMessage) Init() {
m.Id = time.Now().UnixNano()
m.CreatedDate = time.Now()
if hostname, err := m.getHostnameFromEmail(m.Envelope); err == nil {
m.HostnameFrom = hostname
}
if hostname, err := m.getHostnameFromEmail(m.Recipient); err == nil {
m.HostnameTo = hostname
}
}
// получает домен из адреса
func (m *MailMessage) getHostnameFromEmail(email string) (string, error) {
matches := EmailRegexp.FindAllStringSubmatch(email, -1)
if len(matches) == 1 && len(matches[0]) == 2 {
return matches[0][1], nil
} else {
return "", errors.New("invalid email address")
}
}
// возвращает письмо обратно в очередь после ошибки во время отправки
func ReturnMail(event *SendEvent, err error) {
// необходимо проверить сообщение на наличие кода ошибки
// обычно код идет первым
if err != nil {
errorMessage := err.Error()
parts := strings.Split(errorMessage, " ")
if len(parts) > 0 {
// пытаемся получить код
code, e := strconv.Atoi(strings.TrimSpace(parts[0]))
// и создать ошибку
// письмо с ошибкой вернется в другую очередь, отличную от письмо без ошибки
if e == nil {
event.Message.Error = &MailError{errorMessage, code}
}
}
}
// если в событии уже создан клиент
if event.Client != nil {
if event.Client.Worker != nil {
// сбрасываем цепочку команд к почтовому сервису
event.Client.Worker.Reset()
}
}
// отпускаем поток получателя сообщений из очереди
if event.Message.Error == nil {
event.Result <- DelaySendEventResult
} else {
if event.Message.Error.Code == 421 {
event.Result <- DelaySendEventResult
} else {
event.Result <- ErrorSendEventResult
}
}
}