Skip to content

Commit

Permalink
Merge pull request #1 from tr4cks/all-commands-implem
Browse files Browse the repository at this point in the history
All commands implementation
  • Loading branch information
tr4cks authored Jan 10, 2025
2 parents 60752e2 + 770919f commit 68eeca2
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 3 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,14 +358,22 @@ Concerning the `wol` module, as mentioned earlier, it does not allow you to shut

### Discord

In addition to the http server, you can also activate a discord bot to switch on the server with a command from discord, simply by adding the following fields to the configuration file:
In addition to the HTTP server, you can also activate a Discord bot to manage the server with commands from Discord. The following commands are available:

- `/server_status`: Provides the current status of the server.
- `/power_on`: Turns the server on.
- `/power_off`: Turns the server off.

To enable this functionality, simply add the following fields to the configuration file:

```yaml
discord:
bot-token: your_bot_token
guild-id: "your_guild_id" # optional
```

*❗️ If you want to ensure that not everyone can turn off the server, do not forget to set the appropriate permissions on Discord.*

<p align="right">(<a href="#readme-top">back to top</a>)</p>


Expand Down
170 changes: 169 additions & 1 deletion discordbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,77 @@ func (d *DiscordBot) Stop() {
d.logger.Info().Msg("Gracefully shutting down")
}

func (d *DiscordBot) serverStatusHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
logger := d.logger.With().Str("username", i.Member.User.Username).Logger()
logger.Info().Msg("A user tries to check the server status")

var deferStack []func()

defer func() {
if len(deferStack) > 0 {
time.Sleep(10 * time.Second)
}
for i := len(deferStack) - 1; i >= 0; i-- {
deferStack[i]()
}
}()

s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "Retrieving server status in progress... Please wait",
},
})

deferStack = append(deferStack, func() {
s.InteractionResponseDelete(i.Interaction)
})

powerState, ledState := d.module.State()

if powerState.Err != nil {
logger.Error().Err(powerState.Err).Msg("Failed to retrieve POWER state")
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "A problem occurred when switching on the server",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
return
}
if ledState.Err != nil {
logger.Error().Err(ledState.Err).Msg("Failed to retrieve LED state")
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "A problem occurred when switching on the server",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
return
}

if powerState.Value || ledState.Value {
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "Server is ON",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
} else {
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "Server is OFF",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
}
}

func (d *DiscordBot) powerOnHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
logger := d.logger.With().Str("username", i.Member.User.Username).Logger()
logger.Info().Msg("A user attempts to switch on the server")
Expand Down Expand Up @@ -153,11 +224,106 @@ func (d *DiscordBot) powerOnHandler(s *discordgo.Session, i *discordgo.Interacti
})
}

func (d *DiscordBot) powerOffHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
logger := d.logger.With().Str("username", i.Member.User.Username).Logger()
logger.Info().Msg("A user attempts to switch off the server")

var deferStack []func()

defer func() {
if len(deferStack) > 0 {
time.Sleep(10 * time.Second)
}
for i := len(deferStack) - 1; i >= 0; i-- {
deferStack[i]()
}
}()

s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Content: "Server shutdown in progress... Please wait",
},
})

deferStack = append(deferStack, func() {
s.InteractionResponseDelete(i.Interaction)
})

powerState, ledState := d.module.State()

if powerState.Err != nil {
logger.Error().Err(powerState.Err).Msg("Failed to retrieve POWER state")
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "A problem occurred when switching on the server",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
return
}
if ledState.Err != nil {
logger.Error().Err(ledState.Err).Msg("Failed to retrieve LED state")
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "A problem occurred when switching on the server",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
return
}

if !powerState.Value && !ledState.Value {
logger.Info().Msg("The server is already switched off")
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "The server is already switched off",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
return
}

err := d.module.PowerOff()
if err != nil {
logger.Error().Err(err).Msg("A problem occurred when switching off the server")
followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "A problem occurred when switching on the server",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
return
}
logger.Info().Msg("Server switched off")

followupMsg, _ := s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{
Flags: discordgo.MessageFlagsEphemeral,
Content: "The server is now shutting down! This may take a few minutes",
})
deferStack = append(deferStack, func() {
s.FollowupMessageDelete(i.Interaction, followupMsg.ID)
})
}

var commands = []*discordgo.ApplicationCommand{
{
Name: "server_status",
Description: "Provides the current status of the server",
},
{
Name: "power_on",
Description: "Turns the server on",
},
{
Name: "power_off",
Description: "Turns the server off",
},
}

func NewDiscordBot(config *DiscordBotConfig, module modules.Module) (*DiscordBot, error) {
Expand All @@ -180,7 +346,9 @@ func NewDiscordBot(config *DiscordBotConfig, module modules.Module) (*DiscordBot
bot := &DiscordBot{config, module, logger, session, nil}

commandHandlers := map[string]func(*discordgo.Session, *discordgo.InteractionCreate){
"power_on": bot.powerOnHandler,
"server_status": bot.serverStatusHandler,
"power_on": bot.powerOnHandler,
"power_off": bot.powerOffHandler,
}

session.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ var (
rootCmd = &cobra.Command{
Use: appName,
Short: "All-in-one tool for remote server power control",
Version: "1.3.1",
Version: "1.4.0",
Args: cobra.NoArgs,
Run: run,
CompletionOptions: cobra.CompletionOptions{
Expand Down

0 comments on commit 68eeca2

Please sign in to comment.