| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- package tgbot
- import (
- "fmt"
- "os"
- "strconv"
- "strings"
- "time"
- "github.com/mhsanaei/3x-ui/v3/internal/eventbus"
- )
- var cachedHostname string
- func getHostname() string {
- if cachedHostname != "" {
- return cachedHostname
- }
- h, err := os.Hostname()
- if err != nil {
- cachedHostname = "unknown"
- } else {
- cachedHostname = h
- }
- return cachedHostname
- }
- var tgEventLimiter = eventbus.NewRateLimiter(1 * time.Minute)
- // HandleEvent is the eventbus subscriber callback. It formats incoming events
- // as Telegram messages and sends them to all admin chats.
- func (t *Tgbot) HandleEvent(e eventbus.Event) {
- if !t.isEventEnabled(e.Type) {
- return
- }
- if e.Type != eventbus.EventLoginAttempt {
- if !tgEventLimiter.Allow(e.Type, e.Source) {
- return
- }
- }
- msg := t.formatEventMessage(e)
- if msg != "" {
- t.SendMsgToTgbotAdmins(msg)
- }
- }
- func (t *Tgbot) isEventEnabled(eventType eventbus.EventType) bool {
- events, err := t.settingService.GetTgEnabledEvents()
- if err != nil || events == "" {
- return false
- }
- for _, e := range strings.Split(events, ",") {
- if strings.TrimSpace(e) == string(eventType) {
- return true
- }
- }
- return false
- }
- func (t *Tgbot) formatEventMessage(e eventbus.Event) string {
- host := getHostname()
- header := fmt.Sprintf("<b>📡 %s</b>\n", host)
- switch e.Type {
- case eventbus.EventOutboundDown:
- msg := header + t.I18nBot("tgbot.messages.eventOutboundDown",
- "Tag=="+e.Source)
- if data, ok := e.Data.(*eventbus.OutboundHealthData); ok {
- if data.Error != "" {
- msg += "\n" + t.I18nBot("tgbot.messages.eventErrorDetail",
- "Error=="+data.Error)
- }
- if data.Delay > 0 {
- msg += "\n" + t.I18nBot("tgbot.messages.eventDelayDetail",
- "Delay=="+fmt.Sprintf("%d", data.Delay))
- }
- }
- return msg
- case eventbus.EventOutboundUp:
- msg := header + t.I18nBot("tgbot.messages.eventOutboundUp",
- "Tag=="+e.Source)
- if data, ok := e.Data.(*eventbus.OutboundHealthData); ok && data.Delay > 0 {
- msg += "\n" + t.I18nBot("tgbot.messages.eventDelayDetail",
- "Delay=="+fmt.Sprintf("%d", data.Delay))
- }
- return msg
- case eventbus.EventXrayCrash:
- errStr := ""
- if e.Data != nil {
- errStr = fmt.Sprint(e.Data)
- }
- msg := header + "🔥 " + t.I18nBot("tgbot.messages.eventXrayCrash")
- if errStr != "" {
- msg += "\n" + t.I18nBot("tgbot.messages.eventXrayCrashError", "Error=="+errStr)
- }
- return msg
- case eventbus.EventNodeDown:
- msg := header + "🔴 " + t.I18nBot("tgbot.messages.eventNodeDown", "Name=="+e.Source)
- if data, ok := e.Data.(*eventbus.NodeHealthData); ok && data.XrayError != "" {
- msg += "\n" + t.I18nBot("tgbot.messages.eventErrorDetail", "Error=="+data.XrayError)
- }
- return msg
- case eventbus.EventNodeUp:
- msg := header + "🟢 " + t.I18nBot("tgbot.messages.eventNodeUp", "Name=="+e.Source)
- if data, ok := e.Data.(*eventbus.NodeHealthData); ok && data.LatencyMs > 0 {
- msg += "\n" + t.I18nBot("tgbot.messages.eventDelayDetail", "Delay=="+fmt.Sprintf("%d", data.LatencyMs))
- }
- return msg
- case eventbus.EventCPUHigh:
- if data, ok := e.Data.(*eventbus.SystemMetricData); ok {
- tgCpu, err := t.settingService.GetTgCpu()
- if err != nil || tgCpu <= 0 || data.Percent <= float64(tgCpu) {
- return ""
- }
- return header + "🔴 " + t.I18nBot("tgbot.messages.cpuThreshold",
- "Percent=="+strconv.FormatFloat(data.Percent, 'f', 2, 64),
- "Threshold=="+strconv.Itoa(tgCpu))
- }
- return ""
- case eventbus.EventLoginAttempt:
- if data, ok := e.Data.(*eventbus.LoginEventData); ok {
- if data.Status == "success" {
- msg := t.I18nBot("tgbot.messages.loginSuccess")
- msg += t.I18nBot("tgbot.messages.hostname", "Hostname=="+host)
- msg += t.I18nBot("tgbot.messages.username", "Username=="+data.Username)
- msg += t.I18nBot("tgbot.messages.ip", "IP=="+data.IP)
- msg += t.I18nBot("tgbot.messages.time", "Time=="+data.Time)
- return msg
- }
- msg := t.I18nBot("tgbot.messages.loginFailed")
- msg += t.I18nBot("tgbot.messages.hostname", "Hostname=="+host)
- if data.Reason != "" {
- msg += t.I18nBot("tgbot.messages.reason", "Reason=="+data.Reason)
- }
- msg += t.I18nBot("tgbot.messages.username", "Username=="+data.Username)
- msg += t.I18nBot("tgbot.messages.ip", "IP=="+data.IP)
- msg += t.I18nBot("tgbot.messages.time", "Time=="+data.Time)
- return msg
- }
- return header + t.I18nBot("tgbot.messages.eventLoginFallback", "Source=="+e.Source)
- }
- return ""
- }
|