Merge branch 'main' into feat/implement-timetable-fetching
This commit is contained in:
commit
b79451f77a
4 changed files with 47 additions and 43 deletions
21
README.md
21
README.md
|
|
@ -1,25 +1,32 @@
|
|||
# Discord bot for logging Untis timetable to a Discord Webhook and Multiple accounts which can be added using !addaccount in a server whit the Bot
|
||||
## create .env with Credentials:
|
||||
|
||||
## create .env with Credentials
|
||||
|
||||
- UNTIS_PASSWORD
|
||||
- UNTIS_USER
|
||||
- DISCORD_WEBHOOK_URL
|
||||
- DISCORD_BOT_TOKEN
|
||||
- ENC_KEY (generated via head -c 32 /dev/urandom | base64)
|
||||
- URL
|
||||
|
||||
The URL portion should be as follows: "https://thalia.webuntis.com/WebUntis/jsonrpc.do?school=[nameOfYourSchool]"
|
||||
the nameOfYourSchool can be found by accessing your WebUntis page in the browser. The link should be "https://thalia.webuntis.com/WebUntis/?school=[nameOfYourSchool]/basic/login" for the loginPage
|
||||
|
||||
# This is a project for me, issues will be resolved as i find the motivation to do so, improvements may follow in the future
|
||||
### I will try to maintain this project as best as possible. Maybe add a better security to it than to trust the host but for now it is working and that was my goal. Please report any errors you find while using this bot.
|
||||
|
||||
## I will try to maintain this project as best as possible. Maybe add a better security to it than to trust the host but for now it is working and that was my goal. Please report any errors you find while using this bot
|
||||
|
||||
# Installation
|
||||
|
||||
# This is a discord application intended for self hosting. You will need to add an application in the [disord developer website](https://discord.com/developers/applications) for this bot mainly with the read messages and write messages privelege as well as send privat messages for the account handling. If you give it administrator permissions you dont have to worry further but this could present you with a security risk if my github account gets compromised and a malicious update to the script gets pushed which you than use since the bot got access to your whole server.
|
||||
# This is a discord application intended for self hosting. You will need to add an application in the [disord developer website](https://discord.com/developers/applications) for this bot mainly with the read messages and write messages privelege as well as send privat messages for the account handling. If you give it administrator permissions you dont have to worry further but this could present you with a security risk if my github account gets compromised and a malicious update to the script gets pushed which you than use since the bot got access to your whole server
|
||||
|
||||
# I also recommend to use a tool like [coolify](https://coolify.io/) to host the bot. Make sure to add the Env's and add **accounts.json** to persistant storage
|
||||
|
||||
### This bot is self hosted and will run correctly when doing "go run . " in the root folder of the project. **Before usage add the .env file with the Credentials as mentioned above.** When you run the Program for the first time, all important files will be created automatically and the bot is ready to go. The user added via the fields UNTIS_USER and UNTIS_PASSWORD in the .env will be the one where the Webhook is sourced from and the other users will be send a DM after adding their account with the command. Add the Discord Webhook via the DISCORD_WEBHOOK_URL point in the .env and will send the messages for the timetable in there
|
||||
|
||||
### This bot is self hosted and will run correctly when doing "go run . " in the root folder of the project. **Before usage add the .env file with the Credentials as mentioned above.** When you run the Program for the first time, all important files will be created automatically and the bot is ready to go. The user added via the fields UNTIS_USER and UNTIS_PASSWORD in the .env will be the one where the Webhook is sourced from and the other users will be send a DM after adding their account with the command. Add the Discord Webhook via the DISCORD_WEBHOOK_URL point in the .env and will send the messages for the timetable in there.
|
||||
### For multi user support: using the command **!addaccount** in the discord server the bot is used in, it will than dm you with the sign up process prompting you to enter your untis Username and password (no email neededd just untis username). The username is stored as plain text and the password is hashed on the device hosting the bot, so currently it is **not secure** since the host will have access to the hashed password and the hash used! Deleting an account is done by using **!removeaccount** which will delete the account and the password, created with your discord account
|
||||
|
||||
### For multi user support: using the command **!addaccount** in the discord server the bot is used in, it will than dm you with the sign up process prompting you to enter your untis Username and password (no email neededd just untis username). The username is stored as plain text and the password is hashed on the device hosting the bot, so currently it is **not secure** since the host will have access to the hashed password and the hash used! Deleting an account is done by using **!removeaccount** which will delete the account and the password, created with your discord account.
|
||||
## In case your school is using a different timing for the Lessons than mine, you can change the times where you will be notified with the next room and Lesson for the day in **Line 71 in the main.go** file. The bot may not send you a message if there is no lesson after the specified time so that you can leave empty lessons as is for the days
|
||||
|
||||
## In case your school is using a different timing for the Lessons than mine, you can change the times where you will be notified with the next room and Lesson for the day in **Line 71 in the main.go** file. The bot may not send you a message if there is no lesson after the specified time so that you can leave empty lessons as is for the days.
|
||||
# I am neither a representative of Untis, Untis Baden-Württemberg GmbH nor a Developer in their team. This project is based on their API and my code and is not affiliated with them
|
||||
|
||||
# I am neither a representative of Untis, Untis Baden-Württemberg GmbH nor a Developer in their team. This project is based on their API and my code and is not affiliated with them.
|
||||
|
|
@ -30,7 +30,7 @@ type LoginResponse struct {
|
|||
Result Loginresult `json:"result"`
|
||||
}
|
||||
|
||||
var Url = "https://thalia.webuntis.com/WebUntis/jsonrpc.do?school=Mons_Tabor"
|
||||
var Url = os.Getenv("URL")
|
||||
|
||||
// var Password = os.Getenv("UNTIS_PASSWORD")
|
||||
// var USERS = os.Getenv("UNTIS_USER")
|
||||
|
|
|
|||
53
main.go
53
main.go
|
|
@ -35,8 +35,7 @@ type NamedTimetableEntry struct {
|
|||
|
||||
// init and main//
|
||||
func init() {
|
||||
godotenv.Load(".env")
|
||||
err := godotenv.Load()
|
||||
err := godotenv.Load(".env")
|
||||
if err != nil {
|
||||
log.Printf("Error loading .env file: %v", err)
|
||||
}
|
||||
|
|
@ -138,32 +137,26 @@ func scheduleTimetableUpdate() {
|
|||
updateAllUsers()
|
||||
}
|
||||
}()
|
||||
|
||||
// Ticker for scheduled lesson notifications
|
||||
startMinuteTicker(func() {
|
||||
// New robust scheduler
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
now := time.Now()
|
||||
if isScheduledTime(now) {
|
||||
currentTime := now.Format("15:04")
|
||||
scheduledTimes := []string{"07:45", "08:35", "09:35", "10:25", "11:25", "12:15", "13:45", "14:25"}
|
||||
|
||||
for _, t := range scheduledTimes {
|
||||
if t == currentTime {
|
||||
log.Println("Scheduled time reached, running notifications...")
|
||||
accounts, _ := BotStart.LoadAndDecryptAccounts()
|
||||
// Also notify the default user
|
||||
accounts = append(accounts, BotStart.DecryptedAccount{UserID: "default"})
|
||||
for _, acc := range accounts {
|
||||
Run(acc.UserID)
|
||||
Run(acc.UserID, t)
|
||||
}
|
||||
log.Println("Finished running notifications.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func startMinuteTicker(f func()) {
|
||||
now := time.Now()
|
||||
next := now.Truncate(time.Minute).Add(time.Minute)
|
||||
time.Sleep(time.Until(next))
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
go func() {
|
||||
for {
|
||||
f()
|
||||
<-ticker.C
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
|
@ -173,7 +166,7 @@ func Run(userID string) {
|
|||
if userID != "default" {
|
||||
timetableFile = fmt.Sprintf("timetableFilled_%s.json", userID)
|
||||
}
|
||||
log.Printf("Sending next lesson for user %s", userID)
|
||||
log.Printf("Sending next lesson for user %s for time %s", userID, forTime)
|
||||
|
||||
// Check if the timetable file exists
|
||||
if _, err := os.Stat(timetableFile); os.IsNotExist(err) {
|
||||
|
|
@ -185,18 +178,18 @@ func Run(userID string) {
|
|||
roomByStartTime, _ := MapTimeToRoom(timetableFile)
|
||||
subjectByStartTime, _ := MapTimeToSubject(timetableFile)
|
||||
|
||||
now := time.Now().Format("15:04")
|
||||
nextTime, room, foundRoom := NextRoomForTime(roomByStartTime, now)
|
||||
nextTime, room, foundRoom := NextRoomForTime(roomByStartTime, forTime)
|
||||
if !foundRoom {
|
||||
log.Printf("No upcoming lessons found for user %s at %s.", userID, forTime)
|
||||
return // No upcoming lessons
|
||||
}
|
||||
|
||||
subject, foundSubject := NextSubjectForTime(subjectByStartTime, now)
|
||||
subject, foundSubject := NextSubjectForTime(subjectByStartTime, forTime)
|
||||
if !foundSubject {
|
||||
return // Should not happen if room was found
|
||||
}
|
||||
|
||||
status, _ := NextCodeForTime(codeByStartTime, now)
|
||||
status, _ := NextCodeForTime(codeByStartTime, forTime)
|
||||
|
||||
var message string
|
||||
if status != "" {
|
||||
|
|
@ -233,7 +226,7 @@ func NextRoomForTime(roomByStartTime map[string]string, current string) (string,
|
|||
}
|
||||
sort.Slice(times, func(i, j int) bool { return times[i].Before(times[j]) })
|
||||
for _, t := range times {
|
||||
if t.After(now) {
|
||||
if t.After(now) || t.Equal(now) {
|
||||
return timeToStr[t], roomByStartTime[timeToStr[t]], true
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +251,7 @@ func NextSubjectForTime(subjectByStartTime map[string]string, current string) (s
|
|||
}
|
||||
sort.Slice(times, func(i, j int) bool { return times[i].Before(times[j]) })
|
||||
for _, t := range times {
|
||||
if t.After(now) {
|
||||
if t.After(now) || t.Equal(now) {
|
||||
return subjectByStartTime[timeToStr[t]], true
|
||||
}
|
||||
}
|
||||
|
|
@ -283,7 +276,7 @@ func NextCodeForTime(codeByStartTime map[string]string, current string) (string,
|
|||
}
|
||||
sort.Slice(times, func(i, j int) bool { return times[i].Before(times[j]) })
|
||||
for _, t := range times {
|
||||
if t.After(now) {
|
||||
if t.After(now) || t.Equal(now) {
|
||||
return codeByStartTime[timeToStr[t]], true
|
||||
}
|
||||
}
|
||||
|
|
@ -374,7 +367,11 @@ type Field struct {
|
|||
}
|
||||
|
||||
func sendDiscordWebhook(subject string, room string, nextTime string, Status string) {
|
||||
log.Println("Sending Discord webhook notification...")
|
||||
if discordWebhookURL == "" {
|
||||
log.Println("Discord webhook URL not configured, skipping notification.")
|
||||
return
|
||||
}
|
||||
log.Println("Attempting to send Discord webhook notification...")
|
||||
// Create a rich embed message
|
||||
var message string
|
||||
if Status != "" {
|
||||
|
|
|
|||
BIN
untislogger
BIN
untislogger
Binary file not shown.
Loading…
Reference in a new issue