package main import ( "fmt" "math" "strings" "time" "github.com/go-errors/errors" "github.com/samber/lo" tele "gopkg.in/telebot.v3" "git.gensokyo.cafe/kkyy/tgbot_misaka_5882f7/stats" ) func isFromAdmin(sender *tele.User) bool { if sender == nil { return false } _, ok := config.AdminUIDs[sender.ID] return ok } func initBot() (*tele.Bot, error) { pref := tele.Settings{ Token: config.TGBotToken, Poller: &tele.LongPoller{Timeout: 15 * time.Second}, } b, err := tele.NewBot(pref) if err != nil { return nil, errors.Wrap(err, 0) } // command routing b.Handle("/start", handleStartCmd) b.Handle("/traffic", handleTrafficCmd) b.Handle("/me", handleUserInfoCmd) b.Handle("/chat", handleChatInfoCmd) b.Handle("/year_progress", handleYearProgressCmd) return b, nil } func handleStartCmd(c tele.Context) error { if !isFromAdmin(c.Sender()) { return c.Send("Hello, stranger :)") } return c.Send("Hi :)") } func handleTrafficCmd(c tele.Context) error { if !isFromAdmin(c.Sender()) { return nil } dailyTraffic, err := stats.VnstatDailyTraffic(config.WatchedInterface) if err != nil { _ = c.Reply("im die, thank you forever") return err } monthlyTraffic, err := stats.VnstatMonthlyTraffic(config.WatchedInterface) if err != nil { _ = c.Reply("im die, thank you forever") return err } var responseParts = []string{"*Traffic Usage Summaries*\n"} // Yesterday's traffic if present if len(dailyTraffic) > 1 { row := dailyTraffic[len(dailyTraffic)-2] day := fmt.Sprintf("%d-%02d-%02d", row.Date.Year, row.Date.Month, row.Date.Day) responseParts = append(responseParts, fmt.Sprintf("Yesterday (%s):` %s`", day, fmtTraffic(row))) } // Today's traffic if present if len(dailyTraffic) > 0 { row := dailyTraffic[len(dailyTraffic)-1] responseParts = append(responseParts, fmt.Sprintf("Today so far:` %s`\n", fmtTraffic(row))) } // Last month's traffic, if present if len(monthlyTraffic) > 1 { row := monthlyTraffic[len(monthlyTraffic)-2] month := fmt.Sprintf("%d-%02d", row.Date.Year, row.Date.Month) responseParts = append(responseParts, fmt.Sprintf("Last month (%s):` %s`", month, fmtTraffic(row))) } // This month's traffic, if present if len(monthlyTraffic) > 0 { row := monthlyTraffic[len(monthlyTraffic)-1] responseParts = append(responseParts, fmt.Sprintf("This month so far:` %s`", fmtTraffic(row))) responseParts = append(responseParts, drawBarForTrafficRecord(row)) } var respText string if len(responseParts) == 1 { respText = "No traffic data available." } else { respText = strings.Join(responseParts, "\n") } return c.Reply(respText, &tele.SendOptions{ParseMode: tele.ModeMarkdown}) } func fmtTraffic(r stats.VnstatTrafficRecord) string { effective := lo.Max([]uint64{r.Rx, r.Tx}) return fmt.Sprintf("%.2f GiB", float64(effective)/1024/1024/1024) } func handleUserInfoCmd(c tele.Context) error { u := c.Sender() if u == nil { return c.Reply("Unknown.") } replyText := []string{ `*User Info*`, "```", fmt.Sprintf(`ID: %d`, u.ID), fmt.Sprintf(`Username: %s`, u.Username), fmt.Sprintf(`FirstName: %s`, u.FirstName), fmt.Sprintf(`LastName: %s`, u.LastName), fmt.Sprintf(`LanguageCode: %s`, u.LanguageCode), fmt.Sprintf(`IsBot: %t`, u.IsBot), fmt.Sprintf(`IsPremium: %t`, u.IsPremium), "```", } return c.Reply(strings.Join(replyText, "\n"), &tele.SendOptions{ParseMode: tele.ModeMarkdown}) } func handleChatInfoCmd(c tele.Context) error { chat := c.Chat() if chat == nil { return c.Reply("Unknown.") } loc := "" if chat.ChatLocation != nil { loc = chat.ChatLocation.Address } replyText := []string{ `*Chat Info*`, "```", fmt.Sprintf(`ID: %d`, chat.ID), fmt.Sprintf(`Type: %s`, chat.Type), fmt.Sprintf(`Title: %s`, chat.Title), fmt.Sprintf(`FirstName: %s`, chat.FirstName), fmt.Sprintf(`LastName: %s`, chat.LastName), fmt.Sprintf(`Username: %s`, chat.Username), fmt.Sprintf(`SlowMode: %d`, chat.SlowMode), fmt.Sprintf(`StickerSet: %s`, chat.StickerSet), fmt.Sprintf(`CanSetStickerSet: %t`, chat.CanSetStickerSet), fmt.Sprintf(`LinkedChatID: %d`, chat.LinkedChatID), fmt.Sprintf(`ChatLocation: %s`, loc), fmt.Sprintf(`Private: %t`, chat.Private), fmt.Sprintf(`Protected: %t`, chat.Protected), fmt.Sprintf(`NoVoiceAndVideo: %t`, chat.NoVoiceAndVideo), "```", } return c.Reply(strings.Join(replyText, "\n"), &tele.SendOptions{ParseMode: tele.ModeMarkdown}) } func drawBar(progress float64, length int) string { barChars := []rune("░▒▓█") if length <= 0 { return "" } step := 1 / float64(length) buf := make([]rune, length) for i := 0; i < length; i++ { fill := (progress - float64(i)*step) / step fill = math.Min(math.Max(fill, 0), 1) idx := int(math.Round(fill * float64(len(barChars)-1))) buf[i] = barChars[idx] } return string(buf) } func drawBarForTrafficRecord(r stats.VnstatTrafficRecord) string { effective := lo.Max([]uint64{r.Rx, r.Tx}) max := config.MonthlyTrafficLimitGiB ratio := float64(effective) / 1024 / 1024 / 1024 / float64(max) return fmt.Sprintf("%s `%2.0f%%`", drawBar(ratio, 16), ratio*100) } func handleYearProgressCmd(c tele.Context) error { yearStart := time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Local) yearEnd := yearStart.AddDate(1, 0, 0) yearDur := yearEnd.Sub(yearStart) elapsed := time.Since(yearStart) ratio := float64(elapsed) / float64(yearDur) replyText := []string{ fmt.Sprintf("`%d is %2.0f%% complete.`", time.Now().Year(), ratio*100), drawBar(ratio, 20), } return c.Reply(strings.Join(replyText, "\n"), &tele.SendOptions{ParseMode: tele.ModeMarkdown}) }