Update Tg bot related function

This commit is contained in:
FranzKafkayu
2022-04-17 11:21:45 +08:00
parent 8791c8729c
commit 72e206879a
27 changed files with 533 additions and 56 deletions

View File

@@ -163,7 +163,9 @@ class AllSetting {
this.webCertFile = "";
this.webKeyFile = "";
this.webBasePath = "/";
this.tgBotToken = "";
this.tgBotChatId = 0;
this.tgRunTime = "";
this.xrayTemplateConfig = "";
this.timeLocation = "Asia/Shanghai";

View File

@@ -1,11 +1,14 @@
package controller
import (
"github.com/gin-gonic/gin"
"net/http"
"time"
"x-ui/logger"
"x-ui/web/job"
"x-ui/web/service"
"x-ui/web/session"
"github.com/gin-gonic/gin"
)
type LoginForm struct {
@@ -59,6 +62,10 @@ func (a *IndexController) login(c *gin.Context) {
logger.Infof("wrong username or password: \"%s\" \"%s\"", form.Username, form.Password)
pureJsonMsg(c, false, "用户名或密码错误")
return
} else {
timeStr := time.Now().Format("2006-01-02 15:04:05")
logger.Infof("%s login success,Ip Address:%s\n", form.Username, getRemoteIp(c))
job.NewStatsNotifyJob().UserLoginNotify(form.Username, getRemoteIp(c), timeStr)
}
err = session.SetLoginUser(c, user)

View File

@@ -27,12 +27,14 @@ type Pager struct {
}
type AllSetting struct {
WebListen string `json:"webListen" form:"webListen"`
WebPort int `json:"webPort" form:"webPort"`
WebCertFile string `json:"webCertFile" form:"webCertFile"`
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"`
WebBasePath string `json:"webBasePath" form:"webBasePath"`
WebListen string `json:"webListen" form:"webListen"`
WebPort int `json:"webPort" form:"webPort"`
WebCertFile string `json:"webCertFile" form:"webCertFile"`
WebKeyFile string `json:"webKeyFile" form:"webKeyFile"`
WebBasePath string `json:"webBasePath" form:"webBasePath"`
TgBotToken string `json:"tgBotToken" form:"tgBotToken"`
TgBotChatId int `json:"tgBotChatId" form:"tgBotChatId"`
TgRunTime string `json:"tgRunTime" form:"tgRunTime"`
XrayTemplateConfig string `json:"xrayTemplateConfig" form:"xrayTemplateConfig"`
TimeLocation string `json:"timeLocation" form:"timeLocation"`

View File

@@ -71,7 +71,14 @@
<setting-list-item type="textarea" title="xray 配置模版" desc="以该模版为基础生成最终的 xray 配置文件,重启面板生效" v-model="allSetting.xrayTemplateConfig"></setting-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane key="4" tab="其他设置">
<a-tab-pane key="4" tab="TG提醒相关设置">
<a-list item-layout="horizontal" style="background: white">
<setting-list-item type="text" title="电报机器人TOKEN" desc="重启面板生效" v-model="allSetting.tgBotToken"></setting-list-item>
<setting-list-item type="number" title="电报机器人ChatId" desc="重启面板生效" v-model.number="allSetting.tgBotChatId"></setting-list-item>
<setting-list-item type="text" title="电报机器人通知时间" desc="采用Crontab定时格式" v-model="allSetting.tgRunTime"></setting-list-item>
</a-list>
</a-tab-pane>
<a-tab-pane key="5" tab="其他设置">
<a-list item-layout="horizontal" style="background: white">
<setting-list-item type="text" title="时区" desc="定时任务按照该时区的时间运行,重启面板生效" v-model="allSetting.timeLocation"></setting-list-item>
</a-list>

121
web/job/stats_notify_job.go Normal file
View File

@@ -0,0 +1,121 @@
package job
import (
"fmt"
"net"
"os"
"x-ui/logger"
"x-ui/util/common"
"x-ui/web/service"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)
type StatsNotifyJob struct {
enable bool
xrayService service.XrayService
inboundService service.InboundService
settingService service.SettingService
}
func NewStatsNotifyJob() *StatsNotifyJob {
return new(StatsNotifyJob)
}
func (j *StatsNotifyJob) SendMsgToTgbot(msg string) {
//Telegram bot basic info
tgBottoken, err := j.settingService.GetTgBotToken()
if err != nil {
logger.Warning("sendMsgToTgbot failed,GetTgBotToken fail:", err)
return
}
tgBotid, err := j.settingService.GetTgBotChatId()
if err != nil {
logger.Warning("sendMsgToTgbot failed,GetTgBotChatId fail:", err)
return
}
bot, err := tgbotapi.NewBotAPI(tgBottoken)
if err != nil {
fmt.Println("get tgbot error:", err)
return
}
bot.Debug = true
fmt.Printf("Authorized on account %s", bot.Self.UserName)
info := tgbotapi.NewMessage(int64(tgBotid), msg)
//msg.ReplyToMessageID = int(tgBotid)
bot.Send(info)
}
//Here run is a interface method of Job interface
func (j *StatsNotifyJob) Run() {
if !j.xrayService.IsXrayRunning() {
return
}
var info string
//get hostname
name, err := os.Hostname()
if err != nil {
fmt.Println("get hostname error:", err)
return
}
info = fmt.Sprintf("主机名称:%s\r\n", name)
//get ip address
var ip string
netInterfaces, err := net.Interfaces()
if err != nil {
fmt.Println("net.Interfaces failed, err:", err.Error())
return
}
for i := 0; i < len(netInterfaces); i++ {
if (netInterfaces[i].Flags & net.FlagUp) != 0 {
addrs, _ := netInterfaces[i].Addrs()
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
ip = ipnet.IP.String()
break
} else {
ip = ipnet.IP.String()
break
}
}
}
}
}
info += fmt.Sprintf("IP地址:%s\r\n \r\n", ip)
//get traffic
inbouds, err := j.inboundService.GetAllInbounds()
if err != nil {
logger.Warning("StatsNotifyJob run failed:", err)
return
}
//NOTE:If there no any sessions here,need to notify here
//TODO:分节点推送,自动转化格式
for _, inbound := range inbouds {
info += fmt.Sprintf("节点名称:%s\r\n端口:%d\r\n上行流量↑:%s\r\n下行流量↓:%s\r\n总流量:%s\r\n \r\n", inbound.Remark, inbound.Port, common.FormatTraffic(inbound.Up), common.FormatTraffic(inbound.Down), common.FormatTraffic((inbound.Up + inbound.Down)))
}
j.SendMsgToTgbot(info)
}
func (j *StatsNotifyJob) UserLoginNotify(username string, ip string, time string) {
if username == "" || ip == "" || time == "" {
logger.Warning("UserLoginNotify failed,invalid info")
return
}
var msg string
//get hostname
name, err := os.Hostname()
if err != nil {
fmt.Println("get hostname error:", err)
return
}
msg = fmt.Sprintf("面板登录提醒\r\n主机名称:%s\r\n", name)
msg += fmt.Sprintf("时间:%s\r\n", time)
msg += fmt.Sprintf("用户:%s\r\n", username)
msg += fmt.Sprintf("IP:%s\r\n", ip)
j.SendMsgToTgbot(msg)
}

View File

@@ -2,12 +2,13 @@ package service
import (
"fmt"
"gorm.io/gorm"
"time"
"x-ui/database"
"x-ui/database/model"
"x-ui/util/common"
"x-ui/xray"
"gorm.io/gorm"
)
type InboundService struct {

View File

@@ -29,6 +29,10 @@ var defaultValueMap = map[string]string{
"secret": random.Seq(32),
"webBasePath": "/",
"timeLocation": "Asia/Shanghai",
"tgBotEnable": "false",
"tgBotToken": "",
"tgBotChatId": "0",
"tgRunTime": "",
}
type SettingService struct {
@@ -156,6 +160,18 @@ func (s *SettingService) setString(key string, value string) error {
return s.saveSetting(key, value)
}
func (s *SettingService) getBool(key string) (bool, error) {
str, err := s.getString(key)
if err != nil {
return false, err
}
return strconv.ParseBool(str)
}
func (s *SettingService) setBool(key string, value bool) error {
return s.setString(key, strconv.FormatBool(value))
}
func (s *SettingService) getInt(key string) (int, error) {
str, err := s.getString(key)
if err != nil {
@@ -176,6 +192,38 @@ func (s *SettingService) GetListen() (string, error) {
return s.getString("webListen")
}
func (s *SettingService) GetTgBotToken() (string, error) {
return s.getString("tgBotToken")
}
func (s *SettingService) SetTgBotToken(token string) error {
return s.setString("tgBotToken", token)
}
func (s *SettingService) GetTgBotChatId() (int, error) {
return s.getInt("tgBotChatId")
}
func (s *SettingService) SetTgBotChatId(chatId int) error {
return s.setInt("tgBotChatId", chatId)
}
func (s *SettingService) SetTgbotenabled(value bool) error {
return s.setBool("tgBotEnable", value)
}
func (s *SettingService) GetTgbotenabled() (bool, error) {
return s.getBool("tgBotEnable")
}
func (s *SettingService) SetTgbotRuntime(time string) error {
return s.setString("tgRunTime", time)
}
func (s *SettingService) GetTgbotRuntime() (string, error) {
return s.getString("tgRunTime")
}
func (s *SettingService) GetPort() (int, error) {
return s.getInt("webPort")
}

View File

@@ -2,10 +2,11 @@ package service
import (
"errors"
"gorm.io/gorm"
"x-ui/database"
"x-ui/database/model"
"x-ui/logger"
"gorm.io/gorm"
)
type UserService struct {

View File

@@ -3,10 +3,11 @@ package service
import (
"encoding/json"
"errors"
"go.uber.org/atomic"
"sync"
"x-ui/logger"
"x-ui/xray"
"go.uber.org/atomic"
)
var p *xray.Process

View File

@@ -4,13 +4,6 @@ import (
"context"
"crypto/tls"
"embed"
"github.com/BurntSushi/toml"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/robfig/cron/v3"
"golang.org/x/text/language"
"html/template"
"io"
"io/fs"
@@ -27,6 +20,14 @@ import (
"x-ui/web/job"
"x-ui/web/network"
"x-ui/web/service"
"github.com/BurntSushi/toml"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
"github.com/nicksnyder/go-i18n/v2/i18n"
"github.com/robfig/cron/v3"
"golang.org/x/text/language"
)
//go:embed assets/*
@@ -294,9 +295,28 @@ func (s *Server) startTask() {
// 每 30 秒检查一次 inbound 流量超出和到期的情况
s.cron.AddJob("@every 30s", job.NewCheckInboundJob())
// 每一天提示一次流量情况,上海时间8点30
var entry cron.EntryID
isTgbotenabled, err := s.settingService.GetTgbotenabled()
if (err == nil) && (isTgbotenabled) {
runtime, err := s.settingService.GetTgbotRuntime()
if err != nil || runtime == "" {
logger.Errorf("Add NewStatsNotifyJob error[%s],Runtime[%s] invalid,wil run default", err, runtime)
runtime = "@daily"
}
logger.Infof("Tg notify enabled,run at %s", runtime)
entry, err = s.cron.AddJob(runtime, job.NewStatsNotifyJob())
if err != nil {
logger.Warning("Add NewStatsNotifyJob error", err)
return
}
} else {
s.cron.Remove(entry)
}
}
func (s *Server) Start() (err error) {
//这是一个匿名函数,没没有函数名
defer func() {
if err != nil {
s.Stop()
@@ -348,6 +368,7 @@ func (s *Server) Start() (err error) {
listener = network.NewAutoHttpsListener(listener)
listener = tls.NewListener(listener, c)
}
if certFile != "" || keyFile != "" {
logger.Info("web server run https on", listener.Addr())
} else {