Skip to content

Commit

Permalink
Lezioni per generici corsi di laurea (#138)
Browse files Browse the repository at this point in the history
* Fix `GetTimeTable` fetching curriculum

* wip

* Cdls JSON file for lectures

* Fix tests

* Handle lectures with keyboard

* Use `callbacks.go` module

* Avoid panic for `bot.Request()` error

* Use `timetables.json` file

* Group chats auto select degree and/or year

* Use a `timetable` module for common functions

* Add `Url` field on timetable

* wip

* Fix gofmt

* Show 7 days from now

* Remove autoselect

* Add tests

* Update config submodule

* Manage title and fallbacktext

Also, fix the mod for arrays of weekdays and months

* Use callback as interface in `model` module
  • Loading branch information
boozec authored Nov 27, 2023
1 parent 6dcb98d commit 140c84f
Show file tree
Hide file tree
Showing 14 changed files with 474 additions and 193 deletions.
36 changes: 36 additions & 0 deletions bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ 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 {
log.Printf("Error [bot.Request() for the callback]: %s\n", err)
continue
}

callback_text := update.CallbackQuery.Data

if strings.HasPrefix(callback_text, "lectures_") {
handleCallback(bot, &update, "lezioni", callback_text)
}

continue
} else if filterMessage(bot, update.Message) {
continue
Expand Down Expand Up @@ -229,6 +241,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 sending data")
bot.Send(msg)
}
}
}
}

Expand All @@ -247,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")
Expand Down
4 changes: 2 additions & 2 deletions commands/uni.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions commands/uni_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func TestGetTimeTable(t *testing.T) {
courseName string
year int
day time.Time
curriculum string
}
tests := []struct {
name string
Expand Down Expand Up @@ -145,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
Expand All @@ -163,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)
}
Expand Down
96 changes: 5 additions & 91 deletions json/actions.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,98 +91,12 @@
"text": "<b>Lauree Triennali</b>\n\nInformatica\n1\ufe0f\u20e3 1\u00b0 anno (<a href='https://t.me/uniboinfo23'>Telegram</a>, <a href='https://discord.gg/8TWKqfVF'>Discord</a>)\n2\ufe0f\u20e3 2\u00b0 anno (<a href='https://t.me/uniboinfo2223'>Telegram</a>, <a href='https://discord.gg/2cK8ENJ2Vg'>Discord</a>)\n3\ufe0f\u20e3 3\u00b0 anno (<a href='https://t.me/joinchat/jTyZ03cqDRliOTY0'>Telegram</a>, <a href='https://discord.gg/cYFbnGEYAA'>Discord</a>)\n3\ufe0f\u20e3 Ex-3\u00b0 anno (<a href='https://t.me/informaticaunibo2021'>Telegram</a>, <a href='https://discord.gg/N7NJms8d7v'>Discord</a>)\n<a href='https://www.reddit.com/r/unibo_informatica'><b>\ud83d\uddff Subreddit</b></a>\n\nIngegneria Informatica\n1\ufe0f\u20e32\ufe0f\u20e33\ufe0f\u20e3 Tutti gli anni(<a href='https://chat.whatsapp.com/HB1Gp5ZlMzR9xIhgUwEkrA'>Whatsapp</a>)\n4\ufe0f\u20e3 Ex-3\u00b0 anno(<a href='https://t.me/+R7CwyJOvG0AMPRHy'>Telegram</a>)\n\nIngegneria e Science informatiche (Cesena)\n1\ufe0f\u20e32\ufe0f\u20e33\ufe0f\u20e3 Tutti gli anni(<a href='https://chat.whatsapp.com/EA4ndjtOnIa6d8xPwSYA6Q'>Whatsapp</a>)\n\nInformatica per il Management\n1\ufe0f\u20e32\ufe0f\u20e33\ufe0f\u20e3 Tutti gli anni(<a href='https://t.me/unibo_info_manag'>Telegram</a>)\n\n<b>Lauree Magistrali</b>\n\nInformatica\n1\ufe0f\u20e32\ufe0f\u20e3 Tutti gli anni(<a href='https://t.me/mag_informatica_unibo'>Telegram</a>)\n\nArtificial Intelligence\n1\ufe0f\u20e3 1\u00b0 anno (<a href='https://t.me/officialai2324'>Telegram</a>)\n2\ufe0f\u20e3 2\u00b0 anno (<a href='https://t.me/unibo_ai_2022'>Telegram</a>)\n\nIngegneria e Science informatiche (Cesena)\n1\ufe0f\u20e32\ufe0f\u20e3 Tutti gli anni (<a href='https://chat.whatsapp.com/BgwpLOTuBN543W4bjsOxnl'>Whatsapp</a>, <a href='https://t.me/joinchat/BMT7WBYtBSdduMJdhjs3Ew'>Telegram</a>)\n\n<a href='https://t.me/joinchat/4_SVcmVsRAo3MTlk'>\ud83d\udd22 Tutti i gruppi Unibo</a>"
}
},
"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": "<b>Lezioni di oggi (1\u00b0 anno):</b>\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": "<b>Lezioni di oggi (2\u00b0 anno):</b>\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": "<b>Lezioni di oggi:</b>\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": "<b>Lezioni di domani (1\u00b0 anno):</b>\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": "<b>Lezioni di domani (2\u00b0 anno):</b>\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": "<b>Lezioni di domani (3\u00b0 anno):</b>\n",
"fallbackText": "Non ci sono lezioni domani. SMETTILA DI PRESSARMI"
"description": "Orari lezioni",
"title": " <b>Lezioni di %s di (%d\u00b0 anno) di giorno %s</b>",
"fallbackText": "Non ci sono lezioni in questo giorno. SMETTILA DI PRESSARMI"
}
},
"materiali": {
Expand Down
123 changes: 123 additions & 0 deletions model/callback.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package model

import (
"fmt"
"log"
"regexp"
"strconv"
"strings"
"time"

tgbotapi "github.com/musianisamuele/telegram-bot-api"

"github.com/csunibo/informabot/commands"
)

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_<number>" it asks for today or tomorrow
// - Otherwise prints the course year of what timetable the user wants to see
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

if strings.Contains(callback_text, "_day_") {
dayRegex, err := regexp.Compile(`_day_(\d+)`)
if err != nil {
log.Printf("Error [dayRegex]: %s\n", err)
return
}

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)
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
}

timetableKey := callback_text[len("lectures_"):strings.Index(callback_text, "_y_")]

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 = data.FallbackText
} else {
response = fmt.Sprintf(data.Title, timetable.Course, year, timeForLectures.Format("2006-01-02")) + "\n\n" + response
}

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 := ChooseTimetableDay(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 {
timetableName := strings.TrimPrefix(callback_text, "lectures_")
rows := GetLectureYears(callback_text, Timetables[timetableName].Course)
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)
}
}
}

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`")
}
58 changes: 4 additions & 54 deletions model/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -144,59 +143,10 @@ func (data NotLookingForData) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbo
return makeResponseWithText(msg)
}

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.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.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 Lectures) HandleBotCommand(_ *tgbotapi.BotAPI, message *tgbotapi.Message) CommandResponse {
rows := GetTimetableCoursesRows(&Timetables)
keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...)
return makeResponseWithInlineKeyboard(keyboard)
}

func (data ListData) HandleBotCommand(*tgbotapi.BotAPI, *tgbotapi.Message) CommandResponse {
Expand Down
Loading

0 comments on commit 140c84f

Please sign in to comment.