tgbot_event.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package tgbot
  2. import (
  3. "fmt"
  4. "os"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "github.com/mhsanaei/3x-ui/v3/internal/eventbus"
  9. )
  10. var cachedHostname string
  11. func getHostname() string {
  12. if cachedHostname != "" {
  13. return cachedHostname
  14. }
  15. h, err := os.Hostname()
  16. if err != nil {
  17. cachedHostname = "unknown"
  18. } else {
  19. cachedHostname = h
  20. }
  21. return cachedHostname
  22. }
  23. var tgEventLimiter = eventbus.NewRateLimiter(1 * time.Minute)
  24. // HandleEvent is the eventbus subscriber callback. It formats incoming events
  25. // as Telegram messages and sends them to all admin chats.
  26. func (t *Tgbot) HandleEvent(e eventbus.Event) {
  27. if !t.isEventEnabled(e.Type) {
  28. return
  29. }
  30. if e.Type != eventbus.EventLoginAttempt {
  31. if !tgEventLimiter.Allow(e.Type, e.Source) {
  32. return
  33. }
  34. }
  35. msg := t.formatEventMessage(e)
  36. if msg != "" {
  37. t.SendMsgToTgbotAdmins(msg)
  38. }
  39. }
  40. func (t *Tgbot) isEventEnabled(eventType eventbus.EventType) bool {
  41. events, err := t.settingService.GetTgEnabledEvents()
  42. if err != nil || events == "" {
  43. return false
  44. }
  45. for _, e := range strings.Split(events, ",") {
  46. if strings.TrimSpace(e) == string(eventType) {
  47. return true
  48. }
  49. }
  50. return false
  51. }
  52. func (t *Tgbot) formatEventMessage(e eventbus.Event) string {
  53. host := getHostname()
  54. header := fmt.Sprintf("<b>📡 %s</b>\n", host)
  55. switch e.Type {
  56. case eventbus.EventOutboundDown:
  57. msg := header + t.I18nBot("tgbot.messages.eventOutboundDown",
  58. "Tag=="+e.Source)
  59. if data, ok := e.Data.(*eventbus.OutboundHealthData); ok {
  60. if data.Error != "" {
  61. msg += "\n" + t.I18nBot("tgbot.messages.eventErrorDetail",
  62. "Error=="+data.Error)
  63. }
  64. if data.Delay > 0 {
  65. msg += "\n" + t.I18nBot("tgbot.messages.eventDelayDetail",
  66. "Delay=="+fmt.Sprintf("%d", data.Delay))
  67. }
  68. }
  69. return msg
  70. case eventbus.EventOutboundUp:
  71. msg := header + t.I18nBot("tgbot.messages.eventOutboundUp",
  72. "Tag=="+e.Source)
  73. if data, ok := e.Data.(*eventbus.OutboundHealthData); ok && data.Delay > 0 {
  74. msg += "\n" + t.I18nBot("tgbot.messages.eventDelayDetail",
  75. "Delay=="+fmt.Sprintf("%d", data.Delay))
  76. }
  77. return msg
  78. case eventbus.EventXrayCrash:
  79. errStr := ""
  80. if e.Data != nil {
  81. errStr = fmt.Sprint(e.Data)
  82. }
  83. msg := header + "🔥 " + t.I18nBot("tgbot.messages.eventXrayCrash")
  84. if errStr != "" {
  85. msg += "\n" + t.I18nBot("tgbot.messages.eventXrayCrashError", "Error=="+errStr)
  86. }
  87. return msg
  88. case eventbus.EventNodeDown:
  89. msg := header + "🔴 " + t.I18nBot("tgbot.messages.eventNodeDown", "Name=="+e.Source)
  90. if data, ok := e.Data.(*eventbus.NodeHealthData); ok && data.XrayError != "" {
  91. msg += "\n" + t.I18nBot("tgbot.messages.eventErrorDetail", "Error=="+data.XrayError)
  92. }
  93. return msg
  94. case eventbus.EventNodeUp:
  95. msg := header + "🟢 " + t.I18nBot("tgbot.messages.eventNodeUp", "Name=="+e.Source)
  96. if data, ok := e.Data.(*eventbus.NodeHealthData); ok && data.LatencyMs > 0 {
  97. msg += "\n" + t.I18nBot("tgbot.messages.eventDelayDetail", "Delay=="+fmt.Sprintf("%d", data.LatencyMs))
  98. }
  99. return msg
  100. case eventbus.EventCPUHigh:
  101. if data, ok := e.Data.(*eventbus.SystemMetricData); ok {
  102. tgCpu, err := t.settingService.GetTgCpu()
  103. if err != nil || tgCpu <= 0 || data.Percent <= float64(tgCpu) {
  104. return ""
  105. }
  106. return header + "🔴 " + t.I18nBot("tgbot.messages.cpuThreshold",
  107. "Percent=="+strconv.FormatFloat(data.Percent, 'f', 2, 64),
  108. "Threshold=="+strconv.Itoa(tgCpu))
  109. }
  110. return ""
  111. case eventbus.EventLoginAttempt:
  112. if data, ok := e.Data.(*eventbus.LoginEventData); ok {
  113. if data.Status == "success" {
  114. msg := t.I18nBot("tgbot.messages.loginSuccess")
  115. msg += t.I18nBot("tgbot.messages.hostname", "Hostname=="+host)
  116. msg += t.I18nBot("tgbot.messages.username", "Username=="+data.Username)
  117. msg += t.I18nBot("tgbot.messages.ip", "IP=="+data.IP)
  118. msg += t.I18nBot("tgbot.messages.time", "Time=="+data.Time)
  119. return msg
  120. }
  121. msg := t.I18nBot("tgbot.messages.loginFailed")
  122. msg += t.I18nBot("tgbot.messages.hostname", "Hostname=="+host)
  123. if data.Reason != "" {
  124. msg += t.I18nBot("tgbot.messages.reason", "Reason=="+data.Reason)
  125. }
  126. msg += t.I18nBot("tgbot.messages.username", "Username=="+data.Username)
  127. msg += t.I18nBot("tgbot.messages.ip", "IP=="+data.IP)
  128. msg += t.I18nBot("tgbot.messages.time", "Time=="+data.Time)
  129. return msg
  130. }
  131. return header + t.I18nBot("tgbot.messages.eventLoginFallback", "Source=="+e.Source)
  132. }
  133. return ""
  134. }