diff --git a/.gitignore b/.gitignore index 7bdcbe1..b013f95 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ subjects.json teachers.json login.json timetable.json -timetableFilled.json \ No newline at end of file +timetableFilled.json +accounts.json \ No newline at end of file diff --git a/Bot/Untis.go b/Bot/Untis.go index 75aae22..524141b 100644 --- a/Bot/Untis.go +++ b/Bot/Untis.go @@ -54,7 +54,7 @@ func Main() { } func Auth() ([]*http.Cookie, error) { - l := Login{"2023-05-06 15:44:22.215292", "authenticate", Params{os.Getenv("UNTIS_USER"), os.Getenv("UNTIS_PASSWORD"), "WebUntis Test"}, "2.0"} + l := Login{"2023-05-06 15:44:22.215292", "authenticate", Params{USERS, Password, "WebUntis Test"}, "2.0"} loginJSON, err := json.Marshal(l) if err != nil { log.Fatalf("Error marshaling login data: %v", err) diff --git a/Botrun/BotStart.go b/Botrun/BotStart.go new file mode 100644 index 0000000..6989dce --- /dev/null +++ b/Botrun/BotStart.go @@ -0,0 +1,144 @@ +package bot + +import ( + "encoding/json" + "fmt" + "os" + "os/signal" + "sync" + "syscall" + + "github.com/bwmarrin/discordgo" +) + +// Account structure to save in JSON +type Account struct { + UserID string `json:"user_id"` + Username string `json:"username"` + Password string `json:"password"` +} + +// State management for conversation steps +type UserState struct { + Step string // "awaiting_username", "awaiting_password" + Username string // Temporary storage for username until password is received +} + +var ( + userStates = make(map[string]*UserState) // userID -> state + stateMutex sync.Mutex // protect userStates + accountsFile = "accounts.json" +) + +// Save account info to JSON file (appends or updates) +func saveAccount(userID, username, password string) error { + var accounts []Account + + // Load existing accounts if file exists + if data, err := os.ReadFile(accountsFile); err == nil && len(data) > 0 { + _ = json.Unmarshal(data, &accounts) + } + + // Append new account + accounts = append(accounts, Account{ + UserID: userID, + Username: username, + Password: password, + }) + + // Save back to file + newData, err := json.MarshalIndent(accounts, "", " ") + if err != nil { + return err + } + return os.WriteFile(accountsFile, newData, 0644) +} + +func Start() { + token := os.Getenv("DISCORD_BOT_TOKEN") + if token == "" { + fmt.Println("DISCORD_BOT_TOKEN environment variable is not set.") + return + } + + dg, err := discordgo.New("Bot " + token) + if err != nil { + fmt.Println("error creating Discord session,", err) + return + } + + dg.AddHandler(messageCreate) + err = dg.Open() + if err != nil { + fmt.Println("error opening connection,", err) + return + } + + fmt.Println("Bot is now running. Press CTRL+C to exit.") + // Wait for CTRL+C or other term signal to exit. + sc := make(chan os.Signal, 1) + signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill) + <-sc + + dg.Close() +} + +func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { + // Ignore bot's own messages + if m.Author.ID == s.State.User.ID { + return + } + + // Handle "!addaccount" only in guilds (not in DMs) + if m.GuildID != "" && m.Content == "!addaccount" { + // Delete the command for privacy + _ = s.ChannelMessageDelete(m.ChannelID, m.ID) + // Create DM channel + channel, err := s.UserChannelCreate(m.Author.ID) + if err != nil { + fmt.Println("Error creating DM channel:", err) + return + } + s.ChannelMessageSend(channel.ID, "Let's add your account. Please provide your username:") + + stateMutex.Lock() + userStates[m.Author.ID] = &UserState{Step: "awaiting_username"} + stateMutex.Unlock() + return + } + + // Handle DMs for the two-step process + if m.GuildID == "" { + stateMutex.Lock() + state, ok := userStates[m.Author.ID] + stateMutex.Unlock() + if !ok { + return // Not in the process + } + + switch state.Step { + case "awaiting_username": + // Store the username and prompt for password + stateMutex.Lock() + state.Username = m.Content + state.Step = "awaiting_password" + userStates[m.Author.ID] = state + stateMutex.Unlock() + s.ChannelMessageSend(m.ChannelID, "Now, please provide your password:") + case "awaiting_password": + username := state.Username + password := m.Content + // Save to JSON + if err := saveAccount(m.Author.ID, username, password); err != nil { + s.ChannelMessageSend(m.ChannelID, "There was an error saving your account. Please try again later.") + fmt.Println("Error saving account:", err) + } else { + s.ChannelMessageSend(m.ChannelID, "Your account has been saved!") + } + // Cleanup state + stateMutex.Lock() + delete(userStates, m.Author.ID) + stateMutex.Unlock() + } + } +} diff --git a/README.md b/README.md index 29b9b46..bd7bcfe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Discord bot for logging Untis timetable to a Discord Webhook -create .env with Credentials -UNTIS_PASSWORD -UNTIS_USER -DISCORD_WEBHOOK_URL \ No newline at end of file +## create .env with Credentials: +- UNTIS_PASSWORD +- UNTIS_USER +- DISCORD_WEBHOOK_URL +- DISCORD_BOT_TOKEN + +# This is a project for me, issues will be resolved as i find the motivation to do that, improvements may follow in the future \ No newline at end of file diff --git a/go.mod b/go.mod index 76f3f25..a5e1cdb 100644 --- a/go.mod +++ b/go.mod @@ -3,3 +3,10 @@ module untislogger go 1.24 require github.com/joho/godotenv v1.5.1 + +require ( + github.com/bwmarrin/discordgo v0.29.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect +) diff --git a/go.sum b/go.sum index d61b19e..04715b6 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,14 @@ +github.com/bwmarrin/discordgo v0.29.0 h1:FmWeXFaKUwrcL3Cx65c20bTRW+vOb6k8AnaP+EgjDno= +github.com/bwmarrin/discordgo v0.29.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/main.go b/main.go index 64ef261..ba13f74 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,8 @@ import ( "time" Untis "untislogger/Bot" + BotStart "untislogger/Botrun" + "github.com/joho/godotenv" ) @@ -52,6 +54,7 @@ func main() { //Untis.Main() //starting API calls function| happens in schedule func //Run() //Starts logging the timetable for each new Lesson and logs changes + go BotStart.Start() scheduleTimetableUpdate() sigChan := make(chan os.Signal, 1)