diff --git a/src/modules/notification/notification.go b/src/modules/notification/notification.go new file mode 100644 index 00000000..cda49a9c --- /dev/null +++ b/src/modules/notification/notification.go @@ -0,0 +1,11 @@ +package notification + +import ( + "be.ems/src/framework/logger" + "github.com/gin-gonic/gin" +) + +// 模块路由注册 +func Setup(router *gin.Engine) { + logger.Infof("开始加载 ====> notification 模块路由") +} diff --git a/src/modules/notification/service/email.go b/src/modules/notification/service/email.go new file mode 100644 index 00000000..d9a42add --- /dev/null +++ b/src/modules/notification/service/email.go @@ -0,0 +1,126 @@ +package service + +import ( + "bytes" + "crypto/tls" + "fmt" + ht "html/template" + "strings" + "time" + + "github.com/wneessen/go-mail" + + "be.ems/src/framework/config" + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/date" + "be.ems/src/framework/utils/parse" + neDataModel "be.ems/src/modules/network_data/model" +) + +// EmailAlarm 发告警邮件 +func EmailAlarm(a neDataModel.Alarm, neIP string) error { + emailList := fmt.Sprint(config.Get("notification.email.list")) + if len(emailList) == 0 { + return fmt.Errorf("email list is empty") + } + + // 模板 + htmlBodyTemplate := ` + Alarm information +

Sequence: {{.AlarmSeq}}

+

NE Name: {{.NeName}}

+

NE IP: {{.NeIp}}

+

Title: {{.AlarmTitle}}

+

Severity: {{.OrigSeverity}}

+

Event Time: {{.AlarmTime}}

+

Alarm Status: {{.AlarmStatus}}

+ Automatic sent by OMC, please do not reply! + ` + htmlTpl, err := ht.New("htmltpl").Parse(htmlBodyTemplate) + if err != nil { + return fmt.Errorf("EmailAlarm Parse alarmId:%s fail %s", a.AlarmId, err.Error()) + } + // 参数值 + data := map[string]any{ + "AlarmSeq": a.AlarmSeq, + "NeName": a.NeName, + "NeIp": neIP, + "AlarmTitle": a.AlarmTitle, + "OrigSeverity": a.OrigSeverity, + "AlarmTime": date.ParseDateToStr(a.EventTime, time.RFC3339), + "AlarmStatus": a.AlarmStatus, + } + buffer := bytes.NewBuffer(nil) + if err := htmlTpl.Execute(buffer, data); err != nil { + return fmt.Errorf("EmailAlarm Execute alarmId:%s fail %s", a.AlarmId, err.Error()) + } + htmlStr := buffer.String() + + // 发送邮件 + err = EmailSendHTML(htmlStr, strings.Split(emailList, ",")) + if err != nil { + return fmt.Errorf("EmailAlarm alarmId:%s fail %s", a.AlarmId, err.Error()) + } + return err +} + +// EmailSendHTML 发送HTML邮件 +func EmailSendHTML(htmlStr string, toEmailArr []string) error { + // QQ 邮箱: + // SMTP 服务器地址:smtp.qq.com(SSL协议端口:465/994 | 非SSL协议端口:25) + // 163 邮箱: + // SMTP 服务器地址:smtp.163.com(端口:25) + // host := "mail.agrandtech.com" + // port := 25 + // userName := "smtpext@agrandtech.com" + // password := "1000smtp@omc!" + + emailConf := config.Get("notification.email").(map[string]any) + enable := parse.Boolean(emailConf["enable"]) + if !enable { + return fmt.Errorf("email notification not enable") + } + title := fmt.Sprint(emailConf["title"]) + smtp := fmt.Sprint(emailConf["smtp"]) + port := parse.Number(emailConf["port"]) + user := fmt.Sprint(emailConf["user"]) + password := fmt.Sprint(emailConf["password"]) + + message := mail.NewMsg() + // 发件人 + if err := message.From(user); err != nil { + return fmt.Errorf("failed to set From address: %s", err) + } + // 收件人 + hasTo := false + for _, v := range toEmailArr { + if err := message.AddTo(v); err != nil { + logger.Errorf("failed to set To address: %v %s", v, err) + continue + } + hasTo = true + } + if !hasTo { + return fmt.Errorf("failed to set To address not has") + } + // 邮件主题 + message.Subject(title) + // 邮件HTML内容 + message.SetBodyString(mail.TypeTextHTML, htmlStr) + // 连接到邮件SMTP服务器 + client, err := mail.NewClient(smtp, + mail.WithSMTPAuth(mail.SMTPAuthAutoDiscover), + mail.WithUsername(user), + mail.WithPort(int(port)), + mail.WithPassword(password), + mail.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}), + ) + if err != nil { + return fmt.Errorf("failed to create mail client: %s", err) + } + // 发送 + if err := client.DialAndSend(message); err != nil { + return fmt.Errorf("failed to send mail: %s", err) + } + return nil +} diff --git a/src/modules/notification/service/smsc.go b/src/modules/notification/service/smsc.go new file mode 100644 index 00000000..be039b31 --- /dev/null +++ b/src/modules/notification/service/smsc.go @@ -0,0 +1,164 @@ +package service + +import ( + "bytes" + "fmt" + "strings" + tt "text/template" + "time" + + "github.com/linxGnu/gosmpp" + "github.com/linxGnu/gosmpp/data" + "github.com/linxGnu/gosmpp/pdu" + + "be.ems/src/framework/config" + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/date" + "be.ems/src/framework/utils/parse" + neDataModel "be.ems/src/modules/network_data/model" +) + +var smscSession *gosmpp.Session + +// connSM 连接smsc +func connSM() *gosmpp.Session { + if smscSession != nil { + return smscSession + } + smscAddr := fmt.Sprint(config.Get("notification.smsc.addr")) + systemType := fmt.Sprint(config.Get("notification.smsc.systemtype")) + systemID := fmt.Sprint(config.Get("notification.smsc.systemid")) + password := fmt.Sprint(config.Get("notification.smsc.password")) + // 建立连接 + session, err := gosmpp.NewSession( + gosmpp.TXConnector(gosmpp.NonTLSDialer, gosmpp.Auth{ + SMSC: smscAddr, + SystemID: systemID, + Password: password, + SystemType: systemType, + }), + gosmpp.Settings{ + ReadTimeout: 2 * time.Second, + OnSubmitError: func(_ pdu.PDU, err error) { + logger.Errorf("failed to smpp submit error: %s", err.Error()) + }, + OnRebindingError: func(err error) { + logger.Errorf("failed to smpp rebinding error: %s", err.Error()) + }, + }, -1) + if err != nil { + logger.Errorf("failed to create smpp new session error: %s", err.Error()) + return nil + } + // defer session.Close() + smscSession = session + return smscSession +} + +// newSubmitSM 构建短信提交 +func newSubmitSM(destSM string, message string) (*pdu.SubmitSM, error) { + dataCoding := parse.Number(config.Get("notification.smsc.coding")) + enc := data.FromDataCoding(byte(dataCoding)) + srcSM := fmt.Sprint(config.Get("notification.smsc.servicenumber")) + // 源地址 + srcAddr := pdu.NewAddress() + srcAddr.SetTon(5) + srcAddr.SetNpi(0) + err := srcAddr.SetAddress(srcSM) + if err != nil { + return nil, err + } + destAddr := pdu.NewAddress() + destAddr.SetTon(1) + destAddr.SetNpi(1) + err = destAddr.SetAddress(destSM) + if err != nil { + return nil, err + } + // build up submitSM + submitSM := pdu.NewSubmitSM().(*pdu.SubmitSM) + submitSM.SourceAddr = srcAddr + submitSM.DestAddr = destAddr + if err = submitSM.Message.SetMessageWithEncoding(message, enc); err != nil { + return nil, err + } + submitSM.ProtocolID = 0 + submitSM.RegisteredDelivery = 1 + submitSM.ReplaceIfPresentFlag = 0 + submitSM.EsmClass = 0 + return submitSM, nil +} + +// SMSCAlarm 发告警短信 +func SMSCAlarm(a neDataModel.Alarm, neIP string) error { + mobileList := fmt.Sprint(config.Get("notification.smsc.list")) + if len(mobileList) == 0 { + return fmt.Errorf("smsc list is empty") + } + + // 模板 + textBodyTemplate := `Alarm Notification + Sequence: {{.AlarmSeq}}, + NE Name: {{.NeName}} + NE IP: {{.NeIp}} + Title: {{.AlarmTitle}} + Severity: {{.OrigSeverity}} + Event Time: {{.AlarmTime}} + Alarm Status: {{.AlarmStatus}} + Automatic sent by OMC, please do not reply!` + textTpl, err := tt.New("texttpl").Parse(textBodyTemplate) + if err != nil { + return fmt.Errorf("SMSCAlarm alarmId:%s fail %s", a.AlarmId, err.Error()) + } + // 参数值 + data := map[string]any{ + "AlarmSeq": a.AlarmSeq, + "NeName": a.NeName, + "NeIp": neIP, + "AlarmTitle": a.AlarmTitle, + "OrigSeverity": a.OrigSeverity, + "AlarmTime": date.ParseDateToStr(a.EventTime, time.RFC3339), + "AlarmStatus": a.AlarmStatus, + } + buffer := bytes.NewBuffer(nil) + if err := textTpl.Execute(buffer, data); err != nil { + return fmt.Errorf("EmailAlarm Execute alarmId:%s fail %s", a.AlarmId, err.Error()) + } + textStr := buffer.String() + + // 发送短信 + err = SMSCSendText(textStr, strings.Split(mobileList, ",")) + if err != nil { + return fmt.Errorf("EmailAlarm alarmId:%s fail %s", a.AlarmId, err.Error()) + } + return err +} + +// SMSCSendText 发送文本短信 +func SMSCSendText(textStr string, toMobileArr []string) error { + enable := parse.Boolean(config.Get("notification.smsc.enable")) + if !enable { + return fmt.Errorf("smsc notification not enable") + } + sm := connSM() + if sm == nil { + return fmt.Errorf("smpp new session create failed") + } + + errArr := []string{} + for _, v := range toMobileArr { + submitSM, err := newSubmitSM(v, textStr) + if err != nil { + errArr = append(errArr, err.Error()) + continue + } + if err = sm.Transceiver().Submit(submitSM); err != nil { + errArr = append(errArr, err.Error()) + continue + } + } + if len(errArr) > 0 { + return fmt.Errorf("%s", strings.Join(errArr, ",")) + } + return nil +}