mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-21 17:21:09 +01:00
304 lines
9.0 KiB
Go
304 lines
9.0 KiB
Go
package notification
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/apex/log"
|
|
"github.com/crawlab-team/crawlab/core/constants"
|
|
"github.com/crawlab-team/crawlab/core/entity"
|
|
"github.com/crawlab-team/crawlab/core/models/models/v2"
|
|
"github.com/crawlab-team/crawlab/core/models/service"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
type ServiceV2 struct {
|
|
}
|
|
|
|
func (svc *ServiceV2) Start() {
|
|
// initialize data
|
|
if err := svc.initData(); err != nil {
|
|
log.Errorf("[NotificationServiceV2] initializing data error: %v", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func (svc *ServiceV2) initData() (err error) {
|
|
total, err := service.NewModelServiceV2[models.NotificationSettingV2]().Count(nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if total > 0 {
|
|
return nil
|
|
}
|
|
|
|
// data to initialize
|
|
settings := []models.NotificationSettingV2{
|
|
{
|
|
Type: TypeMail,
|
|
Enabled: true,
|
|
Name: "任务通知(邮件)",
|
|
Description: "这是默认的邮件通知。您可以使用您自己的设置进行编辑。",
|
|
TaskTrigger: constants.NotificationTriggerTaskFinish,
|
|
Title: "[Crawlab] 爬虫任务更新: {{$.status}}",
|
|
Template: `尊敬的 {{$.user.username}},
|
|
|
|
请查看下面的任务数据。
|
|
|
|
|键|值|
|
|
|:-:|:--|
|
|
|任务状态|{{$.status}}|
|
|
|任务优先级|{{$.priority}}|
|
|
|任务模式|{{$.mode}}|
|
|
|执行命令|{{$.cmd}}|
|
|
|执行参数|{{$.param}}|
|
|
|错误信息|{{$.error}}|
|
|
|节点|{{$.node.name}}|
|
|
|爬虫|{{$.spider.name}}|
|
|
|项目|{{$.spider.project.name}}|
|
|
|定时任务|{{$.schedule.name}}|
|
|
|结果数|{{$.:task_stat.result_count}}|
|
|
|等待时间(秒)|{#{{$.:task_stat.wait_duration}}/1000#}|
|
|
|运行时间(秒)|{#{{$.:task_stat.runtime_duration}}/1000#}|
|
|
|总时间(秒)|{#{{$.:task_stat.total_duration}}/1000#}|
|
|
|平均结果数/秒|{#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}|
|
|
`,
|
|
Mail: models.NotificationSettingMail{
|
|
Server: "smtp.163.com",
|
|
Port: "465",
|
|
To: "{{$.user[create].email}}",
|
|
},
|
|
},
|
|
{
|
|
Type: TypeMail,
|
|
Enabled: true,
|
|
Name: "Task Change (Mail)",
|
|
Description: "This is the default mail notification. You can edit it with your own settings",
|
|
TaskTrigger: constants.NotificationTriggerTaskFinish,
|
|
Title: "[Crawlab] Task Update: {{$.status}}",
|
|
Template: `Dear {{$.user.username}},
|
|
|
|
Please find the task data as below.
|
|
|
|
|Key|Value|
|
|
|:-:|:--|
|
|
|Task Status|{{$.status}}|
|
|
|Task Priority|{{$.priority}}|
|
|
|Task Mode|{{$.mode}}|
|
|
|Task Command|{{$.cmd}}|
|
|
|Task Params|{{$.param}}|
|
|
|Error Message|{{$.error}}|
|
|
|Node|{{$.node.name}}|
|
|
|Spider|{{$.spider.name}}|
|
|
|Project|{{$.spider.project.name}}|
|
|
|Schedule|{{$.schedule.name}}|
|
|
|Result Count|{{$.:task_stat.result_count}}|
|
|
|Wait Duration (sec)|{#{{$.:task_stat.wait_duration}}/1000#}|
|
|
|Runtime Duration (sec)|{#{{$.:task_stat.runtime_duration}}/1000#}|
|
|
|Total Duration (sec)|{#{{$.:task_stat.total_duration}}/1000#}|
|
|
|Avg Results / Sec|{#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}|
|
|
`,
|
|
Mail: models.NotificationSettingMail{
|
|
Server: "smtp.163.com",
|
|
Port: "465",
|
|
To: "{{$.user[create].email}}",
|
|
},
|
|
},
|
|
{
|
|
Type: TypeMobile,
|
|
Enabled: true,
|
|
Name: "任务通知(移动端)",
|
|
Description: "这是默认的手机通知。您可以使用您自己的设置进行编辑。",
|
|
TaskTrigger: constants.NotificationTriggerTaskFinish,
|
|
Title: "[Crawlab] 任务更新: {{$.status}}",
|
|
Template: `尊敬的 {{$.user.username}},
|
|
|
|
请查看下面的任务数据。
|
|
|
|
- **任务状态**: {{$.status}}
|
|
- **任务优先级**: {{$.priority}}
|
|
- **任务模式**: {{$.mode}}
|
|
- **执行命令**: {{$.cmd}}
|
|
- **执行参数**: {{$.param}}
|
|
- **错误信息**: {{$.error}}
|
|
- **节点**: {{$.node.name}}
|
|
- **爬虫**: {{$.spider.name}}
|
|
- **项目**: {{$.spider.project.name}}
|
|
- **定时任务**: {{$.schedule.name}}
|
|
- **结果数**: {{$.:task_stat.result_count}}
|
|
- **等待时间(秒)**: {#{{$.:task_stat.wait_duration}}/1000#}
|
|
- **运行时间(秒)**: {#{{$.:task_stat.runtime_duration}}/1000#}
|
|
- **总时间(秒)**: {#{{$.:task_stat.total_duration}}/1000#}
|
|
- **平均结果数/秒**: {#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}`,
|
|
Mobile: models.NotificationSettingMobile{},
|
|
},
|
|
{
|
|
Type: TypeMobile,
|
|
Enabled: true,
|
|
Name: "Task Change (Mobile)",
|
|
Description: "This is the default mobile notification. You can edit it with your own settings",
|
|
TaskTrigger: constants.NotificationTriggerTaskFinish,
|
|
Title: "[Crawlab] Task Update: {{$.status}}",
|
|
Template: `Dear {{$.user.username}},
|
|
|
|
Please find the task data as below.
|
|
|
|
- **Task Status**: {{$.status}}
|
|
- **Task Priority**: {{$.priority}}
|
|
- **Task Mode**: {{$.mode}}
|
|
- **Task Command**: {{$.cmd}}
|
|
- **Task Params**: {{$.param}}
|
|
- **Error Message**: {{$.error}}
|
|
- **Node**: {{$.node.name}}
|
|
- **Spider**: {{$.spider.name}}
|
|
- **Project**: {{$.spider.project.name}}
|
|
- **Schedule**: {{$.schedule.name}}
|
|
- **Result Count**: {{$.:task_stat.result_count}}
|
|
- **Wait Duration (sec)**: {#{{$.:task_stat.wait_duration}}/1000#}
|
|
- **Runtime Duration (sec)**: {#{{$.:task_stat.runtime_duration}}/1000#}
|
|
- **Total Duration (sec)**: {#{{$.:task_stat.total_duration}}/1000#}
|
|
- **Avg Results / Sec**: {#{{$.:task_stat.result_count}}/({{$.:task_stat.total_duration}}/1000)#}`,
|
|
Mobile: models.NotificationSettingMobile{},
|
|
},
|
|
}
|
|
_, err = service.NewModelServiceV2[models.NotificationSettingV2]().InsertMany(settings)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (svc *ServiceV2) Send(s *models.NotificationSettingV2, args ...any) {
|
|
content := svc.getContent(s, args...)
|
|
switch s.Type {
|
|
case TypeMail:
|
|
svc.SendMail(s, content)
|
|
case TypeMobile:
|
|
svc.SendMobile(s, content)
|
|
}
|
|
}
|
|
|
|
func (svc *ServiceV2) SendMail(s *models.NotificationSettingV2, content string) {
|
|
// TODO: parse to/cc/bcc
|
|
|
|
// send mail
|
|
err := SendMail(&s.Mail, s.Mail.To, s.Mail.Cc, s.Title, content)
|
|
if err != nil {
|
|
log.Errorf("[NotificationServiceV2] send mail error: %v", err)
|
|
}
|
|
}
|
|
|
|
func (svc *ServiceV2) SendMobile(s *models.NotificationSettingV2, content string) {
|
|
err := SendMobileNotification(s.Mobile.Webhook, s.Title, content)
|
|
if err != nil {
|
|
log.Errorf("[NotificationServiceV2] send mobile notification error: %v", err)
|
|
}
|
|
}
|
|
|
|
func (svc *ServiceV2) getContent(s *models.NotificationSettingV2, args ...any) (content string) {
|
|
switch s.TriggerTarget {
|
|
case constants.NotificationTriggerTargetTask:
|
|
vd := svc.getTaskVariableData(args...)
|
|
switch s.TemplateMode {
|
|
case constants.NotificationTemplateModeMarkdown:
|
|
variables := svc.parseTemplateVariables(s.TemplateMarkdown)
|
|
return svc.getTaskContent(s.TemplateMarkdown, variables, vd)
|
|
case constants.NotificationTemplateModeRichText:
|
|
variables := svc.parseTemplateVariables(s.TemplateRichText)
|
|
return svc.getTaskContent(s.TemplateRichText, variables, vd)
|
|
}
|
|
|
|
case constants.NotificationTriggerTargetNode:
|
|
// TODO: implement
|
|
|
|
}
|
|
|
|
return content
|
|
}
|
|
|
|
func (svc *ServiceV2) getTaskContent(template string, variables []entity.NotificationVariable, vd VariableDataTask) (content string) {
|
|
content = template
|
|
for _, v := range variables {
|
|
switch v.Category {
|
|
case "task":
|
|
switch v.Name {
|
|
case "id":
|
|
content = strings.ReplaceAll(content, v.GetKey(), vd.Task.Id.Hex())
|
|
case "status":
|
|
content = strings.ReplaceAll(content, v.GetKey(), vd.Task.Status)
|
|
case "priority":
|
|
content = strings.ReplaceAll(content, v.GetKey(), fmt.Sprintf("%d", vd.Task.Priority))
|
|
case "mode":
|
|
content = strings.ReplaceAll(content, v.GetKey(), vd.Task.Mode)
|
|
case "cmd":
|
|
content = strings.ReplaceAll(content, v.GetKey(), vd.Task.Cmd)
|
|
case "param":
|
|
content = strings.ReplaceAll(content, v.GetKey(), vd.Task.Param)
|
|
}
|
|
}
|
|
}
|
|
return content
|
|
}
|
|
|
|
func (svc *ServiceV2) getTaskVariableData(args ...any) (vd VariableDataTask) {
|
|
for _, arg := range args {
|
|
switch arg.(type) {
|
|
case *models.TaskV2:
|
|
vd.Task = arg.(*models.TaskV2)
|
|
case *models.TaskStatV2:
|
|
vd.TaskStat = arg.(*models.TaskStatV2)
|
|
case *models.SpiderV2:
|
|
vd.Spider = arg.(*models.SpiderV2)
|
|
case *models.NodeV2:
|
|
vd.Node = arg.(*models.NodeV2)
|
|
case *models.ScheduleV2:
|
|
vd.Schedule = arg.(*models.ScheduleV2)
|
|
}
|
|
}
|
|
return vd
|
|
}
|
|
|
|
func (svc *ServiceV2) parseTemplateVariables(template string) (variables []entity.NotificationVariable) {
|
|
// regex pattern
|
|
regex := regexp.MustCompile("\\$\\{(\\w+):(\\w+)}")
|
|
|
|
// find all matches
|
|
matches := regex.FindAllStringSubmatch(template, -1)
|
|
|
|
// variables map
|
|
variablesMap := make(map[string]entity.NotificationVariable)
|
|
|
|
// iterate over matches
|
|
for _, match := range matches {
|
|
variable := entity.NotificationVariable{
|
|
Category: match[1],
|
|
Name: match[2],
|
|
}
|
|
key := fmt.Sprintf("%s:%s", variable.Category, variable.Name)
|
|
if _, ok := variablesMap[key]; !ok {
|
|
variablesMap[key] = variable
|
|
}
|
|
}
|
|
|
|
// convert map to slice
|
|
for _, variable := range variablesMap {
|
|
variables = append(variables, variable)
|
|
}
|
|
|
|
return variables
|
|
}
|
|
|
|
func newNotificationServiceV2() *ServiceV2 {
|
|
return &ServiceV2{}
|
|
}
|
|
|
|
var _serviceV2 *ServiceV2
|
|
var _serviceV2Once = new(sync.Once)
|
|
|
|
func GetNotificationServiceV2() *ServiceV2 {
|
|
_serviceV2Once.Do(func() {
|
|
_serviceV2 = newNotificationServiceV2()
|
|
})
|
|
return _serviceV2
|
|
}
|