mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-24 23:05:01 +03:00
7d45456feb
Every bot usage command and the hourly report resolved a Telegram ID by expanding every inbound's settings.clients, re-parsing the matched inbounds and looking up each client again by email. With thousands of clients this ran thousands of times per report and showed up as high PostgreSQL CPU, and an account whose lookup came back empty was retried once per client carrying it. The lookup now uses the normalized clients table and its Telegram-ID index in one query per account, and the hourly pass checks each account once. Bot output and thresholds are unchanged; link callbacks now authorize against the current client records instead of possibly stale settings JSON.
514 lines
17 KiB
Go
514 lines
17 KiB
Go
package tgbot
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/config"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/eventbus"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/util/common"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/web/service"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/xray"
|
|
|
|
"github.com/mymmrac/telego"
|
|
tu "github.com/mymmrac/telego/telegoutil"
|
|
)
|
|
|
|
// SendReport sends a periodic report to admin chats.
|
|
func (t *Tgbot) SendReport() {
|
|
runTime, err := t.settingService.GetTgbotRuntime()
|
|
if err == nil && len(runTime) > 0 {
|
|
msg := ""
|
|
msg += t.I18nBot("tgbot.messages.report", "RunTime=="+runTime)
|
|
msg += t.I18nBot("tgbot.messages.datetime", "DateTime=="+time.Now().Format("2006-01-02 15:04:05"))
|
|
t.SendMsgToTgbotAdmins(msg)
|
|
}
|
|
|
|
info := t.sendServerUsage()
|
|
t.SendMsgToTgbotAdmins(info)
|
|
|
|
t.sendExhaustedToAdmins()
|
|
t.notifyExhausted()
|
|
|
|
backupEnable, err := t.settingService.GetTgBotBackup()
|
|
if err == nil && backupEnable {
|
|
t.SendBackupToAdmins()
|
|
}
|
|
}
|
|
|
|
// SendBackupToAdmins sends a database backup to admin chats.
|
|
func (t *Tgbot) SendBackupToAdmins() {
|
|
if !t.IsRunning() {
|
|
return
|
|
}
|
|
dbData, err := t.serverService.GetDb()
|
|
if err != nil {
|
|
logger.Error("Error in getting db backup: ", err)
|
|
}
|
|
dbFilename := t.serverService.BackupFilename("")
|
|
admins := adminSnapshot()
|
|
for i, adminId := range admins {
|
|
t.sendBackupData(adminId, dbData, dbFilename)
|
|
// Add delay between sends to avoid Telegram rate limits
|
|
if i < len(admins)-1 {
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}
|
|
}
|
|
|
|
// sendExhaustedToAdmins sends notifications about exhausted clients to admins.
|
|
func (t *Tgbot) sendExhaustedToAdmins() {
|
|
if !t.IsRunning() {
|
|
return
|
|
}
|
|
for _, adminId := range adminSnapshot() {
|
|
t.getExhausted(adminId)
|
|
}
|
|
}
|
|
|
|
// getServerUsage retrieves and formats server usage information.
|
|
func (t *Tgbot) getServerUsage(chatId int64, messageID ...int) string {
|
|
info := t.prepareServerUsageInfo()
|
|
|
|
keyboard := tu.InlineKeyboard(tu.InlineKeyboardRow(
|
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.refresh")).WithCallbackData(t.encodeQuery("usage_refresh"))))
|
|
|
|
if len(messageID) > 0 {
|
|
t.editMessageTgBot(chatId, messageID[0], info, keyboard)
|
|
} else {
|
|
t.SendMsgToTgbot(chatId, info, keyboard)
|
|
}
|
|
|
|
return info
|
|
}
|
|
|
|
// Send server usage without an inline keyboard
|
|
func (t *Tgbot) sendServerUsage() string {
|
|
info := t.prepareServerUsageInfo()
|
|
return info
|
|
}
|
|
|
|
// prepareServerUsageInfo prepares the server usage information string.
|
|
func (t *Tgbot) prepareServerUsageInfo() string {
|
|
// Check if we have cached data first
|
|
if cachedStats, found := t.getCachedServerStats(); found {
|
|
return cachedStats
|
|
}
|
|
|
|
info, ipv4, ipv6 := "", "", ""
|
|
|
|
// get latest status of server with caching
|
|
if cachedStatus, found := t.getCachedStatus(); found {
|
|
t.lastStatus = cachedStatus
|
|
} else {
|
|
t.lastStatus = t.serverService.GetStatus(t.lastStatus)
|
|
t.setCachedStatus(t.lastStatus)
|
|
}
|
|
var onlines []string
|
|
if process := service.XrayProcess(); process != nil {
|
|
onlines = process.GetOnlineClients()
|
|
}
|
|
|
|
info += t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
|
|
info += t.I18nBot("tgbot.messages.version", "Version=="+config.GetPanelVersion())
|
|
info += t.I18nBot("tgbot.messages.xrayVersion", "XrayVersion=="+fmt.Sprint(t.lastStatus.Xray.Version))
|
|
|
|
// get ip address
|
|
netInterfaces, err := net.Interfaces()
|
|
if err != nil {
|
|
logger.Error("net.Interfaces failed, err: ", err.Error())
|
|
info += t.I18nBot("tgbot.messages.ip", "IP=="+t.I18nBot("tgbot.unknown"))
|
|
info += "\r\n"
|
|
} else {
|
|
for i := range netInterfaces {
|
|
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 {
|
|
ipv4 += ipnet.IP.String() + " "
|
|
} else if ipnet.IP.To16() != nil && !ipnet.IP.IsLinkLocalUnicast() {
|
|
ipv6 += ipnet.IP.String() + " "
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
info += t.I18nBot("tgbot.messages.ipv4", "IPv4=="+ipv4)
|
|
info += t.I18nBot("tgbot.messages.ipv6", "IPv6=="+ipv6)
|
|
}
|
|
|
|
info += t.I18nBot("tgbot.messages.serverUpTime", "UpTime=="+strconv.FormatUint(t.lastStatus.Uptime/86400, 10), "Unit=="+t.I18nBot("tgbot.days"))
|
|
info += t.I18nBot("tgbot.messages.serverLoad", "Load1=="+strconv.FormatFloat(t.lastStatus.Loads[0], 'f', 2, 64), "Load2=="+strconv.FormatFloat(t.lastStatus.Loads[1], 'f', 2, 64), "Load3=="+strconv.FormatFloat(t.lastStatus.Loads[2], 'f', 2, 64))
|
|
info += t.I18nBot("tgbot.messages.serverMemory", "Current=="+common.FormatTraffic(int64(t.lastStatus.Mem.Current)), "Total=="+common.FormatTraffic(int64(t.lastStatus.Mem.Total)))
|
|
info += t.I18nBot("tgbot.messages.onlinesCount", "Count=="+fmt.Sprint(len(onlines)))
|
|
info += t.I18nBot("tgbot.messages.tcpCount", "Count=="+strconv.Itoa(t.lastStatus.TcpCount))
|
|
info += t.I18nBot("tgbot.messages.udpCount", "Count=="+strconv.Itoa(t.lastStatus.UdpCount))
|
|
info += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Sent+t.lastStatus.NetTraffic.Recv)), "Upload=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Sent)), "Download=="+common.FormatTraffic(int64(t.lastStatus.NetTraffic.Recv)))
|
|
info += t.I18nBot("tgbot.messages.xrayStatus", "State=="+fmt.Sprint(t.lastStatus.Xray.State))
|
|
|
|
// Cache the complete server stats
|
|
t.setCachedServerStats(info)
|
|
|
|
return info
|
|
}
|
|
|
|
// UserLoginNotify publishes a login event to the event bus.
|
|
func (t *Tgbot) UserLoginNotify(attempt LoginAttempt) {
|
|
if attempt.Username == "" || attempt.IP == "" || attempt.Time == "" {
|
|
logger.Warning("UserLoginNotify failed, invalid info!")
|
|
return
|
|
}
|
|
|
|
if EventBus == nil {
|
|
return
|
|
}
|
|
|
|
status := "fail"
|
|
if attempt.Status == LoginSuccess {
|
|
status = "success"
|
|
}
|
|
|
|
EventBus.Publish(eventbus.Event{
|
|
Type: eventbus.EventLoginAttempt,
|
|
Source: attempt.IP,
|
|
Data: &eventbus.LoginEventData{
|
|
Username: attempt.Username,
|
|
IP: attempt.IP,
|
|
Time: attempt.Time,
|
|
Status: status,
|
|
Reason: attempt.Reason,
|
|
},
|
|
})
|
|
}
|
|
|
|
// getExhausted retrieves and sends information about exhausted clients.
|
|
func (t *Tgbot) getExhausted(chatId int64) {
|
|
trDiff := int64(0)
|
|
exDiff := int64(0)
|
|
now := time.Now().Unix() * 1000
|
|
var exhaustedInbounds []model.Inbound
|
|
var exhaustedClients []xray.ClientTraffic
|
|
var disabledInbounds []model.Inbound
|
|
var disabledClients []xray.ClientTraffic
|
|
|
|
TrafficThreshold, err := t.settingService.GetTrafficDiff()
|
|
if err == nil && TrafficThreshold > 0 {
|
|
trDiff = int64(TrafficThreshold) * 1073741824
|
|
}
|
|
ExpireThreshold, err := t.settingService.GetExpireDiff()
|
|
if err == nil && ExpireThreshold > 0 {
|
|
exDiff = int64(ExpireThreshold) * 86400000
|
|
}
|
|
inbounds, err := t.inboundService.GetAllInbounds()
|
|
if err != nil {
|
|
logger.Warning("Unable to load Inbounds", err)
|
|
}
|
|
|
|
seenClients := make(map[string]bool)
|
|
for _, inbound := range inbounds {
|
|
if inbound.Enable {
|
|
if (inbound.ExpiryTime > 0 && (inbound.ExpiryTime-now < exDiff)) ||
|
|
(inbound.Total > 0 && (inbound.Total-(inbound.Up+inbound.Down) < trDiff)) {
|
|
exhaustedInbounds = append(exhaustedInbounds, *inbound)
|
|
}
|
|
if len(inbound.ClientStats) > 0 {
|
|
for _, client := range inbound.ClientStats {
|
|
if seenClients[client.Email] {
|
|
continue
|
|
}
|
|
seenClients[client.Email] = true
|
|
if client.Enable {
|
|
if (client.ExpiryTime > 0 && (client.ExpiryTime-now < exDiff)) ||
|
|
(client.Total > 0 && (client.Total-(client.Up+client.Down) < trDiff)) {
|
|
exhaustedClients = append(exhaustedClients, client)
|
|
}
|
|
} else {
|
|
disabledClients = append(disabledClients, client)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
disabledInbounds = append(disabledInbounds, *inbound)
|
|
}
|
|
}
|
|
|
|
// Inbounds
|
|
output := ""
|
|
output += t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.inbounds"))
|
|
output += t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledInbounds)))
|
|
output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(len(exhaustedInbounds)))
|
|
|
|
if len(exhaustedInbounds) > 0 {
|
|
output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+t.I18nBot("tgbot.inbounds"))
|
|
|
|
for _, inbound := range exhaustedInbounds {
|
|
output += t.I18nBot("tgbot.messages.inbound", "Remark=="+inbound.Remark)
|
|
output += t.I18nBot("tgbot.messages.port", "Port=="+strconv.Itoa(inbound.Port))
|
|
output += t.I18nBot("tgbot.messages.traffic", "Total=="+common.FormatTraffic((inbound.Up+inbound.Down)), "Upload=="+common.FormatTraffic(inbound.Up), "Download=="+common.FormatTraffic(inbound.Down))
|
|
if inbound.ExpiryTime == 0 {
|
|
output += t.I18nBot("tgbot.messages.expire", "Time=="+t.I18nBot("tgbot.unlimited"))
|
|
} else {
|
|
output += t.I18nBot("tgbot.messages.expire", "Time=="+time.Unix((inbound.ExpiryTime/1000), 0).Format("2006-01-02 15:04:05"))
|
|
}
|
|
output += "\r\n"
|
|
}
|
|
}
|
|
|
|
// Clients
|
|
exhaustedCC := len(exhaustedClients)
|
|
output += t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.clients"))
|
|
output += t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledClients)))
|
|
output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(exhaustedCC))
|
|
|
|
if exhaustedCC > 0 {
|
|
output += t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+t.I18nBot("tgbot.clients"))
|
|
var buttons []telego.InlineKeyboardButton
|
|
for _, traffic := range exhaustedClients {
|
|
output += t.clientInfoMsg(&traffic, true, false, false, true, true, false)
|
|
output += "\r\n"
|
|
buttons = append(buttons, tu.InlineKeyboardButton(traffic.Email).WithCallbackData(t.encodeQuery("client_get_usage "+traffic.Email)))
|
|
}
|
|
cols := 0
|
|
if exhaustedCC < 11 {
|
|
cols = 1
|
|
} else {
|
|
cols = 2
|
|
}
|
|
output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
|
|
keyboard := tu.InlineKeyboardGrid(tu.InlineKeyboardCols(cols, buttons...))
|
|
t.SendMsgToTgbot(chatId, output, keyboard)
|
|
} else {
|
|
output += t.I18nBot("tgbot.messages.refreshedOn", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
|
|
t.SendMsgToTgbot(chatId, output)
|
|
}
|
|
}
|
|
|
|
// notifyExhausted sends notifications for exhausted clients.
|
|
func (t *Tgbot) notifyExhausted() {
|
|
trDiff := int64(0)
|
|
exDiff := int64(0)
|
|
now := time.Now().Unix() * 1000
|
|
|
|
TrafficThreshold, err := t.settingService.GetTrafficDiff()
|
|
if err == nil && TrafficThreshold > 0 {
|
|
trDiff = int64(TrafficThreshold) * 1073741824
|
|
}
|
|
ExpireThreshold, err := t.settingService.GetExpireDiff()
|
|
if err == nil && ExpireThreshold > 0 {
|
|
exDiff = int64(ExpireThreshold) * 86400000
|
|
}
|
|
inbounds, err := t.inboundService.GetAllInbounds()
|
|
if err != nil {
|
|
logger.Warning("Unable to load Inbounds", err)
|
|
}
|
|
|
|
chatIDsDone := make(map[int64]struct{})
|
|
for _, inbound := range inbounds {
|
|
if inbound.Enable {
|
|
if len(inbound.ClientStats) > 0 {
|
|
clients, err := t.inboundService.ListClientsForInbound(inbound.Id)
|
|
if err == nil {
|
|
for _, client := range clients {
|
|
if client.TgID != 0 {
|
|
chatID := client.TgID
|
|
if _, done := chatIDsDone[chatID]; !done && !checkAdmin(chatID) {
|
|
chatIDsDone[chatID] = struct{}{}
|
|
var disabledClients []xray.ClientTraffic
|
|
var exhaustedClients []xray.ClientTraffic
|
|
traffics, err := t.inboundService.GetClientTrafficTgBot(client.TgID)
|
|
if err != nil {
|
|
logger.Warningf("Unable to load Telegram client traffic for %d: %v", chatID, err)
|
|
}
|
|
if err == nil && len(traffics) > 0 {
|
|
var output strings.Builder
|
|
output.WriteString(t.I18nBot("tgbot.messages.exhaustedCount", "Type=="+t.I18nBot("tgbot.clients")))
|
|
for _, traffic := range traffics {
|
|
if traffic.Enable {
|
|
if (traffic.ExpiryTime > 0 && (traffic.ExpiryTime-now < exDiff)) ||
|
|
(traffic.Total > 0 && (traffic.Total-(traffic.Up+traffic.Down) < trDiff)) {
|
|
exhaustedClients = append(exhaustedClients, *traffic)
|
|
}
|
|
} else {
|
|
disabledClients = append(disabledClients, *traffic)
|
|
}
|
|
}
|
|
if len(exhaustedClients) > 0 {
|
|
output.WriteString(t.I18nBot("tgbot.messages.disabled", "Disabled=="+strconv.Itoa(len(disabledClients))))
|
|
if len(disabledClients) > 0 {
|
|
output.WriteString(t.I18nBot("tgbot.clients"))
|
|
output.WriteString(":\r\n")
|
|
for _, traffic := range disabledClients {
|
|
output.WriteString(" ")
|
|
output.WriteString(traffic.Email)
|
|
}
|
|
output.WriteString("\r\n")
|
|
}
|
|
output.WriteString("\r\n")
|
|
output.WriteString(t.I18nBot("tgbot.messages.depleteSoon", "Deplete=="+strconv.Itoa(len(exhaustedClients))))
|
|
for _, traffic := range exhaustedClients {
|
|
output.WriteString(t.clientInfoMsg(&traffic, true, false, false, true, true, false))
|
|
output.WriteString("\r\n")
|
|
}
|
|
t.SendMsgToTgbot(chatID, output.String())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// onlineClients retrieves and sends information about online clients.
|
|
func (t *Tgbot) onlineClients(chatId int64, messageID ...int) {
|
|
process := service.XrayProcess()
|
|
if process == nil || !process.IsRunning() {
|
|
return
|
|
}
|
|
|
|
onlines := process.GetOnlineClients()
|
|
onlinesCount := len(onlines)
|
|
output := t.I18nBot("tgbot.messages.onlinesCount", "Count=="+fmt.Sprint(onlinesCount))
|
|
keyboard := tu.InlineKeyboard(tu.InlineKeyboardRow(
|
|
tu.InlineKeyboardButton(t.I18nBot("tgbot.buttons.refresh")).WithCallbackData(t.encodeQuery("onlines_refresh"))))
|
|
|
|
if onlinesCount > 0 {
|
|
var buttons []telego.InlineKeyboardButton
|
|
for _, online := range onlines {
|
|
label := online
|
|
if _, inbound, err := t.inboundService.GetClientInboundByEmail(online); err == nil && inbound != nil && inbound.Remark != "" {
|
|
label = online + " - " + inbound.Remark
|
|
}
|
|
buttons = append(buttons, tu.InlineKeyboardButton(label).WithCallbackData(t.encodeQuery("client_get_usage "+online)))
|
|
}
|
|
cols := 0
|
|
if onlinesCount < 21 {
|
|
cols = 2
|
|
} else if onlinesCount < 61 {
|
|
cols = 3
|
|
} else {
|
|
cols = 4
|
|
}
|
|
keyboard.InlineKeyboard = append(keyboard.InlineKeyboard, tu.InlineKeyboardCols(cols, buttons...)...)
|
|
}
|
|
|
|
if len(messageID) > 0 {
|
|
t.editMessageTgBot(chatId, messageID[0], output, keyboard)
|
|
} else {
|
|
t.SendMsgToTgbot(chatId, output, keyboard)
|
|
}
|
|
}
|
|
|
|
// sendBackup sends a backup of the database and configuration files.
|
|
func (t *Tgbot) sendBackup(chatId int64) {
|
|
dbData, err := t.serverService.GetDb()
|
|
if err != nil {
|
|
logger.Error("Error in getting db backup: ", err)
|
|
}
|
|
t.sendBackupData(chatId, dbData, t.serverService.BackupFilename(""))
|
|
}
|
|
|
|
func (t *Tgbot) sendBackupData(chatId int64, dbData []byte, dbFilename string) {
|
|
output := t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
|
|
output += t.I18nBot("tgbot.messages.backupTime", "Time=="+time.Now().Format("2006-01-02 15:04:05"))
|
|
t.SendMsgToTgbot(chatId, output)
|
|
|
|
// Send database backup (SQLite file, or a pg_dump archive on PostgreSQL)
|
|
if dbData != nil {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
document := tu.Document(
|
|
tu.ID(chatId),
|
|
tu.FileFromBytes(dbData, dbFilename),
|
|
)
|
|
_, err := bot.SendDocument(ctx, document)
|
|
cancel()
|
|
if err != nil {
|
|
logger.Error("Error in uploading backup: ", err)
|
|
}
|
|
}
|
|
|
|
// Small delay between file sends
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
// Send config.json backup
|
|
file, err := os.Open(xray.GetConfigPath())
|
|
if err == nil {
|
|
defer file.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
document := tu.Document(
|
|
tu.ID(chatId),
|
|
tu.File(file),
|
|
)
|
|
_, err = bot.SendDocument(ctx, document)
|
|
if err != nil {
|
|
logger.Error("Error in uploading config.json: ", err)
|
|
}
|
|
} else {
|
|
logger.Error("Error in opening config.json file for backup: ", err)
|
|
}
|
|
}
|
|
|
|
// sendBanLogs sends the ban logs to the specified chat.
|
|
func (t *Tgbot) sendBanLogs(chatId int64, dt bool) {
|
|
if dt {
|
|
output := t.I18nBot("tgbot.messages.hostname", "Hostname=="+hostname)
|
|
output += t.I18nBot("tgbot.messages.datetime", "DateTime=="+time.Now().Format("2006-01-02 15:04:05"))
|
|
t.SendMsgToTgbot(chatId, output)
|
|
}
|
|
|
|
file, err := os.Open(xray.GetIPLimitBannedPrevLogPath())
|
|
if err == nil {
|
|
// Check if the file is non-empty before attempting to upload
|
|
fileInfo, _ := file.Stat()
|
|
if fileInfo.Size() > 0 {
|
|
document := tu.Document(
|
|
tu.ID(chatId),
|
|
tu.File(file),
|
|
)
|
|
_, err = bot.SendDocument(context.Background(), document)
|
|
if err != nil {
|
|
logger.Error("Error in uploading IPLimitBannedPrevLog: ", err)
|
|
}
|
|
} else {
|
|
logger.Warning("IPLimitBannedPrevLog file is empty, not uploading.")
|
|
}
|
|
file.Close()
|
|
} else {
|
|
logger.Error("Error in opening IPLimitBannedPrevLog file for backup: ", err)
|
|
}
|
|
|
|
file, err = os.Open(xray.GetIPLimitBannedLogPath())
|
|
if err == nil {
|
|
// Check if the file is non-empty before attempting to upload
|
|
fileInfo, _ := file.Stat()
|
|
if fileInfo.Size() > 0 {
|
|
document := tu.Document(
|
|
tu.ID(chatId),
|
|
tu.File(file),
|
|
)
|
|
_, err = bot.SendDocument(context.Background(), document)
|
|
if err != nil {
|
|
logger.Error("Error in uploading IPLimitBannedLog: ", err)
|
|
}
|
|
} else {
|
|
logger.Warning("IPLimitBannedLog file is empty, not uploading.")
|
|
}
|
|
file.Close()
|
|
} else {
|
|
logger.Error("Error in opening IPLimitBannedLog file for backup: ", err)
|
|
}
|
|
}
|