notification.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package surge
  2. import (
  3. "encoding/base64"
  4. "strings"
  5. "github.com/sagernet/sing-box/experimental/libbox/platform"
  6. "github.com/sagernet/sing-box/script/jsc"
  7. "github.com/sagernet/sing-box/script/modules/boxctx"
  8. E "github.com/sagernet/sing/common/exceptions"
  9. "github.com/sagernet/sing/common/logger"
  10. "github.com/sagernet/sing/service"
  11. "github.com/dop251/goja"
  12. )
  13. type Notification struct {
  14. class jsc.Class[*Module, *Notification]
  15. logger logger.ContextLogger
  16. tag string
  17. platformInterface platform.Interface
  18. }
  19. func createNotification(module *Module) jsc.Class[*Module, *Notification] {
  20. class := jsc.NewClass[*Module, *Notification](module)
  21. class.DefineMethod("post", (*Notification).post)
  22. class.DefineMethod("toString", (*Notification).toString)
  23. return class
  24. }
  25. func newNotification(class jsc.Class[*Module, *Notification]) goja.Value {
  26. context := boxctx.MustFromRuntime(class.Runtime())
  27. return class.New(&Notification{
  28. class: class,
  29. logger: context.Logger,
  30. tag: context.Tag,
  31. platformInterface: service.FromContext[platform.Interface](context.Context),
  32. })
  33. }
  34. func (s *Notification) post(call goja.FunctionCall) any {
  35. var (
  36. title string
  37. subtitle string
  38. body string
  39. openURL string
  40. clipboard string
  41. mediaURL string
  42. mediaData []byte
  43. mediaType string
  44. autoDismiss int
  45. )
  46. title = jsc.AssertString(s.class.Runtime(), call.Argument(0), "title", true)
  47. subtitle = jsc.AssertString(s.class.Runtime(), call.Argument(1), "subtitle", true)
  48. body = jsc.AssertString(s.class.Runtime(), call.Argument(2), "body", true)
  49. options := jsc.AssertObject(s.class.Runtime(), call.Argument(3), "options", true)
  50. if options != nil {
  51. action := jsc.AssertString(s.class.Runtime(), options.Get("action"), "options.action", true)
  52. switch action {
  53. case "open-url":
  54. openURL = jsc.AssertString(s.class.Runtime(), options.Get("url"), "options.url", false)
  55. case "clipboard":
  56. clipboard = jsc.AssertString(s.class.Runtime(), options.Get("clipboard"), "options.clipboard", false)
  57. }
  58. mediaURL = jsc.AssertString(s.class.Runtime(), options.Get("media-url"), "options.media-url", true)
  59. mediaBase64 := jsc.AssertString(s.class.Runtime(), options.Get("media-base64"), "options.media-base64", true)
  60. if mediaBase64 != "" {
  61. mediaBinary, err := base64.StdEncoding.DecodeString(mediaBase64)
  62. if err != nil {
  63. panic(s.class.Runtime().NewGoError(E.Cause(err, "decode media-base64")))
  64. }
  65. mediaData = mediaBinary
  66. mediaType = jsc.AssertString(s.class.Runtime(), options.Get("media-base64-mime"), "options.media-base64-mime", false)
  67. }
  68. autoDismiss = int(jsc.AssertInt(s.class.Runtime(), options.Get("auto-dismiss"), "options.auto-dismiss", true))
  69. }
  70. if title != "" && subtitle == "" && body == "" {
  71. body = title
  72. title = ""
  73. } else if title != "" && subtitle != "" && body == "" {
  74. body = subtitle
  75. subtitle = ""
  76. }
  77. var builder strings.Builder
  78. if title != "" {
  79. builder.WriteString("[")
  80. builder.WriteString(title)
  81. if subtitle != "" {
  82. builder.WriteString(" - ")
  83. builder.WriteString(subtitle)
  84. }
  85. builder.WriteString("]: ")
  86. }
  87. builder.WriteString(body)
  88. s.logger.Info("notification: " + builder.String())
  89. if s.platformInterface != nil {
  90. err := s.platformInterface.SendNotification(&platform.Notification{
  91. Identifier: "surge-script-notification-" + s.tag,
  92. TypeName: "Surge Script Notification (" + s.tag + ")",
  93. TypeID: 11,
  94. Title: title,
  95. Subtitle: subtitle,
  96. Body: body,
  97. OpenURL: openURL,
  98. Clipboard: clipboard,
  99. MediaURL: mediaURL,
  100. MediaData: mediaData,
  101. MediaType: mediaType,
  102. Timeout: autoDismiss,
  103. })
  104. if err != nil {
  105. s.logger.Error(E.Cause(err, "send notification"))
  106. }
  107. }
  108. return nil
  109. }
  110. func (s *Notification) toString(call goja.FunctionCall) any {
  111. return "[sing-box Surge notification]"
  112. }