diff --git a/cmd/discord/main.go b/cmd/discord/main.go index b9286a9..ea537e7 100644 --- a/cmd/discord/main.go +++ b/cmd/discord/main.go @@ -53,12 +53,12 @@ func main() { } if user == nil { - responder.Respond("Ты кто такой? Cъебал.") + _ = responder.Respond("Ты кто такой? Cъебал.") return } if user.State != users.Ready { - responder.Respond("Пошел нахуй.") + _ = responder.Respond("Пошел нахуй.") return } @@ -67,9 +67,9 @@ func main() { User: *user, } - formatter := commands.DiscordFormatter{} + formatter := helpers.DiscordFormatter{} - if len(data) != 0 { + if len(data) > 0 { commands.HandleCallback(context, &responder, &formatter, data) return } diff --git a/cmd/telegram/main.go b/cmd/telegram/main.go index 62420cb..5a7c5ad 100644 --- a/cmd/telegram/main.go +++ b/cmd/telegram/main.go @@ -65,7 +65,7 @@ func handleMessage(update tgbotapi.Update) { user = users.GetByTelegramID(update.Message.ForwardFrom.ID) if user == nil { - responder.Respond("Пользователь не зарегистрирован") + _ = responder.Respond("Пользователь не зарегистрирован") return } @@ -74,12 +74,12 @@ func handleMessage(update tgbotapi.Update) { user = users.GetByTelegramID(update.Message.From.ID) if user == nil { - responder.Respond("Ты кто такой? Уйди.") + _ = responder.Respond("Ты кто такой? Уйди.") return } if user.State != users.Ready { - responder.Respond("Пошел отсюда.") + _ = responder.Respond("Пошел отсюда.") return } @@ -95,7 +95,7 @@ func handleMessage(update tgbotapi.Update) { User: *user, } - formatter := commands.TelegramFormatter{} + formatter := helpers.TelegramFormatter{} commands.HandleCommand(context, &responder, &formatter) } @@ -125,7 +125,7 @@ func handleCallbackQuery(update tgbotapi.Update) { Update: update, } - formatter := commands.TelegramFormatter{} + formatter := helpers.TelegramFormatter{} commands.HandleCallback(context, &responder, &formatter, data) } diff --git a/internal/client/client.go b/internal/client/client.go index 1995182..40210c1 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -7,6 +7,8 @@ import ( "github.com/kiasuo/bot/internal/helpers" "github.com/kiasuo/bot/internal/users" "net/http" + "strconv" + "time" ) const BaseUrl = "https://kiasuo-proxy.oddya.ru/diary" @@ -69,7 +71,7 @@ func RefreshToken(client *Client) error { return nil } -func ClientRequest[T any](client Client, pathname string, method string) (*T, error) { +func requestWithClient[T any](client Client, pathname string, method string) (*T, error) { request, err := http.NewRequest(method, BaseUrl+pathname, nil) if err != nil { @@ -100,11 +102,11 @@ func ClientRequest[T any](client Client, pathname string, method string) (*T, er } func (c Client) GetUser() (*User, error) { - return ClientRequest[User](c, "/api/user", "GET") + return requestWithClient[User](c, "/api/user", "GET") } func (c Client) GetRecipients() (*Recipients, error) { - rawRecipients, err := ClientRequest[RawRecipient](c, "/api/recipients", "GET") + rawRecipients, err := requestWithClient[RawRecipient](c, "/api/recipients", "GET") if err != nil { return nil, err @@ -113,3 +115,23 @@ func (c Client) GetRecipients() (*Recipients, error) { recipients := (*rawRecipients)[c.User.StudentID] return &recipients, nil } + +func (c Client) GetStudyPeriods() (*[]StudyPeriod, error) { + return requestWithClient[[]StudyPeriod](c, "/api/study_periods", "GET") +} + +func (c Client) GetLessons(id int) (*[]Lesson, error) { + rawMarks, err := requestWithClient[RawLessons](c, "/api/lesson_marks/"+strconv.Itoa(id), "GET") + + if err != nil { + return nil, err + } + + return &rawMarks.Lessons, nil +} + +func (c Client) GetSchedule(time time.Time) (*RawSchedule, error) { + year, week := time.ISOWeek() + + return requestWithClient[RawSchedule](c, "/api/schedule?year="+strconv.Itoa(year)+"&week="+strconv.Itoa(week), "GET") +} diff --git a/internal/client/types.go b/internal/client/types.go index 0768e43..9060428 100644 --- a/internal/client/types.go +++ b/internal/client/types.go @@ -1,5 +1,11 @@ package client +import ( + "github.com/kiasuo/bot/internal/helpers" + "strconv" + "time" +) + type Token struct { AccessToken string `json:"accessToken"` RefreshToken string `json:"refreshToken"` @@ -37,3 +43,91 @@ type Student struct { Parents any `json:"parents"` ID *int `json:"id"` } + +type StudyPeriod struct { + ID int `json:"id"` + Text string `json:"text"` + From string `json:"from"` + To string `json:"to"` +} + +func (p StudyPeriod) Match(t time.Time) bool { + from, _ := time.Parse(time.DateOnly, p.From) + + if t.After(from) { + to, _ := time.Parse(time.DateOnly, p.To) + + if t.Before(to) { + return true + } + } + + return false +} + +type RawLessons struct { + Lessons []Lesson `json:"lessons"` +} + +type Lesson struct { + Subject string `json:"subject"` + Marks []Mark `json:"marks"` +} + +func (l Lesson) String() string { + return helpers.HumanizeLesson(l.Subject) +} + +type Mark struct { + // эта ебола будет мне порядок нарушать или нет? + LessonDate string `json:"lesson_date"` + Mark string `json:"mark"` + UpdatedAt time.Time `json:"updated_at"` +} + +type RawSchedule struct { + Schedule []Event `json:"schedule"` + Homeworks []Homework `json:"homeworks"` +} + +type Event struct { + Subject string `json:"subject"` + LessonDate string `json:"lesson_date"` + Number int `json:"lesson_number"` + Homeworks []int `json:"homework_to_check_ids"` + Marks []Mark `json:"marks"` +} + +func (e Event) Date() time.Time { + date, _ := time.Parse(time.DateOnly, e.LessonDate) + return date +} + +func (e Event) String() string { + return strconv.Itoa(e.Number) + ". " + helpers.HumanizeLesson(e.Subject) +} + +type Homework struct { + ID int `json:"id"` + Text string `json:"text"` + Files []File `json:"files"` + Links []Link `json:"links"` +} + +type File struct { + Url string `json:"url"` + Title string `json:"title"` +} + +func (f File) String(formatter helpers.Formatter) string { + return formatter.Link(f.Title, f.Url) +} + +type Link struct { + Url string `json:"url"` + Title string `json:"title"` +} + +func (l Link) String(formatter helpers.Formatter) string { + return formatter.Link(l.Title, l.Url) +} diff --git a/internal/commands/admin.go b/internal/commands/admin.go index 4f6a236..34b9987 100644 --- a/internal/commands/admin.go +++ b/internal/commands/admin.go @@ -8,7 +8,7 @@ import ( const AdminCommandName string = "-internal-admin" -var AdminCommand = Command(func(context Context, responder Responder, formatter Formatter) error { +var AdminCommand = Command(func(context Context, responder Responder, formatter helpers.Formatter) error { user := context.User state := map[users.UserState]string{ @@ -27,7 +27,7 @@ var AdminCommand = Command(func(context Context, responder Responder, formatter KeyboardRow{ KeyboardButton{ Text: helpers.If(user.State == users.Blacklisted, "Разблокировать", "Заблокировать"), - Callback: AdminCommandName + ":blacklist:" + string(rune(user.ID)), + Callback: AdminCommandName + ":blacklist:" + strconv.Itoa(user.ID), }, }, } @@ -41,7 +41,7 @@ var AdminCommand = Command(func(context Context, responder Responder, formatter ) }) -var AdminCallback = Callback(func(context Context, responder Responder, formatter Formatter, data []string) error { +var AdminCallback = Callback(func(context Context, responder Responder, formatter helpers.Formatter, data []string) error { switch data[1] { case "blacklist": isBlacklisted := context.User.State == users.Blacklisted diff --git a/internal/commands/classmates.go b/internal/commands/classmates.go index 162b2e6..ea43262 100644 --- a/internal/commands/classmates.go +++ b/internal/commands/classmates.go @@ -1,8 +1,11 @@ package commands -import "sort" +import ( + "github.com/kiasuo/bot/internal/helpers" + "sort" +) -var ClassmatesCommand = Command(func(context Context, responder Responder, formatter Formatter) error { +var ClassmatesCommand = Command(func(context Context, responder Responder, formatter helpers.Formatter) error { recipients, err := context.GetClient().GetRecipients() if err != nil { diff --git a/internal/commands/commands.go b/internal/commands/commands.go index b92ed7c..b13935d 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -3,16 +3,19 @@ package commands import ( "github.com/bwmarrin/discordgo" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" + "github.com/kiasuo/bot/internal/helpers" "log" ) -type Command func(Context, Responder, Formatter) error +type Command func(Context, Responder, helpers.Formatter) error var commandMap = map[string]Command{ AdminCommandName: AdminCommand, "start": StartCommand, "stop": StopCommand, "settings": SettingsCommand, + "schedule": ScheduleCommand, + "marks": MarksCommand, "classmates": ClassmatesCommand, "teachers": TeachersCommand, } @@ -38,6 +41,14 @@ var publicCommands = []commandConfig{ Name: "settings", Description: "Настройки", }, + { + Name: "schedule", + Description: "Расписание", + }, + { + Name: "marks", + Description: "Оценки", + }, { Name: "classmates", Description: "Список одноклассников", @@ -78,15 +89,17 @@ func ParseTelegramCommands() tgbotapi.SetMyCommandsConfig { return tgbotapi.NewSetMyCommands(commands...) } -type Callback func(Context, Responder, Formatter, []string) error +type Callback func(Context, Responder, helpers.Formatter, []string) error var callbackMap = map[string]Callback{ AdminCommandName: AdminCallback, "stop": StopCallback, "settings": SettingsCallback, + "schedule": ScheduleCallback, + "marks": MarksCallback, } -func HandleCommand(context Context, responder Responder, formatter Formatter) { +func HandleCommand(context Context, responder Responder, formatter helpers.Formatter) { command := commandMap[context.Command] if command == nil { @@ -95,10 +108,10 @@ func HandleCommand(context Context, responder Responder, formatter Formatter) { } log.Println("Handling command", context.Command) - handleError(command(context, responder, formatter)) + handleError(responder, command(context, responder, formatter)) } -func HandleCallback(context Context, responder Responder, formatter Formatter, data []string) { +func HandleCallback(context Context, responder Responder, formatter helpers.Formatter, data []string) { callback := callbackMap[context.Command] if callback == nil { @@ -107,11 +120,12 @@ func HandleCallback(context Context, responder Responder, formatter Formatter, d } log.Println("Handling callback", context.Command) - handleError(callback(context, responder, formatter, data)) + handleError(responder, callback(context, responder, formatter, data)) } -func handleError(err error) { +func handleError(responder Responder, err error) { if err != nil { log.Printf("Error: %v", err) + _ = responder.Respond("Произошла ошибка. Попробуйте позже.") } } diff --git a/internal/commands/marks.go b/internal/commands/marks.go new file mode 100644 index 0000000..5c38467 --- /dev/null +++ b/internal/commands/marks.go @@ -0,0 +1,85 @@ +package commands + +import ( + "github.com/kiasuo/bot/internal/client" + "github.com/kiasuo/bot/internal/helpers" + "strconv" + "strings" + "time" +) + +func marksCommand(context Context, responder Responder, formatter helpers.Formatter, periodId int) error { + periods, err := context.GetClient().GetStudyPeriods() + + if err != nil { + return err + } + + row := KeyboardRow{} + now := time.Now() + var period *client.StudyPeriod + + for _, p := range *periods { + row = append(row, KeyboardButton{ + Text: p.Text, + Callback: "marks:" + strconv.Itoa(p.ID), + }) + + if periodId == p.ID || (periodId == 0 && p.Match(now)) { + period = &p + } + } + + keyboard := Keyboard{row} + + if period == nil { + return responder.RespondWithKeyboard(keyboard, "Каникулы?") + } + + marks, err := context.GetClient().GetLessons(period.ID) + + if err != nil { + return err + } + + var result strings.Builder + result.WriteString(formatter.Title("Оценки за " + period.Text)) + + for _, lesson := range *marks { + line := lesson.String() + + if len(lesson.Marks) > 0 { + marksLine := "" + + for i, mark := range lesson.Marks { + if i > 0 { + marksLine += ", " + } + + marksLine += mark.Mark + + if mark.UpdatedAt.After(context.User.LastMarksUpdate) { + marksLine += "⁺" + } + } + + line += ": " + formatter.Code(marksLine) + } else { + line += ": " + formatter.Code("-") + } + + result.WriteString(formatter.Item(line)) + } + + context.User.UpdateLastMarksUpdate() + return responder.RespondWithKeyboard(keyboard, result.String()) +} + +var MarksCommand = Command(func(context Context, responder Responder, formatter helpers.Formatter) error { + return marksCommand(context, responder, formatter, 0) +}) + +var MarksCallback = Callback(func(context Context, responder Responder, formatter helpers.Formatter, data []string) error { + id, _ := strconv.Atoi(data[1]) + return marksCommand(context, responder, formatter, id) +}) diff --git a/internal/commands/schedule.go b/internal/commands/schedule.go new file mode 100644 index 0000000..33a6e65 --- /dev/null +++ b/internal/commands/schedule.go @@ -0,0 +1,102 @@ +package commands + +import ( + "github.com/kiasuo/bot/internal/helpers" + "strings" + "time" +) + +func scheduleCommand(context Context, responder Responder, formatter helpers.Formatter, t time.Time) error { + data, err := context.GetClient().GetSchedule(t) + + if err != nil { + return err + } + + keyboard := Keyboard{ + KeyboardRow{ + KeyboardButton{ + Text: "Предыдущая неделя", + Callback: "schedule:" + t.AddDate(0, 0, -7).Format(time.DateOnly), + }, + { + Text: "Следующая неделя", + Callback: "schedule:" + t.AddDate(0, 0, 7).Format(time.DateOnly), + }, + }, + } + + if len(data.Schedule) == 0 { + return responder.RespondWithKeyboard(keyboard, "Расписания нет. Отдыхаем?") + } + + var result strings.Builder + result.WriteString(formatter.Title("Расписание")) + date := "" + + for _, event := range data.Schedule { + if event.LessonDate != date { + result.WriteString(formatter.Title(formatDate(event.Date()))) + date = event.LessonDate + } + + result.WriteString(formatter.Line(event.String())) + + if len(event.Marks) > 0 { + marks := "Оценки: " + + for i, mark := range event.Marks { + if i > 0 { + marks += ", " + } + + marks += mark.Mark + } + + result.WriteString(formatter.Item(marks)) + } + + for _, homeworkId := range event.Homeworks { + for _, homework := range data.Homeworks { + if homework.ID != homeworkId { + continue + } + + if homework.Text != "" { + result.WriteString(formatter.Item(homework.Text)) + } + + for _, file := range homework.Files { + result.WriteString(formatter.Item(file.String(formatter))) + } + + for _, link := range homework.Links { + result.WriteString(formatter.Item(link.String(formatter))) + } + } + } + } + + return responder.RespondWithKeyboard(keyboard, result.String()) +} + +var ScheduleCommand = Command(func(context Context, responder Responder, formatter helpers.Formatter) error { + return scheduleCommand(context, responder, formatter, time.Now()) +}) + +var ScheduleCallback = Callback(func(context Context, responder Responder, formatter helpers.Formatter, data []string) error { + time, _ := time.Parse(time.DateOnly, data[1]) + return scheduleCommand(context, responder, formatter, time) +}) + +func formatDate(t time.Time) string { + return map[time.Weekday]string{ + time.Monday: "Понедельник", + time.Tuesday: "Вторник", + time.Wednesday: "Среда", + time.Thursday: "Четверг", + time.Friday: "Пятница", + time.Saturday: "Суббота", + time.Sunday: "Воскресенье", + }[t.Weekday()] + ", " + t.Format("02.01") +} diff --git a/internal/commands/settings.go b/internal/commands/settings.go index 6b568d7..920aaca 100644 --- a/internal/commands/settings.go +++ b/internal/commands/settings.go @@ -2,10 +2,11 @@ package commands import ( "github.com/kiasuo/bot/internal/client" + "github.com/kiasuo/bot/internal/helpers" "strconv" ) -var SettingsCommand = Command(func(context Context, responder Responder, formatter Formatter) error { +var SettingsCommand = Command(func(context Context, responder Responder, formatter helpers.Formatter) error { user := context.User keyboard := Keyboard{ @@ -26,7 +27,7 @@ var SettingsCommand = Command(func(context Context, responder Responder, formatt return responder.RespondWithKeyboard(keyboard, "Ученик: %s", formatter.Bold(user.StudentNameAcronym)) }) -var SettingsCallback = Callback(func(context Context, responder Responder, formatter Formatter, data []string) error { +var SettingsCallback = Callback(func(context Context, responder Responder, formatter helpers.Formatter, data []string) error { switch data[1] { case "userStudents": return getUserStudents(context, responder) @@ -72,7 +73,7 @@ func getUserStudents(context Context, responder Responder) error { return responder.RespondWithKeyboard(keyboard, "Выберите ребенка из списка:") } -func updateUserStudent(context Context, responder Responder, formatter Formatter, data []string) error { +func updateUserStudent(context Context, responder Responder, formatter helpers.Formatter, data []string) error { studentID, err := strconv.Atoi(data[2]) studentNameAcronym := data[3] diff --git a/internal/commands/start.go b/internal/commands/start.go index 95b5ae5..bfbdb2c 100644 --- a/internal/commands/start.go +++ b/internal/commands/start.go @@ -1,5 +1,7 @@ package commands -var StartCommand = Command(func(_ Context, responder Responder, _ Formatter) error { +import "github.com/kiasuo/bot/internal/helpers" + +var StartCommand = Command(func(_ Context, responder Responder, _ helpers.Formatter) error { return responder.Respond("Привет!") }) diff --git a/internal/commands/stop.go b/internal/commands/stop.go index 96f68a5..e5cea9e 100644 --- a/internal/commands/stop.go +++ b/internal/commands/stop.go @@ -1,6 +1,8 @@ package commands -var StopCommand = Command(func(_ Context, responder Responder, _ Formatter) error { +import "github.com/kiasuo/bot/internal/helpers" + +var StopCommand = Command(func(_ Context, responder Responder, _ helpers.Formatter) error { keyboard := Keyboard{ KeyboardRow{ KeyboardButton{ @@ -13,6 +15,6 @@ var StopCommand = Command(func(_ Context, responder Responder, _ Formatter) erro return responder.RespondWithKeyboard(keyboard, "Нам очень жаль, что Вы решили покинуть нас. Нажмите ниже, чтобы удалить все данные.") }) -var StopCallback = Callback(func(context Context, responder Responder, formatter Formatter, data []string) error { +var StopCallback = Callback(func(context Context, responder Responder, formatter helpers.Formatter, data []string) error { return responder.Respond("Скоро сделаю!") }) diff --git a/internal/commands/teachers.go b/internal/commands/teachers.go index 7aad518..f62feaa 100644 --- a/internal/commands/teachers.go +++ b/internal/commands/teachers.go @@ -6,7 +6,7 @@ import ( "strings" ) -var TeachersCommand = Command(func(context Context, responder Responder, formatter Formatter) error { +var TeachersCommand = Command(func(context Context, responder Responder, formatter helpers.Formatter) error { recipients, err := context.GetClient().GetRecipients() if err != nil { diff --git a/internal/commands/formatter.go b/internal/helpers/formatters.go similarity index 67% rename from internal/commands/formatter.go rename to internal/helpers/formatters.go index 2f7e948..e3fbf59 100644 --- a/internal/commands/formatter.go +++ b/internal/helpers/formatters.go @@ -1,10 +1,12 @@ -package commands +package helpers type Formatter interface { Title(title string) string Item(item string) string Bold(text string) string Code(text string) string + Line(text string) string + Link(text, url string) string } type TelegramFormatter struct{} @@ -25,6 +27,14 @@ func (f TelegramFormatter) Bold(text string) string { return "*" + text + "*" } +func (f TelegramFormatter) Line(text string) string { + return text + "\n" +} + +func (f TelegramFormatter) Link(text, url string) string { + return "[" + text + "](" + url + ")" +} + type DiscordFormatter struct{} func (f DiscordFormatter) Title(title string) string { @@ -42,3 +52,11 @@ func (f DiscordFormatter) Bold(text string) string { func (f DiscordFormatter) Code(text string) string { return "`" + text + "`" } + +func (f DiscordFormatter) Line(text string) string { + return text + "\n" +} + +func (f DiscordFormatter) Link(text, url string) string { + return "[" + text + "](" + url + ")" +} diff --git a/internal/commands/formatter_test.go b/internal/helpers/formatters_test.go similarity index 64% rename from internal/commands/formatter_test.go rename to internal/helpers/formatters_test.go index b42275c..78ce016 100644 --- a/internal/commands/formatter_test.go +++ b/internal/helpers/formatters_test.go @@ -1,4 +1,4 @@ -package commands +package helpers import "testing" @@ -39,6 +39,22 @@ func TestTelegramFormatterCode(t *testing.T) { } } +func TestTelegramFormatterLine(t *testing.T) { + result := telegramFormatter.Line("test") + + if result != "test\n" { + t.Errorf("TelegramFormatter.Line() = %s; want test\n", result) + } +} + +func TestTelegramFormatterLink(t *testing.T) { + result := telegramFormatter.Link("test", "http://example.com") + + if result != "[test](http://example.com)" { + t.Errorf("TelegramFormatter.Link() = %s; want [test](http://example.com)", result) + } +} + func TestDiscordFormatterTitle(t *testing.T) { result := discordFormatter.Title("test") @@ -70,3 +86,19 @@ func TestDiscordFormatterCode(t *testing.T) { t.Errorf("DiscordFormatter.Code() = %s; want `test`", result) } } + +func TestDiscordFormatterLine(t *testing.T) { + result := discordFormatter.Line("test") + + if result != "test\n" { + t.Errorf("DiscordFormatter.Line() = %s; want test\n", result) + } +} + +func TestDiscordFormatterLink(t *testing.T) { + result := discordFormatter.Link("test", "http://example.com") + + if result != "[test](http://example.com)" { + t.Errorf("DiscordFormatter.Link() = %s; want [test](http://example.com)", result) + } +} diff --git a/internal/helpers/helpers.go b/internal/helpers/helpers.go index 856adb0..4333b08 100644 --- a/internal/helpers/helpers.go +++ b/internal/helpers/helpers.go @@ -64,7 +64,15 @@ func HumanizeLesson(lesson string) string { case "алгебра и начала математического анализа": return "Алгебра" case "искусственный интеллект": - return "ИИ" + return "Информатика (ИИ)" + case "россия - мои горизонты": + return "Профориентация" + case "учимся писать сочинение": + return "Русский язык (сочинение)" + case "трудные вопросы орфографии и пунктуации": + return "Русский язык (консультация)" + case "решение задач повышенной сложности": + return "Математика (консультация)" default: return lesson } diff --git a/internal/users/users.go b/internal/users/users.go index 96882b9..cebaa80 100644 --- a/internal/users/users.go +++ b/internal/users/users.go @@ -6,6 +6,7 @@ import ( "github.com/kiasuo/bot/internal/helpers" _ "github.com/lib/pq" "log" + "time" ) type UserState int @@ -26,6 +27,7 @@ type User struct { StudentID int StudentNameAcronym string State UserState + LastMarksUpdate time.Time } var db *sql.DB @@ -62,7 +64,8 @@ func createTable() { refresh_token VARCHAR(32), student_id INTEGER, student_name_acronym TEXT, - state INTEGER NOT NULL + state INTEGER NOT NULL, + last_marks_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `) } @@ -85,7 +88,7 @@ func queryRow(query string, args ...any) *User { var user User - err := rows.Scan(&user.ID, &user.TelegramID, &user.DiscordID, &user.AccessToken, &user.RefreshToken, &user.StudentID, &user.StudentNameAcronym, &user.State) + err := rows.Scan(&user.ID, &user.TelegramID, &user.DiscordID, &user.AccessToken, &user.RefreshToken, &user.StudentID, &user.StudentNameAcronym, &user.State, &user.LastMarksUpdate) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -125,3 +128,7 @@ func (u User) UpdateStudent(studentID int, studentNameAcronym string) { func (u User) UpdateDiscord(discordID string) { query("UPDATE users SET discord_id = $1 WHERE id = $2", discordID, u.ID) } + +func (u User) UpdateLastMarksUpdate() { + query("UPDATE users SET last_marks_update = CURRENT_TIMESTAMP WHERE id = $1", u.ID) +}