diff --git a/modules/database/database_struct.go b/modules/database/database_struct.go index a5550c6..84f21b3 100644 --- a/modules/database/database_struct.go +++ b/modules/database/database_struct.go @@ -9,8 +9,13 @@ type User struct { type Position string const ( - NotStarted Position = "not_started" - Ready Position = "ready" + NotStarted Position = "not_started" // Только начал диалог с ботом + Ready Position = "ready" // Готов к дальнейшим действиям + Add Position = "add" // Подключает личное расписание + SelAddGroup Position = "select_add_group" // Выбирает группу в личное расписание + SelAddStaff Position = "select_add_staff" // Выбирает преподавателя в личное расписание + SelSeeGroup Position = "select_see_group" // Выбирает группу для автономной карточки + SelSeeStaff Position = "select_see_staff" // Выбирает преподавателя для автономной карточки ) type TgUser struct { @@ -37,6 +42,7 @@ type Teacher struct { TeacherId int64 `xorm:"pk"` FirstName string // Фамилия LastName string // Имя, отчество и прочие окончания + ShortName string // Инициалы SpecName string // Место работы } diff --git a/modules/ssau_parser/database.go b/modules/ssau_parser/database.go index 08bec26..b411ec3 100644 --- a/modules/ssau_parser/database.go +++ b/modules/ssau_parser/database.go @@ -1,7 +1,6 @@ package ssau_parser import ( - "log" "strings" "git.l9labs.ru/anufriev.g.a/l9_stud_bot/modules/database" @@ -81,17 +80,26 @@ func checkGroupOrTeacher(db *xorm.Engine, sh WeekShedule) error { } if !exists { - name := strings.Split(sh.FullName, " ") - log.Println(name) - teacher := database.Teacher{ - TeacherId: sh.SheduleId, - FirstName: name[0], - LastName: strings.Join(name[1:], " "), - SpecName: sh.SpecName, - } + teacher := ParseTeacherName(sh.FullName) + teacher.TeacherId = sh.SheduleId + teacher.SpecName = sh.SpecName db.InsertOne(teacher) } } return nil } + +func ParseTeacherName(fullName string) database.Teacher { + name := strings.Split(fullName, " ") + var short_name []string + for _, a := range name[1:] { + short_name = append(short_name, a[:2]) + } + teacher := database.Teacher{ + FirstName: name[0], + LastName: strings.Join(name[1:], " "), + ShortName: strings.Join(short_name, ". ") + ".", + } + return teacher +} diff --git a/modules/tg/bot.go b/modules/tg/bot.go index c5b771b..0c9ed39 100644 --- a/modules/tg/bot.go +++ b/modules/tg/bot.go @@ -120,11 +120,14 @@ func (bot *Bot) HandleUpdate(update tgbotapi.Update) error { return err } bot.Debug.Printf("Message [%d] <%s> %s", user.L9Id, user.Name, msg.Text) - if user.PosTag == database.NotStarted { + switch user.PosTag { + case database.NotStarted: err = bot.Start(user) - if err != nil { - return err - } + case database.Ready: + err = bot.Find(user, msg.Text) + } + if err != nil { + return err } } return nil diff --git a/modules/tg/handlers.go b/modules/tg/handlers.go index d856059..f0b2541 100644 --- a/modules/tg/handlers.go +++ b/modules/tg/handlers.go @@ -1,12 +1,18 @@ package tg import ( + "fmt" + "strings" + "git.l9labs.ru/anufriev.g.a/l9_stud_bot/modules/database" + "git.l9labs.ru/anufriev.g.a/l9_stud_bot/modules/ssau_parser" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" + "xorm.io/builder" ) +// Приветственное сообщение func (bot *Bot) Start(user *database.TgUser) error { - user.PosTag = "ready" + user.PosTag = database.Ready _, err := bot.DB.Update(user) if err != nil { return err @@ -20,106 +26,138 @@ func (bot *Bot) Start(user *database.TgUser) error { return err } -/* - func (bot *Bot) Find(query string) error { - var groups []database.Group - bot.DB.Where(builder.Like{"GroupName", query}).Find(&groups) +// Поиск расписания по запросу +func (bot *Bot) Find(user *database.TgUser, query string) error { + // Поиск в БД + var groups []database.Group + bot.DB.Where(builder.Like{"GroupName", query}).Find(&groups) - var teachers []database.Teacher - bot.DB.Where(builder.Like{"LastName", query}).Find(&teachers) + var teachers []database.Teacher + bot.DB.Where(builder.Like{"FirstName", query}).Find(&teachers) - list, _ := ssau_parser.SearchInRasp(query) + // Поиск на сайте + list, siteErr := ssau_parser.SearchInRasp(query) - allGroups := groups - allTeachers := teachers + allGroups := groups + allTeachers := teachers - for _, elem := range list { - if strings.Contains(elem.Url, "group") { - exists := false - for _, group := range groups { - if elem.Id == group.GroupId { - exists = true - break - } - } - if !exists { - allGroups = append(allGroups, database.Group{GroupId: elem.Id, GroupName: elem.Text}) + // Добавляем результаты поиска на сайте к результатам из БД + for _, elem := range list { + if strings.Contains(elem.Url, "group") { + exists := false + for _, group := range groups { + if elem.Id == group.GroupId { + exists = true + break } } - if strings.Contains(elem.Url, "staff") { - exists := false - for _, teacher := range teachers { - if elem.Id == teacher.TeacherId { - exists = true - break - } - } - if !exists { - name := strings.Split(elem.Text, " ") - allTeachers = append(allTeachers, database.Teacher{ - TeacherId: elem.Id, - LastName: name[0], - FirstName: name[1], - }) - } + if !exists { + allGroups = append(allGroups, database.Group{GroupId: elem.Id, GroupName: elem.Text}) } } + if strings.Contains(elem.Url, "staff") { + exists := false + for _, teacher := range teachers { + if elem.Id == teacher.TeacherId { + exists = true + break + } + } + if !exists { + teacher := ssau_parser.ParseTeacherName(elem.Text) + teacher.TeacherId = elem.Id + allTeachers = append(allTeachers, teacher) + } + } + } - if len(allGroups) == 1 || len(allTeachers) == 1 { - if bot.TG_user.PosTag == "add" { - msg := tgbotapi.NewMessage(bot.TG_user.TgId, "Подключено!") - keyboard := tgbotapi.NewReplyKeyboard([]tgbotapi.KeyboardButton{tgbotapi.NewKeyboardButton("Главное меню")}) - msg.ReplyMarkup = keyboard - bot.TG.Send(msg) + // Если получен единственный результат, сразу выдать (подключить) расписание + if len(allGroups) == 1 || len(allTeachers) == 1 { + if user.PosTag == database.Add { + msg := tgbotapi.NewMessage(user.TgId, "Подключено!") + keyboard := tgbotapi.NewReplyKeyboard( + []tgbotapi.KeyboardButton{ + tgbotapi.NewKeyboardButton("Моё расписание"), + }) + msg.ReplyMarkup = keyboard + bot.TG.Send(msg) + } else { + var sheduleId int64 + var isGroup bool + if len(allGroups) == 1 { + sheduleId = allGroups[0].GroupId + isGroup = true } else { - var sheduleId int64 - var isGroup bool - if len(allGroups) == 1 { - sheduleId = allGroups[0].GroupId - isGroup = true - } else { - sheduleId = allTeachers[0].TeacherId - isGroup = false - } - shedule := database.ShedulesInUser{ - IsTeacher: !isGroup, - SheduleId: sheduleId, - } + sheduleId = allTeachers[0].TeacherId + isGroup = false + } + shedule := database.ShedulesInUser{ + IsTeacher: !isGroup, + SheduleId: sheduleId, + } + msg := tgbotapi.NewMessage( + user.TgId, + fmt.Sprintf( + "Тут должно было быть расписание, но его пока не завезли\n%d", + shedule.SheduleId, + ), + ) + bot.TG.Send(msg) + /* err := bot.GetSummary([]database.ShedulesInUser{shedule}, false) if err != nil { return err - } - } - bot.TG_user.PosTag = "ready" - err := bot.UpdateUserDB() - return err - } else if len(allGroups) != 0 { - if bot.TG_user.PosTag == "add" { - bot.TG_user.PosTag = "confirm_add_group" - } else { - bot.TG_user.PosTag = "confirm_see_group" - } - msg := tgbotapi.NewMessage(bot.TG_user.TgId, "Вот что я нашёл.\nВыбери свою группу") - msg.ReplyMarkup = GenerateKeyboard(GenerateGroupsArray(allGroups), query) - bot.TG.Send(msg) - } else if len(allTeachers) != 0 { - if bot.TG_user.PosTag == "add" { - bot.TG_user.PosTag = "confirm_add_teacher" - } else { - bot.TG_user.PosTag = "confirm_see_teacher" - } - msg := tgbotapi.NewMessage(bot.TG_user.TgId, "Вот что я нашёл.\nВыбери нужного преподавателя") - msg.ReplyMarkup = GenerateKeyboard(GenerateTeachersArray(allTeachers), query) - bot.TG.Send(msg) + }*/ + } + user.PosTag = database.Ready + err := bot.UpdateUserDB(user) + return err + + // Если получено несколько групп + } else if len(allGroups) != 0 { + if user.PosTag == database.Add { + user.PosTag = database.SelAddGroup } else { - msg := tgbotapi.NewMessage(bot.TG_user.TgId, "К сожалению, я ничего не нашёл ):\nПроверь свой запрос") - bot.TG.Send(msg) + user.PosTag = database.SelSeeGroup + } + msg := tgbotapi.NewMessage(user.TgId, "Вот что я нашёл\nВыбери нужную группу") + msg.ReplyMarkup = GenerateKeyboard(GenerateGroupsArray(allGroups)) + bot.TG.Send(msg) + // Если получено несколько преподавателей + } else if len(allTeachers) != 0 { + if user.PosTag == database.Add { + user.PosTag = database.SelAddStaff + } else { + user.PosTag = database.SelSeeStaff + } + msg := tgbotapi.NewMessage(user.TgId, "Вот что я нашёл\nВыбери нужного преподавателя") + msg.ReplyMarkup = GenerateKeyboard(GenerateTeachersArray(allTeachers)) + bot.TG.Send(msg) + // Если ничего не получено + } else { + var msg tgbotapi.MessageConfig + if siteErr != nil { + msg = tgbotapi.NewMessage( + user.TgId, + "К сожалению, у меня ничего не нашлось, а на сайте ssau.ru/rasp произошла какая-то ошибка :(\n"+ + "Повтори попытку позже", + ) + bot.Debug.Printf("sasau error: %s", siteErr) + } else { + msg = tgbotapi.NewMessage( + user.TgId, + "К сожалению, я ничего не нашёл ):\nПроверь свой запрос", + ) } - _, err := bot.DB.Update(bot.TG_user) - return err + bot.TG.Send(msg) } + _, err := bot.DB.Update(user) + return err +} + +/* func (bot *Bot) SeeShedule(query *tgbotapi.CallbackQuery) error { isGroup := bot.TG_user.PosTag == "confirm_see_group" groupId, err := strconv.ParseInt(query.Data, 0, 64) diff --git a/modules/tg/tg_test.go b/modules/tg/tg_test.go index df4e7ec..0666736 100644 --- a/modules/tg/tg_test.go +++ b/modules/tg/tg_test.go @@ -47,6 +47,14 @@ func initTestBot() *Bot { if err != nil { log.Fatal(err) } + _, err = bot.DB.Where("teacherid >= 0").Delete(&database.Teacher{}) + if err != nil { + log.Fatal(err) + } + _, err = bot.DB.Where("groupid >= 0").Delete(&database.Group{}) + if err != nil { + log.Fatal(err) + } return bot } func TestInitBot(t *testing.T) { @@ -74,6 +82,13 @@ func TestInitUser(t *testing.T) { } } +var dialog = []string{ + "/start", + "2305", + "Балякин", + //"230", +} + func TestHandleUpdate(t *testing.T) { bot := initTestBot() @@ -82,11 +97,13 @@ func TestHandleUpdate(t *testing.T) { update := tgbotapi.Update{ Message: &tgbotapi.Message{ From: &user, - Text: "/start", }, } - err := bot.HandleUpdate(update) - if err != nil { - log.Fatal(err) + for _, query := range dialog { + update.Message.Text = query + err := bot.HandleUpdate(update) + if err != nil { + log.Fatal(err) + } } } diff --git a/modules/tg/utils.go b/modules/tg/utils.go new file mode 100644 index 0000000..17b4960 --- /dev/null +++ b/modules/tg/utils.go @@ -0,0 +1,176 @@ +package tg + +import ( + "fmt" + "strconv" + "strings" + + "git.l9labs.ru/anufriev.g.a/l9_stud_bot/modules/database" + tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" +) + +// Создание ряда кнопок из списка групп +func GenerateGroupsArray(groups []database.Group) []tgbotapi.InlineKeyboardButton { + var grKeys []tgbotapi.InlineKeyboardButton + for _, gr := range groups { + grKeys = append(grKeys, tgbotapi.NewInlineKeyboardButtonData(gr.GroupName, strconv.FormatInt(gr.GroupId, 10))) + } + return grKeys +} + +func GenerateName(t database.Teacher) string { + var initials string + for _, n := range strings.Split(t.FirstName, " ") { + initials += fmt.Sprintf("%s.", n[:2]) + } + name := fmt.Sprintf("%s %s", t.LastName, initials) + return name +} + +// Создание ряда кнопок из списка преподавателей +func GenerateTeachersArray(teachers []database.Teacher) []tgbotapi.InlineKeyboardButton { + var teacherKeys []tgbotapi.InlineKeyboardButton + for _, t := range teachers { + name := fmt.Sprintf("%s %s", t.FirstName, t.ShortName) + teacherKeys = append(teacherKeys, tgbotapi.NewInlineKeyboardButtonData(name, strconv.FormatInt(t.TeacherId, 10))) + } + return teacherKeys +} + +// Создание полноценной клавиатуры выбора +func GenerateKeyboard(array []tgbotapi.InlineKeyboardButton) tgbotapi.InlineKeyboardMarkup { + var keys []tgbotapi.InlineKeyboardButton + var markup [][]tgbotapi.InlineKeyboardButton + // Разбиваем список кнопок в ряды по 3 кнопки + for _, key := range array { + keys = append(keys, key) + if len(keys) >= 3 { + markup = append(markup, keys) + keys = []tgbotapi.InlineKeyboardButton{} + } + } + markup = append(markup, keys) + no_one := tgbotapi.NewInlineKeyboardButtonData("Отмена", "cancel") + markup = append(markup, []tgbotapi.InlineKeyboardButton{no_one}) + return tgbotapi.InlineKeyboardMarkup{InlineKeyboard: markup} +} + +func SummaryKeyboard(clickedButton string, sheduleId int64, isTeacher bool, dt int) tgbotapi.InlineKeyboardMarkup { + tail := GenerateButtonTail(sheduleId, 0, isTeacher) + + near := []tgbotapi.InlineKeyboardButton{ + tgbotapi.NewInlineKeyboardButtonData("Краткая сводка", "near"+tail), + } + day := []tgbotapi.InlineKeyboardButton{ + tgbotapi.NewInlineKeyboardButtonData("День", "day"+tail), + } + week := []tgbotapi.InlineKeyboardButton{ + tgbotapi.NewInlineKeyboardButtonData("Неделя", "week"+tail), + } + + update := GenerateButtonTail(sheduleId, dt, isTeacher) + var arrows []tgbotapi.InlineKeyboardButton + if clickedButton == "day" || clickedButton == "week" { + prev_arrow := GenerateButtonTail(sheduleId, dt-1, isTeacher) + next_arrow := GenerateButtonTail(sheduleId, dt+1, isTeacher) + arrows = []tgbotapi.InlineKeyboardButton{ + tgbotapi.NewInlineKeyboardButtonData("⏮", clickedButton+prev_arrow), + tgbotapi.NewInlineKeyboardButtonData("🔄", clickedButton+update), + tgbotapi.NewInlineKeyboardButtonData("⏭", clickedButton+next_arrow), + } + } else { + arrows = []tgbotapi.InlineKeyboardButton{ + tgbotapi.NewInlineKeyboardButtonData("🔄", clickedButton+update), + } + } + /*options := []tgbotapi.InlineKeyboardButton{ + tgbotapi.NewInlineKeyboardButtonData("Настройки", "options"), + }*/ + + var markup [][]tgbotapi.InlineKeyboardButton + switch clickedButton { + case "day": + markup = [][]tgbotapi.InlineKeyboardButton{ + arrows, near, week, + } + case "week": + markup = [][]tgbotapi.InlineKeyboardButton{ + arrows, near, day, + } + default: + markup = [][]tgbotapi.InlineKeyboardButton{ + arrows, day, week, + } + } + /*if sheduleId == 0 { + markup = append(markup, options) + }*/ + return tgbotapi.InlineKeyboardMarkup{InlineKeyboard: markup} +} + +func GenerateButtonTail(sheduleId int64, dt int, isTeacher bool) string { + var tail string + if sheduleId == 0 { + tail = fmt.Sprintf("_personal_%d_0", dt) + } else if isTeacher { + tail = fmt.Sprintf("_teacher_%d_%d", dt, sheduleId) + } else { + tail = fmt.Sprintf("_group_%d_%d", dt, sheduleId) + } + return tail +} + +/* +func (bot *Bot) EditOrSend(str string, markup tgbotapi.InlineKeyboardMarkup, editMsg ...tgbotapi.Message) { + if len(editMsg) > 0 { + msg := tgbotapi.NewEditMessageText( + editMsg[0].Chat.ID, + editMsg[0].MessageID, + str, + ) + msg.ReplyMarkup = &markup + bot.TG.Request(msg) + } else { + msg := tgbotapi.NewMessage(bot.TG_user.TgId, str) + msg.ReplyMarkup = markup + bot.TG.Send(msg) + } +}*/ + +func ParseQuery(data []string) ([]database.ShedulesInUser, int, error) { + isGroup := data[1] == "group" + sheduleId, err := strconv.ParseInt(data[3], 0, 64) + if err != nil { + return nil, 0, err + } + shedule := database.ShedulesInUser{ + IsTeacher: !isGroup, + SheduleId: sheduleId, + } + dt, err := strconv.ParseInt(data[2], 0, 0) + if err != nil { + return nil, 0, err + } + return []database.ShedulesInUser{shedule}, int(dt), nil +} + +var SumKey = []string{"near", "day", "week"} + +func KeywordContains(str string, keywords []string) bool { + for _, key := range keywords { + if strings.Contains(str, key) { + return true + } + } + return false +} + +func (bot *Bot) DeleteMsg(query *tgbotapi.CallbackQuery) { + delete := tgbotapi.NewDeleteMessage(query.From.ID, query.Message.MessageID) + bot.TG.Request(delete) +} + +func (bot *Bot) UpdateUserDB(user *database.TgUser) error { + _, err := bot.DB.ID(user.L9Id).Update(user) + return err +}