From d52be14b87f094f6ae93e3ddc48482291b803fbf Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Mon, 20 Nov 2023 14:22:45 +0100 Subject: [PATCH 01/19] Fix `GetTimeTable` fetching curriculum --- commands/uni.go | 4 ++-- commands/uni_test.go | 10 ++++++++++ model/controller.go | 4 ++-- model/model.go | 7 ++++--- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/commands/uni.go b/commands/uni.go index 75aeb55..128ffb5 100644 --- a/commands/uni.go +++ b/commands/uni.go @@ -50,10 +50,10 @@ func (t *LezioniTime) UnmarshalJSON(data []byte) error { // GetTimeTable returns an HTML string containing the timetable for the given // course on the given date. Returns an empty string if there are no lessons. -func GetTimeTable(courseType, courseName string, year int, day time.Time) (string, error) { +func GetTimeTable(courseType, courseName string, curriculum string, year int, day time.Time) (string, error) { interval := &timetable.Interval{Start: day, End: day} - events, err := timetable.FetchTimetable(courseType, courseName, "", year, interval) + events, err := timetable.FetchTimetable(courseType, courseName, curriculum, year, interval) if err != nil { log.Printf("Error getting timetable: %s\n", err) return "", err diff --git a/commands/uni_test.go b/commands/uni_test.go index 48f3133..bb66908 100644 --- a/commands/uni_test.go +++ b/commands/uni_test.go @@ -123,6 +123,16 @@ func TestGetTimeTable(t *testing.T) { }, want: "", }, + { + name: "Weekday for master", + args: args{ + courseType: "magistrale", + courseName: "informatica", + year: 1, + day: time.Date(2023, 11, 20, 0, 0, 0, 0, time.UTC), + }, + want: `🕘`, + }, { name: "Weekday", args: args{ diff --git a/model/controller.go b/model/controller.go index 8862a53..6d38004 100644 --- a/model/controller.go +++ b/model/controller.go @@ -141,7 +141,7 @@ func (data YearlyData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Me func (data TodayLecturesData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { - response, err := commands.GetTimeTable(data.Course.Type, data.Course.Name, data.Course.Year, time.Now()) + response, err := commands.GetTimeTable(data.Course.Type, data.Course.Name, data.Course.Curriculum, data.Course.Year, time.Now()) if err != nil { log.Printf("Error [TodayLecturesData]: %s\n", err) return makeResponseWithText("Bot internal Error, contact developers") @@ -160,7 +160,7 @@ func (data TodayLecturesData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Messa func (data TomorrowLecturesData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { tomorrowTime := time.Now().AddDate(0, 0, 1) - response, err := commands.GetTimeTable(data.Course.Type, data.Course.Name, data.Course.Year, tomorrowTime) + response, err := commands.GetTimeTable(data.Course.Type, data.Course.Name, data.Course.Curriculum, data.Course.Year, tomorrowTime) if err != nil { log.Printf("Error [TomorrowLecturesData]: %s\n", err) return makeResponseWithText("Bot internal Error, contact developers") diff --git a/model/model.go b/model/model.go index 9d632a6..1605739 100644 --- a/model/model.go +++ b/model/model.go @@ -132,9 +132,10 @@ type YearlyData struct { } type CourseId struct { - Type string `json:"type"` - Name string `json:"name"` - Year int `json:"year"` + Type string `json:"type"` + Name string `json:"name"` + Year int `json:"year"` + Curriculum string `json:"curriculum"` } type TodayLecturesData struct { From a96ada8c57fe204141a628ebc0203a0a8f774072 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Mon, 20 Nov 2023 19:41:38 +0100 Subject: [PATCH 02/19] wip --- bot/bot.go | 18 ++++++++++++++++++ model/controller.go | 12 ++++++++++++ model/description.go | 4 ++++ model/model.go | 6 ++++++ model/responses.go | 21 ++++++++++++++++++--- 5 files changed, 58 insertions(+), 3 deletions(-) diff --git a/bot/bot.go b/bot/bot.go index 1dd891e..8073414 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -35,6 +35,15 @@ func run(bot *tgbotapi.BotAPI) { for update := range updates { if update.Message == nil { + callback := tgbotapi.NewCallback(update.CallbackQuery.ID, update.CallbackQuery.Data) + if _, err := bot.Request(callback); err != nil { + panic(err) + } + + msg := tgbotapi.NewMessage(update.CallbackQuery.Message.Chat.ID, update.CallbackQuery.Data) + if _, err := bot.Send(msg); err != nil { + panic(err) + } continue } else if filterMessage(bot, update.Message) { continue @@ -229,6 +238,15 @@ func executeCommand(bot *tgbotapi.BotAPI, update *tgbotapi.Update, commandIndex if newCommand.HasNextCommand() { handleAction(bot, update, newCommand.NextCommand) } + + if newCommand.HasRows() { + msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text) + msg.ReplyMarkup = newCommand.Rows + if _, err := bot.Send(msg); err != nil { + msg = tgbotapi.NewMessage(update.Message.Chat.ID, "Error retrieving lectures") + bot.Send(msg) + } + } } } diff --git a/model/controller.go b/model/controller.go index 6d38004..ce9e0f1 100644 --- a/model/controller.go +++ b/model/controller.go @@ -121,6 +121,18 @@ func (data NotLookingForData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbo return makeResponseWithText(msg) } +func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse { + keyboard := tgbotapi.NewInlineKeyboardMarkup( + tgbotapi.NewInlineKeyboardRow( + tgbotapi.NewInlineKeyboardButtonData("Informatica", "lectures_informatica"), + ), + tgbotapi.NewInlineKeyboardRow( + tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale", "lectures_lm_informatica"), + ), + ) + return makeResponseWithInlineKeyboard(keyboard) +} + func (data YearlyData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse { chatTitle := strings.ToLower(message.Chat.Title) diff --git a/model/description.go b/model/description.go index 89bbcf8..1860360 100644 --- a/model/description.go +++ b/model/description.go @@ -20,6 +20,10 @@ func (d YearlyData) GetDescription() string { return d.Description } +func (d Lectures) GetDescription() string { + return d.Description +} + func (d TodayLecturesData) GetDescription() string { return d.Description } diff --git a/model/model.go b/model/model.go index 1605739..a94e5a5 100644 --- a/model/model.go +++ b/model/model.go @@ -25,6 +25,8 @@ func GetActionFromType(name string, commandType string) Action { data = LookingForData{} case "notLookingFor": data = NotLookingForData{} + case "buttonsLecture": + data = Lectures{} case "yearly": data = YearlyData{} case "todayLectures": @@ -125,6 +127,10 @@ type NotLookingForData struct { NotFoundError string `json:"notFoundError"` } +type Lectures struct { + Description string `json:"description"` +} + type YearlyData struct { Description string `json:"description"` Command string `json:"command"` diff --git a/model/responses.go b/model/responses.go index fa7961c..da80be8 100644 --- a/model/responses.go +++ b/model/responses.go @@ -2,29 +2,39 @@ // between the computations of the bot and the driver code, in bot.go package model +import tgbotapi "github.com/musianisamuele/telegram-bot-api" + // CommandResponse is returned by the command handler, it contains information // about the command computation. type CommandResponse struct { Text string NextCommand string + Rows tgbotapi.InlineKeyboardMarkup } // makeResponse creates a CommandResponse with the given text and nextCommand -func makeResponse(text string, nextCommand string) CommandResponse { +func makeResponse(text string, nextCommand string, rows tgbotapi.InlineKeyboardMarkup) CommandResponse { return CommandResponse{ Text: text, NextCommand: nextCommand, + Rows: rows, } } // makeResponseWithText creates a CommandResponse with the given text (and no nextCommand) func makeResponseWithText(text string) CommandResponse { - return makeResponse(text, "") + return makeResponse(text, "", tgbotapi.InlineKeyboardMarkup{}) } // makeResponseWithNextCommand creates a CommandResponse with the given nextCommand (and no text) func makeResponseWithNextCommand(nextCommand string) CommandResponse { - return makeResponse("", nextCommand) + return makeResponse("", nextCommand, tgbotapi.InlineKeyboardMarkup{}) +} + +// makeResponseWithInlineKeyboard creates a CommandResponse with the given array +// of elements for the keyboard array. +func makeResponseWithInlineKeyboard(rows tgbotapi.InlineKeyboardMarkup) CommandResponse { + return makeResponse("", "", rows) } // IsEmpty returns true if the CommandResponse has no text and no nextCommand @@ -41,3 +51,8 @@ func (r CommandResponse) HasText() bool { func (r CommandResponse) HasNextCommand() bool { return r.NextCommand != "" } + +// HasButtonRows returns true if the CommandResponse has some rows +func (r CommandResponse) HasRows() bool { + return len(r.Rows.InlineKeyboard) > 0 +} From 5579a071971d11b301f2f2a0d4f0e57eabea4eb2 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Mon, 20 Nov 2023 20:41:35 +0100 Subject: [PATCH 03/19] Cdls JSON file for lectures --- json/cdls.json | 22 ++++++++++++++++++++++ model/controller.go | 19 +++++++++++-------- model/globals.go | 6 ++++++ model/model.go | 17 ++++++++++++++++- model/parse.go | 20 ++++++++++++++++++++ 5 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 json/cdls.json diff --git a/json/cdls.json b/json/cdls.json new file mode 100644 index 0000000..b8dc73a --- /dev/null +++ b/json/cdls.json @@ -0,0 +1,22 @@ +{ + "informatica": { + "course": "Informatica" + }, + "lm_informatica": { + "course": "Informatica magistrale", + "curricula": [ + { + "name": "Tecniche del software", + "callback": "software_techniques" + }, + { + "name": "Informatica per il management", + "callback": "management" + }, + { + "name": "Sistemi e reti", + "callback": "systems_and_networks" + } + ] + } +} diff --git a/model/controller.go b/model/controller.go index ce9e0f1..8aa6989 100644 --- a/model/controller.go +++ b/model/controller.go @@ -122,14 +122,17 @@ func (data NotLookingForData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbo } func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse { - keyboard := tgbotapi.NewInlineKeyboardMarkup( - tgbotapi.NewInlineKeyboardRow( - tgbotapi.NewInlineKeyboardButtonData("Informatica", "lectures_informatica"), - ), - tgbotapi.NewInlineKeyboardRow( - tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale", "lectures_lm_informatica"), - ), - ) + rows := make([][]tgbotapi.InlineKeyboardButton, len(Cdls)) + + i := 0 + for callback, cdl := range Cdls { + fmt.Println(callback, cdl.Course) + row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(cdl.Course, fmt.Sprintf("lectures_%s", callback))) + rows[i] = row + i++ + } + fmt.Println(rows) + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) return makeResponseWithInlineKeyboard(keyboard) } diff --git a/model/globals.go b/model/globals.go index fba57fb..ebe361d 100644 --- a/model/globals.go +++ b/model/globals.go @@ -19,6 +19,7 @@ var ( Settings SettingsStruct Teachings map[string]Teaching Groups GroupsStruct + Cdls map[string]Cdl ) func InitGlobals() { @@ -57,4 +58,9 @@ func InitGlobals() { if err != nil { log.Fatalf("Error reading or creating groups.json file: %s", err.Error()) } + + Cdls, err = ParseCdls() + if err != nil { + log.Fatalf(err.Error()) + } } diff --git a/model/model.go b/model/model.go index a94e5a5..3050a37 100644 --- a/model/model.go +++ b/model/model.go @@ -102,6 +102,21 @@ type Degree struct { Chat string `json:"chat"` } +// cdls.json + +type Curriculum struct { + Name string `json:"name"` + Callback string `json:"callback"` +} + +// Recognized by a callback string +type Cdl struct { + // Course name + Course string `json:"course"` + // Array of curricula, each curriculum has a callback too + Curricula []Curriculum `json:"curricula"` +} + // SECTION ACTION STRUCTS DATA type MessageData struct { Text string `json:"text"` @@ -128,7 +143,7 @@ type NotLookingForData struct { } type Lectures struct { - Description string `json:"description"` + Description string `json:"description"` } type YearlyData struct { diff --git a/model/parse.go b/model/parse.go index 92af848..b163589 100644 --- a/model/parse.go +++ b/model/parse.go @@ -21,6 +21,7 @@ const ( configSubpath = "config/" degreesFile = "degrees.json" teachingsFile = "teachings.json" + cdlFile = "cdls.json" ) func ParseAutoReplies() (autoReplies []AutoReply, err error) { @@ -206,3 +207,22 @@ func ParseOrCreateGroups() (GroupsStruct, error) { } func SaveGroups(groups GroupsStruct) error { return utils.WriteJSONFile(groupsFile, groups) } + +func ParseCdls() (cdls map[string]Cdl, err error) { + filepath := filepath.Join(jsonPath, cdlFile) + file, err := os.Open(filepath) + defer file.Close() + if err != nil { + return nil, fmt.Errorf("error reading %s file: %w", cdlFile, err) + } + + var mapData map[string]Cdl + + err = json.NewDecoder(file).Decode(&mapData) + if err != nil { + return nil, fmt.Errorf("error parsing %s file: %w", filepath, err) + } + + cdls = mapData + return +} From 6ddead41604965709d96be9f71bb31a4f231d776 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Mon, 20 Nov 2023 20:47:26 +0100 Subject: [PATCH 04/19] Fix tests --- commands/uni_test.go | 15 +++------------ model/responses_test.go | 4 +++- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/commands/uni_test.go b/commands/uni_test.go index bb66908..e0c2507 100644 --- a/commands/uni_test.go +++ b/commands/uni_test.go @@ -106,6 +106,7 @@ func TestGetTimeTable(t *testing.T) { courseName string year int day time.Time + curriculum string } tests := []struct { name string @@ -123,16 +124,6 @@ func TestGetTimeTable(t *testing.T) { }, want: "", }, - { - name: "Weekday for master", - args: args{ - courseType: "magistrale", - courseName: "informatica", - year: 1, - day: time.Date(2023, 11, 20, 0, 0, 0, 0, time.UTC), - }, - want: `🕘`, - }, { name: "Weekday", args: args{ @@ -155,7 +146,7 @@ func TestGetTimeTable(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetTimeTable(tt.args.courseType, tt.args.courseName, tt.args.year, tt.args.day) + got, err := GetTimeTable(tt.args.courseType, tt.args.courseName, tt.args.curriculum, tt.args.year, tt.args.day) if err != nil && !tt.error { t.Errorf("GetTimeTable() error = %v", err) return @@ -173,7 +164,7 @@ func TestGetTimeTable(t *testing.T) { func TestWeekend(t *testing.T) { date := time.Date(2023, 3, 11, 0, 0, 0, 0, time.UTC) - result, err := GetTimeTable("laurea", "informatica", 1, date) + result, err := GetTimeTable("laurea", "informatica", "", 1, date) if err != nil { t.Fatalf("Error while getting timetable: %s", err) } diff --git a/model/responses_test.go b/model/responses_test.go index 67a0294..d5ba23b 100644 --- a/model/responses_test.go +++ b/model/responses_test.go @@ -3,6 +3,8 @@ package model import ( "reflect" "testing" + + tgbotapi "github.com/musianisamuele/telegram-bot-api" ) func TestCommandResponse_IsEmpty(t *testing.T) { @@ -160,7 +162,7 @@ func Test_makeResponse(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := makeResponse(tt.args.text, tt.args.nextCommand); !reflect.DeepEqual(got, tt.want) { + if got := makeResponse(tt.args.text, tt.args.nextCommand, tgbotapi.InlineKeyboardMarkup{}); !reflect.DeepEqual(got, tt.want) { t.Errorf("makeResponse() = %v, want %v", got, tt.want) } }) From b3faec317bac069a7833763a39b9dfa77df418e8 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Mon, 20 Nov 2023 23:28:34 +0100 Subject: [PATCH 05/19] Handle lectures with keyboard --- bot/bot.go | 58 +++++++++++++++++++++++++-- json/actions.json | 94 ++------------------------------------------ json/cdls.json | 39 +++++++++--------- model/controller.go | 58 --------------------------- model/description.go | 12 ------ model/model.go | 36 ++--------------- 6 files changed, 83 insertions(+), 214 deletions(-) diff --git a/bot/bot.go b/bot/bot.go index 8073414..0decf43 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -3,11 +3,15 @@ package bot import ( "fmt" "log" + "regexp" + "strconv" "strings" + "time" tgbotapi "github.com/musianisamuele/telegram-bot-api" "golang.org/x/exp/slices" + "github.com/csunibo/informabot/commands" "github.com/csunibo/informabot/model" "github.com/csunibo/informabot/utils" ) @@ -40,10 +44,58 @@ func run(bot *tgbotapi.BotAPI) { panic(err) } - msg := tgbotapi.NewMessage(update.CallbackQuery.Message.Chat.ID, update.CallbackQuery.Data) - if _, err := bot.Send(msg); err != nil { - panic(err) + callback_text := update.CallbackQuery.Data + + if strings.HasPrefix(callback_text, "lectures_") { + if strings.HasSuffix(callback_text, "_today") || strings.HasSuffix(callback_text, "_tomorrow") { + timeForLectures := time.Now() + + if strings.HasSuffix(callback_text, "_tomorrow") { + timeForLectures = timeForLectures.AddDate(0, 0, 1) + } + + yearRegex := regexp.MustCompile(`_y_(\d)_`) + year, _ := strconv.Atoi(yearRegex.FindString(callback_text)[3:4]) + + cdlKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] + + cdl := model.Cdls[cdlKey] + response, err := commands.GetTimeTable(cdl.Type, cdl.Name, cdl.Curriculum, year, timeForLectures) + if err != nil { + log.Printf("Error [TomorrowLecturesData]: %s\n", err) + } + + editConfig := tgbotapi.NewEditMessageText(int64(update.CallbackQuery.Message.Chat.ID), update.CallbackQuery.Message.MessageID, response) + editConfig.ParseMode = tgbotapi.ModeHTML + bot.Send(editConfig) + + } else if strings.Contains(callback_text, "_y_") { + rows := make([][]tgbotapi.InlineKeyboardButton, 2) + rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) + rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) + + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + editConfig := tgbotapi.NewEditMessageReplyMarkup(int64(update.CallbackQuery.Message.Chat.ID), update.CallbackQuery.Message.MessageID, keyboard) + bot.Send(editConfig) + } else { + years_nro := 3 + if strings.HasPrefix(callback_text, "lectures_lm") { + years_nro = 2 + } + rows := make([][]tgbotapi.InlineKeyboardButton, years_nro) + + i := 1 + for i <= years_nro { + row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s: %d^ anno", model.Cdls[strings.TrimPrefix(callback_text, "lectures_")].Course, i), fmt.Sprintf("%s_y_%d", callback_text, i))) + rows[i-1] = row + i++ + } + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + editConfig := tgbotapi.NewEditMessageReplyMarkup(int64(update.CallbackQuery.Message.Chat.ID), update.CallbackQuery.Message.MessageID, keyboard) + bot.Send(editConfig) + } } + continue } else if filterMessage(bot, update.Message) { continue diff --git a/json/actions.json b/json/actions.json index ca49834..6fad819 100644 --- a/json/actions.json +++ b/json/actions.json @@ -83,98 +83,10 @@ "text": "Lauree Triennali\n\nInformatica\n1\ufe0f\u20e3 1\u00b0 anno (Telegram, Discord)\n2\ufe0f\u20e3 2\u00b0 anno (Telegram, Discord)\n3\ufe0f\u20e3 3\u00b0 anno (Telegram, Discord)\n3\ufe0f\u20e3 Ex-3\u00b0 anno (Telegram, Discord)\n\ud83d\uddff Subreddit\n\nIngegneria Informatica\n1\ufe0f\u20e32\ufe0f\u20e33\ufe0f\u20e3 Tutti gli anni(Whatsapp)\n4\ufe0f\u20e3 Ex-3\u00b0 anno(Telegram)\n\nIngegneria e Science informatiche (Cesena)\n1\ufe0f\u20e32\ufe0f\u20e33\ufe0f\u20e3 Tutti gli anni(Whatsapp)\n\nInformatica per il Management\n1\ufe0f\u20e32\ufe0f\u20e33\ufe0f\u20e3 Tutti gli anni(Telegram)\n\nLauree Magistrali\n\nInformatica\n1\ufe0f\u20e32\ufe0f\u20e3 Tutti gli anni(Telegram)\n\nArtificial Intelligence\n1\ufe0f\u20e3 1\u00b0 anno (Telegram)\n2\ufe0f\u20e3 2\u00b0 anno (Telegram)\n\nIngegneria e Science informatiche (Cesena)\n1\ufe0f\u20e32\ufe0f\u20e3 Tutti gli anni (Whatsapp, Telegram)\n\n\ud83d\udd22 Tutti i gruppi Unibo" } }, - "lezionioggi": { - "type": "yearly", + "lezioni": { + "type": "buttonsLecture", "data": { - "description": "Orari lezioni di oggi (tuo anno)", - "command": "lezionioggi", - "noYear": "Questo non sembra essere un gruppo generale per nessun anno di corso. Per usare questo comando da qui, devi specificare /lezionioggi1, /lezionioggi2 o /lezionioggi3." - } - }, - "lezionioggi1": { - "type": "todayLectures", - "data": { - "description": "Orari lezioni di oggi (1\u00b0 anno)", - "course": { - "year": 1, - "type": "laurea", - "name": "informatica" - }, - "title": "Lezioni di oggi (1\u00b0 anno):\n", - "fallbackText": "Non ci sono lezioni oggi. SMETTILA DI PRESSARMI" - } - }, - "lezionioggi2": { - "type": "todayLectures", - "data": { - "description": "Orari lezioni di oggi (2\u00b0 anno)", - "course": { - "year": 2, - "type": "laurea", - "name": "informatica" - }, - "title": "Lezioni di oggi (2\u00b0 anno):\n", - "fallbackText": "Non ci sono lezioni oggi. SMETTILA DI PRESSARMI" - } - }, - "lezionioggi3": { - "type": "todayLectures", - "data": { - "description": "Orari lezioni di oggi (3\u00b0 anno)", - "course": { - "year": 3, - "type": "laurea", - "name": "informatica" - }, - "title": "Lezioni di oggi:\n", - "fallbackText": "Non ci sono lezioni oggi. SMETTILA DI PRESSARMI" - } - }, - "lezionidomani": { - "type": "yearly", - "data": { - "description": "Orari lezioni di domani (tuo anno)", - "command": "lezionidomani", - "noYear": "Questo non sembra essere un gruppo generale per nessun anno di corso. Per usare questo comando da qui, devi specificare /lezionidomani1, /lezionidomani2 o /lezionidomani3." - } - }, - "lezionidomani1": { - "type": "tomorrowLectures", - "data": { - "description": "Orari lezioni di domani (1\u00b0 anno)", - "course": { - "year": 1, - "type": "laurea", - "name": "informatica" - }, - "title": "Lezioni di domani (1\u00b0 anno):\n", - "fallbackText": "Non ci sono lezioni domani. SMETTILA DI PRESSARMI" - } - }, - "lezionidomani2": { - "type": "tomorrowLectures", - "data": { - "description": "Orari lezioni di domani (2\u00b0 anno)", - "course": { - "year": 2, - "type": "laurea", - "name": "informatica" - }, - "title": "Lezioni di domani (2\u00b0 anno):\n", - "fallbackText": "Non ci sono lezioni domani. SMETTILA DI PRESSARMI" - } - }, - "lezionidomani3": { - "type": "tomorrowLectures", - "data": { - "description": "Orari lezioni di domani (3\u00b0 anno)", - "course": { - "year": 3, - "type": "laurea", - "name": "informatica" - }, - "title": "Lezioni di domani (3\u00b0 anno):\n", - "fallbackText": "Non ci sono lezioni domani. SMETTILA DI PRESSARMI" + "description": "Orari lezioni" } }, "materiali": { diff --git a/json/cdls.json b/json/cdls.json index b8dc73a..d1c69d6 100644 --- a/json/cdls.json +++ b/json/cdls.json @@ -1,22 +1,25 @@ { - "informatica": { - "course": "Informatica" + "l_informatica": { + "course": "Informatica", + "type": "laurea", + "name": "informatica" }, - "lm_informatica": { - "course": "Informatica magistrale", - "curricula": [ - { - "name": "Tecniche del software", - "callback": "software_techniques" - }, - { - "name": "Informatica per il management", - "callback": "management" - }, - { - "name": "Sistemi e reti", - "callback": "systems_and_networks" - } - ] + "lm_informatica_software_techniques": { + "course": "Informatica magistrale - Tecniche del software", + "type": "magistrale", + "name": "informatica", + "curricula": "A58-000" + }, + "lm_informatica_management": { + "course": "Informatica magistrale - Informatica per il management", + "type": "magistrale", + "name": "informatica", + "curricula": "991-000" + }, + "lm_informatica_systems_and_networks": { + "course": "Informatica magistrale - Sistemi e reti", + "type": "magistrale", + "name": "informatica", + "curricula": "992-000" } } diff --git a/model/controller.go b/model/controller.go index 8aa6989..f4cee47 100644 --- a/model/controller.go +++ b/model/controller.go @@ -10,7 +10,6 @@ import ( tgbotapi "github.com/musianisamuele/telegram-bot-api" "golang.org/x/exp/slices" - "github.com/csunibo/informabot/commands" "github.com/csunibo/informabot/utils" ) @@ -126,71 +125,14 @@ func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Mess i := 0 for callback, cdl := range Cdls { - fmt.Println(callback, cdl.Course) row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(cdl.Course, fmt.Sprintf("lectures_%s", callback))) rows[i] = row i++ } - fmt.Println(rows) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) return makeResponseWithInlineKeyboard(keyboard) } -func (data YearlyData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse { - chatTitle := strings.ToLower(message.Chat.Title) - - // check if string contains the year number - if strings.Contains(chatTitle, "primo") || - strings.Contains(chatTitle, "first") { - return makeResponseWithNextCommand(data.Command + "1") - } else if strings.Contains(chatTitle, "secondo") || - strings.Contains(chatTitle, "second") { - return makeResponseWithNextCommand(data.Command + "2") - } else if strings.Contains(chatTitle, "terzo") || - strings.Contains(chatTitle, "third") { - return makeResponseWithNextCommand(data.Command + "3") - } else { - return makeResponseWithText(data.NoYear) - } -} - -func (data TodayLecturesData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { - - response, err := commands.GetTimeTable(data.Course.Type, data.Course.Name, data.Course.Curriculum, data.Course.Year, time.Now()) - if err != nil { - log.Printf("Error [TodayLecturesData]: %s\n", err) - return makeResponseWithText("Bot internal Error, contact developers") - } - - var msg string - if response != "" { - msg = data.Title + response - } else { - msg = data.FallbackText - } - - return makeResponseWithText(msg) -} - -func (data TomorrowLecturesData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { - tomorrowTime := time.Now().AddDate(0, 0, 1) - - response, err := commands.GetTimeTable(data.Course.Type, data.Course.Name, data.Course.Curriculum, data.Course.Year, tomorrowTime) - if err != nil { - log.Printf("Error [TomorrowLecturesData]: %s\n", err) - return makeResponseWithText("Bot internal Error, contact developers") - } - - var msg string - if response != "" { - msg = data.Title + response - } else { - msg = data.FallbackText - } - - return makeResponseWithText(msg) -} - func (data ListData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { resultText := data.Header diff --git a/model/description.go b/model/description.go index 1860360..5820b3e 100644 --- a/model/description.go +++ b/model/description.go @@ -16,22 +16,10 @@ func (d NotLookingForData) GetDescription() string { return d.Description } -func (d YearlyData) GetDescription() string { - return d.Description -} - func (d Lectures) GetDescription() string { return d.Description } -func (d TodayLecturesData) GetDescription() string { - return d.Description -} - -func (d TomorrowLecturesData) GetDescription() string { - return d.Description -} - func (d ListData) GetDescription() string { return d.Description } diff --git a/model/model.go b/model/model.go index 3050a37..ff819c9 100644 --- a/model/model.go +++ b/model/model.go @@ -27,12 +27,6 @@ func GetActionFromType(name string, commandType string) Action { data = NotLookingForData{} case "buttonsLecture": data = Lectures{} - case "yearly": - data = YearlyData{} - case "todayLectures": - data = TodayLecturesData{} - case "tomorrowLectures": - data = TomorrowLecturesData{} case "list": data = ListData{} case "luck": @@ -111,10 +105,10 @@ type Curriculum struct { // Recognized by a callback string type Cdl struct { - // Course name - Course string `json:"course"` - // Array of curricula, each curriculum has a callback too - Curricula []Curriculum `json:"curricula"` + Course string `json:"course"` // Course title + Name string `json:"name"` // Course name + Type string `json:"type"` // Type (laurea|magistrale) + Curriculum string `json:"curricula"` // Curriculum } // SECTION ACTION STRUCTS DATA @@ -146,28 +140,6 @@ type Lectures struct { Description string `json:"description"` } -type YearlyData struct { - Description string `json:"description"` - Command string `json:"command"` - NoYear string `json:"noYear"` -} - -type CourseId struct { - Type string `json:"type"` - Name string `json:"name"` - Year int `json:"year"` - Curriculum string `json:"curriculum"` -} - -type TodayLecturesData struct { - Description string `json:"description"` - Course CourseId `json:"course"` - Title string `json:"title"` - FallbackText string `json:"fallbackText"` -} - -type TomorrowLecturesData TodayLecturesData - type ListData struct { Description string `json:"description"` Header string `json:"header"` From 5a8d83b35cd93ae946ac6311f6d65ef67feb3786 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 21 Nov 2023 17:14:24 +0100 Subject: [PATCH 06/19] Use `callbacks.go` module --- bot/bot.go | 52 +------------------------- bot/callbacks.go | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 51 deletions(-) create mode 100644 bot/callbacks.go diff --git a/bot/bot.go b/bot/bot.go index 0decf43..be6f8bc 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -3,15 +3,11 @@ package bot import ( "fmt" "log" - "regexp" - "strconv" "strings" - "time" tgbotapi "github.com/musianisamuele/telegram-bot-api" "golang.org/x/exp/slices" - "github.com/csunibo/informabot/commands" "github.com/csunibo/informabot/model" "github.com/csunibo/informabot/utils" ) @@ -47,53 +43,7 @@ func run(bot *tgbotapi.BotAPI) { callback_text := update.CallbackQuery.Data if strings.HasPrefix(callback_text, "lectures_") { - if strings.HasSuffix(callback_text, "_today") || strings.HasSuffix(callback_text, "_tomorrow") { - timeForLectures := time.Now() - - if strings.HasSuffix(callback_text, "_tomorrow") { - timeForLectures = timeForLectures.AddDate(0, 0, 1) - } - - yearRegex := regexp.MustCompile(`_y_(\d)_`) - year, _ := strconv.Atoi(yearRegex.FindString(callback_text)[3:4]) - - cdlKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] - - cdl := model.Cdls[cdlKey] - response, err := commands.GetTimeTable(cdl.Type, cdl.Name, cdl.Curriculum, year, timeForLectures) - if err != nil { - log.Printf("Error [TomorrowLecturesData]: %s\n", err) - } - - editConfig := tgbotapi.NewEditMessageText(int64(update.CallbackQuery.Message.Chat.ID), update.CallbackQuery.Message.MessageID, response) - editConfig.ParseMode = tgbotapi.ModeHTML - bot.Send(editConfig) - - } else if strings.Contains(callback_text, "_y_") { - rows := make([][]tgbotapi.InlineKeyboardButton, 2) - rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) - rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) - - keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) - editConfig := tgbotapi.NewEditMessageReplyMarkup(int64(update.CallbackQuery.Message.Chat.ID), update.CallbackQuery.Message.MessageID, keyboard) - bot.Send(editConfig) - } else { - years_nro := 3 - if strings.HasPrefix(callback_text, "lectures_lm") { - years_nro = 2 - } - rows := make([][]tgbotapi.InlineKeyboardButton, years_nro) - - i := 1 - for i <= years_nro { - row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s: %d^ anno", model.Cdls[strings.TrimPrefix(callback_text, "lectures_")].Course, i), fmt.Sprintf("%s_y_%d", callback_text, i))) - rows[i-1] = row - i++ - } - keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) - editConfig := tgbotapi.NewEditMessageReplyMarkup(int64(update.CallbackQuery.Message.Chat.ID), update.CallbackQuery.Message.MessageID, keyboard) - bot.Send(editConfig) - } + lecturesCallback(bot, &update, callback_text) } continue diff --git a/bot/callbacks.go b/bot/callbacks.go new file mode 100644 index 0000000..5c0e7e1 --- /dev/null +++ b/bot/callbacks.go @@ -0,0 +1,95 @@ +package bot + +import ( + "fmt" + "log" + "regexp" + "strconv" + "strings" + "time" + + tgbotapi "github.com/musianisamuele/telegram-bot-api" + + "github.com/csunibo/informabot/commands" + "github.com/csunibo/informabot/model" +) + +// Handle the callback for the lectures command (`/lezioni`) +// Parse the `callback_text` to check which operation it must to do: +// - If the string ends with "_today" or "_tomorrow" it returns the timetable +// - If the string just contains a "_y_" it asks for today or tomorrow +// - Otherwise prints the course year of what timetable the user wants to see +func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_text string) { + var chatId = int64(update.CallbackQuery.Message.Chat.ID) + var messageId = update.CallbackQuery.Message.MessageID + + if strings.HasSuffix(callback_text, "_today") || strings.HasSuffix(callback_text, "_tomorrow") { + timeForLectures := time.Now() + + if strings.HasSuffix(callback_text, "_tomorrow") { + timeForLectures = timeForLectures.AddDate(0, 0, 1) + } + + yearRegex, err := regexp.Compile(`_y_(\d)_`) + if err != nil { + log.Printf("Error [yearRegex]: %s\n", err) + return + } + + year, err := strconv.Atoi(yearRegex.FindString(callback_text)[3:4]) + if err != nil { + log.Printf("Error [convert to integer the year regex]: %s\n", err) + return + } + + cdlKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] + + cdl := model.Cdls[cdlKey] + response, err := commands.GetTimeTable(cdl.Type, cdl.Name, cdl.Curriculum, year, timeForLectures) + if err != nil { + log.Printf("Error [GetTimeTable]: %s\n", err) + } + + editConfig := tgbotapi.NewEditMessageText(chatId, messageId, response) + editConfig.ParseMode = tgbotapi.ModeHTML + _, err = bot.Send(editConfig) + if err != nil { + log.Printf("Error [bot.Send() for the NewEditMessageText]: %s\n", err) + } + } else if strings.Contains(callback_text, "_y_") { + rows := make([][]tgbotapi.InlineKeyboardButton, 2) + rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) + rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) + + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + editConfig := tgbotapi.NewEditMessageReplyMarkup(chatId, messageId, keyboard) + _, err := bot.Send(editConfig) + if err != nil { + log.Printf("Error [bot.Send() for the NewEditMessageReplyMarkup]: %s\n", err) + } + } else { + cdlName := strings.TrimPrefix(callback_text, "lectures_") + yearsNro := 3 + // Master degrees has a duration of only 2 years + if strings.HasPrefix(callback_text, "lectures_lm") { + yearsNro = 2 + } + rows := make([][]tgbotapi.InlineKeyboardButton, yearsNro) + + i := 1 + for i <= yearsNro { + buttonText := fmt.Sprintf("%s: %d^ anno", model.Cdls[cdlName].Course, i) + buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) + row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) + rows[i-1] = row + + i++ + } + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + editConfig := tgbotapi.NewEditMessageReplyMarkup(chatId, messageId, keyboard) + _, err := bot.Send(editConfig) + if err != nil { + log.Printf("Error [bot.Send() for the NewEditMessageReplyMarkup]: %s\n", err) + } + } +} From d9dc2c5cb68c6a7205e95b93905b9a8c5a29793a Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Tue, 21 Nov 2023 17:20:11 +0100 Subject: [PATCH 07/19] Avoid panic for `bot.Request()` error --- bot/bot.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/bot.go b/bot/bot.go index be6f8bc..4396629 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -37,7 +37,8 @@ func run(bot *tgbotapi.BotAPI) { if update.Message == nil { callback := tgbotapi.NewCallback(update.CallbackQuery.ID, update.CallbackQuery.Data) if _, err := bot.Request(callback); err != nil { - panic(err) + log.Printf("Error [bot.Request() for the callback]: %s\n", err) + continue } callback_text := update.CallbackQuery.Data From 9ee1aeb040b3c92c036da6a6633e3cbb2a806ffb Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Wed, 22 Nov 2023 16:29:49 +0100 Subject: [PATCH 08/19] Use `timetables.json` file --- bot/callbacks.go | 4 ++-- json/cdls.json | 25 ------------------------- model/controller.go | 4 ++-- model/globals.go | 4 ++-- model/model.go | 2 +- model/parse.go | 20 ++++++++++---------- 6 files changed, 17 insertions(+), 42 deletions(-) delete mode 100644 json/cdls.json diff --git a/bot/callbacks.go b/bot/callbacks.go index 5c0e7e1..e3bd09b 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -44,7 +44,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te cdlKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] - cdl := model.Cdls[cdlKey] + cdl := model.Timetables[cdlKey] response, err := commands.GetTimeTable(cdl.Type, cdl.Name, cdl.Curriculum, year, timeForLectures) if err != nil { log.Printf("Error [GetTimeTable]: %s\n", err) @@ -78,7 +78,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te i := 1 for i <= yearsNro { - buttonText := fmt.Sprintf("%s: %d^ anno", model.Cdls[cdlName].Course, i) + buttonText := fmt.Sprintf("%s: %d^ anno", model.Timetables[cdlName].Course, i) buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) rows[i-1] = row diff --git a/json/cdls.json b/json/cdls.json deleted file mode 100644 index d1c69d6..0000000 --- a/json/cdls.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "l_informatica": { - "course": "Informatica", - "type": "laurea", - "name": "informatica" - }, - "lm_informatica_software_techniques": { - "course": "Informatica magistrale - Tecniche del software", - "type": "magistrale", - "name": "informatica", - "curricula": "A58-000" - }, - "lm_informatica_management": { - "course": "Informatica magistrale - Informatica per il management", - "type": "magistrale", - "name": "informatica", - "curricula": "991-000" - }, - "lm_informatica_systems_and_networks": { - "course": "Informatica magistrale - Sistemi e reti", - "type": "magistrale", - "name": "informatica", - "curricula": "992-000" - } -} diff --git a/model/controller.go b/model/controller.go index f4cee47..4fe1a56 100644 --- a/model/controller.go +++ b/model/controller.go @@ -121,10 +121,10 @@ func (data NotLookingForData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbo } func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse { - rows := make([][]tgbotapi.InlineKeyboardButton, len(Cdls)) + rows := make([][]tgbotapi.InlineKeyboardButton, len(Timetables)) i := 0 - for callback, cdl := range Cdls { + for callback, cdl := range Timetables { row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(cdl.Course, fmt.Sprintf("lectures_%s", callback))) rows[i] = row i++ diff --git a/model/globals.go b/model/globals.go index ebe361d..9e631c1 100644 --- a/model/globals.go +++ b/model/globals.go @@ -19,7 +19,7 @@ var ( Settings SettingsStruct Teachings map[string]Teaching Groups GroupsStruct - Cdls map[string]Cdl + Timetables map[string]Timetable ) func InitGlobals() { @@ -59,7 +59,7 @@ func InitGlobals() { log.Fatalf("Error reading or creating groups.json file: %s", err.Error()) } - Cdls, err = ParseCdls() + Timetables, err = ParseTimetables() if err != nil { log.Fatalf(err.Error()) } diff --git a/model/model.go b/model/model.go index ff819c9..7f86a74 100644 --- a/model/model.go +++ b/model/model.go @@ -104,7 +104,7 @@ type Curriculum struct { } // Recognized by a callback string -type Cdl struct { +type Timetable struct { Course string `json:"course"` // Course title Name string `json:"name"` // Course name Type string `json:"type"` // Type (laurea|magistrale) diff --git a/model/parse.go b/model/parse.go index b163589..ee337d9 100644 --- a/model/parse.go +++ b/model/parse.go @@ -16,12 +16,12 @@ import ( ) const ( - jsonPath = "./json/" - groupsFile = "groups.json" - configSubpath = "config/" - degreesFile = "degrees.json" - teachingsFile = "teachings.json" - cdlFile = "cdls.json" + jsonPath = "./json/" + groupsFile = "groups.json" + configSubpath = "config/" + degreesFile = "degrees.json" + teachingsFile = "teachings.json" + timetablesFile = "timetables.json" ) func ParseAutoReplies() (autoReplies []AutoReply, err error) { @@ -208,15 +208,15 @@ func ParseOrCreateGroups() (GroupsStruct, error) { func SaveGroups(groups GroupsStruct) error { return utils.WriteJSONFile(groupsFile, groups) } -func ParseCdls() (cdls map[string]Cdl, err error) { - filepath := filepath.Join(jsonPath, cdlFile) +func ParseTimetables() (cdls map[string]Timetable, err error) { + filepath := filepath.Join(jsonPath, configSubpath, timetablesFile) file, err := os.Open(filepath) defer file.Close() if err != nil { - return nil, fmt.Errorf("error reading %s file: %w", cdlFile, err) + return nil, fmt.Errorf("error reading %s file: %w", timetablesFile, err) } - var mapData map[string]Cdl + var mapData map[string]Timetable err = json.NewDecoder(file).Decode(&mapData) if err != nil { From c564f015627ade7c8ba8d4c06fa0256a6ad5cae4 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Wed, 22 Nov 2023 18:37:16 +0100 Subject: [PATCH 09/19] Group chats auto select degree and/or year --- bot/callbacks.go | 10 +++---- model/controller.go | 65 ++++++++++++++++++++++++++++++++++++++------- model/globals.go | 2 +- model/model.go | 10 ++++--- model/parse.go | 4 +-- 5 files changed, 69 insertions(+), 22 deletions(-) diff --git a/bot/callbacks.go b/bot/callbacks.go index e3bd09b..c6889b3 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -42,10 +42,10 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te return } - cdlKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] + timetableKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] - cdl := model.Timetables[cdlKey] - response, err := commands.GetTimeTable(cdl.Type, cdl.Name, cdl.Curriculum, year, timeForLectures) + timetable := model.Timetables[timetableKey] + response, err := commands.GetTimeTable(timetable.Type, timetable.Name, timetable.Curriculum, year, timeForLectures) if err != nil { log.Printf("Error [GetTimeTable]: %s\n", err) } @@ -68,7 +68,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te log.Printf("Error [bot.Send() for the NewEditMessageReplyMarkup]: %s\n", err) } } else { - cdlName := strings.TrimPrefix(callback_text, "lectures_") + timetableName := strings.TrimPrefix(callback_text, "lectures_") yearsNro := 3 // Master degrees has a duration of only 2 years if strings.HasPrefix(callback_text, "lectures_lm") { @@ -78,7 +78,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te i := 1 for i <= yearsNro { - buttonText := fmt.Sprintf("%s: %d^ anno", model.Timetables[cdlName].Course, i) + buttonText := fmt.Sprintf("%s: %d^ anno", model.Timetables[timetableName].Course, i) buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) rows[i-1] = row diff --git a/model/controller.go b/model/controller.go index 4fe1a56..65aa27d 100644 --- a/model/controller.go +++ b/model/controller.go @@ -7,10 +7,9 @@ import ( "strings" "time" + "github.com/csunibo/informabot/utils" tgbotapi "github.com/musianisamuele/telegram-bot-api" "golang.org/x/exp/slices" - - "github.com/csunibo/informabot/utils" ) func (data MessageData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { @@ -121,16 +120,62 @@ func (data NotLookingForData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbo } func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse { - rows := make([][]tgbotapi.InlineKeyboardButton, len(Timetables)) + // Check if `chatId` is a valid group for a year. Used to auto-select some info + // for the `/lezioni` command. + var groupYear *Year = nil + for _, degree := range Degrees { + for _, year := range degree.Years { + if year.GroupId == message.Chat.ID { + println(year.Chat) + groupYear = &year + break + } + } + } - i := 0 - for callback, cdl := range Timetables { - row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(cdl.Course, fmt.Sprintf("lectures_%s", callback))) - rows[i] = row - i++ + if groupYear != nil { + if len(groupYear.Timetables) == 1 { + callback_text := fmt.Sprintf("lectures_%s_y_%d_", groupYear.Timetables[0], groupYear.Year) + rows := make([][]tgbotapi.InlineKeyboardButton, 2) + rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) + rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) + + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + return makeResponseWithInlineKeyboard(keyboard) + } else { + callback_text := fmt.Sprintf("lectures_%s", groupYear.Timetables[0]) + timetableName := strings.TrimPrefix(callback_text, "lectures_") + yearsNro := 3 + // Master degrees has a duration of only 2 years + if strings.HasPrefix(callback_text, "lectures_lm") { + yearsNro = 2 + } + rows := make([][]tgbotapi.InlineKeyboardButton, yearsNro) + + i := 1 + for i <= yearsNro { + buttonText := fmt.Sprintf("%s: %d^ anno", Timetables[timetableName].Course, i) + buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) + row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) + rows[i-1] = row + + i++ + } + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + return makeResponseWithInlineKeyboard(keyboard) + } + } else { + rows := make([][]tgbotapi.InlineKeyboardButton, len(Timetables)) + + i := 0 + for callback, timetable := range Timetables { + row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(timetable.Course, fmt.Sprintf("lectures_%s", callback))) + rows[i] = row + i++ + } + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + return makeResponseWithInlineKeyboard(keyboard) } - keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) - return makeResponseWithInlineKeyboard(keyboard) } func (data ListData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { diff --git a/model/globals.go b/model/globals.go index 9e631c1..54bbf5f 100644 --- a/model/globals.go +++ b/model/globals.go @@ -19,7 +19,7 @@ var ( Settings SettingsStruct Teachings map[string]Teaching Groups GroupsStruct - Timetables map[string]Timetable + Timetables map[string]Timetable ) func InitGlobals() { diff --git a/model/model.go b/model/model.go index 7f86a74..6f92e0e 100644 --- a/model/model.go +++ b/model/model.go @@ -83,9 +83,11 @@ type YearStudyDiagram struct { } type Year struct { - Year int64 `json:"year"` - Chat string `json:"chat"` - Teachings YearStudyDiagram `json:"teachings"` + Year int64 `json:"year"` + Chat string `json:"chat"` + Teachings YearStudyDiagram `json:"teachings"` + GroupId int64 `json:"group"` + Timetables []string `json:"timetables"` } type Degree struct { @@ -96,7 +98,7 @@ type Degree struct { Chat string `json:"chat"` } -// cdls.json +// timetables.json type Curriculum struct { Name string `json:"name"` diff --git a/model/parse.go b/model/parse.go index ee337d9..d330e01 100644 --- a/model/parse.go +++ b/model/parse.go @@ -208,7 +208,7 @@ func ParseOrCreateGroups() (GroupsStruct, error) { func SaveGroups(groups GroupsStruct) error { return utils.WriteJSONFile(groupsFile, groups) } -func ParseTimetables() (cdls map[string]Timetable, err error) { +func ParseTimetables() (timetables map[string]Timetable, err error) { filepath := filepath.Join(jsonPath, configSubpath, timetablesFile) file, err := os.Open(filepath) defer file.Close() @@ -223,6 +223,6 @@ func ParseTimetables() (cdls map[string]Timetable, err error) { return nil, fmt.Errorf("error parsing %s file: %w", filepath, err) } - cdls = mapData + timetables = mapData return } From fb948b8d57a37e3797671b703c8f81af85fd2e6c Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Wed, 22 Nov 2023 21:02:42 +0100 Subject: [PATCH 10/19] Use a `timetable` module for common functions --- bot/callbacks.go | 23 ++----------------- model/controller.go | 34 ++++++---------------------- model/timetables.go | 55 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 48 deletions(-) create mode 100644 model/timetables.go diff --git a/bot/callbacks.go b/bot/callbacks.go index c6889b3..c700929 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -1,7 +1,6 @@ package bot import ( - "fmt" "log" "regexp" "strconv" @@ -57,10 +56,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te log.Printf("Error [bot.Send() for the NewEditMessageText]: %s\n", err) } } else if strings.Contains(callback_text, "_y_") { - rows := make([][]tgbotapi.InlineKeyboardButton, 2) - rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) - rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) - + rows := model.ChooseTimetableDay(callback_text) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) editConfig := tgbotapi.NewEditMessageReplyMarkup(chatId, messageId, keyboard) _, err := bot.Send(editConfig) @@ -69,22 +65,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te } } else { timetableName := strings.TrimPrefix(callback_text, "lectures_") - yearsNro := 3 - // Master degrees has a duration of only 2 years - if strings.HasPrefix(callback_text, "lectures_lm") { - yearsNro = 2 - } - rows := make([][]tgbotapi.InlineKeyboardButton, yearsNro) - - i := 1 - for i <= yearsNro { - buttonText := fmt.Sprintf("%s: %d^ anno", model.Timetables[timetableName].Course, i) - buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) - row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) - rows[i-1] = row - - i++ - } + rows := model.GetLectureYears(callback_text, model.Timetables[timetableName].Course) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) editConfig := tgbotapi.NewEditMessageReplyMarkup(chatId, messageId, keyboard) _, err := bot.Send(editConfig) diff --git a/model/controller.go b/model/controller.go index 65aa27d..62e9ce1 100644 --- a/model/controller.go +++ b/model/controller.go @@ -136,43 +136,23 @@ func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Mess if groupYear != nil { if len(groupYear.Timetables) == 1 { callback_text := fmt.Sprintf("lectures_%s_y_%d_", groupYear.Timetables[0], groupYear.Year) - rows := make([][]tgbotapi.InlineKeyboardButton, 2) - rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) - rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) + rows := ChooseTimetableDay(callback_text) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) return makeResponseWithInlineKeyboard(keyboard) } else { - callback_text := fmt.Sprintf("lectures_%s", groupYear.Timetables[0]) - timetableName := strings.TrimPrefix(callback_text, "lectures_") - yearsNro := 3 - // Master degrees has a duration of only 2 years - if strings.HasPrefix(callback_text, "lectures_lm") { - yearsNro = 2 - } - rows := make([][]tgbotapi.InlineKeyboardButton, yearsNro) - - i := 1 - for i <= yearsNro { - buttonText := fmt.Sprintf("%s: %d^ anno", Timetables[timetableName].Course, i) - buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) - row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) - rows[i-1] = row + timetables := make(map[string]Timetable, len(groupYear.Timetables)) + for _, t := range groupYear.Timetables { + timetables[t] = Timetables[t] - i++ } + rows := GetTimetableCoursesRows(&timetables) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) return makeResponseWithInlineKeyboard(keyboard) - } - } else { - rows := make([][]tgbotapi.InlineKeyboardButton, len(Timetables)) - i := 0 - for callback, timetable := range Timetables { - row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(timetable.Course, fmt.Sprintf("lectures_%s", callback))) - rows[i] = row - i++ } + } else { + rows := GetTimetableCoursesRows(&Timetables) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) return makeResponseWithInlineKeyboard(keyboard) } diff --git a/model/timetables.go b/model/timetables.go new file mode 100644 index 0000000..e543847 --- /dev/null +++ b/model/timetables.go @@ -0,0 +1,55 @@ +package model + +import ( + "fmt" + "strings" + + tgbotapi "github.com/musianisamuele/telegram-bot-api" +) + +type InlineKeyboardRows [][]tgbotapi.InlineKeyboardButton + +// Returns a group of button rows for a selected groups on `timetables` +func GetTimetableCoursesRows(timetables *map[string]Timetable) InlineKeyboardRows { + rows := make([][]tgbotapi.InlineKeyboardButton, len(*timetables)) + + i := 0 + for callback, timetable := range *timetables { + row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(timetable.Course, fmt.Sprintf("lectures_%s", callback))) + rows[i] = row + i++ + } + + return rows +} + +// Returns buttons which permits to choose the day for the timetable +func ChooseTimetableDay(callback_text string) InlineKeyboardRows { + rows := make([][]tgbotapi.InlineKeyboardButton, 2) + rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) + rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) + + return rows +} + +// Returns a group of buttons rows for the available years of a `course` +func GetLectureYears(callback_text string, course string) InlineKeyboardRows { + yearsNro := 3 + // Master degrees has a duration of only 2 years + if strings.HasPrefix(callback_text, "lectures_lm") { + yearsNro = 2 + } + rows := make([][]tgbotapi.InlineKeyboardButton, yearsNro) + + i := 1 + for i <= yearsNro { + buttonText := fmt.Sprintf("%s: %d^ anno", course, i) + buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) + row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) + rows[i-1] = row + + i++ + } + + return rows +} From f1fd36b0814f355c040d33565819a309252a3cc9 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Wed, 22 Nov 2023 22:52:08 +0100 Subject: [PATCH 11/19] Add `Url` field on timetable --- bot/callbacks.go | 2 +- commands/uni.go | 4 +++- commands/uni_test.go | 5 +++-- model/model.go | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/bot/callbacks.go b/bot/callbacks.go index c700929..10e2418 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -44,7 +44,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te timetableKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] timetable := model.Timetables[timetableKey] - response, err := commands.GetTimeTable(timetable.Type, timetable.Name, timetable.Curriculum, year, timeForLectures) + response, err := commands.GetTimeTable(timetable.Type, timetable.Name, timetable.Curriculum, timetable.Url, year, timeForLectures) if err != nil { log.Printf("Error [GetTimeTable]: %s\n", err) } diff --git a/commands/uni.go b/commands/uni.go index 128ffb5..ecc74b2 100644 --- a/commands/uni.go +++ b/commands/uni.go @@ -50,9 +50,11 @@ func (t *LezioniTime) UnmarshalJSON(data []byte) error { // GetTimeTable returns an HTML string containing the timetable for the given // course on the given date. Returns an empty string if there are no lessons. -func GetTimeTable(courseType, courseName string, curriculum string, year int, day time.Time) (string, error) { +func GetTimeTable(courseType, courseName string, curriculum string, url string, year int, day time.Time) (string, error) { interval := &timetable.Interval{Start: day, End: day} + // FIXME: after the edit on unibo-go the line below will be valid + // events, err := timetable.FetchTimetable(courseType, courseName, curriculum, url, year, interval) events, err := timetable.FetchTimetable(courseType, courseName, curriculum, year, interval) if err != nil { log.Printf("Error getting timetable: %s\n", err) diff --git a/commands/uni_test.go b/commands/uni_test.go index e0c2507..ecc33db 100644 --- a/commands/uni_test.go +++ b/commands/uni_test.go @@ -107,6 +107,7 @@ func TestGetTimeTable(t *testing.T) { year int day time.Time curriculum string + url string } tests := []struct { name string @@ -146,7 +147,7 @@ func TestGetTimeTable(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetTimeTable(tt.args.courseType, tt.args.courseName, tt.args.curriculum, tt.args.year, tt.args.day) + got, err := GetTimeTable(tt.args.courseType, tt.args.courseName, tt.args.curriculum, tt.args.url, tt.args.year, tt.args.day) if err != nil && !tt.error { t.Errorf("GetTimeTable() error = %v", err) return @@ -164,7 +165,7 @@ func TestGetTimeTable(t *testing.T) { func TestWeekend(t *testing.T) { date := time.Date(2023, 3, 11, 0, 0, 0, 0, time.UTC) - result, err := GetTimeTable("laurea", "informatica", "", 1, date) + result, err := GetTimeTable("laurea", "informatica", "", "orario-lezioni", 1, date) if err != nil { t.Fatalf("Error while getting timetable: %s", err) } diff --git a/model/model.go b/model/model.go index 6f92e0e..466c890 100644 --- a/model/model.go +++ b/model/model.go @@ -109,8 +109,9 @@ type Curriculum struct { type Timetable struct { Course string `json:"course"` // Course title Name string `json:"name"` // Course name - Type string `json:"type"` // Type (laurea|magistrale) + Type string `json:"type"` // Type (laurea|magistrale|2cycle) Curriculum string `json:"curricula"` // Curriculum + Url string `json:"url"` } // SECTION ACTION STRUCTS DATA From 466254900fe98149947a8020c417273e662eefbf Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Wed, 22 Nov 2023 23:06:07 +0100 Subject: [PATCH 12/19] wip --- bot/callbacks.go | 4 ++++ model/controller.go | 5 ++--- model/model.go | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/bot/callbacks.go b/bot/callbacks.go index 10e2418..e31e7d5 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -51,6 +51,10 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te editConfig := tgbotapi.NewEditMessageText(chatId, messageId, response) editConfig.ParseMode = tgbotapi.ModeHTML + + if response == "" { + editConfig = tgbotapi.NewEditMessageText(chatId, messageId, "Non ci sono lezioni in questo giorno. SMETTILA DI PRESSARMI") + } _, err = bot.Send(editConfig) if err != nil { log.Printf("Error [bot.Send() for the NewEditMessageText]: %s\n", err) diff --git a/model/controller.go b/model/controller.go index 62e9ce1..0ec0b07 100644 --- a/model/controller.go +++ b/model/controller.go @@ -7,9 +7,10 @@ import ( "strings" "time" - "github.com/csunibo/informabot/utils" tgbotapi "github.com/musianisamuele/telegram-bot-api" "golang.org/x/exp/slices" + + "github.com/csunibo/informabot/utils" ) func (data MessageData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { @@ -126,7 +127,6 @@ func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Mess for _, degree := range Degrees { for _, year := range degree.Years { if year.GroupId == message.Chat.ID { - println(year.Chat) groupYear = &year break } @@ -149,7 +149,6 @@ func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Mess rows := GetTimetableCoursesRows(&timetables) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) return makeResponseWithInlineKeyboard(keyboard) - } } else { rows := GetTimetableCoursesRows(&Timetables) diff --git a/model/model.go b/model/model.go index 466c890..dffa7dd 100644 --- a/model/model.go +++ b/model/model.go @@ -140,7 +140,7 @@ type NotLookingForData struct { } type Lectures struct { - Description string `json:"description"` + Description string `json:"description"` } type ListData struct { From bc5e5205745768d12a397eb16d7c695da577581b Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Thu, 23 Nov 2023 08:25:45 +0100 Subject: [PATCH 13/19] Fix gofmt --- model/model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/model.go b/model/model.go index dffa7dd..466c890 100644 --- a/model/model.go +++ b/model/model.go @@ -140,7 +140,7 @@ type NotLookingForData struct { } type Lectures struct { - Description string `json:"description"` + Description string `json:"description"` } type ListData struct { From fc4e4837f9ddd6c147257273b2381ff774bab81f Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Thu, 23 Nov 2023 10:26:26 +0100 Subject: [PATCH 14/19] Show 7 days from now --- bot/callbacks.go | 16 ++++++++++++---- model/timetables.go | 18 +++++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/bot/callbacks.go b/bot/callbacks.go index e31e7d5..a8d67ec 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -22,13 +22,21 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te var chatId = int64(update.CallbackQuery.Message.Chat.ID) var messageId = update.CallbackQuery.Message.MessageID - if strings.HasSuffix(callback_text, "_today") || strings.HasSuffix(callback_text, "_tomorrow") { - timeForLectures := time.Now() + if strings.Contains(callback_text, "_day_") { + dayRegex, err := regexp.Compile(`_day_(\d+)`) + if err != nil { + log.Printf("Error [dayRegex]: %s\n", err) + return + } - if strings.HasSuffix(callback_text, "_tomorrow") { - timeForLectures = timeForLectures.AddDate(0, 0, 1) + unixTime, err := strconv.ParseInt(dayRegex.FindString(callback_text)[5:], 10, 64) + if err != nil { + log.Printf("Error [unixTime]: %s\n", err) + return } + timeForLectures := time.Unix(unixTime, 0) + yearRegex, err := regexp.Compile(`_y_(\d)_`) if err != nil { log.Printf("Error [yearRegex]: %s\n", err) diff --git a/model/timetables.go b/model/timetables.go index e543847..27c66f9 100644 --- a/model/timetables.go +++ b/model/timetables.go @@ -3,6 +3,7 @@ package model import ( "fmt" "strings" + "time" tgbotapi "github.com/musianisamuele/telegram-bot-api" ) @@ -25,9 +26,20 @@ func GetTimetableCoursesRows(timetables *map[string]Timetable) InlineKeyboardRow // Returns buttons which permits to choose the day for the timetable func ChooseTimetableDay(callback_text string) InlineKeyboardRows { - rows := make([][]tgbotapi.InlineKeyboardButton, 2) - rows[0] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Oggi", fmt.Sprintf("%s_today", callback_text))) - rows[1] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Domani", fmt.Sprintf("%s_tomorrow", callback_text))) + rows := make([][]tgbotapi.InlineKeyboardButton, 7) + var weekdays = [7]string{ + "Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", + } + var months = [12]string{ + "Dicembre", "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", + } + + dt := time.Now() + + for day := 0; day < 7; day++ { + rows[day] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s %d %s", weekdays[dt.Weekday()], dt.Day(), months[dt.Month()]), fmt.Sprintf("%s_day_%d", callback_text, dt.Unix()))) + dt = dt.AddDate(0, 0, 1) + } return rows } From 3a5d53b377d31cc12cca47b4ea9d3211c8e182b1 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Thu, 23 Nov 2023 16:15:33 +0100 Subject: [PATCH 15/19] Remove autoselect --- bot/callbacks.go | 2 +- commands/uni.go | 4 +--- commands/uni_test.go | 5 ++--- model/controller.go | 37 +++---------------------------------- model/model.go | 9 +++------ 5 files changed, 10 insertions(+), 47 deletions(-) diff --git a/bot/callbacks.go b/bot/callbacks.go index a8d67ec..4e7ed56 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -52,7 +52,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te timetableKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] timetable := model.Timetables[timetableKey] - response, err := commands.GetTimeTable(timetable.Type, timetable.Name, timetable.Curriculum, timetable.Url, year, timeForLectures) + response, err := commands.GetTimeTable(timetable.Type, timetable.Name, timetable.Curriculum, year, timeForLectures) if err != nil { log.Printf("Error [GetTimeTable]: %s\n", err) } diff --git a/commands/uni.go b/commands/uni.go index ecc74b2..128ffb5 100644 --- a/commands/uni.go +++ b/commands/uni.go @@ -50,11 +50,9 @@ func (t *LezioniTime) UnmarshalJSON(data []byte) error { // GetTimeTable returns an HTML string containing the timetable for the given // course on the given date. Returns an empty string if there are no lessons. -func GetTimeTable(courseType, courseName string, curriculum string, url string, year int, day time.Time) (string, error) { +func GetTimeTable(courseType, courseName string, curriculum string, year int, day time.Time) (string, error) { interval := &timetable.Interval{Start: day, End: day} - // FIXME: after the edit on unibo-go the line below will be valid - // events, err := timetable.FetchTimetable(courseType, courseName, curriculum, url, year, interval) events, err := timetable.FetchTimetable(courseType, courseName, curriculum, year, interval) if err != nil { log.Printf("Error getting timetable: %s\n", err) diff --git a/commands/uni_test.go b/commands/uni_test.go index ecc33db..e0c2507 100644 --- a/commands/uni_test.go +++ b/commands/uni_test.go @@ -107,7 +107,6 @@ func TestGetTimeTable(t *testing.T) { year int day time.Time curriculum string - url string } tests := []struct { name string @@ -147,7 +146,7 @@ func TestGetTimeTable(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := GetTimeTable(tt.args.courseType, tt.args.courseName, tt.args.curriculum, tt.args.url, tt.args.year, tt.args.day) + got, err := GetTimeTable(tt.args.courseType, tt.args.courseName, tt.args.curriculum, tt.args.year, tt.args.day) if err != nil && !tt.error { t.Errorf("GetTimeTable() error = %v", err) return @@ -165,7 +164,7 @@ func TestGetTimeTable(t *testing.T) { func TestWeekend(t *testing.T) { date := time.Date(2023, 3, 11, 0, 0, 0, 0, time.UTC) - result, err := GetTimeTable("laurea", "informatica", "", "orario-lezioni", 1, date) + result, err := GetTimeTable("laurea", "informatica", "", 1, date) if err != nil { t.Fatalf("Error while getting timetable: %s", err) } diff --git a/model/controller.go b/model/controller.go index 0ec0b07..5a242b9 100644 --- a/model/controller.go +++ b/model/controller.go @@ -121,40 +121,9 @@ func (data NotLookingForData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbo } func (data Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse { - // Check if `chatId` is a valid group for a year. Used to auto-select some info - // for the `/lezioni` command. - var groupYear *Year = nil - for _, degree := range Degrees { - for _, year := range degree.Years { - if year.GroupId == message.Chat.ID { - groupYear = &year - break - } - } - } - - if groupYear != nil { - if len(groupYear.Timetables) == 1 { - callback_text := fmt.Sprintf("lectures_%s_y_%d_", groupYear.Timetables[0], groupYear.Year) - rows := ChooseTimetableDay(callback_text) - - keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) - return makeResponseWithInlineKeyboard(keyboard) - } else { - timetables := make(map[string]Timetable, len(groupYear.Timetables)) - for _, t := range groupYear.Timetables { - timetables[t] = Timetables[t] - - } - rows := GetTimetableCoursesRows(&timetables) - keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) - return makeResponseWithInlineKeyboard(keyboard) - } - } else { - rows := GetTimetableCoursesRows(&Timetables) - keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) - return makeResponseWithInlineKeyboard(keyboard) - } + rows := GetTimetableCoursesRows(&Timetables) + keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) + return makeResponseWithInlineKeyboard(keyboard) } func (data ListData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse { diff --git a/model/model.go b/model/model.go index 466c890..73e17e0 100644 --- a/model/model.go +++ b/model/model.go @@ -83,11 +83,9 @@ type YearStudyDiagram struct { } type Year struct { - Year int64 `json:"year"` - Chat string `json:"chat"` - Teachings YearStudyDiagram `json:"teachings"` - GroupId int64 `json:"group"` - Timetables []string `json:"timetables"` + Year int64 `json:"year"` + Chat string `json:"chat"` + Teachings YearStudyDiagram `json:"teachings"` } type Degree struct { @@ -111,7 +109,6 @@ type Timetable struct { Name string `json:"name"` // Course name Type string `json:"type"` // Type (laurea|magistrale|2cycle) Curriculum string `json:"curricula"` // Curriculum - Url string `json:"url"` } // SECTION ACTION STRUCTS DATA From bde33a8d7b3217fa644d31184e702a5928b0ae70 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Fri, 24 Nov 2023 11:27:59 +0100 Subject: [PATCH 16/19] Add tests --- model/timetables_test.go | 151 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 model/timetables_test.go diff --git a/model/timetables_test.go b/model/timetables_test.go new file mode 100644 index 0000000..70b2d52 --- /dev/null +++ b/model/timetables_test.go @@ -0,0 +1,151 @@ +package model + +import ( + "fmt" + "testing" + "time" + + tgbotapi "github.com/musianisamuele/telegram-bot-api" +) + +func TestGetTimetableCoursesRows(t *testing.T) { + var timetables = map[string]Timetable{ + "l_informatica": { + Course: "Informatica", + Type: "laurea", + Name: "informatica", + }, + "lm_informatica_software_techniques": { + Course: "Informatica Magistrale - Tecniche del software", + Type: "magistrale", + Name: "informatica", + Curriculum: "A58-000", + }, + } + + type args struct { + data map[string]Timetable + } + tests := []struct { + name string + args args + want InlineKeyboardRows + }{ + { + name: "All the timetables from the map", + args: args{data: timetables}, + want: InlineKeyboardRows{ + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica", "lectures_l_informatica")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale - Tecniche del software", "lectures_lm_informatica_software_techniques")), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got InlineKeyboardRows = GetTimetableCoursesRows(&tt.args.data) + if len(got) != len(tt.want) { + t.Errorf("GetTimetableCoursesRows() = %v, want %v", got, tt.want) + } else { + for i, v := range got { + for j, w := range v { + if w.Text != tt.want[i][j].Text || *w.CallbackData != *tt.want[i][j].CallbackData { + t.Errorf("GetTimetableCoursesRows() = %v, want %v", w, tt.want[i][j]) + } + } + } + } + }) + } + +} + +func TestChooseTimetableDay(t *testing.T) { + dt := time.Now() + var weekdays = [7]string{ + "Domenica", "Lunedì", "Martedì", "Mercoledì", "Giovedì", "Venerdì", "Sabato", + } + var months = [12]string{ + "Dicembre", "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", + } + + type args struct { + data string + } + tests := []struct { + name string + args args + want InlineKeyboardRows + }{ + { + name: "Get lectures for the week", + args: args{data: "lectures_lm_informatica_software_techniques"}, + want: make([][]tgbotapi.InlineKeyboardButton, 7), + }, + } + + for day := 0; day < 7; day++ { + tests[0].want[day] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s %d %s", weekdays[dt.Weekday()], dt.Day(), months[dt.Month()]), fmt.Sprintf("%s_day_%d", "lectures_lm_informatica_software_techniques", dt.Unix()))) + dt = dt.AddDate(0, 0, 1) + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got InlineKeyboardRows = ChooseTimetableDay(tt.args.data) + if len(got) != len(tt.want) { + t.Errorf("ChooseTimetableDay() = %v, want %v", got, tt.want) + } else { + for i, v := range got { + for j, w := range v { + if w.Text != tt.want[i][j].Text || *w.CallbackData != *tt.want[i][j].CallbackData { + t.Errorf("ChooseTimetableDay() = %v, want %v", w, tt.want[i][j]) + } + } + } + } + }) + } +} + +func TestGetLectureYears(t *testing.T) { + type args struct { + data [2]string + } + tests := []struct { + name string + args args + want InlineKeyboardRows + }{ + { + name: "Get rows for bachelor's degree", + args: args{data: [2]string{"lectures_l_informatica", "Informatica"}}, + want: InlineKeyboardRows{ + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 1^ anno", "lectures_l_informatica_y_1")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 2^ anno", "lectures_l_informatica_y_2")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 3^ anno", "lectures_l_informatica_y_3")), + }, + }, + { + name: "Get rows for master's degree", + args: args{data: [2]string{"lectures_lm_informatica_software_techniques", "Informatica Magistrale"}}, + want: InlineKeyboardRows{ + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale: 1^ anno", "lectures_lm_informatica_software_techniques_y_1")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale: 2^ anno", "lectures_lm_informatica_software_techniques_y_2")), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var got InlineKeyboardRows = GetLectureYears(tt.args.data[0], tt.args.data[1]) + if len(got) != len(tt.want) { + t.Errorf("GetLectureYears() = %v, want %v", got, tt.want) + } else { + for i, v := range got { + for j, w := range v { + if w.Text != tt.want[i][j].Text || *w.CallbackData != *tt.want[i][j].CallbackData { + t.Errorf("GetLectureYears() = %v, want %v", w, tt.want[i][j]) + } + } + } + } + }) + } +} From 08cdc02169169d8e84f5a84d501dd9993268ba97 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Fri, 24 Nov 2023 22:06:25 +0100 Subject: [PATCH 17/19] Update config submodule --- json/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/json/config b/json/config index 3af261d..3cdafd3 160000 --- a/json/config +++ b/json/config @@ -1 +1 @@ -Subproject commit 3af261d47e23780abde419870c9f938703fc81e1 +Subproject commit 3cdafd3a34e1f5df05d8678bbd6636226f562b15 From 93248213022f9086a70c6ce6b1e8e33b4ab46733 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Mon, 27 Nov 2023 15:28:12 +0100 Subject: [PATCH 18/19] Manage title and fallbacktext Also, fix the mod for arrays of weekdays and months --- bot/bot.go | 2 +- bot/callbacks.go | 10 +++++++--- model/model.go | 13 ++++++++----- model/timetables.go | 2 +- model/timetables_test.go | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/bot/bot.go b/bot/bot.go index 4396629..0c3e7de 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -246,7 +246,7 @@ func executeCommand(bot *tgbotapi.BotAPI, update *tgbotapi.Update, commandIndex msg := tgbotapi.NewMessage(update.Message.Chat.ID, update.Message.Text) msg.ReplyMarkup = newCommand.Rows if _, err := bot.Send(msg); err != nil { - msg = tgbotapi.NewMessage(update.Message.Chat.ID, "Error retrieving lectures") + msg = tgbotapi.NewMessage(update.Message.Chat.ID, "Error sending data") bot.Send(msg) } } diff --git a/bot/callbacks.go b/bot/callbacks.go index 4e7ed56..5ea0666 100644 --- a/bot/callbacks.go +++ b/bot/callbacks.go @@ -1,6 +1,7 @@ package bot import ( + "fmt" "log" "regexp" "strconv" @@ -57,12 +58,15 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te log.Printf("Error [GetTimeTable]: %s\n", err) } + if response == "" { + response = timetable.FallbackText + } else { + response = fmt.Sprintf(timetable.Title, timetable.Course, year, timeForLectures.Format("2006-01-02")) + "\n\n" + response + } + editConfig := tgbotapi.NewEditMessageText(chatId, messageId, response) editConfig.ParseMode = tgbotapi.ModeHTML - if response == "" { - editConfig = tgbotapi.NewEditMessageText(chatId, messageId, "Non ci sono lezioni in questo giorno. SMETTILA DI PRESSARMI") - } _, err = bot.Send(editConfig) if err != nil { log.Printf("Error [bot.Send() for the NewEditMessageText]: %s\n", err) diff --git a/model/model.go b/model/model.go index 7df1f1c..e1c49f2 100644 --- a/model/model.go +++ b/model/model.go @@ -112,10 +112,12 @@ type Curriculum struct { // Recognized by a callback string type Timetable struct { - Course string `json:"course"` // Course title - Name string `json:"name"` // Course name - Type string `json:"type"` // Type (laurea|magistrale|2cycle) - Curriculum string `json:"curricula"` // Curriculum + Course string `json:"course"` // Course title + Name string `json:"name"` // Course name + Type string `json:"type"` // Type (laurea|magistrale|2cycle) + Curriculum string `json:"curricula"` // Curriculum + Title string `json:"title"` + FallbackText string `json:"fallbackText"` } // SECTION ACTION STRUCTS DATA @@ -150,7 +152,8 @@ type NotLookingForData struct { } type Lectures struct { - Description string `json:"description"` + Description string `json:"description"` + FallbackText string `json:"fallbackText"` } type ListData struct { diff --git a/model/timetables.go b/model/timetables.go index 27c66f9..4138e97 100644 --- a/model/timetables.go +++ b/model/timetables.go @@ -37,7 +37,7 @@ func ChooseTimetableDay(callback_text string) InlineKeyboardRows { dt := time.Now() for day := 0; day < 7; day++ { - rows[day] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s %d %s", weekdays[dt.Weekday()], dt.Day(), months[dt.Month()]), fmt.Sprintf("%s_day_%d", callback_text, dt.Unix()))) + rows[day] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s %d %s", weekdays[dt.Weekday()%7], dt.Day(), months[dt.Month()%12]), fmt.Sprintf("%s_day_%d", callback_text, dt.Unix()))) dt = dt.AddDate(0, 0, 1) } diff --git a/model/timetables_test.go b/model/timetables_test.go index 70b2d52..36553d5 100644 --- a/model/timetables_test.go +++ b/model/timetables_test.go @@ -84,7 +84,7 @@ func TestChooseTimetableDay(t *testing.T) { } for day := 0; day < 7; day++ { - tests[0].want[day] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s %d %s", weekdays[dt.Weekday()], dt.Day(), months[dt.Month()]), fmt.Sprintf("%s_day_%d", "lectures_lm_informatica_software_techniques", dt.Unix()))) + tests[0].want[day] = tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("%s %d %s", weekdays[dt.Weekday()%7], dt.Day(), months[dt.Month()%12]), fmt.Sprintf("%s_day_%d", "lectures_lm_informatica_software_techniques", dt.Unix()))) dt = dt.AddDate(0, 0, 1) } for _, tt := range tests { From 22e8102886b3e1b2c5806de0222d72136f11cca5 Mon Sep 17 00:00:00 2001 From: Santo Cariotti Date: Mon, 27 Nov 2023 19:37:59 +0100 Subject: [PATCH 19/19] Use callback as interface in `model` module --- bot/bot.go | 17 +++++++++- json/actions.json | 4 ++- bot/callbacks.go => model/callback.go | 47 ++++++++++++++++++++++----- model/model.go | 2 ++ model/timetables.go | 2 +- model/timetables_test.go | 46 ++++++++++++++------------ 6 files changed, 87 insertions(+), 31 deletions(-) rename bot/callbacks.go => model/callback.go (57%) diff --git a/bot/bot.go b/bot/bot.go index 0c3e7de..35fabc6 100644 --- a/bot/bot.go +++ b/bot/bot.go @@ -44,7 +44,7 @@ func run(bot *tgbotapi.BotAPI) { callback_text := update.CallbackQuery.Data if strings.HasPrefix(callback_text, "lectures_") { - lecturesCallback(bot, &update, callback_text) + handleCallback(bot, &update, "lezioni", callback_text) } continue @@ -268,6 +268,21 @@ func handleAction(bot *tgbotapi.BotAPI, update *tgbotapi.Update, commandName str return false } +// Handle a callback searching a the good action +func handleCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, commandName string, callback_text string) bool { + idx := slices.IndexFunc(model.Actions, func(action model.Action) bool { + return action.Name == commandName + }) + + if idx != -1 { + model.Actions[idx].Data.HandleBotCallback(bot, update, callback_text) + + return true + } + + return false +} + func filterMessage(bot *tgbotapi.BotAPI, message *tgbotapi.Message) bool { if message.Dice != nil { // msg := tgbotapi.NewMessage(message.Chat.ID, "Found a dice") diff --git a/json/actions.json b/json/actions.json index 268d0c7..11e9b2b 100644 --- a/json/actions.json +++ b/json/actions.json @@ -94,7 +94,9 @@ "lezioni": { "type": "buttonsLecture", "data": { - "description": "Orari lezioni" + "description": "Orari lezioni", + "title": " Lezioni di %s di (%d\u00b0 anno) di giorno %s", + "fallbackText": "Non ci sono lezioni in questo giorno. SMETTILA DI PRESSARMI" } }, "materiali": { diff --git a/bot/callbacks.go b/model/callback.go similarity index 57% rename from bot/callbacks.go rename to model/callback.go index 5ea0666..ceeabaf 100644 --- a/bot/callbacks.go +++ b/model/callback.go @@ -1,4 +1,4 @@ -package bot +package model import ( "fmt" @@ -11,15 +11,34 @@ import ( tgbotapi "github.com/musianisamuele/telegram-bot-api" "github.com/csunibo/informabot/commands" - "github.com/csunibo/informabot/model" ) +func (_ MessageData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `MessageData`") +} + +func (_ HelpData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `HelpData`") +} + +func (_ IssueData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `IssueData`") +} + +func (_ LookingForData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `LookingForData`") +} + +func (_ NotLookingForData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `NotLookingForData`") +} + // Handle the callback for the lectures command (`/lezioni`) // Parse the `callback_text` to check which operation it must to do: // - If the string ends with "_today" or "_tomorrow" it returns the timetable // - If the string just contains a "_y_" it asks for today or tomorrow // - Otherwise prints the course year of what timetable the user wants to see -func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_text string) { +func (data Lectures) HandleBotCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_text string) { var chatId = int64(update.CallbackQuery.Message.Chat.ID) var messageId = update.CallbackQuery.Message.MessageID @@ -52,16 +71,16 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te timetableKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")] - timetable := model.Timetables[timetableKey] + timetable := Timetables[timetableKey] response, err := commands.GetTimeTable(timetable.Type, timetable.Name, timetable.Curriculum, year, timeForLectures) if err != nil { log.Printf("Error [GetTimeTable]: %s\n", err) } if response == "" { - response = timetable.FallbackText + response = data.FallbackText } else { - response = fmt.Sprintf(timetable.Title, timetable.Course, year, timeForLectures.Format("2006-01-02")) + "\n\n" + response + response = fmt.Sprintf(data.Title, timetable.Course, year, timeForLectures.Format("2006-01-02")) + "\n\n" + response } editConfig := tgbotapi.NewEditMessageText(chatId, messageId, response) @@ -72,7 +91,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te log.Printf("Error [bot.Send() for the NewEditMessageText]: %s\n", err) } } else if strings.Contains(callback_text, "_y_") { - rows := model.ChooseTimetableDay(callback_text) + rows := ChooseTimetableDay(callback_text) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) editConfig := tgbotapi.NewEditMessageReplyMarkup(chatId, messageId, keyboard) _, err := bot.Send(editConfig) @@ -81,7 +100,7 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te } } else { timetableName := strings.TrimPrefix(callback_text, "lectures_") - rows := model.GetLectureYears(callback_text, model.Timetables[timetableName].Course) + rows := GetLectureYears(callback_text, Timetables[timetableName].Course) keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...) editConfig := tgbotapi.NewEditMessageReplyMarkup(chatId, messageId, keyboard) _, err := bot.Send(editConfig) @@ -90,3 +109,15 @@ func lecturesCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_te } } } + +func (_ ListData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `ListData`") +} + +func (_ LuckData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `LuckData`") +} + +func (_ InvalidData) HandleBotCallback(_bot *tgbotapi.BotAPI, _udpate *tgbotapi.Update, _callback_text string) { + log.Printf("`HandleBotCallback` not defined for `InvalidData`") +} diff --git a/model/model.go b/model/model.go index e1c49f2..4e98815 100644 --- a/model/model.go +++ b/model/model.go @@ -9,6 +9,7 @@ import ( type DataInterface interface { HandleBotCommand(bot *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse + HandleBotCallback(bot *tgbotapi.BotAPI, update *tgbotapi.Update, callback_text string) GetDescription() string } @@ -153,6 +154,7 @@ type NotLookingForData struct { type Lectures struct { Description string `json:"description"` + Title string `json:"title"` FallbackText string `json:"fallbackText"` } diff --git a/model/timetables.go b/model/timetables.go index 4138e97..ed980b5 100644 --- a/model/timetables.go +++ b/model/timetables.go @@ -55,7 +55,7 @@ func GetLectureYears(callback_text string, course string) InlineKeyboardRows { i := 1 for i <= yearsNro { - buttonText := fmt.Sprintf("%s: %d^ anno", course, i) + buttonText := fmt.Sprintf("%s: %d\u00b0 anno", course, i) buttonCallback := fmt.Sprintf("%s_y_%d", callback_text, i) row := tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData(buttonText, buttonCallback)) rows[i-1] = row diff --git a/model/timetables_test.go b/model/timetables_test.go index 36553d5..d1e10c0 100644 --- a/model/timetables_test.go +++ b/model/timetables_test.go @@ -9,12 +9,16 @@ import ( ) func TestGetTimetableCoursesRows(t *testing.T) { - var timetables = map[string]Timetable{ + timetables := make([]map[string]Timetable, 2) + + timetables[0] = map[string]Timetable{ "l_informatica": { Course: "Informatica", Type: "laurea", Name: "informatica", }, + } + timetables[1] = map[string]Timetable{ "lm_informatica_software_techniques": { Course: "Informatica Magistrale - Tecniche del software", Type: "magistrale", @@ -22,34 +26,36 @@ func TestGetTimetableCoursesRows(t *testing.T) { Curriculum: "A58-000", }, } + wants := make([]InlineKeyboardRows, 2) + wants[0] = InlineKeyboardRows{tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica", "lectures_l_informatica"))} + wants[1] = InlineKeyboardRows{tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale - Tecniche del software", "lectures_lm_informatica_software_techniques"))} type args struct { - data map[string]Timetable + data []map[string]Timetable } tests := []struct { name string args args - want InlineKeyboardRows + want []InlineKeyboardRows }{ { name: "All the timetables from the map", args: args{data: timetables}, - want: InlineKeyboardRows{ - tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica", "lectures_l_informatica")), - tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale - Tecniche del software", "lectures_lm_informatica_software_techniques")), - }, + want: wants, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - var got InlineKeyboardRows = GetTimetableCoursesRows(&tt.args.data) - if len(got) != len(tt.want) { - t.Errorf("GetTimetableCoursesRows() = %v, want %v", got, tt.want) - } else { - for i, v := range got { - for j, w := range v { - if w.Text != tt.want[i][j].Text || *w.CallbackData != *tt.want[i][j].CallbackData { - t.Errorf("GetTimetableCoursesRows() = %v, want %v", w, tt.want[i][j]) + for idx, timetable := range tt.args.data { + var got InlineKeyboardRows = GetTimetableCoursesRows(&timetable) + if len(got) != len(tt.want[idx]) { + t.Errorf("GetTimetableCoursesRows() = %v, want %v", got, tt.want) + } else { + for i, v := range got { + for j, w := range v { + if w.Text != tt.want[idx][i][j].Text || *w.CallbackData != *tt.want[idx][i][j].CallbackData { + t.Errorf("GetTimetableCoursesRows() = %v, want %v", w, tt.want[idx][i][j]) + } } } } @@ -118,17 +124,17 @@ func TestGetLectureYears(t *testing.T) { name: "Get rows for bachelor's degree", args: args{data: [2]string{"lectures_l_informatica", "Informatica"}}, want: InlineKeyboardRows{ - tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 1^ anno", "lectures_l_informatica_y_1")), - tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 2^ anno", "lectures_l_informatica_y_2")), - tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 3^ anno", "lectures_l_informatica_y_3")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 1\u00b0 anno", "lectures_l_informatica_y_1")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 2\u00b0 anno", "lectures_l_informatica_y_2")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica: 3\u00b0 anno", "lectures_l_informatica_y_3")), }, }, { name: "Get rows for master's degree", args: args{data: [2]string{"lectures_lm_informatica_software_techniques", "Informatica Magistrale"}}, want: InlineKeyboardRows{ - tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale: 1^ anno", "lectures_lm_informatica_software_techniques_y_1")), - tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale: 2^ anno", "lectures_lm_informatica_software_techniques_y_2")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale: 1\u00b0 anno", "lectures_lm_informatica_software_techniques_y_1")), + tgbotapi.NewInlineKeyboardRow(tgbotapi.NewInlineKeyboardButtonData("Informatica Magistrale: 2\u00b0 anno", "lectures_lm_informatica_software_techniques_y_2")), }, }, }