├── deploy.env
├── internal
├── backup
│ ├── command.go
│ └── schedule.go
├── database
│ ├── sqlite
│ │ ├── users
│ │ │ ├── level.go
│ │ │ ├── state.go
│ │ │ ├── create.go
│ │ │ ├── get.go
│ │ │ ├── schema.go
│ │ │ └── update.go
│ │ ├── students
│ │ │ ├── search.go
│ │ │ └── schema.go
│ │ ├── bunstudent.go
│ │ └── bun.go
│ ├── bbolt
│ │ ├── connect.go
│ │ └── users.go
│ └── pg
│ │ ├── connect.go
│ │ ├── bigcache.go
│ │ ├── event.go
│ │ ├── qa.go
│ │ ├── timetable.go
│ │ └── schema.go
├── handle
│ ├── profile.go
│ ├── admin
│ │ ├── backup.go
│ │ ├── log.go
│ │ ├── status.go
│ │ ├── update.go
│ │ └── test.go
│ ├── menu.go
│ ├── radio.go
│ ├── assistants
│ │ ├── studentlist.go
│ │ ├── assistant_task.go
│ │ ├── assistant.go
│ │ ├── calendar.go
│ │ ├── search.go
│ │ ├── vanban.go
│ │ ├── timetable.go
│ │ └── student.go
│ ├── photo.go
│ ├── about.go
│ ├── event.go
│ ├── tailieu.go
│ ├── start.go
│ ├── qa.go
│ ├── submit.go
│ └── info.go
└── config
│ └── env.go
├── main.go
├── pkg
├── helpers
│ ├── time.go
│ ├── bottom_menu.go
│ ├── isAdmin.go
│ ├── deleteCache.go
│ ├── month_menu.go
│ ├── task_menu.go
│ ├── passcode_menu.go
│ ├── qa_menu.go
│ ├── printout.go
│ ├── menu.go
│ ├── student_check_menu.go
│ ├── assistant_menu.go
│ └── timetable_menu.go
├── log
│ ├── read.go
│ ├── slog.go
│ └── show.go
├── cache
│ ├── bigcache.go
│ └── test.go
└── reference
│ └── longtext.go
├── tuhuebot.service
├── Makefile
├── cmd
├── handle.go
└── app.go
├── .gitignore
├── go.mod
├── README.md
└── go.sum
/deploy.env:
--------------------------------------------------------------------------------
1 | TOKEN=
2 | PASSCODE=123456
3 | ADMIN_ID=123456
4 | PG=
5 |
--------------------------------------------------------------------------------
/internal/backup/command.go:
--------------------------------------------------------------------------------
1 | package backup
2 |
3 | // Backup by command.
4 |
--------------------------------------------------------------------------------
/internal/backup/schedule.go:
--------------------------------------------------------------------------------
1 | package backup
2 |
3 | //backup automatically
4 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/cmd"
5 | )
6 |
7 | func main() {
8 | cmd.Init()
9 | }
10 |
--------------------------------------------------------------------------------
/internal/database/sqlite/users/level.go:
--------------------------------------------------------------------------------
1 | package users
2 |
3 | import "github.com/uptrace/bun"
4 |
5 | func UserLevel(db *bun.DB, telegram_id int64) int {
6 | current_user := GetCurrentUser(db, telegram_id)
7 | return int(current_user.Level)
8 | }
9 |
--------------------------------------------------------------------------------
/internal/handle/profile.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | func Profile(b *tele.Bot) {
8 | b.Handle("/profile", func(c tele.Context) error {
9 | return c.Send("Work in progress - Check out this link: https://google.com")
10 | })
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/internal/handle/admin/backup.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | func Backup(b *tele.Bot) {
8 | b.Handle("backup", func(c tele.Context) error {
9 | intro := "🅰🅱©↩📧🎏⛽♓ℹ🗾🎋👢Ⓜ♑⭕🅿♌⚡🌴⛎✌Ⓩ"
10 | return c.Send(intro, "manual backup")
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/helpers/time.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | func GetCurrentMonth() int {
9 | fmt.Println("currentMonth is call")
10 | timeLoc, _ := time.LoadLocation("Asia/Ho_Chi_Minh")
11 | currentTime := time.Now().In(timeLoc)
12 | current_Month := int(currentTime.Month())
13 | return current_Month
14 | }
15 |
--------------------------------------------------------------------------------
/internal/handle/menu.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/pkg/helpers"
5 | tele "gopkg.in/telebot.v3"
6 | )
7 |
8 | func Menu(b *tele.Bot) {
9 | b.Handle(&helpers.AskMenu, func(c tele.Context) error {
10 | intro := "🅰🅱©↩📧🎏⛽♓ℹ🗾🎋👢Ⓜ♑⭕🅿♌⚡🌴⛎✌Ⓩ"
11 | return c.Send(intro, helpers.MainMenu_InlineKeys)
12 | })
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/internal/handle/radio.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | func Radio(b *tele.Bot) {
8 | b.Handle("/radio", func(c tele.Context) error {
9 | // Send an MP3 file from a URL
10 | mp3 := &tele.Audio{File: tele.FromURL("https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3")}
11 | return c.Send(mp3)
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/internal/database/bbolt/connect.go:
--------------------------------------------------------------------------------
1 | package bbolt
2 |
3 | import (
4 | "log"
5 |
6 | bolt "go.etcd.io/bbolt"
7 | )
8 |
9 | var db *bolt.DB
10 | var err error
11 |
12 | func Connect() *bolt.DB {
13 | db, err = bolt.Open("tuhuebot_kv.db", 0600, nil)
14 | if err != nil {
15 | log.Fatal(err)
16 | }
17 | CreateUserBucket()
18 | return db
19 | }
20 |
21 | func BBolt() *bolt.DB {
22 | return db
23 | }
24 |
--------------------------------------------------------------------------------
/internal/handle/assistants/studentlist.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/internal/config"
5 | "github.com/yeungon/tuhuebot/pkg/helpers"
6 | tele "gopkg.in/telebot.v3"
7 | )
8 |
9 | func Studentlist(b *tele.Bot) {
10 | studentList := config.Get().STUDENT_LIST
11 | b.Handle(&helpers.Students_List, func(c tele.Context) error {
12 | return c.Send(studentList)
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/tuhuebot.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=TUHUEBOT Go application
3 | [Service]
4 | Type=simple
5 | Restart=always
6 | RestartSec=5s
7 | WorkingDirectory=/www/wwwroot/tuhue.bot/tuhuebot
8 | ExecStart=/www/wwwroot/tuhue.bot/tuhuebot/bin/tuhuebot
9 | [Install]
10 | WantedBy=multi-user.target
11 | #Using Golang log instead of systemd
12 | #StandardOutput=append:/var/log/tuhuebot.log
13 | #StandardError=append:/var/log/tuhuebot-error.log
--------------------------------------------------------------------------------
/internal/handle/photo.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | func Photo(b *tele.Bot) {
8 | b.Handle("/photo", func(c tele.Context) error {
9 | photo := &tele.Photo{File: tele.FromURL("https://i.ibb.co/LQ5KqFJ/bit-ly-ktu-form.png")}
10 | return c.Send(photo)
11 |
12 | })
13 | b.Handle("/link", func(c tele.Context) error {
14 | return c.Send("Check out this link: https://google.com")
15 | })
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/helpers/bottom_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var AskMenu = tele.InlineButton{
9 | Unique: "btn_callback1_ask_menu",
10 | Text: "Trở về menu chính",
11 | Data: "button1_clicked",
12 | }
13 |
14 | // Export the inline keyboard with 5 buttons
15 | var AskMenu_InlineKeys = &tele.ReplyMarkup{
16 | InlineKeyboard: [][]tele.InlineButton{
17 | {AskMenu},
18 | },
19 | }
20 |
--------------------------------------------------------------------------------
/internal/database/pg/connect.go:
--------------------------------------------------------------------------------
1 | package pg
2 |
3 | import (
4 | "database/sql"
5 |
6 | "github.com/uptrace/bun"
7 | "github.com/uptrace/bun/dialect/pgdialect"
8 | "github.com/uptrace/bun/driver/pgdriver"
9 | "github.com/yeungon/tuhuebot/internal/config"
10 | )
11 |
12 | var dbpg *bun.DB
13 |
14 | func Connect() {
15 | var pg_url = config.Get().PG
16 | sqldb := sql.OpenDB(pgdriver.NewConnector(pgdriver.WithDSN(pg_url)))
17 | dbpg = bun.NewDB(sqldb, pgdialect.New())
18 | }
19 |
20 | func PG() *bun.DB {
21 | return dbpg
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/helpers/isAdmin.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | "log"
5 | "strconv"
6 |
7 | "github.com/yeungon/tuhuebot/internal/config"
8 | tele "gopkg.in/telebot.v3"
9 | )
10 |
11 | func IsAdmin(c tele.Context) bool {
12 | admin_id := config.Get().AdminID
13 | current_user := c.Sender()
14 | current_user_id := strconv.Itoa(int(current_user.ID))
15 | if admin_id != current_user_id {
16 | log.Println("Current user is not the admin")
17 | return false
18 | }
19 |
20 | log.Println("admin is using the feature")
21 | return true
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/helpers/deleteCache.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/allegro/bigcache/v3"
8 | )
9 |
10 | // DeleteCache removes an entry from the cache using the provided key.
11 | func DeleteCache(cache *bigcache.BigCache, key string) {
12 | err := cache.Delete(key)
13 | if err != nil {
14 | if err == bigcache.ErrEntryNotFound {
15 | fmt.Printf("Cache entry with key '%s' does not exist.\n", key)
16 | } else {
17 | log.Printf("Failed to delete cache entry with key '%s': %v\n", key, err)
18 | }
19 | } else {
20 | fmt.Printf("Cache entry with key '%s' has been deleted.\n", key)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/pkg/log/read.go:
--------------------------------------------------------------------------------
1 | package logging
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "os"
7 | )
8 |
9 | func Read() {
10 | file, err := os.Open("tuhuebot.json")
11 | if err != nil {
12 | fmt.Printf("Failed to open log file: %v\n", err)
13 | return
14 | }
15 | defer file.Close()
16 |
17 | fmt.Println("[")
18 | scanner := bufio.NewScanner(file)
19 | first := true
20 | for scanner.Scan() {
21 | if !first {
22 | fmt.Println(",")
23 | }
24 | fmt.Print(scanner.Text())
25 | first = false
26 | }
27 | fmt.Println("\n]")
28 |
29 | if err := scanner.Err(); err != nil {
30 | fmt.Printf("Error reading file: %v\n", err)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/cache/bigcache.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "math/rand"
7 | "strconv"
8 | "time"
9 |
10 | "github.com/allegro/bigcache/v3"
11 | )
12 |
13 | var cache *bigcache.BigCache
14 |
15 | var random_number = rand.Intn(100)
16 |
17 | func Cache() {
18 | cache, _ = bigcache.New(context.Background(), bigcache.DefaultConfig(10*time.Minute))
19 |
20 | random_string := strconv.Itoa(random_number)
21 |
22 | fmt.Println("random number inside cache", random_string)
23 |
24 | cache.Set("my-unique-key", []byte(random_string))
25 |
26 | entry, _ := cache.Get("my-unique-key")
27 | fmt.Println(string(entry))
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/helpers/month_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var PreviousMonth = tele.InlineButton{
9 | Unique: "btn_callback1_previous_month",
10 | Text: "Sự kiện tháng trước ⏪",
11 | Data: "button1_clicked",
12 | }
13 |
14 | var NextMonth = tele.InlineButton{
15 | Unique: "btn_callback2_next_month",
16 | Text: "⏩ Sự kiện tháng tới",
17 | Data: "button2_clicked",
18 | }
19 |
20 | // Export the inline keyboard with 5 buttons
21 | var MonthMenu_InlineKeys = &tele.ReplyMarkup{
22 | InlineKeyboard: [][]tele.InlineButton{
23 | {PreviousMonth, NextMonth},
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/internal/handle/admin/log.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/pkg/helpers"
5 | logging "github.com/yeungon/tuhuebot/pkg/log"
6 | tele "gopkg.in/telebot.v3"
7 | )
8 |
9 | func Log(b *tele.Bot) {
10 | b.Handle("log", func(c tele.Context) error {
11 | if helpers.IsAdmin(c) == false {
12 | return nil
13 | }
14 | file_location := "tuhuebot.json"
15 | logging.Show(c, file_location)
16 |
17 | return nil
18 | })
19 |
20 | b.Handle("Log", func(c tele.Context) error {
21 | if helpers.IsAdmin(c) == false {
22 | return nil
23 | }
24 | file_location := "tuhuebot.json"
25 | logging.Show(c, file_location)
26 |
27 | return nil
28 | })
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/internal/database/sqlite/students/search.go:
--------------------------------------------------------------------------------
1 | package students
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 |
8 | "github.com/uptrace/bun"
9 | )
10 |
11 | func SearchStudent(db *bun.DB, searchQuery string) []StudentFTS {
12 | var ctx = context.Background()
13 | var studentSearch []StudentFTS
14 |
15 | // Quote the search query for exact matching if it contains special characters
16 | quotedQuery := fmt.Sprintf("\"%s\"", searchQuery)
17 |
18 | err := db.NewSelect().
19 | Model(&studentSearch).
20 | Where("students_fts MATCH ?", quotedQuery).
21 | Limit(100).
22 | Scan(ctx)
23 | if err != nil {
24 | log.Fatalf("Query failed: %v", err)
25 | }
26 |
27 | return studentSearch
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/helpers/task_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var PreviousMonth_Task = tele.InlineButton{
9 | Unique: "btn_callback1_previous_month_task_assisstant",
10 | Text: "Việc tháng trước ⏪",
11 | Data: "button1_clicked",
12 | }
13 |
14 | var NextMonth_Task = tele.InlineButton{
15 | Unique: "btn_callback2_next_month_task_assisstant",
16 | Text: "⏩ Việc tháng tới",
17 | Data: "button2_clicked",
18 | }
19 |
20 | // Export the inline keyboard with 5 buttons
21 | var TaskMenu_InlineKeys = &tele.ReplyMarkup{
22 | InlineKeyboard: [][]tele.InlineButton{
23 | {PreviousMonth_Task, NextMonth_Task},
24 | },
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/helpers/passcode_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var Back_To_Main_Menu = tele.InlineButton{
9 | Unique: "btn_callback1_main_menu",
10 | Text: "Menu chính ✅",
11 | Data: "button1_clicked",
12 | }
13 |
14 | // Define five inline buttons
15 | var Keep_Typing_Passcode = tele.InlineButton{
16 | Unique: "btn_callback1_retry_password",
17 | Text: "🔐 Nhập mật khẩu",
18 | Data: "button1_clicked",
19 | }
20 |
21 | // Export the inline keyboard with 5 buttons
22 | var Passcode_Menu_InlineKeys = &tele.ReplyMarkup{
23 | InlineKeyboard: [][]tele.InlineButton{
24 | {Back_To_Main_Menu, Keep_Typing_Passcode},
25 | },
26 | }
27 |
--------------------------------------------------------------------------------
/internal/database/sqlite/users/state.go:
--------------------------------------------------------------------------------
1 | package users
2 |
3 | import "github.com/uptrace/bun"
4 |
5 | func UserState(db *bun.DB, telegram_id int64) bool {
6 | current_user := GetCurrentUser(db, telegram_id)
7 | return current_user.State
8 | }
9 |
10 | func UserStateAsking(db *bun.DB, telegram_id int64) bool {
11 | current_user := GetCurrentUser(db, telegram_id)
12 | return current_user.StateAsking
13 | }
14 |
15 | func UserStateChecking(db *bun.DB, telegram_id int64) bool {
16 | current_user := GetCurrentUser(db, telegram_id)
17 | return current_user.StateChecking
18 | }
19 |
20 | func UserStateFetching(db *bun.DB, telegram_id int64) bool {
21 | current_user := GetCurrentUser(db, telegram_id)
22 | return current_user.StateFetching
23 | }
24 |
--------------------------------------------------------------------------------
/internal/database/sqlite/bunstudent.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "database/sql"
5 | "log"
6 |
7 | "github.com/uptrace/bun"
8 | "github.com/uptrace/bun/dialect/sqlitedialect"
9 | "github.com/uptrace/bun/driver/sqliteshim"
10 | "github.com/yeungon/tuhuebot/internal/database/sqlite/students"
11 | )
12 |
13 | var dbstudent *bun.DB
14 |
15 | func BunStudentConnect() {
16 | sqldb, err := sql.Open(sqliteshim.ShimName, "file:students.db?cache=shared&_journal_mode=WAL")
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 | // Create a Bun database client.
21 | dbstudent = bun.NewDB(sqldb, sqlitedialect.New())
22 | MigrateStudent()
23 | }
24 |
25 | func DBSTUDENT() *bun.DB {
26 | return dbstudent
27 | }
28 |
29 | func MigrateStudent() {
30 | students.CreateTableStudents(dbstudent)
31 | }
32 |
--------------------------------------------------------------------------------
/internal/database/sqlite/bun.go:
--------------------------------------------------------------------------------
1 | package sqlite
2 |
3 | import (
4 | "database/sql"
5 | "log"
6 |
7 | "github.com/uptrace/bun"
8 | "github.com/uptrace/bun/dialect/sqlitedialect"
9 | "github.com/uptrace/bun/driver/sqliteshim"
10 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
11 | )
12 |
13 | var db *bun.DB
14 |
15 | func BunConnect() {
16 | //Reference: // https://bun.uptrace.dev/guide/golang-orm.html#using-bun-with-existing-code
17 | sqldb, err := sql.Open(sqliteshim.ShimName, "file:tuhuebot.db?cache=shared&_journal_mode=WAL")
18 | if err != nil {
19 | log.Fatal(err)
20 | }
21 | // Create a Bun database client.
22 | db = bun.NewDB(sqldb, sqlitedialect.New())
23 | Migrate()
24 | }
25 |
26 | func DB() *bun.DB {
27 | return db
28 | }
29 |
30 | func Migrate() {
31 | users.CreateTable(db)
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/helpers/qa_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var Back_QA = tele.InlineButton{
9 | Unique: "btn_callback1_qa_back",
10 | Text: "Trước ⏪",
11 | Data: "button1_clicked",
12 | }
13 |
14 | // Define five inline buttons
15 | var Post_QA = tele.InlineButton{
16 | Unique: "btn_callback1_qa_post",
17 | Text: "Đặt câu hỏi",
18 | Data: "button1_clicked",
19 | }
20 |
21 | // Define five inline buttons
22 | var Forward_QA = tele.InlineButton{
23 | Unique: "btn_callback1_qa_forward",
24 | Text: "⏩ Kế tiếp",
25 | Data: "button1_clicked",
26 | }
27 |
28 | // Export the inline keyboard with 5 buttons
29 | var QA_Menu_InlineKeys = &tele.ReplyMarkup{
30 | InlineKeyboard: [][]tele.InlineButton{
31 | {Back_QA, Post_QA, Forward_QA},
32 | },
33 | }
34 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Variables
2 | SERVICE_FILE := tuhuebot.service
3 | INSTALL_PATH := /etc/systemd/system/
4 | # Install the service file to /etc/systemd/system/
5 | copy:
6 | sudo cp $(SERVICE_FILE) $(INSTALL_PATH)
7 | create:
8 | sudo mkdir -p bin
9 | sudo chmod 7777 bin
10 | install: copy reload create build start enable status log
11 | run: build
12 | @ ./bin/tuhuebot
13 | build:
14 | @go build -o bin/tuhuebot -buildvcs=false
15 | dev:
16 | go run main.go
17 | start:
18 | sudo systemctl start tuhuebot
19 | enable:
20 | sudo systemctl enable tuhuebot
21 | stop:
22 | sudo systemctl stop tuhuebot
23 | restart:
24 | sudo systemctl restart tuhuebot
25 | status:
26 | sudo systemctl status tuhuebot
27 | pull:
28 | sudo git pull
29 | update: pull build restart status log
30 | #Print out the log after running with systemd
31 | log:
32 | journalctl -u tuhuebot -f
--------------------------------------------------------------------------------
/internal/database/pg/bigcache.go:
--------------------------------------------------------------------------------
1 | package pg
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "time"
8 |
9 | "github.com/allegro/bigcache/v3"
10 | )
11 |
12 | // Global variable for BigCache
13 | var Cache *bigcache.BigCache
14 |
15 | // Initialize BigCache
16 | func init() {
17 | var err error
18 | config := bigcache.Config{
19 | Shards: 1024, // Number of cache shards
20 | LifeWindow: 20000 * time.Minute, // Cache entries will expire after 10 minutes
21 | Verbose: true,
22 | OnRemoveWithReason: func(key string, entry []byte, reason bigcache.RemoveReason) {
23 | fmt.Printf("Entry with key '%s' was removed! Reason: %v\n", key, reason)
24 | },
25 | }
26 | Cache, err = bigcache.New(context.Background(), config)
27 | if err != nil {
28 | log.Fatalf("Failed to initialize BigCache: %v", err)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/internal/handle/admin/status.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
7 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
8 | "github.com/yeungon/tuhuebot/pkg/helpers"
9 | tele "gopkg.in/telebot.v3"
10 | )
11 |
12 | func FetchStatus(c tele.Context) {
13 | if helpers.IsAdmin(c) == false {
14 | c.Send("Bot không xử lý command bạn vừa gửi lên!")
15 | return
16 | }
17 | db := sqlite.DB()
18 | total := users.GetTotalUser(db)
19 | info := fmt.Sprintf("Tổng số người dùng hiện tại: %d.", total)
20 | c.Send(info)
21 | }
22 |
23 | func Status(b *tele.Bot) {
24 | b.Handle("status", func(c tele.Context) error {
25 | FetchStatus(c)
26 | return nil
27 | })
28 | b.Handle("Status", func(c tele.Context) error {
29 | FetchStatus(c)
30 | return nil
31 | })
32 | b.Handle("/status", func(c tele.Context) error {
33 | FetchStatus(c)
34 | return nil
35 | })
36 | }
37 |
--------------------------------------------------------------------------------
/pkg/helpers/printout.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | func PrintStruct(v interface{}) {
9 | // Get the value and type of the struct
10 | value := reflect.ValueOf(v)
11 | typeOfValue := value.Type()
12 |
13 | // Check if the input is a pointer to a struct
14 | if value.Kind() == reflect.Ptr {
15 | value = value.Elem() // Get the value pointed to by the pointer
16 | typeOfValue = value.Type()
17 | }
18 |
19 | // Ensure the value is a struct
20 | if value.Kind() != reflect.Struct {
21 | fmt.Println("Provided value is not a struct.")
22 | return
23 | }
24 |
25 | // Iterate over the struct fields
26 | for i := 0; i < value.NumField(); i++ {
27 | field := typeOfValue.Field(i) // Get the struct field's metadata
28 | fieldValue := value.Field(i).Interface() // Get the value of the field
29 |
30 | // Print the field name, type, and value
31 | fmt.Printf("%s (%s): %v\n", field.Name, field.Type, fieldValue)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/cmd/handle.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/internal/handle"
5 | admin "github.com/yeungon/tuhuebot/internal/handle/admin"
6 | assistants "github.com/yeungon/tuhuebot/internal/handle/assistants"
7 | tele "gopkg.in/telebot.v3"
8 | )
9 |
10 | func Handle(b *tele.Bot) {
11 | // Admin----------------
12 | admin.UpdateCache(b)
13 | admin.Status(b)
14 | admin.Log(b)
15 | admin.Backup(b)
16 | admin.Test(b)
17 |
18 | // Assistants--level 2--------------
19 | assistants.Assistant(b)
20 | assistants.AssistantTask(b)
21 | assistants.Vanban(b)
22 | assistants.Calendar(b)
23 | assistants.Studentlist(b)
24 | assistants.TimeTable(b)
25 | assistants.StudentCheck(b)
26 | assistants.SearchStudent(b)
27 |
28 | // Public--level 1 --------------
29 | handle.About(b)
30 | handle.Qa(b)
31 | handle.Info(b)
32 | handle.Start(b)
33 | handle.Tailieu(b)
34 | handle.Photo(b)
35 | handle.Radio(b)
36 | handle.Event(b)
37 |
38 | handle.Profile(b)
39 | handle.Menu(b)
40 |
41 | handle.Submit(b) //Should be put at the end of the list as this handle will receive the post (submit) from user
42 | }
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # If you prefer the allow list template instead of the deny list, see community template:
2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
3 | #
4 | # Binaries for programs and plugins
5 | .DS_Store
6 | *.exe
7 | *.exe~
8 | *.dll
9 | *.so
10 | *.dylib
11 |
12 | # Test binary, built with `go test -c`
13 | *.test
14 |
15 | # Output of the go coverage tool, specifically when used with LiteIDE
16 | *.out
17 |
18 | # Dependency directories (remove the comment below to include it)
19 | # vendor/
20 |
21 | # Go workspace file
22 | go.work
23 |
24 | #built application
25 | bin
26 | bin/*
27 |
28 | #database
29 | #This .gitignore file configuration first ignores all files in the root directory (/*) and then explicitly whitelists (with !) .go files inside the config directory (/config/*.go).
30 | config/*
31 | !/config/*.go
32 | #
33 |
34 | .env
35 |
36 | .vscode
37 |
38 | #sqlite database
39 | tuhuebot.db
40 | students.db
41 | #bbolt k-v database
42 | tuhuebot_kv.db
43 |
44 | # log file
45 | tuhuebot.json
46 |
47 | vanban
48 | vanban/*
49 | bin/*
50 | dethi
51 | dethi/*
52 | tmp
53 | tmp/*
54 |
--------------------------------------------------------------------------------
/pkg/log/slog.go:
--------------------------------------------------------------------------------
1 | package logging
2 |
3 | import (
4 | "log"
5 | "log/slog"
6 | "os"
7 | )
8 |
9 | var logFile *os.File
10 |
11 | // Log sets up the global logger with a JSON handler.
12 | func Log() {
13 | var err error
14 | logFile, err = os.OpenFile("tuhuebot.json", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
15 | if err != nil {
16 | log.Fatalf("Failed to open log file: %v", err)
17 | }
18 |
19 | // Create a JSON handler that writes to the file
20 | logger := slog.New(slog.NewJSONHandler(logFile, &slog.HandlerOptions{
21 | Level: slog.LevelDebug, // Capture debug messages and above
22 | }))
23 |
24 | // Set the logger as the default so that slog uses it
25 | slog.SetDefault(logger)
26 | }
27 |
28 | // CloseLog ensures that the log file is properly closed and flushed.
29 | func CloseLog() {
30 | if logFile != nil {
31 | // Flush any buffered data to the file
32 | if err := logFile.Sync(); err != nil {
33 | log.Printf("Failed to flush log file: %v\n", err)
34 | }
35 | // Close the log file
36 | if err := logFile.Close(); err != nil {
37 | log.Printf("Failed to close log file: %v\n", err)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/internal/handle/about.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/pkg/helpers"
5 | tele "gopkg.in/telebot.v3"
6 | )
7 |
8 | var (
9 | intro = "Đây là phần mềm dạng bot hỗ trợ tự động, giúp sinh viên tiếp cận thông tin học tập nhanh và thuận lợi hơn. Thông tin mang giá trị tham khảo. \n\nHãy liên hệ trực tiếp với giảng viên hoặc cố vấn học tập để có được trợ giúp chi tiết hơn! \n\n Địa chỉ của bot tại https://t.me/tuhuebot.\n\n Bot hoạt động từ 11/2024 đến nay."
10 |
11 | photo = &tele.Photo{
12 | Caption: "Mã QR truy cập bot", // Set caption here
13 | File: tele.FromURL("https://res.cloudinary.com/yeungon/image/upload/v1728269711/905e81fe3c8985d7dc98_huwerc.jpg"),
14 | }
15 | )
16 |
17 | func dispath(c tele.Context) {
18 | c.Send(intro)
19 | c.Send(photo)
20 | c.Send("⭕🅿♌⚡🌴⛎♈📈❌✌Ⓩ", helpers.AskMenu_InlineKeys)
21 | }
22 |
23 | func About(b *tele.Bot) {
24 | b.Handle("/about", func(c tele.Context) error {
25 | dispath(c)
26 | return nil
27 | })
28 |
29 | b.Handle("about", func(c tele.Context) error {
30 | dispath(c)
31 | return nil
32 | })
33 |
34 | b.Handle(&helpers.Intro, func(c tele.Context) error {
35 | dispath(c)
36 | return nil
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/internal/database/sqlite/users/create.go:
--------------------------------------------------------------------------------
1 | package users
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 |
8 | "github.com/uptrace/bun"
9 | )
10 |
11 | func CreateUser(db *bun.DB, users []*User) {
12 | ctx := context.Background()
13 |
14 | for _, user := range users {
15 | // Check if the user with the same TelegramUserID already exists
16 | existingUser := new(User)
17 | userExists, err := db.NewSelect().Model(existingUser).
18 | Where("telegram_user_id = ?", user.TelegramUserID).
19 | Exists(ctx)
20 |
21 | // Handle errors while querying
22 | if err != nil {
23 | log.Printf("Failed to check if user exists: %v", err)
24 | continue // Skip to the next user if there's an error
25 | }
26 |
27 | if userExists {
28 | // If user already exists, log and skip insertion
29 | log.Printf("User with TelegramUserID %d already exists. Skipping insertion.", user.TelegramUserID)
30 | continue
31 | }
32 |
33 | // No user found; proceed to insert the new user
34 | _, err = db.NewInsert().Model(user).Exec(ctx)
35 | if err != nil {
36 | log.Fatalf("Failed to insert user: %v", err)
37 | }
38 |
39 | fmt.Println("Created new user with TelegramUserID:", user.TelegramUserID)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/helpers/menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var Intro = tele.InlineButton{
9 | Unique: "btn_callback1_gioithieu",
10 | Text: "Giới thiệu",
11 | Data: "button1_clicked",
12 | }
13 |
14 | var QA = tele.InlineButton{
15 | Unique: "btn_callback2_qa",
16 | Text: "Hỏi - Đáp",
17 | Data: "button2_clicked",
18 | }
19 |
20 | var Assistant = tele.InlineButton{
21 | Unique: "btn_callback3_guide",
22 | Text: "Trợ lý",
23 | Data: "button3_clicked",
24 | }
25 |
26 | var Materials = tele.InlineButton{
27 | Unique: "btn_callback4_material",
28 | Text: "Tài liệu",
29 | Data: "button4_clicked",
30 | }
31 |
32 | var Info = tele.InlineButton{
33 | Unique: "thongtinhuuich",
34 | Text: "Thông tin hữu ích",
35 | Data: "button5_clicked",
36 | }
37 |
38 | var Event = tele.InlineButton{
39 | Unique: "event_tracking",
40 | Text: "Sự kiện",
41 | Data: "button6_clicked",
42 | }
43 |
44 | // Export the inline keyboard with 5 buttons
45 | var MainMenu_InlineKeys = &tele.ReplyMarkup{
46 | InlineKeyboard: [][]tele.InlineButton{
47 | {Intro, Event},
48 | {QA, Info},
49 | {Materials, Assistant}, // Row 4: Button 4
50 | },
51 | }
52 |
--------------------------------------------------------------------------------
/internal/handle/admin/update.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | "fmt"
5 | "log/slog"
6 | "strconv"
7 |
8 | "github.com/yeungon/tuhuebot/internal/config"
9 | "github.com/yeungon/tuhuebot/internal/database/pg"
10 | "github.com/yeungon/tuhuebot/pkg/helpers"
11 | tele "gopkg.in/telebot.v3"
12 | )
13 |
14 | func UpdateCache(b *tele.Bot) {
15 | b.Handle("update", func(c tele.Context) error {
16 | handleUpdate(c)
17 | return nil
18 | })
19 | b.Handle("Update", func(c tele.Context) error {
20 | handleUpdate(c)
21 | return nil
22 | })
23 | }
24 |
25 | func handleUpdate(c tele.Context) error {
26 | current_user_id := strconv.FormatInt(c.Sender().ID, 10)
27 | AdminID := config.Get().AdminID
28 | if current_user_id != AdminID {
29 | c.Send("Sorry, bot không xử lý các thông tin người dùng gửi lên!")
30 | fmt.Println("Don't handle as the current user is not admin")
31 | return nil
32 | }
33 |
34 | // only handle if triggered/called by admin
35 | fmt.Println(current_user_id)
36 | helpers.DeleteCache(pg.Cache, "events_data")
37 | helpers.DeleteCache(pg.Cache, "qa_data")
38 | helpers.DeleteCache(pg.Cache, "timetable_data")
39 | slog.Info("Remove the cache", "user", "admin", "module", "auth")
40 | content := "Cache từ bigCache đã được xóa!"
41 | return c.Send(content)
42 | }
43 |
--------------------------------------------------------------------------------
/pkg/helpers/student_check_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var Back_To_Main_Menu_Second = tele.InlineButton{
9 | Unique: "btn_callback1_main_menu",
10 | Text: "Menu chính ⤶",
11 | Data: "button1_clicked",
12 | }
13 |
14 | // Define five inline buttons
15 | var Back_To_Main_Menu_Assistant = tele.InlineButton{
16 | Unique: "btn_callback1_main_menu_assistant",
17 | Text: "Menu trợ lý ⏎",
18 | Data: "button1_clicked",
19 | }
20 |
21 | // Define five inline buttons
22 | var Keep_Checking_Student = tele.InlineButton{
23 | Unique: "btn_callback1_keep_check_student",
24 | Text: "Tra điểm sinh viên theo mã sinh viên",
25 | Data: "button1_clicked",
26 | }
27 |
28 | // Define five inline buttons
29 | var Keep_Searching_Student = tele.InlineButton{
30 | Unique: "btn_callback1_keep_searching_student",
31 | Text: "Tiếp tục tìm kiếm thông tin sinh viên",
32 | Data: "button1_clicked",
33 | }
34 |
35 | // Export the inline keyboard with 5 buttons
36 | var Student_Check_Menu_InlineKeys = &tele.ReplyMarkup{
37 | InlineKeyboard: [][]tele.InlineButton{
38 | {Back_To_Main_Menu_Second},
39 | {Back_To_Main_Menu_Assistant},
40 | {Keep_Searching_Student},
41 | {Keep_Checking_Student},
42 | },
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/log/show.go:
--------------------------------------------------------------------------------
1 | package logging
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "os"
7 | "strings"
8 |
9 | tele "gopkg.in/telebot.v3"
10 | )
11 |
12 | func Show(c tele.Context, file string) {
13 | logs, err := ReadLastNLines(file, 20)
14 | if err != nil {
15 | fmt.Printf("Failed to read log file: %v\n", err)
16 | return
17 | }
18 | // Format the logs into a single message
19 | message := strings.Join(logs, "\n")
20 | if len(message) > 4096 { // Telegram's message limit
21 | message = message[:4096] + "\n...(truncated)"
22 | }
23 |
24 | c.Send(message)
25 | }
26 |
27 | // Function to read the last N lines from a file
28 | func ReadLastNLines(filename string, n int) ([]string, error) {
29 | file, err := os.Open(filename)
30 | if err != nil {
31 | return nil, fmt.Errorf("could not open file: %v", err)
32 | }
33 | defer file.Close()
34 |
35 | scanner := bufio.NewScanner(file)
36 | lines := []string{}
37 |
38 | // Read the file line by line and store in a slice
39 | for scanner.Scan() {
40 | lines = append(lines, scanner.Text())
41 | }
42 | if err := scanner.Err(); err != nil {
43 | return nil, fmt.Errorf("error reading file: %v", err)
44 | }
45 |
46 | // Get the last N lines (or all if there are fewer than N)
47 | if len(lines) > n {
48 | return lines[len(lines)-n:], nil
49 | }
50 | return lines, nil
51 | }
52 |
--------------------------------------------------------------------------------
/internal/handle/assistants/assistant_task.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "strconv"
5 |
6 | "github.com/yeungon/tuhuebot/internal/database/pg"
7 | "github.com/yeungon/tuhuebot/pkg/helpers"
8 | tele "gopkg.in/telebot.v3"
9 | )
10 |
11 | func HandleAssistantTask(c tele.Context, month int) {
12 | pgdata := pg.PG()
13 | events := pg.GetEvent(pgdata)
14 | current_thang := helpers.GetCurrentMonth()
15 | info_thang := int(current_thang + month)
16 |
17 | if info_thang > 12 {
18 | info_thang = 1
19 | }
20 |
21 | introduction := "Các công việc cần chú ý của tháng " + strconv.Itoa(info_thang) + ": "
22 | c.Send(introduction)
23 | for _, event := range events {
24 | if int(event.Month) == info_thang {
25 | c.Send(event.EventTasks)
26 | }
27 | }
28 | var currentMonthEvents = "Theo dõi các nhiệm vụ theo tháng: "
29 | c.Send(currentMonthEvents, helpers.TaskMenu_InlineKeys)
30 | }
31 |
32 | func AssistantTask(b *tele.Bot) {
33 | b.Handle(&helpers.Assistant_Tasks, func(c tele.Context) error {
34 |
35 | HandleAssistantTask(c, 0)
36 | return nil
37 | })
38 |
39 | b.Handle(&helpers.PreviousMonth_Task, func(c tele.Context) error {
40 | HandleAssistantTask(c, -1)
41 | return nil
42 | })
43 |
44 | b.Handle(&helpers.NextMonth_Task, func(c tele.Context) error {
45 | HandleAssistantTask(c, 1)
46 | return nil
47 | })
48 | }
49 |
--------------------------------------------------------------------------------
/cmd/app.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "time"
7 |
8 | "github.com/yeungon/tuhuebot/internal/config"
9 | "github.com/yeungon/tuhuebot/internal/database/bbolt"
10 | "github.com/yeungon/tuhuebot/internal/database/pg"
11 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
12 | logging "github.com/yeungon/tuhuebot/pkg/log"
13 | tele "gopkg.in/telebot.v3"
14 | )
15 |
16 | func Init() {
17 | logging.Log()
18 | config.New()
19 |
20 | // =============sqlite=============
21 | sqlite.BunConnect()
22 | db := sqlite.DB()
23 | defer db.Close()
24 |
25 | sqlite.BunStudentConnect()
26 | dbstudent := sqlite.DBSTUDENT()
27 | defer dbstudent.Close()
28 | //================PG=================
29 | pg.Connect()
30 | pgdatabase := pg.PG().DB
31 | defer pgdatabase.Close()
32 | //===============bbolt==================
33 | dbbolt := bbolt.Connect()
34 | defer dbbolt.Close()
35 |
36 | token := config.Get().Token
37 | Pref := tele.Settings{
38 | Token: token,
39 | Poller: &tele.LongPoller{Timeout: 10 * time.Second},
40 | }
41 |
42 | b, err := tele.NewBot(Pref)
43 | if err != nil {
44 | log.Fatal(err)
45 | return
46 | }
47 | fmt.Println("Hi, your bot is running as expected!")
48 | Handle(b)
49 |
50 | // When shutting down the application, ensure the log is closed properly
51 | defer logging.CloseLog()
52 | b.Start()
53 | }
54 |
--------------------------------------------------------------------------------
/internal/handle/event.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "strconv"
5 |
6 | "github.com/yeungon/tuhuebot/internal/database/pg"
7 | "github.com/yeungon/tuhuebot/pkg/helpers"
8 | tele "gopkg.in/telebot.v3"
9 | )
10 |
11 | func HandleEvent(c tele.Context, month int) {
12 | pgdata := pg.PG()
13 | events := pg.GetEvent(pgdata)
14 | current_thang := helpers.GetCurrentMonth()
15 | info_thang := int(current_thang + month)
16 |
17 | if info_thang > 12 {
18 | info_thang = 1
19 | }
20 |
21 | if info_thang < 1 {
22 | info_thang = 12
23 | }
24 |
25 | introduction := "Các sự kiện đáng chú ý của tháng " + strconv.Itoa(info_thang) + ": "
26 | c.Send(introduction)
27 | for _, event := range events {
28 | if int(event.Month) == info_thang {
29 | c.Send(event.EventData)
30 | }
31 | }
32 |
33 | var currentMonthEvents = "⭐⭐⭐Theo dõi các sự kiện: ⭐⭐⭐"
34 | c.Send(currentMonthEvents, helpers.MonthMenu_InlineKeys)
35 | }
36 |
37 | func Event(b *tele.Bot) {
38 | b.Handle("/event", func(c tele.Context) error {
39 | HandleEvent(c, 0)
40 | return nil
41 | })
42 |
43 | b.Handle(&helpers.Event, func(c tele.Context) error {
44 | HandleEvent(c, 0)
45 | return nil
46 | })
47 |
48 | b.Handle(&helpers.PreviousMonth, func(c tele.Context) error {
49 | HandleEvent(c, -1)
50 | return nil
51 | })
52 |
53 | b.Handle(&helpers.NextMonth, func(c tele.Context) error {
54 | HandleEvent(c, 1)
55 | return nil
56 | })
57 | }
58 |
--------------------------------------------------------------------------------
/internal/handle/tailieu.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/pkg/helpers"
5 | tele "gopkg.in/telebot.v3"
6 | )
7 |
8 | func Tailieu(b *tele.Bot) { // Define the inline button
9 | // Define the first inline button
10 | dethi := tele.InlineButton{
11 | Unique: "btn_callback1",
12 | Text: "Đề thi",
13 | Data: "button1_clicked",
14 | }
15 |
16 | // Define the second inline button
17 | decuong := tele.InlineButton{
18 | Unique: "btn_callback2",
19 | Text: "Đề cương",
20 | Data: "button2_clicked",
21 | }
22 |
23 | // Create the reply markup and add both buttons in a single row
24 | inlineKeys := &tele.ReplyMarkup{}
25 | inlineKeys.InlineKeyboard = [][]tele.InlineButton{
26 | {dethi}, // Row 1: Button 1
27 | {decuong}, // Row 2: Button 2
28 | }
29 |
30 | b.Handle("/tailieu", func(c tele.Context) error {
31 | // Create the reply markup and add the button
32 | return c.Send("Bạn đang duyệt kho tài liệu học tập:", inlineKeys)
33 |
34 | })
35 |
36 | b.Handle(&helpers.Materials, func(c tele.Context) error {
37 | // Create the reply markup and add the button
38 | return c.Send("Bạn đang duyệt kho tài liệu học tập:", inlineKeys)
39 |
40 | })
41 |
42 | b.Handle(&dethi, func(c tele.Context) error {
43 | return c.Send("Nội dung này đang cập nhật")
44 | })
45 |
46 | b.Handle(&decuong, func(c tele.Context) error {
47 | return c.Send("Nội dung này đang cập nhật. ")
48 | })
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/pkg/cache/test.go:
--------------------------------------------------------------------------------
1 | package cache
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 | "log"
8 | "time"
9 |
10 | "github.com/allegro/bigcache/v3"
11 | )
12 |
13 | type XataData struct {
14 | Name string `json:"name"`
15 | Value int `json:"value"`
16 | }
17 |
18 | func TestCache() {
19 | // Configure the cache with 10 minutes expiration
20 | cache, err := bigcache.New(context.Background(), bigcache.DefaultConfig(10*time.Minute))
21 |
22 | if err != nil {
23 | log.Fatal(err)
24 | }
25 |
26 | // Simulate fetching data from Xata.io
27 | dataFromXata := XataData{
28 | Name: "Sample Item",
29 | Value: 42,
30 | }
31 |
32 | // Serialize the data to JSON
33 | jsonData, err := json.Marshal(dataFromXata)
34 | if err != nil {
35 | log.Fatal(err)
36 | }
37 |
38 | // Store the JSON in BigCache with a key
39 | key := "xataData"
40 | err = cache.Set(key, jsonData)
41 | if err != nil {
42 | log.Fatal(err)
43 | }
44 |
45 | fmt.Println("Data stored in cache.")
46 |
47 | // Retrieve the data from cache
48 | cachedData, err := cache.Get(key)
49 | if err != nil {
50 | log.Fatal(err)
51 | }
52 |
53 | // Deserialize the JSON back into a struct
54 | var retrievedData XataData
55 | err = json.Unmarshal(cachedData, &retrievedData)
56 | if err != nil {
57 | log.Fatal(err)
58 | }
59 |
60 | // Print the retrieved data
61 | fmt.Printf("Retrieved data: %+v\n", retrievedData)
62 | fmt.Println("name", retrievedData.Name)
63 | }
64 |
--------------------------------------------------------------------------------
/internal/database/pg/event.go:
--------------------------------------------------------------------------------
1 | package pg
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 |
10 | "github.com/uptrace/bun"
11 | )
12 |
13 | func GetEvent(db *bun.DB) []Events {
14 | var ctx = context.Background()
15 | cacheKey := "events_data"
16 |
17 | // Check if data is in the cache
18 | cachedData, err := Cache.Get(cacheKey)
19 | if err == nil {
20 | // Cache hit - unmarshal and return cached data
21 | var cachedEvents []Events
22 | err = json.Unmarshal(cachedData, &cachedEvents)
23 | if err == nil {
24 | fmt.Println("Returning data from cache")
25 | return cachedEvents
26 | }
27 | }
28 |
29 | // Cache miss - query the database
30 | var event []Events
31 | err = db.NewSelect().
32 | Model(&event).
33 | Order("month ASC").
34 | Scan(ctx)
35 | if err != nil {
36 | if err == sql.ErrNoRows {
37 | fmt.Println("No events found.")
38 | return []Events{} // Return a zero-value slice if no events are found.
39 | }
40 |
41 | log.Fatal("Failed to retrieve events:", err)
42 | }
43 |
44 | // Store the fetched data in the cache
45 | data, err := json.Marshal(event)
46 | if err != nil {
47 | log.Printf("Failed to marshal event data: %v", err)
48 | } else {
49 | err = Cache.Set(cacheKey, data)
50 | if err != nil {
51 | log.Printf("Failed to cache the result: %v", err)
52 | }
53 | }
54 |
55 | fmt.Println("Succeeded fetching data from events table stored at XATA.io")
56 | return event
57 | }
58 |
--------------------------------------------------------------------------------
/internal/handle/assistants/assistant.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 |
7 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
8 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
9 | "github.com/yeungon/tuhuebot/pkg/helpers"
10 | tele "gopkg.in/telebot.v3"
11 | )
12 |
13 | func HandleAssistant(c tele.Context) error {
14 | user := c.Sender().ID
15 | db := sqlite.DB()
16 | current_user := users.GetCurrentUser(db, user)
17 |
18 | userInfo := current_user.FirstName
19 | if len(userInfo) == 0 {
20 | userInfo = *current_user.Username
21 | }
22 | if len(userInfo) == 0 {
23 | userInfo = strconv.FormatInt(current_user.ID, 10)
24 | }
25 |
26 | if current_user.Level > 1 {
27 | message := fmt.Sprintf("Xin chào %v, các tùy chọn nghiệp vụ:", userInfo)
28 | c.Send(message, helpers.Assitant_InlineKeys)
29 | return nil
30 | }
31 |
32 | content := "Đây là góc hỗ trợ trợ lý. Bạn cần có mật khẩu để truy xuất dữ liệu! Bạn chỉ cần nhập khẩu đúng một lần duy nhất.\n\nXin nhập mật khẩu: "
33 | users.SetUserState(db, user, true)
34 | c.Send(content)
35 | return nil
36 | }
37 |
38 | func Assistant(b *tele.Bot) {
39 | b.Handle("/assistant", func(c tele.Context) error {
40 | HandleAssistant(c)
41 | return nil
42 | })
43 |
44 | b.Handle(&helpers.Assistant, func(c tele.Context) error {
45 | HandleAssistant(c)
46 | return nil
47 | })
48 |
49 | b.Handle(&helpers.Back_To_Main_Menu_Assistant, func(c tele.Context) error {
50 | HandleAssistant(c)
51 | return nil
52 | })
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/internal/database/sqlite/users/get.go:
--------------------------------------------------------------------------------
1 | package users
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "fmt"
7 | "log"
8 |
9 | "github.com/uptrace/bun"
10 | )
11 |
12 | func GetCurrentUser(db *bun.DB, telegram_id int64) User {
13 | var ctx = context.Background()
14 | // Retrieve all users.
15 | var currentUser User
16 | err := db.NewSelect().
17 | Model(¤tUser).
18 | Where("telegram_user_id = ?", telegram_id).
19 | Scan(ctx)
20 | if err != nil {
21 | if err == sql.ErrNoRows {
22 | fmt.Println("No user found with the provided Telegram ID.")
23 | return User{} // Return a zero-value User if no user is found.
24 | }
25 |
26 | log.Fatal("Failed to retrieve users:", err)
27 | }
28 | return currentUser
29 | }
30 |
31 | func GetAllUser(db *bun.DB) []User {
32 | var ctx = context.Background()
33 | // Retrieve all users.
34 | var userList []User
35 | err := db.NewSelect().
36 | Model(&userList).
37 | Order("id ASC").
38 | Scan(ctx)
39 | if err != nil {
40 | if err == sql.ErrNoRows {
41 | fmt.Println("No user found")
42 | return []User{} // Return a zero-value User if no user is found.
43 | }
44 | log.Fatal("Failed to retrieve users:", err)
45 | }
46 |
47 | return userList
48 | }
49 |
50 | func GetTotalUser(db *bun.DB) int {
51 | var ctx = context.Background()
52 | count, err := db.NewSelect().Model((*User)(nil)).Count(ctx)
53 | if err != nil {
54 | if err == sql.ErrNoRows {
55 | return 0 // Return a zero-value User if no user is found.
56 | }
57 | log.Fatal("Failed to retrieve total users:", err)
58 | }
59 | return count
60 | }
61 |
--------------------------------------------------------------------------------
/internal/config/env.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "log"
5 | "os"
6 | "sync"
7 |
8 | "github.com/joho/godotenv"
9 | )
10 |
11 | type Env struct {
12 | Token string
13 | AdminID string
14 | PASSCODE string
15 | PG string
16 | SPH_USERNAME string
17 | SPH_PASSWORD string
18 | STUDENT_LIST string
19 | SPH_URL_ENDPOINT string
20 | SECRET_FIRST string
21 | SECRET_SECOND string
22 | }
23 |
24 | var once sync.Once
25 | var env *Env
26 |
27 | func New() {
28 | once.Do(func() {
29 | err := godotenv.Load(".env")
30 | if err != nil {
31 | log.Fatal("Error loading .env file")
32 | }
33 |
34 | token := os.Getenv("TOKEN")
35 | admin_id := os.Getenv("ADMIN_ID")
36 | pass_code := os.Getenv("PASSCODE")
37 | postgresq := os.Getenv("PG")
38 | sph_username := os.Getenv("SPH_USERNAME")
39 | sph_password := os.Getenv("SPH_PASSWORD")
40 | student_list := os.Getenv("STUDENT_LIST")
41 | sph_url_endpoint := os.Getenv("SPH_URL_ENDPOINT")
42 | secret_first := os.Getenv("SECRET_FIRST")
43 | secret_second := os.Getenv("SECRET_SECOND")
44 |
45 | env = &Env{
46 | Token: token,
47 | AdminID: admin_id,
48 | PASSCODE: pass_code,
49 | PG: postgresq,
50 | SPH_USERNAME: sph_username,
51 | SPH_PASSWORD: sph_password,
52 | STUDENT_LIST: student_list,
53 | SPH_URL_ENDPOINT: sph_url_endpoint,
54 | SECRET_FIRST: secret_first,
55 | SECRET_SECOND: secret_second,
56 | }
57 |
58 | })
59 | }
60 |
61 | func Get() *Env {
62 | return env
63 | }
64 |
--------------------------------------------------------------------------------
/internal/handle/start.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
7 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
8 | "github.com/yeungon/tuhuebot/pkg/helpers"
9 | tele "gopkg.in/telebot.v3"
10 | )
11 |
12 | func Name(firstName, username string) string {
13 | if len(firstName) == 0 {
14 | return username
15 | } else {
16 | return firstName
17 | }
18 | }
19 |
20 | func Start(b *tele.Bot) {
21 | b.Handle("/start", func(c tele.Context) error {
22 | var (
23 | user = c.Sender()
24 | intro = "Welcome onboard"
25 | welcome = "Chào mừng bạn đến với bot hỗ trợ học tập tự động. Chúc bạn một ngày tốt lành.\n\nDưới đây là các chức năng, thông tin chính hiện có của bot:"
26 | )
27 |
28 | // Extract user details
29 | firstName := user.FirstName
30 | lastName := user.LastName
31 | username := user.Username
32 | telegramUserID := user.ID
33 | isBot := user.IsBot
34 |
35 | var lastNamePtr *string
36 | if lastName != "" {
37 | lastNamePtr = &lastName // Set lastName if it's not empty
38 | }
39 |
40 | name := Name(firstName, username)
41 | introduction := fmt.Sprintf("%s %s. %s", intro, name, welcome)
42 | // Store the user in the database, TODO: check the user if it has been stored.
43 |
44 | db := sqlite.DB()
45 | users_data := []*users.User{
46 | {TelegramUserID: telegramUserID,
47 | FirstName: firstName,
48 | LastName: lastNamePtr,
49 | Username: &username, // Username is also a pointer
50 | IsBot: isBot,
51 | },
52 | }
53 |
54 | // Store the user in the database (consider checking if the user already exists)
55 | users.CreateUser(db, users_data)
56 |
57 | c.Send(introduction, helpers.MainMenu_InlineKeys)
58 | return nil
59 | })
60 | }
61 |
--------------------------------------------------------------------------------
/internal/database/pg/qa.go:
--------------------------------------------------------------------------------
1 | package pg
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 |
10 | "github.com/uptrace/bun"
11 | )
12 |
13 | func GetQuestionAnswer(db *bun.DB) []QA {
14 | var ctx = context.Background()
15 | cacheKey := "qa_data"
16 |
17 | // Check if data is in the cache
18 | cachedData, err := Cache.Get(cacheKey)
19 | if err == nil {
20 | // Cache hit - unmarshal and return cached data
21 | var cachedQAs []QA
22 | err = json.Unmarshal(cachedData, &cachedQAs)
23 | if err == nil {
24 | fmt.Println("Returning data from QA cache named qa_data")
25 | return cachedQAs
26 | }
27 | }
28 |
29 | // Cache miss - query the database
30 | var question_answer []QA
31 | err = db.NewSelect().
32 | Model(&question_answer).
33 | Where("published=TRUE").
34 | Order("xata_createdat ASC").
35 | Scan(ctx)
36 | if err != nil {
37 | if err == sql.ErrNoRows {
38 | fmt.Println("No question_answer found.")
39 | return []QA{} // Return a zero-value slice if no events are found.
40 | }
41 |
42 | log.Fatal("Failed to retrieve question_answer:", err)
43 | }
44 |
45 | // Store the fetched data in the cache
46 | data, err := json.Marshal(question_answer)
47 | if err != nil {
48 | log.Printf("Failed to marshal question_answer data: %v", err)
49 | } else {
50 | err = Cache.Set(cacheKey, data)
51 | if err != nil {
52 | log.Printf("Failed to cache the result: %v", err)
53 | }
54 | }
55 |
56 | fmt.Println("Succeeded fetching data from qa table stored at XATA.io")
57 | return question_answer
58 | }
59 |
60 | func CreateQA(db *bun.DB, newQA *QA) {
61 | ctx := context.Background()
62 | _, err := db.NewInsert().Model(newQA).Exec(ctx)
63 | if err != nil {
64 | log.Fatalf("Failed to insert new question: %v", err)
65 | }
66 |
67 | fmt.Println("New question inserted successfully!")
68 | }
69 |
--------------------------------------------------------------------------------
/pkg/helpers/assistant_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | tele "gopkg.in/telebot.v3"
5 | )
6 |
7 | // Define five inline buttons
8 | var LecturerTimeTable = tele.InlineButton{
9 | Unique: "btn_callback1_time_table_intro",
10 | Text: "Lịch dạy giảng viên",
11 | Data: "button1_clicked",
12 | }
13 |
14 | // Define five inline buttons
15 | var Assistant_Tasks = tele.InlineButton{
16 | Unique: "btn_callback1_time_assistant_tasks",
17 | Text: "Công việc trợ lý theo tháng",
18 | Data: "button1_clicked",
19 | }
20 |
21 | // Define five inline buttons
22 | var Tracking_Announcement = tele.InlineButton{
23 | Unique: "btn_callback1_time_tracking_announcement",
24 | Text: "Theo dõi công văn mới nhất",
25 | Data: "button1_clicked",
26 | }
27 |
28 | // Define five inline buttons
29 | var Calendar_Tracking = tele.InlineButton{
30 | Unique: "btn_callback1_time_calendar_tracking",
31 | Text: "Lịch công tác tuần",
32 | Data: "button1_clicked",
33 | }
34 |
35 | // Define five inline buttons
36 | var Students_List = tele.InlineButton{
37 | Unique: "btn_callback1_student_list",
38 | Text: "Danh sách sinh viên",
39 | Data: "button1_clicked",
40 | }
41 |
42 | // Define five inline buttons
43 | var Students_Check = tele.InlineButton{
44 | Unique: "btn_callback1_student_check",
45 | Text: "Tra điểm sinh viên theo mã SV",
46 | Data: "button1_clicked",
47 | }
48 |
49 | // Define five inline buttons
50 | var Students_Search = tele.InlineButton{
51 | Unique: "btn_callback1_student_search",
52 | Text: "Tìm kiếm sinh viên",
53 | Data: "button1_clicked_search",
54 | }
55 |
56 | var Assitant_InlineKeys = &tele.ReplyMarkup{
57 | InlineKeyboard: [][]tele.InlineButton{
58 | {Tracking_Announcement},
59 | {Calendar_Tracking},
60 | {Assistant_Tasks},
61 | {LecturerTimeTable},
62 | {Students_Check},
63 | {Students_Search},
64 | {Students_List},
65 | },
66 | }
67 |
--------------------------------------------------------------------------------
/internal/database/sqlite/students/schema.go:
--------------------------------------------------------------------------------
1 | package students
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 |
8 | "github.com/uptrace/bun"
9 | )
10 |
11 | type StudentFTS struct {
12 | bun.BaseModel `bun:"table:students_fts,alias:s"`
13 |
14 | ID int `bun:"id,pk,nullzero"` // Corresponds to `id UNINDEXED`
15 | Name string `bun:"name,nullzero"` // Corresponds to `name`
16 | StudentCode string `bun:"student_code,nullzero"` // Corresponds to `student_code`
17 | Gender string `bun:"gender,nullzero"` // Corresponds to `gender`
18 | DOB string `bun:"dob,nullzero"` // Corresponds to `dob`
19 | DOBFormat string `bun:"dob_format,nullzero"` // Corresponds to `dob_format`
20 | Class string `bun:"class,nullzero"` // Corresponds to `class`
21 | ClassCode string `bun:"class_code,nullzero"` // Corresponds to `class_code`
22 | Ethnic string `bun:"ethnic,nullzero"` // Corresponds to `ethnic`
23 | NationalID string `bun:"national_id,nullzero"` // Corresponds to `national_id`
24 | Phone string `bun:"phone,nullzero"` // Corresponds to `phone`
25 | Email string `bun:"email,nullzero"` // Corresponds to `email`
26 | Province string `bun:"province,nullzero"` // Corresponds to `province`
27 | Address string `bun:"address,nullzero"` // Corresponds to `address`
28 | Notes string `bun:"notes,nullzero"` // Corresponds to `notes`
29 | }
30 |
31 | func CreateTableStudents(db *bun.DB) {
32 | var ctx = context.Background()
33 | // Migrate the schema: create the "StudentFTS" table if it doesn't exist.
34 | res, err := db.NewCreateTable().Model((*StudentFTS)(nil)).IfNotExists().Exec(ctx)
35 | if err != nil {
36 | log.Fatal("Failed to create table:", err)
37 | }
38 | info := fmt.Sprintf("The table 'StudentFTS' created. Rows affected: %d", res)
39 | fmt.Println(info)
40 | }
41 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/yeungon/tuhuebot
2 |
3 | go 1.23.0
4 |
5 | toolchain go1.24.0
6 |
7 | require (
8 | github.com/allegro/bigcache/v3 v3.1.0
9 | github.com/go-rod/rod v0.116.2
10 | github.com/joho/godotenv v1.5.1
11 | github.com/uptrace/bun v1.2.11
12 | github.com/uptrace/bun/dialect/pgdialect v1.2.11
13 | github.com/uptrace/bun/dialect/sqlitedialect v1.2.11
14 | github.com/uptrace/bun/driver/pgdriver v1.2.11
15 | github.com/uptrace/bun/driver/sqliteshim v1.2.11
16 | go.etcd.io/bbolt v1.4.0
17 | gopkg.in/telebot.v3 v3.3.8
18 | )
19 |
20 | require (
21 | github.com/dustin/go-humanize v1.0.1 // indirect
22 | github.com/google/uuid v1.6.0 // indirect
23 | github.com/jinzhu/inflection v1.0.0 // indirect
24 | github.com/mattn/go-isatty v0.0.20 // indirect
25 | github.com/mattn/go-sqlite3 v1.14.24 // indirect
26 | github.com/ncruces/go-strftime v0.1.9 // indirect
27 | github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
28 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
29 | github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
30 | github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
31 | github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
32 | github.com/ysmood/fetchup v0.2.3 // indirect
33 | github.com/ysmood/goob v0.4.0 // indirect
34 | github.com/ysmood/got v0.40.0 // indirect
35 | github.com/ysmood/gson v0.7.3 // indirect
36 | github.com/ysmood/leakless v0.9.0 // indirect
37 | go.opentelemetry.io/otel v1.34.0 // indirect
38 | go.opentelemetry.io/otel/trace v1.34.0 // indirect
39 | golang.org/x/crypto v0.35.0 // indirect
40 | golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 // indirect
41 | golang.org/x/sys v0.30.0 // indirect
42 | mellium.im/sasl v0.3.2 // indirect
43 | modernc.org/libc v1.61.13 // indirect
44 | modernc.org/mathutil v1.7.1 // indirect
45 | modernc.org/memory v1.8.2 // indirect
46 | modernc.org/sqlite v1.36.0 // indirect
47 | )
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Introduction
3 |
4 | [`tuhuebot`](https://github.com/yeungon/tuhuebot) is a Telegram bot written in Go that offers various functionalities for interacting with users on Telegram. It is designed to be easily deployable and maintainable, with support for building and running through a `Makefile` and a `systemd` service for automatic management.
5 |
6 | ## How to Deploy `tuhuebot` Using `systemd`
7 |
8 | 1. **Clone the Repository:**
9 |
10 | `git clone https://github.com/yeungon/tuhuebot.git`
11 |
12 | `cd tuhuebot`
13 |
14 | 2. **Install dependencies:**
15 |
16 | `go mod tidy`
17 |
18 | 3. **Build the binary:**
19 |
20 | `make build`
21 |
22 | 4. **Configure the service file yourselt and copy to system:**
23 |
24 | Change the content of tuhuebot.service if needed (location of the code, for example).
25 |
26 | `sudo cp tuhuebot.service /etc/systemd/system/`
27 |
28 | 5. **Enable and Start the Service:**
29 |
30 | `sudo systemctl enable tuhuebot`
31 |
32 | `sudo systemctl start tuhuebot`
33 |
34 | 6. **Check Service Status: Verify that the bot is running correctly:**
35 |
36 | `sudo systemctl status tuhuebot`
37 |
38 | ## Database
39 |
40 | Currently `@tuhuebot` is using sqlite and postgresql database. Cache implemented via bigCache.
41 |
42 | ## Update the code and re-deploy
43 |
44 | Here’s a short guide for updating and redeploying your `tuhuebot` project:
45 |
46 | 1. **Pull Latest Changes**
47 | Login to your server, go to the folder where the code is put. First, we update your local repository with the latest code from GitHub:
48 | `sudo git pull`
49 |
50 | 2. **Build the Project**
51 | Compile the code and prepare it for deployment:
52 | `make build`
53 |
54 | 3. **Restart the Service**
55 | Use `make` to restart the systemd service for the changes to take effect:
56 | `make restart`
57 |
58 | This process will update your repository, build the project, and restart the service to apply any new changes.
--------------------------------------------------------------------------------
/internal/handle/assistants/calendar.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "time"
7 |
8 | "github.com/go-rod/rod"
9 | "github.com/go-rod/rod/lib/launcher"
10 | "github.com/yeungon/tuhuebot/pkg/helpers"
11 | tele "gopkg.in/telebot.v3"
12 | )
13 |
14 | var calendar_url string = "https://www.dhsphue.edu.vn/lichxem.aspx"
15 |
16 | func FetchCalendar() {
17 | // Launch a new browser with custom flags using rod.Launcher
18 | url := launcher.New().NoSandbox(true).MustLaunch()
19 | // Connect to the launched browser using ControlURL
20 | browser := rod.New().ControlURL(url).MustConnect()
21 | page := browser.MustPage(calendar_url).MustWindowFullscreen()
22 |
23 | page.MustWaitStable().MustScreenshot("vanban/" + FileNameCalendar())
24 |
25 | }
26 |
27 | func FileNameCalendar() string {
28 | timeLoc, _ := time.LoadLocation("Asia/Ho_Chi_Minh")
29 | currentTime := time.Now().In(timeLoc)
30 | file_Name := fmt.Sprintf("%v_%v_%v.png", currentTime.Year(), currentTime.Month(), currentTime.Weekday())
31 | return file_Name
32 | }
33 |
34 | func Calendar(b *tele.Bot) {
35 | b.Handle(&helpers.Calendar_Tracking, func(c tele.Context) error {
36 | if helpers.IsAdmin(c) == false {
37 | return nil
38 | }
39 | var fileName = "vanban/" + FileNameCalendar()
40 | //var fileName = "vanban/" + FileName()
41 | if _, err := os.Stat(fileName); err == nil {
42 | fmt.Println("File exists:", fileName)
43 | c.Send("Đây là lịch công tác theo tuần, ảnh chụp tại thời điểm gần đây nhất. Thời gian: " + TimeFetch())
44 | } else if os.IsNotExist(err) {
45 | c.Send("Đang truy cập và lấy dữ liệu văn bản. Xin đợi... Thời gian: " + TimeFetch())
46 | FetchCalendar()
47 | fmt.Println("File does not exist:", fileName)
48 | } else {
49 | fmt.Println("Error checking file:", err)
50 | }
51 |
52 | a := &tele.Photo{File: tele.FromDisk(fileName)}
53 |
54 | fmt.Println(FileNameCalendar())
55 | c.Send(a)
56 |
57 | return nil
58 | })
59 | }
60 |
--------------------------------------------------------------------------------
/internal/handle/assistants/search.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
7 | "github.com/yeungon/tuhuebot/internal/database/sqlite/students"
8 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
9 | "github.com/yeungon/tuhuebot/pkg/helpers"
10 | tele "gopkg.in/telebot.v3"
11 | )
12 |
13 | func SearchStudent(b *tele.Bot) {
14 | StartSearchStudent(b)
15 | KeepSearchingStudent(b)
16 | }
17 |
18 | func StartSearchStudent(b *tele.Bot) {
19 | b.Handle(&helpers.Students_Search, func(c tele.Context) error {
20 | c.Send("Chế độ tìm kiếm đã bật! 🌿 Nhập thông tin (tên, ngày sinh, quê, hoặc lớp vv...) bất kì để tìm sinh viên! ╰┈➤⤵")
21 | user_id := c.Sender().ID
22 | db := sqlite.DB()
23 | users.SetUserStateFetching(db, user_id, true)
24 | users.SetUserStateChecking(db, user_id, false)
25 | c.Send(user_id)
26 | return nil
27 | })
28 | }
29 |
30 | func KeepSearchingStudent(b *tele.Bot) {
31 | b.Handle(&helpers.Keep_Searching_Student, func(c tele.Context) error {
32 | user_id := c.Sender().ID
33 | db := sqlite.DB()
34 | users.SetUserStateFetching(db, user_id, true)
35 | users.SetUserStateChecking(db, user_id, false)
36 | c.Send("Chế độ tìm kiếm đã được bật lại. Xin nhập từ khóa tìm kiếm! 🌿")
37 | return nil
38 | })
39 | }
40 |
41 | func StudentSearchFetch(c tele.Context, keyword string) error {
42 | db := sqlite.DBSTUDENT()
43 | studentSearch := students.SearchStudent(db, keyword)
44 | total := len(studentSearch)
45 | if total == 0 {
46 | message := fmt.Sprintf("Không tìm thấy thông tin với từ khóa %s.", keyword)
47 | return c.Send(message)
48 | }
49 | result_message := fmt.Sprintf("Tìm được %v kết quả (hiển thị tối đa 100) với từ khóa: %s.", total, keyword)
50 |
51 | c.Send(result_message)
52 | for _, student := range studentSearch {
53 | message := fmt.Sprintf(
54 | "Tên: %s\nMã sinh viên: %s\nGiới tính: %s\nNgày sinh: %s\nLớp: %s\nDân tộc: %s\nCăn cước: %s\nPhone: %s\nEmail: %s\nTỉnh: %s\nĐịa chỉ: %s\nGhi chú: %s",
55 | student.Name,
56 | student.StudentCode,
57 | student.Gender,
58 | student.DOB,
59 | student.Class,
60 | student.Ethnic,
61 | student.NationalID,
62 | student.Phone,
63 | student.Email,
64 | student.Province,
65 | student.Address,
66 | student.Notes,
67 | )
68 |
69 | if err := c.Send(message); err != nil {
70 | return fmt.Errorf("failed to send message for student %s: %w", student.Name, err)
71 | }
72 | }
73 |
74 | return nil
75 | }
76 |
--------------------------------------------------------------------------------
/pkg/reference/longtext.go:
--------------------------------------------------------------------------------
1 | package reference
2 |
3 | var longtext = `Understanding the Log Message
4 | WAL File:
5 |
6 | The Write-Ahead Log (WAL) is a mechanism used by Pebble (and many other databases) to ensure durability and consistency. It logs every write operation before applying the changes to the main database files.
7 | In this message, demo/000005.log refers to a specific WAL file.
8 | Log Number 000005:
9 |
10 | This represents the unique identifier or sequence number for the WAL file. It helps the system know the order of operations and which WAL files are being processed.
11 | Stopped Reading at Offset 36:
12 |
13 | The "offset" refers to the byte position in the WAL file where Pebble stopped reading. In this case, it stopped reading at offset 36, likely because it reached the end of the WAL file, or there were no more records to process.
14 | Replayed 1 Keys in 1 Batches:
15 |
16 | This indicates that, during the replay of this WAL file, Pebble found 1 key (key-value pair) to process, and it was replayed as part of 1 batch.
17 | Replaying is a process where Pebble reads the WAL file to apply the recorded changes into the main data structures (such as tables and SSTables). This is typically done during recovery after a crash or restart.
18 | When This Log Might Occur
19 | Recovery Process:
20 |
21 | This log usually appears when Pebble is recovering from a previous state, such as when you restart the application or after a crash. It reads through the WAL files to make sure any uncommitted or pending changes are applied to the database.
22 | Normal Database Operation:
23 |
24 | The message can also appear during normal operation when Pebble is flushing the contents of a WAL file to SSTables (Sorted String Tables) and no more changes remain in that specific WAL.
25 | Low Write Activity:
26 |
27 | Since the log mentions that only 1 key was replayed, it suggests that there wasn't much recent write activity before this WAL file was closed or rotated. It might be typical if the database has low traffic or if a sync operation was triggered.
28 | Implications
29 | This message generally does not indicate an error; it's part of Pebble's normal operation, ensuring that data is safely written and applied.
30 | If you encounter unexpected behavior or suspect a performance issue, check the logs for any other messages related to WAL or SSTables. Frequent flushing or replaying could indicate a configuration issue or a pattern of frequent writes that is affecting performance.
31 | Would you like to dig deeper into any aspect of this process?`
32 |
--------------------------------------------------------------------------------
/internal/database/pg/timetable.go:
--------------------------------------------------------------------------------
1 | package pg
2 |
3 | import (
4 | "context"
5 | "database/sql"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 |
10 | "github.com/uptrace/bun"
11 | )
12 |
13 | func GetTimeTable(db *bun.DB) []TimeTable {
14 | var ctx = context.Background()
15 | cacheKey := "timetable_data"
16 |
17 | // Check if data is in the cache
18 | cachedData, err := Cache.Get(cacheKey)
19 | if err == nil {
20 | // Cache hit - unmarshal and return cached data
21 | var timeTable []TimeTable
22 | err = json.Unmarshal(cachedData, &timeTable)
23 | if err == nil {
24 | fmt.Println("Returning data from cache")
25 | return timeTable
26 | }
27 | }
28 |
29 | // Cache miss - query the database
30 | var timeTable []TimeTable
31 | err = db.NewSelect().
32 | Model(&timeTable).
33 | Scan(ctx)
34 | if err != nil {
35 | if err == sql.ErrNoRows {
36 | fmt.Println("No timetable found.")
37 | return []TimeTable{} // Return a zero-value slice if no events are found.
38 | }
39 |
40 | log.Fatal("Failed to retrieve timetable:", err)
41 | }
42 |
43 | // Store the fetched data in the cache
44 | data, err := json.Marshal(timeTable)
45 | if err != nil {
46 | log.Printf("Failed to marshal timetable data: %v", err)
47 | } else {
48 | err = Cache.Set(cacheKey, data)
49 | if err != nil {
50 | log.Printf("Failed to cache the result: %v", err)
51 | }
52 | }
53 |
54 | fmt.Println("Succeeded fetching data from time_table table stored at XATA.io")
55 | return timeTable
56 | }
57 |
58 | func GetTimeTableByDay(db *bun.DB, dayColumn string) []TimeTable {
59 | var ctx = context.Background()
60 | cacheKey := "timetable_data"
61 |
62 | // Check if data is in the cache
63 | cachedData, err := Cache.Get(cacheKey)
64 | if err == nil {
65 | // Cache hit - unmarshal and return cached data
66 | var timeTable []TimeTable
67 | err = json.Unmarshal(cachedData, &timeTable)
68 | if err == nil {
69 | fmt.Println("Returning data from cache")
70 | return timeTable
71 | }
72 | }
73 |
74 | // Cache miss - query the database
75 | var timeTable []TimeTable
76 | err = db.NewSelect().
77 | Model(&timeTable).
78 | Where(fmt.Sprintf("%s IS NOT NULL AND %s != ''", dayColumn, dayColumn)).
79 | Scan(ctx)
80 | if err != nil {
81 | if err == sql.ErrNoRows {
82 | fmt.Println("No timetable found.")
83 | return []TimeTable{} // Return a zero-value slice if no events are found.
84 | }
85 |
86 | log.Fatal("Failed to retrieve timetable:", err)
87 | }
88 |
89 | // Dont store in the cache because the data is not full.
90 | fmt.Println("Succeeded fetching data from time_table table stored at XATA.io")
91 | return timeTable
92 | }
93 |
--------------------------------------------------------------------------------
/pkg/helpers/timetable_menu.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import tele "gopkg.in/telebot.v3"
4 |
5 | // Define five inline buttons
6 | var LecturerTimeTable_General = tele.InlineButton{
7 | Unique: "btn_callback1_timetable_tasks_general",
8 | Text: "Thời khóa biểu chung",
9 | Data: "button1_clicked",
10 | }
11 |
12 | // Define five inline buttons
13 | var LecturerTimeTable_Today = tele.InlineButton{
14 | Unique: "btn_callback1_timetable_tasks_date_today",
15 | Text: "Thời khóa biểu hôm nay",
16 | Data: "button1_clicked",
17 | }
18 |
19 | // Define five inline buttons
20 | var LecturerTimeTable_Date_Week = tele.InlineButton{
21 | Unique: "btn_callback1_timetable_tasks_date",
22 | Text: "Thời khóa biểu theo ngày",
23 | Data: "button1_clicked",
24 | }
25 |
26 | // Define five inline buttons
27 | var LecturerTimeTable_Date_Monday = tele.InlineButton{
28 | Unique: "btn_callback1_timetable_tasks_date_monday",
29 | Text: "Thứ Hai",
30 | Data: "button1_clicked",
31 | }
32 |
33 | // Define five inline buttons
34 | var LecturerTimeTable_Date_Tuesday = tele.InlineButton{
35 | Unique: "btn_callback1_timetable_tasks_date_tuesday",
36 | Text: "Thứ Ba",
37 | Data: "button1_clicked",
38 | }
39 |
40 | var LecturerTimeTable_Date_Wednesday = tele.InlineButton{
41 | Unique: "btn_callback1_timetable_tasks_date_wednesday",
42 | Text: "Thứ Tư",
43 | Data: "button1_clicked",
44 | }
45 |
46 | var LecturerTimeTable_Date_Thursday = tele.InlineButton{
47 | Unique: "btn_callback1_timetable_tasks_date_thursday",
48 | Text: "Thứ Năm",
49 | Data: "button1_clicked",
50 | }
51 |
52 | var LecturerTimeTable_Date_Friday = tele.InlineButton{
53 | Unique: "btn_callback1_timetable_tasks_date_friday",
54 | Text: "Thứ Sáu",
55 | Data: "button1_clicked",
56 | }
57 |
58 | var LecturerTimeTable_Date_Saturday = tele.InlineButton{
59 | Unique: "btn_callback1_timetable_tasks_date_saturday",
60 | Text: "Thứ Bảy",
61 | Data: "button1_clicked",
62 | }
63 |
64 | var LecturerTimeTable_Date_Sunday = tele.InlineButton{
65 | Unique: "btn_callback1_timetable_tasks_date_sunday",
66 | Text: "Chủ Nhật",
67 | Data: "button1_clicked",
68 | }
69 |
70 | var TimeTable_InlineKeys = &tele.ReplyMarkup{
71 | InlineKeyboard: [][]tele.InlineButton{
72 | {LecturerTimeTable_General},
73 | {LecturerTimeTable_Today},
74 | {LecturerTimeTable_Date_Week},
75 | },
76 | }
77 |
78 | var TimeTable_InlineKeys_Weekday = &tele.ReplyMarkup{
79 | InlineKeyboard: [][]tele.InlineButton{
80 | {LecturerTimeTable_Date_Monday}, {LecturerTimeTable_Date_Tuesday},
81 | {LecturerTimeTable_Date_Wednesday}, {LecturerTimeTable_Date_Thursday},
82 | {LecturerTimeTable_Date_Friday}, {LecturerTimeTable_Date_Saturday},
83 | {LecturerTimeTable_Date_Sunday},
84 | },
85 | }
86 |
--------------------------------------------------------------------------------
/internal/handle/assistants/vanban.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "time"
7 |
8 | "github.com/go-rod/rod"
9 | "github.com/go-rod/rod/lib/launcher"
10 | "github.com/yeungon/tuhuebot/internal/config"
11 | "github.com/yeungon/tuhuebot/pkg/helpers"
12 | tele "gopkg.in/telebot.v3"
13 | )
14 |
15 | func FolderExists(path string) (bool, error) {
16 | info, err := os.Stat(path)
17 | if os.IsNotExist(err) {
18 | return false, nil
19 | }
20 | if err != nil {
21 | return false, err
22 | }
23 | return info.IsDir(), nil
24 | }
25 |
26 | func createFolder() {
27 | dirName := "vanban"
28 | exists, err := FolderExists(dirName)
29 | if err != nil {
30 | fmt.Println("Error:", err)
31 | } else if exists {
32 | fmt.Printf("Folder %s exists\n!", dirName)
33 | } else {
34 | // Create the directory
35 | err = os.Mkdir(dirName, 0755) // 0755 is the permission
36 | if err != nil {
37 | fmt.Println("Error creating directory:", err)
38 | return
39 | }
40 | fmt.Println("Directory created successfully:", dirName)
41 | }
42 | }
43 |
44 | func FileName() string {
45 | timeLoc, _ := time.LoadLocation("Asia/Ho_Chi_Minh")
46 | currentTime := time.Now().In(timeLoc)
47 | file_Name := fmt.Sprintf("%v_%v_%v_%v.png", currentTime.Year(), currentTime.Month(), currentTime.Day(), currentTime.Hour())
48 | return file_Name
49 | }
50 |
51 | func TimeFetch() string {
52 | timeLoc, _ := time.LoadLocation("Asia/Ho_Chi_Minh")
53 | currentTime := time.Now().In(timeLoc)
54 | time_fetched := fmt.Sprintf("%v giờ, ngày %v, tháng %v, năm %v", currentTime.Hour(), currentTime.Day(), currentTime.Month(), currentTime.Year())
55 | return time_fetched
56 | }
57 |
58 | func HandleVanban(c tele.Context) {
59 | // Launch a new browser with custom flags using rod.Launcher
60 | url := launcher.New().NoSandbox(true).MustLaunch()
61 | // Connect to the launched browser using ControlURL
62 | browser := rod.New().ControlURL(url).MustConnect()
63 | page := browser.MustPage("https://qlvb.dhsphue.edu.vn").MustWindowFullscreen()
64 |
65 | var sph_username = config.Get().SPH_USERNAME
66 | var sph_password = config.Get().SPH_PASSWORD
67 | page.MustElement("#txt_uid").MustInput(sph_username)
68 | page.MustElement("#txt_pws").MustInput(sph_password)
69 | page.MustElement("#bt_login").MustClick()
70 | time.Sleep(300 * time.Microsecond)
71 | page.MustWaitStable().MustElement("#bt_main").MustClick()
72 |
73 | createFolder()
74 |
75 | page.MustWaitStable().MustScreenshot("vanban/" + FileName())
76 | // time.Sleep(time.Hour)
77 |
78 | }
79 |
80 | func Vanban(b *tele.Bot) {
81 | b.Handle(&helpers.Tracking_Announcement, func(c tele.Context) error {
82 | var fileName = "vanban/" + FileName()
83 | if _, err := os.Stat(fileName); err == nil {
84 | fmt.Println("File exists:", fileName)
85 | c.Send("Đây là file ảnh chụp tại thời điểm gần đây nhất. Thời gian: " + TimeFetch())
86 | } else if os.IsNotExist(err) {
87 | c.Send("Đang truy cập và lấy dữ liệu văn bản. Xin đợi... Thời gian: " + TimeFetch())
88 | HandleVanban(c)
89 | fmt.Println("File does not exist:", fileName)
90 | } else {
91 | fmt.Println("Error checking file:", err)
92 | }
93 | a := &tele.Photo{File: tele.FromDisk(fileName)}
94 | c.Send(a)
95 | return nil
96 | })
97 | }
98 |
--------------------------------------------------------------------------------
/internal/database/pg/schema.go:
--------------------------------------------------------------------------------
1 | package pg
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/uptrace/bun"
7 | )
8 |
9 | // User represents a Telegram user or bot with Bun ORM.
10 | type Events struct {
11 | bun.BaseModel `bun:"table:events"` // Specifies the table name for the Bun ORM.
12 | XataID string `bun:",notnull,unique"` // Primary key with auto-increment.
13 | EventData string `bun:",nullzero"` //
14 | EventTasks string `bun:",nullzero"` //
15 | EventTracking int64 `bun:",nullzero"` //
16 | EventState bool `bun:",nullzero,default:false"` //
17 | Month int64 `bun:",nullzero"` //
18 | XataCreatedat time.Time `bun:",notnull,default:current_timestamp"` // Created at timestamp
19 | XataUpdatedat time.Time `bun:",notnull,default:current_timestamp"` // Updated at timestamp
20 | }
21 |
22 | type QA struct {
23 | bun.BaseModel `bun:"table:qa"` // Specifies the table name for the Bun ORM.
24 | XataID string `bun:",notnull,unique"` // Primary key with auto-increment.
25 | UserAsked string `bun:",nullzero"` //
26 | UserAnswered string `bun:",nullzero,default:null"` // Dự trữ
27 | Question string `bun:",nullzero"` //
28 | Answer *string `bun:",nullzero"` //
29 | Published bool `bun:",notnull,default:false"` //
30 | XataCreatedat time.Time `bun:",notnull,default:current_timestamp"` // Created at timestamp
31 | XataUpdatedat time.Time `bun:",notnull,default:current_timestamp"` // Updated at timestamp
32 | }
33 |
34 | type TimeTable struct {
35 | bun.BaseModel `bun:"table:timetable"` // Specifies the table name for the Bun ORM.
36 | XataID string `bun:",notnull,unique"` // Primary key with auto-increment.
37 | LecturerName string `bun:",nullzero"` //
38 | Year int `bun:",nullzero"` //
39 | Semester int `bun:",notnull,default:1"` //
40 | Monday string `bun:",nullzero"` //
41 | Tuesday string `bun:",nullzero"` //
42 | Wednesday string `bun:",nullzero"` //
43 | Thursday string `bun:",nullzero"` //
44 | Friday string `bun:",nullzero"` //
45 | Saturday string `bun:",nullzero"` //
46 | Sunday string `bun:",nullzero"` //
47 | Notes string `bun:",nullzero"` //
48 | XataCreatedat time.Time `bun:",notnull,default:current_timestamp"` // Created at timestamp
49 | XataUpdatedat time.Time `bun:",notnull,default:current_timestamp"` // Updated at timestamp
50 | }
51 |
--------------------------------------------------------------------------------
/internal/database/bbolt/users.go:
--------------------------------------------------------------------------------
1 | package bbolt
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | bolt "go.etcd.io/bbolt"
8 | )
9 |
10 | // Define a bucket name.
11 | var bucketName = []byte("users")
12 |
13 | func CreateUserBucket() {
14 | // Ensure the bucket exists (only once).
15 | err = db.Update(func(tx *bolt.Tx) error {
16 | _, err := tx.CreateBucketIfNotExists(bucketName)
17 | if err != nil {
18 | return fmt.Errorf("create bucket: %s", err)
19 | }
20 | return nil
21 | })
22 | if err != nil {
23 | log.Fatal(err)
24 | }
25 | }
26 |
27 | func UserAdd(key []byte, value []byte) {
28 | err = db.Update(func(tx *bolt.Tx) error {
29 | bucket := tx.Bucket([]byte("users"))
30 | if bucket == nil {
31 | return fmt.Errorf("bucket %q not found", []byte("users"))
32 | }
33 | // CREATE: Add a key-value pair.
34 | err = bucket.Put(key, value)
35 | if err != nil {
36 | return fmt.Errorf("put value: %s", err)
37 | }
38 |
39 | //fmt.Println("Value inserted")
40 | return nil
41 | })
42 | if err != nil {
43 | log.Fatal(err)
44 | }
45 | }
46 | func UserRead(key []byte) []byte {
47 | var val []byte
48 |
49 | // READ: Retrieve a value.
50 | err := db.View(func(tx *bolt.Tx) error {
51 | bucket := tx.Bucket([]byte("users"))
52 | if bucket == nil {
53 | return fmt.Errorf("bucket %q not found", "users")
54 | }
55 |
56 | // Get the value from the bucket.
57 | val = bucket.Get(key)
58 | if val == nil {
59 | return fmt.Errorf("key %q not found", key)
60 | }
61 |
62 | fmt.Printf("Read value: %s\n", val)
63 | fmt.Printf("Read value: %s\n", string(val))
64 | fmt.Printf("Read value: %v\n", val)
65 | return nil
66 | })
67 |
68 | if err != nil {
69 | log.Printf("Error reading from bucket: %v", err)
70 | return nil
71 | }
72 |
73 | return val
74 | }
75 |
76 | // PrintAllKeyValues prints all key-value pairs in a specified bucket.
77 | func PrintAllKeyValues(bucketName string) {
78 | err := db.View(func(tx *bolt.Tx) error {
79 | bucket := tx.Bucket([]byte(bucketName))
80 | if bucket == nil {
81 | return fmt.Errorf("bucket %q not found", bucketName)
82 | }
83 |
84 | // Use a cursor to iterate over all key-value pairs in the bucket.
85 | cursor := bucket.Cursor()
86 | for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
87 | fmt.Printf("Key: %s, Value: %s\n", k, v)
88 | }
89 |
90 | return nil
91 | })
92 |
93 | if err != nil {
94 | log.Printf("Error reading from bucket %q: %v", bucketName, err)
95 | }
96 | }
97 |
98 | // CleanAllKeyValues deletes all key-value pairs in a specified bucket.
99 | func CleanAllKeyValues(bucketName string) {
100 | err := db.Update(func(tx *bolt.Tx) error {
101 | bucket := tx.Bucket([]byte(bucketName))
102 | if bucket == nil {
103 | return fmt.Errorf("bucket %q not found", bucketName)
104 | }
105 |
106 | // Use a cursor to iterate over all key-value pairs in the bucket.
107 | cursor := bucket.Cursor()
108 | for k, _ := cursor.First(); k != nil; k, _ = cursor.Next() {
109 | // Delete each key.
110 | if err := bucket.Delete(k); err != nil {
111 | return fmt.Errorf("failed to delete key %q: %v", k, err)
112 | }
113 | }
114 |
115 | fmt.Printf("All key-value pairs in bucket %q have been deleted.\n", bucketName)
116 | return nil
117 | })
118 |
119 | if err != nil {
120 | log.Printf("Error cleaning up bucket %q: %v", bucketName, err)
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/internal/database/sqlite/users/schema.go:
--------------------------------------------------------------------------------
1 | package users
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "time"
8 |
9 | "github.com/uptrace/bun"
10 | )
11 |
12 | // User represents a Telegram user or bot with Bun ORM.
13 | type User struct {
14 | bun.BaseModel `bun:"table:users"` // Specifies the table name for the Bun ORM.
15 | ID int64 `bun:",pk,autoincrement"` // Primary key with auto-increment.
16 | TelegramUserID int64 `bun:",notnull,unique"` // Telegram user ID.
17 | IsBot bool `bun:",notnull"` // Indicates if this user is a bot.
18 | Level int64 `bun:",notnull,default:1"` //
19 | State bool `bun:",notnull,default:false"` //
20 | StateAsking bool `bun:",notnull,default:false"` // If receive the question
21 | StateChecking bool `bun:",notnull,default:false"` // If receive the question
22 | StateFetching bool `bun:",notnull,default:false"` // If receive the question
23 | StateManipulating bool `bun:",notnull,default:false"` // If receive the question
24 | StateBlocking bool `bun:",notnull,default:false"` // If receive the question
25 | AttemptCounting int64 `bun:",nullzero,default:0"` // How many time the user type the password
26 | QuestionAnswerTracking int64 `bun:",nullzero,default:0"` // How many time the user type the password
27 | ExternalData int64 `bun:",nullzero,default:0"` // How many time the user type the password
28 | FirstName string `bun:",nullzero"` // User's or bot's first name.
29 | LastName *string `bun:",nullzero"` // Optional. User's or bot's last name.
30 | Username *string `bun:",nullzero"` // Optional. User's or bot's username.
31 | LanguageCode *string `bun:",nullzero"` // Optional. User's language.
32 | IsPremium bool `bun:",notnull,default:false"` // Optional. True if the user is a Premium user.
33 | AddedToAttachmentMenu bool `bun:",notnull,default:false"` // Indicates if the user added the bot to the attachment menu.
34 | CanJoinGroups bool `bun:",notnull,default:false"` // Indicates if the bot can be invited to groups.
35 | CanReadAllGroupMessages bool `bun:",notnull,default:false"` // Indicates if privacy mode is disabled.
36 | SupportsInlineQueries bool `bun:",notnull,default:false"` // Indicates if the bot supports inline queries.
37 | CreatedAt time.Time `bun:",notnull,default:current_timestamp"` // Created at timestamp
38 | UpdatedAt time.Time `bun:",notnull,default:current_timestamp"` // Updated at timestamp
39 | }
40 |
41 | func CreateTable(db *bun.DB) {
42 | var ctx = context.Background()
43 | // Migrate the schema: create the "users" table if it doesn't exist.
44 | res, err := db.NewCreateTable().Model((*User)(nil)).IfNotExists().Exec(ctx)
45 | if err != nil {
46 | log.Fatal("Failed to create table:", err)
47 | }
48 | info := fmt.Sprintf("The table 'users' created. Rows affected: %d", res)
49 | fmt.Println(info)
50 | }
51 |
--------------------------------------------------------------------------------
/internal/database/sqlite/users/update.go:
--------------------------------------------------------------------------------
1 | package users
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "log"
7 | "time"
8 |
9 | "github.com/uptrace/bun"
10 | )
11 |
12 | // SetUserState updates the state of a user based on the provided Telegram ID and state.
13 | func SetUserState(db *bun.DB, telegram_id int64, state bool) {
14 | var ctx = context.Background()
15 | var currentUser User
16 | currentUser.State = state
17 | currentUser.UpdatedAt = time.Now()
18 | // Execute the update query to change the state.
19 | _, err := db.NewUpdate().
20 | Model(¤tUser).
21 | Column("state").
22 | Where("telegram_user_id = ?", telegram_id).
23 | Exec(ctx)
24 | if err != nil {
25 | log.Fatal("There is something wrong with updating the user state: ", err)
26 | }
27 |
28 | fmt.Printf("Updating state to %v for user with Telegram ID %d\n", state, telegram_id)
29 | }
30 |
31 | // SetUserState updates the state of a user based on the provided Telegram ID and state.
32 | func SetUserStateAsking(db *bun.DB, telegram_id int64, state bool) {
33 | var ctx = context.Background()
34 | var currentUser User
35 | currentUser.StateAsking = state
36 | currentUser.UpdatedAt = time.Now()
37 | // Execute the update query to change the state_asking.
38 | _, err := db.NewUpdate().
39 | Model(¤tUser).
40 | Column("state_asking").
41 | Where("telegram_user_id = ?", telegram_id).
42 | Exec(ctx)
43 | if err != nil {
44 | log.Fatal("There is something wrong with updating the user state_asking: ", err)
45 | }
46 |
47 | fmt.Printf("Updating state to %v for user with Telegram ID %d\n", state, telegram_id)
48 | }
49 |
50 | // SetUserState updates the state of a user based on the provided Telegram ID and state.
51 | func SetUserStateChecking(db *bun.DB, telegram_id int64, state bool) {
52 | var ctx = context.Background()
53 | var currentUser User
54 | currentUser.StateChecking = state
55 | currentUser.UpdatedAt = time.Now()
56 | // Execute the update query to change the state_asking.
57 | _, err := db.NewUpdate().
58 | Model(¤tUser).
59 | Column("state_checking").
60 | Where("telegram_user_id = ?", telegram_id).
61 | Exec(ctx)
62 | if err != nil {
63 | log.Fatal("There is something wrong with updating the user state: ", err)
64 | }
65 |
66 | fmt.Printf("Updating state_checking to %v for user with Telegram ID %d\n", state, telegram_id)
67 | }
68 |
69 | func SetUserStateFetching(db *bun.DB, telegram_id int64, state bool) {
70 | var ctx = context.Background()
71 | var currentUser User
72 | currentUser.StateFetching = state
73 | currentUser.UpdatedAt = time.Now()
74 | // Execute the update query to change the state_asking.
75 | _, err := db.NewUpdate().
76 | Model(¤tUser).
77 | Column("state_fetching").
78 | Where("telegram_user_id = ?", telegram_id).
79 | Exec(ctx)
80 | if err != nil {
81 | log.Fatal("There is something wrong with updating the user state: ", err)
82 | }
83 |
84 | fmt.Printf("Updating state_fetching to %v for user with Telegram ID %d\n", state, telegram_id)
85 | }
86 |
87 | // SetUserState updates the state of a user based on the provided Telegram ID and state.
88 | func SetUserLevel(db *bun.DB, telegram_id int64, level int64) {
89 | var ctx = context.Background()
90 | var currentUser User
91 | currentUser.Level = level
92 | currentUser.UpdatedAt = time.Now()
93 | // Execute the update query to change the state.
94 | _, err := db.NewUpdate().
95 | Model(¤tUser).
96 | Column("level").
97 | Where("telegram_user_id = ?", telegram_id).
98 | Exec(ctx)
99 | if err != nil {
100 | log.Fatal("There is something wrong with updating the user state: ", err)
101 | }
102 |
103 | fmt.Printf("Updating level to %v for user with Telegram ID %d\n", level, telegram_id)
104 | }
105 |
--------------------------------------------------------------------------------
/internal/handle/qa.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "encoding/binary"
5 | "fmt"
6 | "strconv"
7 |
8 | "github.com/yeungon/tuhuebot/internal/database/bbolt"
9 | "github.com/yeungon/tuhuebot/internal/database/pg"
10 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
11 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
12 | "github.com/yeungon/tuhuebot/pkg/helpers"
13 | tele "gopkg.in/telebot.v3"
14 | )
15 |
16 | func IntToBytes(n int) []byte {
17 | b := make([]byte, 8) // Use 8 bytes for int64 or uint64
18 | binary.BigEndian.PutUint64(b, uint64(n))
19 | return b
20 | }
21 |
22 | func BytesToInt(b []byte) int {
23 | return int(binary.BigEndian.Uint64(b))
24 | }
25 |
26 | func UpdateOffset(c tele.Context, stepstate int, possible_offset int) int {
27 | current_user := c.Sender()
28 | current_user_kv := fmt.Sprintf("%v", current_user.ID)
29 | current_user_pagination := bbolt.UserRead([]byte(current_user_kv))
30 |
31 | var currentState int
32 |
33 | if len(current_user_pagination) == 0 {
34 | // If no value exists, initialize it to 0.
35 | fmt.Println("current_user_pagination empty byte[]")
36 | currentState = 0
37 | } else {
38 | // Convert the existing byte array to an integer.
39 | currentState = BytesToInt(current_user_pagination)
40 | fmt.Printf("Current state as int: %d\n", currentState)
41 | }
42 |
43 | // Calculate the new state by adding the step.
44 | newState := currentState + stepstate
45 | if newState < 0 {
46 | newState = 0
47 | }
48 |
49 | if newState >= possible_offset {
50 | newState = possible_offset
51 | }
52 |
53 | // Defer the update so that it happens after returning the new state.
54 | defer func() {
55 | fmt.Printf("Saving new state: %d\n", newState)
56 | bbolt.UserAdd([]byte(current_user_kv), IntToBytes(newState))
57 | }()
58 |
59 | // Return the new state.
60 | return newState
61 | }
62 |
63 | func FetchQAPG(b *tele.Bot, c tele.Context, control int) {
64 | current_user := c.Sender()
65 |
66 | pgdata := pg.PG()
67 | question_answer := pg.GetQuestionAnswer(pgdata)
68 | total_qa := len(question_answer)
69 | possible_offset := total_qa / 5
70 |
71 | inform_Message := fmt.Sprintf("Các câu hỏi thường gặp. Hiện có %d câu hỏi.", total_qa)
72 |
73 | c.Send(inform_Message)
74 |
75 | fmt.Printf("Tổng số qa: %d\n", total_qa)
76 | // fmt.Printf("possible_offset: %d\n", possible_offset)
77 |
78 | offset := UpdateOffset(c, control, possible_offset)
79 |
80 | // fmt.Printf("Offset hiện tại: %d \n", offset)
81 |
82 | step := 5 * offset
83 | starting := 0 + step
84 | ending := starting + 5
85 |
86 | if ending >= total_qa {
87 | ending = total_qa
88 | starting = ending - 5
89 | step = ending
90 | }
91 |
92 | if starting < 0 {
93 | starting = 0
94 | ending = starting + 5
95 | step = 0
96 | }
97 |
98 | portion_slice := question_answer[starting:ending]
99 |
100 | fmt.Println("starting-ending: ", starting, ending)
101 |
102 | for index, record := range portion_slice {
103 | if record.Published == true {
104 | index_string := strconv.Itoa(index + starting + 1)
105 | questionMsg := "🌓 🅀🅄🄴🅂🅃🄸🄾🄽 " + index_string + ": " + record.Question + ""
106 | b.Send(current_user, questionMsg, &tele.SendOptions{
107 | ParseMode: "HTML",
108 | })
109 | // answerMsgTexta := "✅ 🄰🄽🅂🅆🄴🅁: " + record.Answer
110 | answerMsgTexta := "✅ " + *record.Answer
111 | b.Send(current_user, answerMsgTexta, &tele.SendOptions{
112 | ParseMode: "HTML",
113 | })
114 | }
115 | }
116 | }
117 |
118 | func Qa(b *tele.Bot) {
119 | b.Handle("/qa", func(c tele.Context) error {
120 | FetchQAPG(b, c, 0)
121 | c.Send("Xem các câu hỏi khác", helpers.QA_Menu_InlineKeys)
122 | return nil
123 | })
124 |
125 | b.Handle(&helpers.QA, func(c tele.Context) error {
126 | FetchQAPG(b, c, 0)
127 | c.Send("Xem các câu hỏi khác", helpers.QA_Menu_InlineKeys)
128 | return nil
129 | })
130 | ControlQuestion(b)
131 | PostQuestion(b)
132 | }
133 |
134 | func ControlQuestion(b *tele.Bot) {
135 | b.Handle(&helpers.Back_QA, func(c tele.Context) error {
136 | fmt.Println("Control -")
137 | FetchQAPG(b, c, -1)
138 | c.Send("Xem các câu hỏi khác", helpers.QA_Menu_InlineKeys)
139 | return nil
140 | })
141 |
142 | b.Handle(&helpers.Forward_QA, func(c tele.Context) error {
143 | fmt.Println("Control +")
144 | FetchQAPG(b, c, 1)
145 | c.Send("Xem các câu hỏi khác", helpers.QA_Menu_InlineKeys)
146 | return nil
147 | })
148 | }
149 |
150 | func PostQuestion(b *tele.Bot) {
151 | b.Handle(&helpers.Post_QA, func(c tele.Context) error {
152 | // c.Send(current)
153 | user_id := c.Sender().ID
154 | user := c.Sender().ID
155 | db := sqlite.DB()
156 | users.SetUserStateAsking(db, user_id, true)
157 | current_user_asking := users.UserStateAsking(db, user)
158 | fmt.Println(current_user_asking)
159 | c.Send("Bot đã bật chế độ nhận câu hỏi. Xin đặt câu hỏi! 🔓")
160 | return nil
161 | })
162 | }
163 |
--------------------------------------------------------------------------------
/internal/handle/assistants/timetable.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | "time"
7 |
8 | "github.com/yeungon/tuhuebot/internal/database/pg"
9 | "github.com/yeungon/tuhuebot/pkg/helpers"
10 | tele "gopkg.in/telebot.v3"
11 | )
12 |
13 | func TimeTable(b *tele.Bot) {
14 | b.Handle(&helpers.LecturerTimeTable, func(c tele.Context) error {
15 | c.Send("Lịch dạy giảng viên:", helpers.TimeTable_InlineKeys)
16 | return nil
17 | })
18 | b.Handle(&helpers.LecturerTimeTable_General, func(c tele.Context) error {
19 | //c.Send("Thời khóa biểu chung:", helpers.TimeTable_InlineKeys)
20 | c.Send("Lịch dạy giảng viên")
21 | pgdata := pg.PG()
22 | timetable := pg.GetTimeTable(pgdata)
23 | for _, tkb := range timetable {
24 | message := fmt.Sprintf("%s\n\n", tkb.LecturerName)
25 | if tkb.Monday != "" {
26 | message += "✅Thứ hai: " + tkb.Monday + "\n"
27 | }
28 | if tkb.Tuesday != "" {
29 | message += "✅Thứ ba: " + tkb.Tuesday + "\n"
30 | }
31 | if tkb.Wednesday != "" {
32 | message += "✅Thứ tư: " + tkb.Wednesday + "\n"
33 | }
34 | if tkb.Thursday != "" {
35 | message += "✅Thứ năm: " + tkb.Thursday + "\n"
36 | }
37 | if tkb.Friday != "" {
38 | message += "✅Thứ sáu: " + tkb.Friday + "\n"
39 | }
40 | if tkb.Saturday != "" {
41 | message += "✅Thứ bảy: " + tkb.Saturday + "\n"
42 | }
43 | if tkb.Sunday != "" {
44 | message += "✅Chủ nhật: " + tkb.Sunday + "\n"
45 | }
46 | if tkb.Notes != "" {
47 | message += "\n🔥Lưu ý: " + tkb.Notes + "\n"
48 | }
49 | c.Send(message)
50 | }
51 | return nil
52 | })
53 |
54 | b.Handle(&helpers.LecturerTimeTable_Today, func(c tele.Context) error {
55 | day_vietnamese := []string{"Chủ Nhật", "Thứ Hai", "Thứ Ba", "Thứ Tư", "Thứ Năm", "Thứ Sáu", "Thứ Bảy"}
56 | timeLoc, _ := time.LoadLocation("Asia/Ho_Chi_Minh")
57 | today := time.Now().In(timeLoc)
58 | currentDay := today.Weekday()
59 | today_string := strings.ToLower(currentDay.String())
60 | dayNumber := int(currentDay)
61 | c.Send("Thời khóa biểu hôm nay " + day_vietnamese[dayNumber])
62 | fetchTimeTable(c, today_string, dayNumber)
63 | return nil
64 | })
65 |
66 | b.Handle(&helpers.LecturerTimeTable_Date_Week, func(c tele.Context) error {
67 | c.Send("Lịch dạy giảng viên theo ngày:", helpers.TimeTable_InlineKeys_Weekday)
68 | return nil
69 | })
70 |
71 | b.Handle(&helpers.LecturerTimeTable_Date_Monday, func(c tele.Context) error {
72 | c.Send("Lịch dạy giảng viên Thứ Hai:")
73 | fetchTimeTable(c, "monday", 1)
74 | return nil
75 | })
76 |
77 | b.Handle(&helpers.LecturerTimeTable_Date_Tuesday, func(c tele.Context) error {
78 | c.Send("Lịch dạy giảng viên Thứ Ba:")
79 | fetchTimeTable(c, "tuesday", 2)
80 | return nil
81 | })
82 |
83 | b.Handle(&helpers.LecturerTimeTable_Date_Wednesday, func(c tele.Context) error {
84 | c.Send("Lịch dạy giảng viên Thứ Tư:")
85 | fetchTimeTable(c, "wednesday", 3)
86 | return nil
87 | })
88 |
89 | b.Handle(&helpers.LecturerTimeTable_Date_Thursday, func(c tele.Context) error {
90 | c.Send("Lịch dạy giảng viên Thứ Năm:")
91 | fetchTimeTable(c, "thursday", 4)
92 | return nil
93 | })
94 |
95 | b.Handle(&helpers.LecturerTimeTable_Date_Friday, func(c tele.Context) error {
96 | c.Send("Lịch dạy giảng viên Thứ Sáu:")
97 | fetchTimeTable(c, "friday", 5)
98 | return nil
99 | })
100 |
101 | b.Handle(&helpers.LecturerTimeTable_Date_Saturday, func(c tele.Context) error {
102 | c.Send("Lịch dạy giảng viên Thứ Bảy:")
103 | fetchTimeTable(c, "saturday", 6)
104 | return nil
105 | })
106 |
107 | b.Handle(&helpers.LecturerTimeTable_Date_Sunday, func(c tele.Context) error {
108 | c.Send("Lịch dạy giảng viên Chủ Nhật:")
109 | fetchTimeTable(c, "sunday", 0)
110 | return nil
111 | })
112 |
113 | }
114 |
115 | func fetchTimeTable(c tele.Context, dayFetching string, dayNumber int) {
116 | pgdata := pg.PG()
117 | timetable := pg.GetTimeTableByDay(pgdata, dayFetching)
118 | for _, tkb := range timetable {
119 | lecturer := fmt.Sprintf("%s\n", tkb.LecturerName)
120 | message := ""
121 | if tkb.Monday != "" {
122 | if dayNumber == 1 {
123 | message += "✅Thứ hai: " + tkb.Monday + "\n"
124 | }
125 | }
126 | if tkb.Tuesday != "" {
127 | if dayNumber == 2 {
128 | message += "✅Thứ ba: " + tkb.Tuesday + "\n"
129 | }
130 | }
131 | if tkb.Wednesday != "" {
132 | if dayNumber == 3 {
133 | message += "✅Thứ tư: " + tkb.Wednesday + "\n"
134 |
135 | }
136 | }
137 | if tkb.Thursday != "" {
138 | if dayNumber == 4 {
139 | message += "✅Thứ năm: " + tkb.Thursday + "\n"
140 | }
141 |
142 | }
143 | if tkb.Friday != "" {
144 | if dayNumber == 5 {
145 | message += "✅Thứ sáu: " + tkb.Friday + "\n"
146 | }
147 |
148 | }
149 | if tkb.Saturday != "" {
150 | if dayNumber == 6 {
151 | message += "✅Thứ bảy: " + tkb.Saturday + "\n"
152 | }
153 |
154 | }
155 | if tkb.Sunday != "" {
156 | if dayNumber == 0 {
157 | message += "✅Chủ nhật: " + tkb.Sunday + "\n"
158 | }
159 | }
160 | if len(message) > 0 {
161 | if tkb.Notes != "" {
162 | message += "\n🔥Lưu ý: " + tkb.Notes + "\n"
163 | }
164 | message_send := lecturer + message
165 | c.Send(message_send)
166 | }
167 | }
168 |
169 | }
170 |
--------------------------------------------------------------------------------
/internal/handle/assistants/student.go:
--------------------------------------------------------------------------------
1 | package assistants
2 |
3 | import (
4 | "crypto/md5"
5 | "fmt"
6 | "io"
7 | "time"
8 |
9 | "encoding/hex"
10 | "encoding/json"
11 | "log"
12 | "net/http"
13 |
14 | "github.com/yeungon/tuhuebot/internal/config"
15 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
16 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
17 | "github.com/yeungon/tuhuebot/pkg/helpers"
18 | tele "gopkg.in/telebot.v3"
19 | )
20 |
21 | func StudentCheck(b *tele.Bot) {
22 | KeepCheckingStudent(b)
23 | PostCheckStudent(b)
24 | }
25 |
26 | func PostCheckStudent(b *tele.Bot) {
27 | b.Handle(&helpers.Students_Check, func(c tele.Context) error {
28 | // c.Send(current)
29 | user_id := c.Sender().ID
30 | user := c.Sender().ID
31 | db := sqlite.DB()
32 | users.SetUserStateChecking(db, user_id, true)
33 | current_user_checking := users.UserStateChecking(db, user)
34 | fmt.Println(current_user_checking)
35 | c.Send("Bot đã bật chế độ xem thông tin sinh viên. Xin nhập mã sinh viên! 💡")
36 | return nil
37 | })
38 | }
39 |
40 | func KeepCheckingStudent(b *tele.Bot) {
41 | b.Handle(&helpers.Keep_Checking_Student, func(c tele.Context) error {
42 | // c.Send(current)
43 | user_id := c.Sender().ID
44 | db := sqlite.DB()
45 | users.SetUserStateChecking(db, user_id, true)
46 | c.Send("Chế độ xem thông tin sinh viên đã được bật lại. Xin nhập mã sinh viên! 🌿")
47 | return nil
48 | })
49 | }
50 |
51 | func StudentCheckFetch(c tele.Context, studentID string) error {
52 | baseurl := config.Get().SPH_URL_ENDPOINT
53 | key := generateHash()
54 | endpoint := fmt.Sprintf("%s?key=%s&id=%s", baseurl, key, studentID)
55 | data, err := fetchData(endpoint)
56 |
57 | if err != nil {
58 | log.Printf("Error fetching data: %v", err)
59 | return c.Send("Không tìm thấy thông tin, xin kiểm tra mã sinh viên. Vui lòng thử lại sau!")
60 | }
61 |
62 | fmt.Println("Fetched data successfully:")
63 |
64 | // Format the student info
65 | studentInfo := fmt.Sprintf(
66 | "Thông tin sinh viên:\nMã số: %s\nHọ và tên: %s\nLớp: %s\nSố điện thoại: %s\n",
67 | data.Info.FMasv, data.Info.FHoten, data.Info.FLop, data.Info.FPhone,
68 | )
69 |
70 | // Send student info first
71 | err = c.Send(studentInfo)
72 | if err != nil {
73 | log.Printf("Error sending student info: %v", err)
74 | return c.Send("Không thể gửi thông tin sinh viên.")
75 | }
76 |
77 | // Format scores (split if too long)
78 | const maxMessageLength = 4000 // Telegram's limit for a single message
79 | scores := "Kết quả học tập:\n"
80 | messages := []string{}
81 | for _, diem := range data.Diem {
82 | entry := fmt.Sprintf(
83 | "Học kỳ: %s | Môn học: %s (%s)\nĐiểm: %s | Điểm QT: %s | Điểm thi: %s | ĐVHT: %s\n\n",
84 | diem.Hk, diem.Tennh, diem.Mamh, diem.Diem, diem.Diemqt, diem.Diemthi, diem.Dvht,
85 | )
86 |
87 | if len(scores)+len(entry) > maxMessageLength {
88 | messages = append(messages, scores)
89 | scores = "Kết quả học tập:\n" // Start a new message
90 | }
91 |
92 | scores += entry
93 | }
94 |
95 | // Append the final message if there's remaining content
96 | if scores != "Kết quả học tập:\n" {
97 | messages = append(messages, scores)
98 | }
99 |
100 | // Send scores in parts
101 | for _, message := range messages {
102 | err = c.Send(message)
103 | if err != nil {
104 | log.Printf("Error sending scores: %v", err)
105 | return c.Send("Không thể gửi toàn bộ kết quả học tập. Vui lòng thử lại sau.")
106 | }
107 | }
108 |
109 | return nil
110 |
111 | }
112 |
113 | type ResponseData struct {
114 | Info struct {
115 | FMasv string `json:"f_masv"`
116 | FHoten string `json:"f_hoten"`
117 | FLop string `json:"f_lop"`
118 | FPhone string `json:"f_phone"`
119 | } `json:"info"`
120 | Diem []struct {
121 | Hk string `json:"HK"`
122 | Mamh string `json:"MAMH"`
123 | Diem string `json:"DIEM"`
124 | Diemqt string `json:"DIEMQT"`
125 | Diemthi string `json:"DIEMTHI"`
126 | Diemtl string `json:"DIEMTL"`
127 | Tennh string `json:"tennh"`
128 | Dvht string `json:"dvht"`
129 | } `json:"diem"`
130 | }
131 |
132 | func fetchData(url string) (*ResponseData, error) {
133 | resp, err := http.Get(url)
134 | if err != nil {
135 | return nil, fmt.Errorf("failed to fetch data: %w", err)
136 | }
137 | defer resp.Body.Close()
138 |
139 | // Check if the status code is 200 OK
140 | if resp.StatusCode != http.StatusOK {
141 | return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
142 | }
143 |
144 | // Read the response body
145 | body, err := io.ReadAll(resp.Body)
146 |
147 | if err != nil {
148 | return nil, fmt.Errorf("failed to read response body: %w", err)
149 | }
150 |
151 | // Parse the JSON response
152 | var data ResponseData
153 | if err := json.Unmarshal(body, &data); err != nil {
154 | return nil, fmt.Errorf("failed to parse JSON: %w", err)
155 | }
156 | return &data, nil
157 | }
158 |
159 | func generateHash() string {
160 | loc, _ := time.LoadLocation("Asia/Ho_Chi_Minh")
161 | time.Local = loc
162 | prefix := config.Get().SECRET_FIRST
163 | suffix := config.Get().SECRET_SECOND
164 | currentTime := time.Now().In(loc)
165 | dateString := currentTime.Format("212006") // dmyyyy format
166 | input := prefix + dateString + suffix
167 | hash := md5.Sum([]byte(input))
168 | return hex.EncodeToString(hash[:])
169 | }
170 |
--------------------------------------------------------------------------------
/internal/handle/submit.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | "strings"
7 | "time"
8 |
9 | "github.com/yeungon/tuhuebot/internal/config"
10 | "github.com/yeungon/tuhuebot/internal/database/pg"
11 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
12 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
13 | "github.com/yeungon/tuhuebot/internal/handle/assistants"
14 | "github.com/yeungon/tuhuebot/pkg/helpers"
15 | "gopkg.in/telebot.v3"
16 | tele "gopkg.in/telebot.v3"
17 | )
18 |
19 | func check_passcode(user_input string) bool {
20 | passcode := config.Get().PASSCODE
21 | fmt.Println(passcode)
22 | if passcode == user_input {
23 | return true
24 | }
25 | return false
26 | }
27 |
28 | func getName(c tele.Context) string {
29 | name := c.Sender().FirstName
30 | if len(name) > 0 {
31 | return name
32 | }
33 | return c.Sender().Username
34 | }
35 |
36 | func notifyAdmin(b *tele.Bot, message string) error {
37 | admin_id := config.Get().AdminID
38 | num, err := strconv.ParseInt(admin_id, 10, 64)
39 | if err != nil {
40 | fmt.Println("Error converting string to int64:", err)
41 | return err
42 | }
43 | b.Send(tele.ChatID(num), "Một câu hỏi mới vừa được gửi với nội dung:\n "+message)
44 | return nil
45 | }
46 |
47 | func Submit(b *tele.Bot) {
48 | b.Handle(tele.OnText, func(c tele.Context) error {
49 | user := c.Sender().ID
50 | db := sqlite.DB()
51 | current_user := users.GetCurrentUser(db, user)
52 |
53 | if current_user.State == true {
54 | user_input := strings.TrimSpace(c.Text())
55 | passcode := check_passcode(user_input)
56 | users.SetUserState(db, user, false)
57 |
58 | if passcode == true {
59 | users.SetUserLevel(db, user, 2)
60 | c.Send("Welcome " + getName(c) + ". Đây là khu vực truy cập dữ liệu level 2, hỗ trợ đa dạng hơn các nghiệp vụ xử lý thông tin. Bạn sẽ không cần nhập mật khẩu khi truy xuất.")
61 | return nil
62 | } else {
63 | c.Send("😮💨 Mật khẩu không chính xác!")
64 | c.Send("Tùy chọn tiếp theo 👇", helpers.Passcode_Menu_InlineKeys)
65 | return nil
66 | }
67 | }
68 |
69 | if current_user.StateAsking == true {
70 | user_asked := strconv.FormatInt(c.Sender().ID, 10)
71 | fmt.Println(user_asked)
72 | user_input := strings.TrimSpace(c.Text())
73 | // Store the question into the database at XATA
74 | pgdata := pg.PG()
75 | var answer string = "" // No answer provided
76 | newQA := &pg.QA{
77 | UserAsked: user_asked, // Example user ID
78 | Question: user_input,
79 | Answer: &answer,
80 | XataCreatedat: time.Now(),
81 | XataUpdatedat: time.Now(),
82 | }
83 | pg.CreateQA(pgdata, newQA)
84 | //Close the asking question state_asking
85 | users.SetUserStateAsking(db, user, false)
86 |
87 | c.Send("Cảm ơn bạn đã đặt câu hỏi 🤗💯. Bot sẽ cập nhật dữ liệu khi có câu trả lời.\nTruy cập /qa để theo dõi!\n")
88 | c.Send("Chế độ nhận câu hỏi đã đóng!🔒")
89 | notifyAdmin(b, user_input)
90 | return nil
91 | }
92 |
93 | if current_user.StateChecking == true {
94 | user_input := strings.ToUpper(strings.TrimSpace(c.Text()))
95 |
96 | if len(user_input) != 10 {
97 | c.Send("😮💨 Mã số sinh viên không chính xác! Mã số sinh viên có 10 ký tự. Hệ thống hiện chỉ hỗ trợ sinh viên khoa Giáo dục Tiểu học. Xin nhập lại mã sinh viên: ")
98 | c.Send("Nhập lại mã sinh viên hoặc chọn các nghiệp vụ khác 👇", helpers.Student_Check_Menu_InlineKeys)
99 | return nil
100 | }
101 |
102 | assistants.StudentCheckFetch(c, user_input)
103 | users.SetUserStateChecking(db, user, false)
104 |
105 | // Emulate red emphasis using emojis
106 | htmlMessage := `🔴 Chế độ xem thông tin sinh viên đã đóng!🔒⛔`
107 | c.Send(htmlMessage, &telebot.SendOptions{ParseMode: telebot.ModeHTML})
108 |
109 | c.Send("Các tùy chọn nghiệp vụ tiếp theo 👇", helpers.Student_Check_Menu_InlineKeys)
110 | return nil
111 | }
112 |
113 | if current_user.StateFetching == true {
114 | user_input := strings.TrimSpace(c.Text())
115 | assistants.StudentSearchFetch(c, user_input)
116 | users.SetUserStateFetching(db, user, false)
117 |
118 | // c.Send("Chế độ tìm kiếm thông tin sinh viên đã đóng!🔒")
119 |
120 | // Emulate red emphasis using emojis
121 | htmlMessage := `🔴 Chế độ tìm kiếm thông tin sinh viên đã đóng!🔒⛔`
122 | c.Send(htmlMessage, &telebot.SendOptions{ParseMode: telebot.ModeHTML})
123 |
124 | c.Send("Các tùy chọn nghiệp vụ tiếp theo 👇", helpers.Student_Check_Menu_InlineKeys)
125 | return nil
126 | }
127 |
128 | // Thông báo chung
129 | fmt.Println(user)
130 | return c.Send("Sorry, bot không xử lý các thông tin bạn gửi lên!")
131 | })
132 |
133 | b.Handle(&helpers.Back_To_Main_Menu, func(c tele.Context) error {
134 | intro := "🅰🅱©↩📧🎏⛽♓ℹ🗾🎋👢Ⓜ♑⭕🅿♌⚡🌴⛎✌Ⓩ"
135 | return c.Send(intro, helpers.MainMenu_InlineKeys)
136 | })
137 |
138 | b.Handle(&helpers.Keep_Typing_Passcode, func(c tele.Context) error {
139 | intro := "Xin nhập lại mật khẩu!"
140 | user := c.Sender().ID
141 | db := sqlite.DB()
142 | users.SetUserState(db, user, true)
143 | return c.Send(intro)
144 | })
145 |
146 | b.Handle(tele.OnPhoto, func(c tele.Context) error {
147 | fmt.Println(tele.OnPhoto)
148 | user := c.Sender()
149 | fmt.Println(user)
150 | return c.Send("Sorry, bot không xử lý file ảnh bạn gửi lên!")
151 | })
152 |
153 | b.Handle(tele.OnPoll, func(c tele.Context) error {
154 | fmt.Println(tele.OnPoll)
155 | user := c.Sender()
156 | fmt.Println(user)
157 | return c.Send("Sorry, bot không xử lý bảng poll bạn gửi lên!")
158 | })
159 | }
160 |
--------------------------------------------------------------------------------
/internal/handle/admin/test.go:
--------------------------------------------------------------------------------
1 | package admin
2 |
3 | import (
4 | "encoding/binary"
5 | "fmt"
6 | "log/slog"
7 | "math/rand"
8 | "strconv"
9 | "time"
10 |
11 | "github.com/yeungon/tuhuebot/internal/database/bbolt"
12 | "github.com/yeungon/tuhuebot/internal/database/pg"
13 | "github.com/yeungon/tuhuebot/internal/database/sqlite"
14 | "github.com/yeungon/tuhuebot/internal/database/sqlite/students"
15 | "github.com/yeungon/tuhuebot/internal/database/sqlite/users"
16 | "github.com/yeungon/tuhuebot/pkg/cache"
17 | "github.com/yeungon/tuhuebot/pkg/helpers"
18 | tele "gopkg.in/telebot.v3"
19 | )
20 |
21 | func Test(b *tele.Bot) {
22 | random := rand.Intn(100)
23 |
24 | fmt.Println(random)
25 |
26 | callback_random := strconv.Itoa(random)
27 | // Create an inline button with data
28 | inlineBtn := tele.InlineButton{
29 | Unique: "my_callback",
30 | Text: callback_random,
31 | Data: callback_random, // Set the callback data
32 | }
33 |
34 | // Handle the callback for the button
35 | b.Handle(&inlineBtn, func(c tele.Context) error {
36 | random := rand.Intn(100)
37 | callback_random := strconv.Itoa(random)
38 |
39 | // <-- update the previous inline button instead of creating a new one
40 | inlineBtn = tele.InlineButton{
41 | Unique: "my_callback",
42 | Text: callback_random,
43 | Data: callback_random,
44 | }
45 |
46 | // Respond to the user
47 | c.Send("You clicked the button! Random number: " + callback_random)
48 |
49 | // Send a new button with updated data
50 | inlineKeys := [][]tele.InlineButton{
51 | {inlineBtn},
52 | }
53 |
54 | return c.Send("Click the button below for a new random number:", &tele.ReplyMarkup{
55 | InlineKeyboard: inlineKeys,
56 | })
57 | })
58 |
59 | // Handle start command
60 | b.Handle("/test", func(c tele.Context) error {
61 | if helpers.IsAdmin(c) == false {
62 | return nil
63 | }
64 |
65 | // test cache
66 | cache.TestCache()
67 |
68 | // Create a reply with the inline button
69 | inlineKeys := [][]tele.InlineButton{
70 | {inlineBtn},
71 | }
72 | return c.Send("Click the button below:", &tele.ReplyMarkup{
73 | InlineKeyboard: inlineKeys,
74 | })
75 | })
76 |
77 | b.Handle("/state", func(c tele.Context) error {
78 | if helpers.IsAdmin(c) == false {
79 | return nil
80 | }
81 | //fmt.Println(c.Message().Text)
82 | hello := "Hello world"
83 |
84 | answerMsgText := "Đang test state management🍟" + hello
85 | b.Send(c.Sender(), answerMsgText, &tele.SendOptions{
86 | ParseMode: "HTML",
87 | })
88 | return nil
89 | })
90 |
91 | b.Handle("/bun", func(c tele.Context) error {
92 | if helpers.IsAdmin(c) == false {
93 | return nil
94 | }
95 | fmt.Println("Testing bun Student DB ORM")
96 | db := sqlite.DBSTUDENT()
97 | student_search := students.SearchStudent(db, "lê thị loan")
98 | // fmt.Println(student_search)
99 | for _, student := range student_search {
100 | fmt.Println(student.Name)
101 | c.Send(student.Name)
102 | }
103 |
104 | return nil
105 | })
106 |
107 | b.Handle("/user", func(c tele.Context) error {
108 | if helpers.IsAdmin(c) == false {
109 | return nil
110 | }
111 | user := c.Sender().ID
112 | db := sqlite.DB()
113 | current_user := users.GetCurrentUser(db, user)
114 |
115 | users.SetUserState(db, user, false)
116 |
117 | state := users.UserState(db, user)
118 |
119 | info := fmt.Sprintf("%v - %s - trạng thái mở hay đóng %v", *current_user.Username, current_user.FirstName, state)
120 | c.Send(info)
121 | c.Send("Hello, testing user")
122 | return nil
123 | })
124 |
125 | b.Handle("/get", func(c tele.Context) error {
126 | if helpers.IsAdmin(c) == false {
127 | return nil
128 | }
129 | //user := c.Sender()
130 | // helpers.PrintStruct(user)
131 | pgdata := pg.PG()
132 | // events := pg.GetEvent(pgdata)
133 |
134 | newQA := &pg.QA{
135 | UserAsked: "1", // Example user ID
136 | UserAnswered: "some_user", // Example answerer
137 | Question: "hello What is the capital of France?",
138 | Published: false, // Default to false
139 | XataCreatedat: time.Now(),
140 | XataUpdatedat: time.Now(),
141 | }
142 |
143 | pg.CreateQA(pgdata, newQA)
144 |
145 | // db := sqlite.DB()
146 | // usersList := users.GetAllUser(db)
147 | // first := usersList[0]
148 |
149 | // fmt.Println(first)
150 |
151 | // // Print the retrieved users.
152 | // for _, event := range events {
153 | // event := fmt.Sprintf("%d - %s ", event.Month, event.EventData)
154 | // c.Send(event)
155 | // }
156 |
157 | fmt.Println("Testing bun ORM - Get data from sql")
158 | return nil
159 | })
160 |
161 | b.Handle("/log", func(c tele.Context) error {
162 | if helpers.IsAdmin(c) == false {
163 | return nil
164 | }
165 |
166 | // Log some messages with structured data
167 | slog.Debug("This is a debug message", "module", "auth", "status", "success")
168 | slog.Info("User logged in", "user", "john_doe", "module", "auth")
169 | slog.Warn("Disk space is running low", "module", "storage", "free_space", "5GB")
170 | slog.Error("Failed to connect to database", "module", "db", "error", "connection timeout")
171 |
172 | fmt.Println("Testing log")
173 | return nil
174 | })
175 | b.Handle("/bbolt", func(c tele.Context) error {
176 | if helpers.IsAdmin(c) == false {
177 | return nil
178 | }
179 | fmt.Println("testing kv bbolt")
180 |
181 | random := rand.Intn(100)
182 |
183 | byteData := []byte(strconv.Itoa(random))
184 |
185 | bbolt.UserAdd([]byte("12345"), byteData)
186 | return nil
187 | })
188 |
189 | b.Handle("/read", func(c tele.Context) error {
190 | if helpers.IsAdmin(c) == false {
191 | return nil
192 | }
193 | bbolt.PrintAllKeyValues("users")
194 | return nil
195 | })
196 |
197 | b.Handle("/add", func(c tele.Context) error {
198 | if helpers.IsAdmin(c) == false {
199 | return nil
200 | }
201 | current_user := c.Sender()
202 | current_user_kv := fmt.Sprintf("%v", current_user.ID)
203 | bbolt.UserAdd([]byte(current_user_kv), IntToBytes(100))
204 |
205 | return nil
206 | })
207 |
208 | b.Handle("/clean", func(c tele.Context) error {
209 | if helpers.IsAdmin(c) == false {
210 | return nil
211 | }
212 | bbolt.CleanAllKeyValues("users")
213 | fmt.Println("Clean up the k-v")
214 | return nil
215 | })
216 |
217 | b.Handle("/admin", func(c tele.Context) error {
218 | if helpers.IsAdmin(c) == false {
219 | return nil
220 | }
221 | return nil
222 | })
223 | }
224 |
225 | func IntToBytes(n int) []byte {
226 | b := make([]byte, 8) // Use 8 bytes for int64 or uint64
227 | binary.BigEndian.PutUint64(b, uint64(n))
228 | return b
229 | }
230 |
--------------------------------------------------------------------------------
/internal/handle/info.go:
--------------------------------------------------------------------------------
1 | package handle
2 |
3 | import (
4 | "github.com/yeungon/tuhuebot/pkg/helpers"
5 | tele "gopkg.in/telebot.v3"
6 | )
7 |
8 | var (
9 | deparment_room = "Một số phòng chức năng:\n\nPhòng y tế: EI5, EI6\nTổ dữ liệu: Dãy L, tầng 1\n Phòng Đào tạo và CTSV: dãy F, tầng 1\n"
10 | location_coso_2 = `ơ sở 2: Đường Võ Văn Kiệt - Phường An Tây - Thành phố Huế,`
11 | thesis_requirement = "https://tieuhoc.org/static/images/2024_quydinh_lam_khoaluan.png"
12 | master_plan = "https://tieuhoc.org/master/2024_2025.jpg"
13 | wifi_password = "Trường: dhsph19572010\nKhoa: TU16051996"
14 | sotay_sinhvien_url = "https://tieuhoc.org/vanban/quydinh/SOTAYSINHVIEN_2021_tieuhoc.pdf"
15 | dieukien_lam_btl = `
16 | Điều kiện để sinh viên được làm BTL:
17 |
18 | 1. SV được đăng ký thực hiện BTL sau khi đã tích luỹ tối thiểu 15 tín chỉ (TC), có điểm TBC tích lũy đạt từ 2,4 trở lên. SV thực hiện BTL phải tham gia học tập
19 | chuyên cần và thực hiện các yêu cầu học tập của GV.
20 | 2. Được GV phụ trách học phần đề nghị và Tổ trưởng chuyên môn quản lí học phần duyệt.
21 | 3. Trong mỗi học kì, một SV chỉ được phép ực hiện tối đa 02 BTL.
22 |
23 | Điều kiện để giảng viên hướng dẫn BTL
24 | 1. Để được tham gia hướng dẫn BTL, GV đã giảng dạy đại học từ 1 năm trở lên. GV dạy học phần nào, thì hướng dẫn và chấm BTL của học phần đó.
25 | 2. Trong một năm học hướng dẫn không quá 12 BTL; không tham gia hướng dẫn BTL của người thân (vợ, chồng, con; anh, chi, em ruột).`
26 |
27 | dieukien_lam_khoaluan = `
28 | Điều kiện để sinh viên được đăng ký làm Khóa luận:
29 | ⠀⋆˚✿˖°⋆˚✿˖°⋆˚✿˖°
30 | a. Để được nhận làm KL, sinh viên cần hội đủ các điều kiện sau đây:
31 |
32 | a-1. Tổng số tín chỉ tích lũy đạt tối thiểu 75% khối lượng kiến thức tích lũy/khối lượng chương trình đào tạo của ngành học (xếp hạng SV năm thứ tư) và điểm trung bình chung tích lũy phải đạt từ 2,80 trở lên. (trong chương trình đào tạo có tính số lượng TC của các HP ngoại ngữ không chuyên)
33 |
34 | a-2. Đã thực hiện ít nhất 1 TL có kết quả đạt từ 8,0 điểm trở lên.
35 |
36 | a-3 . Đã tích luỹ một HP chuyên môn có từ 2 TC trở lên liên quan đến chuyên ngành mà SV đăng ký đề tài KL và đạt kết quả từ 8,5 điểm trở lên.
37 |
38 | a-4. Số HP còn nợ hoặc học lại trong các học kỳ trước đó không quá 02 và không vượt quá tổng số 5 TC.
39 |
40 | b. Sinh viên làm đề tài NCKH độc lập và đã nghiệm thu được ưu tiên chọn giao thực hiện KL.
41 |
42 | c. Mỗi Khoa xét duyệt số lượng SV được làm KL theo ngành học và không vượt quá 50% tổng số SV của khóa học thuộc ngành xét.
43 |
44 | d. Những trường hợp đặc biệt sẽ trình Hiệu trưởng quyết định.`
45 | dieukien_lamtieuluan = `
46 | TIỂU LUẬN
47 |
48 | 1. Nội dung TL nhằm giải quyết một vấn đề liên quan đến HP đang học (có từ 2 TC ở nội dung lý thuyết trở lên). TL phải được hoàn thành cùng với thời gian kết thúc học phần và nộp cho GV hướng dẫn để tổ chức đánh giá ở tổ chuyên môn vào cuối học kì đó.
49 |
50 | TL dài không quá 25 trang đánh máy vi tính đối với các ngành khoa học tự nhiên và không quá 30 trang đối với các ngành khoa học xã hội (không kể biểu bảng, hình vẽ...).
51 |
52 | 2. Hình thức TL không áp dụng đối với các HP thực hành hoặc có thời gian học dưới 6 tuần.
53 |
54 | 3. Danh sách SV làm TL của GV (theo mẫu Phụ lục 02) được Khoa tổng hợp (theo mẫu Phụ lục 03) phải báo cáo với Trường qua Phòng Khảo thí và Đảm bảo chất lượng giáo dục (KT&ĐBCLGD) trước khi Khoa và GV triển khai cho SV thực hiện.
55 |
56 | Thời gian nộp danh sách tổng hợp: trước thời gian tổ chức thi kết thúc học phần ít nhất là 4 tuần.
57 |
58 | SV và GV chịu trách nhiệm về việc đảm bảo các điều kiện, yêu cầu trong quy định này.
59 |
60 | 4. Nếu có trở ngại trong việc làm TL, sinh viên phải báo cáo với GV để kịp thời xử lí. Việc chậm trễ có thể ảnh hưởng đến quyền lợi của SV. Khi có nguyện vọng, SV có thể làm đơn xin thôi làm TL (theo mẫu Phụ lục 15) trong khoảng thời gian trước kỳ thi kết thúc HP ít nhất 02 tuần, Khoa và GV xét duyệt, nộp danh sách SV thôi làm TL cho Phòng KT&ĐBCLGD để bổ sung SV vào danh sách thi.
61 |
62 | Điều kiện để sinh viên được làm tiểu luận (TL):
63 |
64 | 1. SV được đăng ký thực hiện TL sau khi đã tích luỹ tối thiểu 30 TC, có điểm TBC tích lũy đạt từ 2,5 trở lên và tối đa chỉ có 1 HP (có từ 2 TC trở lên) chưa đạt tích lũy.
65 |
66 | 2. SV thực hiện TL phải tham gia học tập chuyên cần và thực hiện các yêu cầu học tập của GV.
67 |
68 | 3. Được GV phụ trách học phần đề nghị, Tổ trưởng chuyên môn duyệt và báo cáo cho Trưởng khoa.
69 |
70 | 4. Trong mỗi học kỳ, một SV chỉ được phép thực hiện 01 TL.
71 |
72 | Quy định chi tiết tại: https://tieuhoc.org/vanban/quydinh/quy_dinh_lam_khoaluan_tieuluan_20191101081351_2505_qd_dhsp.pdf
73 | `
74 | dieukien_xet_totnghiep = `
75 | 1) Hoàn thành đủ số tín chỉ, đúng số môn theo chương trình của khóa mình theo học.
76 |
77 | 2) Có chứng chỉ Giáo dục thể chất.
78 |
79 | 3) Có chứng chỉ Giáo dục quốc phòng.
80 |
81 | 4) Có chứng chỉ ngoại ngữ B1 hoặc tương đương. Sinh viên Lào, Campuchia được miễn chứng chỉ ngoại ngữ.
82 |
83 | 5) Làm đơn xét tốt nghiệp.
84 | `
85 | )
86 |
87 | func Info(b *tele.Bot) {
88 | kehoachnamhoc := tele.InlineButton{
89 | Unique: "btn_callback_kehoachnamhoc",
90 | Text: "Kế hoạch năm học",
91 | Data: "button1_clicked",
92 | }
93 |
94 | sodotruong := tele.InlineButton{
95 | Unique: "btn_callback_sodotruong",
96 | Text: "Sơ đồ trường",
97 | Data: "button1_clicked",
98 | }
99 |
100 | // Define the second inline button
101 | sotay_sinhvien := tele.InlineButton{
102 | Unique: "btn_callback2_sotaysinhvien",
103 | Text: "Sổ tay sinh viên",
104 | Data: "button2_clicked",
105 | }
106 |
107 | matkhau_wifi := tele.InlineButton{
108 | Unique: "btn_callback_matkhau_wifi",
109 | Text: "Mật khẩu Wifi",
110 | Data: "button1_clicked",
111 | }
112 |
113 | quydinh_lam_btl := tele.InlineButton{
114 | Unique: "btn_callback2_quydinh_lam_btl",
115 | Text: "Điều kiện làm bài tập lớn",
116 | Data: "button2_clicked",
117 | }
118 | quydinh_lamtieuluan := tele.InlineButton{
119 | Unique: "btn_callback2_quydinh_lam_tieuluan",
120 | Text: "Điều kiện làm tiểu luận",
121 | Data: "button2_clicked",
122 | }
123 |
124 | // Define the second inline button
125 | quydinh_lamkhoaluan := tele.InlineButton{
126 | Unique: "btn_callback2_quydinh_lamkhoaluan",
127 | Text: "Điều kiện làm khóa lụân",
128 | Data: "button2_clicked",
129 | }
130 |
131 | // Define the second inline button
132 | quydinh_xet_totnghiep := tele.InlineButton{
133 | Unique: "btn_callback2_quydinh_xettotnghiep",
134 | Text: "Điều kiện xét tốt nghiệp",
135 | Data: "button2_clicked",
136 | }
137 |
138 | // Create the reply markup and add both buttons in a single row
139 | inlineKeys := &tele.ReplyMarkup{}
140 | inlineKeys.InlineKeyboard = [][]tele.InlineButton{
141 | {sodotruong, sotay_sinhvien}, // Row 1: Button 1
142 | {kehoachnamhoc, matkhau_wifi},
143 | {quydinh_lam_btl},
144 | {quydinh_lamtieuluan},
145 | {quydinh_lamkhoaluan},
146 | {quydinh_xet_totnghiep},
147 | }
148 |
149 | b.Handle("/info", func(c tele.Context) error {
150 | // Create the reply markup and add the button
151 | return c.Send("Một số thông tin hữu tích:\n\n", inlineKeys)
152 |
153 | })
154 |
155 | b.Handle("info", func(c tele.Context) error {
156 | // Create the reply markup and add the button
157 | return c.Send("Một số thông tin hữu tích:\n\n", inlineKeys)
158 |
159 | })
160 |
161 | b.Handle(&helpers.Info, func(c tele.Context) error {
162 | return c.Send("Một số thông tin hữu tích:\n\n", inlineKeys)
163 | })
164 |
165 | b.Handle(&sodotruong, func(c tele.Context) error {
166 | link := "https://tieuhoc.org/map/sodo.jpg"
167 | photo := &tele.Photo{File: tele.FromURL(link)}
168 | c.Send(photo)
169 | c.Send(deparment_room)
170 | return nil
171 | })
172 |
173 | b.Handle(&sotay_sinhvien, func(c tele.Context) error {
174 | return c.Send(sotay_sinhvien_url)
175 | })
176 |
177 | b.Handle(&matkhau_wifi, func(c tele.Context) error {
178 | return c.Send(wifi_password)
179 | })
180 |
181 | b.Handle(&kehoachnamhoc, func(c tele.Context) error {
182 | photo := &tele.Photo{File: tele.FromURL(master_plan)}
183 | return c.Send(photo)
184 | })
185 |
186 | b.Handle(&quydinh_lam_btl, func(c tele.Context) error {
187 | return c.Send(dieukien_lam_btl)
188 |
189 | })
190 |
191 | b.Handle(&quydinh_lamtieuluan, func(c tele.Context) error {
192 | return c.Send(dieukien_lamtieuluan)
193 |
194 | })
195 |
196 | b.Handle(&quydinh_lamkhoaluan, func(c tele.Context) error {
197 | return c.Send(dieukien_lam_khoaluan)
198 | })
199 |
200 | b.Handle(&quydinh_xet_totnghiep, func(c tele.Context) error {
201 | return c.Send(dieukien_xet_totnghiep)
202 | })
203 |
204 | }
205 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
20 | cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
21 | cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
22 | cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
23 | cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
24 | cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
25 | cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
26 | cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
27 | cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
28 | cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
29 | cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
30 | cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
31 | cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
32 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
33 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
34 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
35 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
36 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
37 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
38 | cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
39 | cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
40 | cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
41 | cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
42 | cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
43 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
44 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
45 | cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
46 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
47 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
48 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
49 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
50 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
51 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
52 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
53 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
54 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
55 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
56 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
57 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
58 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
59 | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
60 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
61 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
62 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
63 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
64 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
65 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
66 | github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
67 | github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
68 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
69 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
70 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
71 | github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
72 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
73 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
74 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
75 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
76 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
77 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
78 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
79 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
80 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
81 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
82 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
83 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
84 | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
85 | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
86 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
87 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
88 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
89 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
90 | github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
91 | github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
92 | github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
93 | github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
94 | github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
95 | github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
96 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
97 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
98 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
99 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
100 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
101 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
102 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
103 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
104 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
105 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
106 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
107 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
108 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
109 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
110 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
111 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
112 | github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
113 | github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
114 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
115 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
116 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
117 | github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
118 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
119 | github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
120 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
121 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
122 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
123 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
124 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
125 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
126 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
127 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
128 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
129 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
130 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
131 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
132 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
133 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
134 | github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
135 | github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=
136 | github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=
137 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
138 | github.com/goccy/go-yaml v1.9.5/go.mod h1:U/jl18uSupI5rdI2jmuCswEA2htH9eXfferR3KfscvA=
139 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
140 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
141 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
142 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
143 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
144 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
145 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
146 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
147 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
148 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
149 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
150 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
151 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
152 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
153 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
154 | github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
155 | github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
156 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
157 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
158 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
159 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
160 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
161 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
162 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
163 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
164 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
165 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
166 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
167 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
168 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
169 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
170 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
171 | github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
172 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
173 | github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
174 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
175 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
176 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
177 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
178 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
179 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
180 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
181 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
182 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
183 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
184 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
185 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
186 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
187 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
188 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
189 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
190 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
191 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
192 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
193 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
194 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
195 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
196 | github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
197 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
198 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
199 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
200 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
201 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
202 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
203 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
204 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
205 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
206 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
207 | github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
208 | github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
209 | github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
210 | github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
211 | github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
212 | github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
213 | github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
214 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
215 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
216 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
217 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
218 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
219 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
220 | github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
221 | github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
222 | github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
223 | github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
224 | github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
225 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
226 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
227 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
228 | github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0=
229 | github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
230 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
231 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
232 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
233 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
234 | github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
235 | github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
236 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
237 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
238 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
239 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
240 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
241 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
242 | github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
243 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
244 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
245 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
246 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
247 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
248 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
249 | github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
250 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
251 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
252 | github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
253 | github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
254 | github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
255 | github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
256 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
257 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
258 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
259 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
260 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
261 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
262 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
263 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
264 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
265 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
266 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
267 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
268 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
269 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
270 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
271 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
272 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
273 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
274 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
275 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
276 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
277 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
278 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
279 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
280 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
281 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
282 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
283 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
284 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
285 | github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
286 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
287 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
288 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
289 | github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
290 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
291 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
292 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
293 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
294 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
295 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
296 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
297 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
298 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
299 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
300 | github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
301 | github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
302 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
303 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
304 | github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
305 | github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
306 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
307 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
308 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
309 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
310 | github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
311 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
312 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
313 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
314 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
315 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
316 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
317 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
318 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
319 | github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
320 | github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
321 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
322 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
323 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
324 | github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
325 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
326 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
327 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
328 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
329 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
330 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
331 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
332 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
333 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
334 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
335 | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
336 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
337 | github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
338 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
339 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
340 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
341 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
342 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
343 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
344 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
345 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
346 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
347 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
348 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
349 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
350 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
351 | github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
352 | github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
353 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
354 | github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
355 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
356 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
357 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
358 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
359 | github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
360 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
361 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
362 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
363 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
364 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
365 | github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
366 | github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
367 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
368 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
369 | github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
370 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
371 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
372 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
373 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
374 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
375 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
376 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
377 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
378 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
379 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
380 | github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
381 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
382 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
383 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
384 | github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
385 | github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
386 | github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
387 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
388 | github.com/uptrace/bun v1.2.11 h1:l9dTymsdZZAoSZ1+Qo3utms0RffgkDbIv+1UGk8N1wQ=
389 | github.com/uptrace/bun v1.2.11/go.mod h1:ww5G8h59UrOnCHmZ8O1I/4Djc7M/Z3E+EWFS2KLB6dQ=
390 | github.com/uptrace/bun/dialect/pgdialect v1.2.11 h1:n0VKWm1fL1dwJK5TRxYYLaRKRe14BOg2+AQgpvqzG/M=
391 | github.com/uptrace/bun/dialect/pgdialect v1.2.11/go.mod h1:NvV1S/zwtwBnW8yhJ3XEKAQEw76SkeH7yUhfrx3W1Eo=
392 | github.com/uptrace/bun/dialect/sqlitedialect v1.2.11 h1:t4OIcbkWnRPshRj7ZnbHVwUENa3OHhCUruyFcl3P+TY=
393 | github.com/uptrace/bun/dialect/sqlitedialect v1.2.11/go.mod h1:XHFFTvdlNtNFWPhpRAConN6DnVgt9EHr5G5IIarHYyg=
394 | github.com/uptrace/bun/driver/pgdriver v1.2.11 h1:nqU0ORMh8cESUqGZNGPAMdFF6YrU2Rr2liRs6bZNRDc=
395 | github.com/uptrace/bun/driver/pgdriver v1.2.11/go.mod h1:suBR8qaazdzlPAjVIlmC93yGCUzP6Au71WVgySfv6Qw=
396 | github.com/uptrace/bun/driver/sqliteshim v1.2.11 h1:7+CtLNTcGkWMK0/9Jj3aQFqdvRWqZc+7VTt2yFyJxA8=
397 | github.com/uptrace/bun/driver/sqliteshim v1.2.11/go.mod h1:Fgjwpep/hbjk/wgkatnzzGoKbkaEPHCufxDKWR+kawI=
398 | github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
399 | github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
400 | github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
401 | github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
402 | github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
403 | github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
404 | github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
405 | github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=
406 | github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg=
407 | github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk=
408 | github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q=
409 | github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg=
410 | github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY=
411 | github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM=
412 | github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=
413 | github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=
414 | github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=
415 | github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=
416 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
417 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
418 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
419 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
420 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
421 | go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
422 | go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
423 | go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
424 | go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
425 | go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
426 | go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
427 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
428 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
429 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
430 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
431 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
432 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
433 | go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
434 | go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
435 | go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
436 | go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
437 | go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
438 | go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
439 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
440 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
441 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
442 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
443 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
444 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
445 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
446 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
447 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
448 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
449 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
450 | golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
451 | golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
452 | golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
453 | golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
454 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
455 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
456 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
457 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
458 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
459 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
460 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
461 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
462 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
463 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
464 | golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c=
465 | golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
466 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
467 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
468 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
469 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
470 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
471 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
472 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
473 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
474 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
475 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
476 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
477 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
478 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
479 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
480 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
481 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
482 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
483 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
484 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
485 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
486 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
487 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
488 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
489 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
490 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
491 | golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
492 | golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
493 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
494 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
495 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
496 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
497 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
498 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
499 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
500 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
501 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
502 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
503 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
504 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
505 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
506 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
507 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
508 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
509 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
510 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
511 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
512 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
513 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
514 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
515 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
516 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
517 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
518 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
519 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
520 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
521 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
522 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
523 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
524 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
525 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
526 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
527 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
528 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
529 | golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
530 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
531 | golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
532 | golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
533 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
534 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
535 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
536 | golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
537 | golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
538 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
539 | golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
540 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
541 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
542 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
543 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
544 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
545 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
546 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
547 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
548 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
549 | golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
550 | golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
551 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
552 | golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
553 | golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
554 | golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
555 | golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
556 | golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
557 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
558 | golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
559 | golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
560 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
561 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
562 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
563 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
564 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
565 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
566 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
567 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
568 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
569 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
570 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
571 | golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
572 | golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
573 | golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
574 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
575 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
576 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
577 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
578 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
579 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
580 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
581 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
582 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
583 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
584 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
585 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
586 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
587 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
588 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
589 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
590 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
591 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
592 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
593 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
594 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
595 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
596 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
597 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
598 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
599 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
600 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
601 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
602 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
603 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
604 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
605 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
606 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
607 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
608 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
609 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
610 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
611 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
612 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
613 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
614 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
615 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
616 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
617 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
618 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
619 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
620 | golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
621 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
622 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
623 | golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
624 | golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
625 | golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
626 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
627 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
628 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
629 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
630 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
631 | golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
632 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
633 | golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
634 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
635 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
636 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
637 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
638 | golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
639 | golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
640 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
641 | golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
642 | golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
643 | golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
644 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
645 | golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
646 | golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
647 | golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
648 | golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
649 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
650 | golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
651 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
652 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
653 | golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
654 | golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
655 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
656 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
657 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
658 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
659 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
660 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
661 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
662 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
663 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
664 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
665 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
666 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
667 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
668 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
669 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
670 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
671 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
672 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
673 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
674 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
675 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
676 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
677 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
678 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
679 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
680 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
681 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
682 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
683 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
684 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
685 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
686 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
687 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
688 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
689 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
690 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
691 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
692 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
693 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
694 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
695 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
696 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
697 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
698 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
699 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
700 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
701 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
702 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
703 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
704 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
705 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
706 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
707 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
708 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
709 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
710 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
711 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
712 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
713 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
714 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
715 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
716 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
717 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
718 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
719 | golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
720 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
721 | golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
722 | golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
723 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
724 | golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
725 | golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
726 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
727 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
728 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
729 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
730 | golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
731 | golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
732 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
733 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
734 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
735 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
736 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
737 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
738 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
739 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
740 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
741 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
742 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
743 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
744 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
745 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
746 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
747 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
748 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
749 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
750 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
751 | google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
752 | google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
753 | google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
754 | google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
755 | google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
756 | google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
757 | google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
758 | google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
759 | google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
760 | google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
761 | google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
762 | google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
763 | google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
764 | google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
765 | google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
766 | google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
767 | google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
768 | google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
769 | google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
770 | google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko=
771 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
772 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
773 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
774 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
775 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
776 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
777 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
778 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
779 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
780 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
781 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
782 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
783 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
784 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
785 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
786 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
787 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
788 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
789 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
790 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
791 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
792 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
793 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
794 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
795 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
796 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
797 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
798 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
799 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
800 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
801 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
802 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
803 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
804 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
805 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
806 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
807 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
808 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
809 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
810 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
811 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
812 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
813 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
814 | google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
815 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
816 | google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
817 | google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
818 | google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
819 | google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
820 | google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
821 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
822 | google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
823 | google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
824 | google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
825 | google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
826 | google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
827 | google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
828 | google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
829 | google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
830 | google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
831 | google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
832 | google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
833 | google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
834 | google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
835 | google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
836 | google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
837 | google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
838 | google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
839 | google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
840 | google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
841 | google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
842 | google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
843 | google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
844 | google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
845 | google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
846 | google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
847 | google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
848 | google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
849 | google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
850 | google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
851 | google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
852 | google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
853 | google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
854 | google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
855 | google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
856 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
857 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
858 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
859 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
860 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
861 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
862 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
863 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
864 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
865 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
866 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
867 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
868 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
869 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
870 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
871 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
872 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
873 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
874 | google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
875 | google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
876 | google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
877 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
878 | google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
879 | google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
880 | google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
881 | google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
882 | google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
883 | google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
884 | google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
885 | google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
886 | google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
887 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
888 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
889 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
890 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
891 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
892 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
893 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
894 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
895 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
896 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
897 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
898 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
899 | google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
900 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
901 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
902 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
903 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
904 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
905 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
906 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
907 | gopkg.in/telebot.v3 v3.3.8 h1:uVDGjak9l824FN9YARWUHMsiNZnlohAVwUycw21k6t8=
908 | gopkg.in/telebot.v3 v3.3.8/go.mod h1:1mlbqcLTVSfK9dx7fdp+Nb5HZsy4LLPtpZTKmwhwtzM=
909 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
910 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
911 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
912 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
913 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
914 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
915 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
916 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
917 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
918 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
919 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
920 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
921 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
922 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
923 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
924 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
925 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
926 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
927 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
928 | mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
929 | mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
930 | modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
931 | modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
932 | modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
933 | modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
934 | modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
935 | modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
936 | modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
937 | modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
938 | modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
939 | modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
940 | modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
941 | modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
942 | modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
943 | modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
944 | modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
945 | modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
946 | modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
947 | modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
948 | modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8=
949 | modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
950 | modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
951 | modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
952 | modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
953 | modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
954 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
955 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
956 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
957 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
958 |
--------------------------------------------------------------------------------