gameStatusUpdate/server/plugin.go
2024-11-05 07:24:44 -08:00

208 lines
5.4 KiB
Go

package main
import (
"encoding/json"
"fmt"
"net/http"
"os/exec"
"strings"
"sync"
"time"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
)
// Plugin implements the interface expected by the Mattermost server to communicate between the server and plugin processes.
type Plugin struct {
plugin.MattermostPlugin
configurationLock sync.RWMutex
configuration *configuration
stopChannel chan struct{}
}
// Known game processes
var knownGames = map[string]bool{
"FortniteClient-Win64-Shipping.exe": true,
"OpenDental.exe": true,
"chrome.exe": false,
}
// ReceiveProcessList handles incoming process list data from the desktop app
func (p *Plugin) ReceiveProcessList(w http.ResponseWriter, r *http.Request) {
var payload struct {
ProcessList string `json:"processList"`
UserID string `json:"userID"` // Assuming you're sending userID from the desktop app
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
p.API.LogError("Failed to decode process list", "error", err.Error())
http.Error(w, "Invalid request payload", http.StatusBadRequest)
return
}
// Check for known games in the process list
for gameName := range knownGames {
if strings.Contains(payload.ProcessList, gameName) {
if err := p.SetUserGameStatus(payload.UserID, gameName); err != nil {
p.API.LogError("Failed to set user game status", "error", err.Error())
}
return
}
}
// Clear the user status if no known games are found
if err := p.SetUserGameStatus(payload.UserID, ""); err != nil {
p.API.LogError("Failed to clear user game status", "error", err.Error())
}
}
// GetActiveGame scans processes to determine the active game based on known game names
func (p *Plugin) GetActiveGame() (string, error) {
cmd := exec.Command("ps", "-e")
output, err := cmd.Output()
if err != nil {
return "", err
}
lines := strings.Split(string(output), "\n")
for _, line := range lines {
for gameName := range knownGames {
if strings.Contains(line, gameName) {
return gameName, nil
}
}
}
return "", nil
}
// SetUserGameStatus updates the user's custom status to reflect the game they are playing
func (p *Plugin) SetUserGameStatus(userID, game string) error {
status := fmt.Sprintf("playing: %s", game)
user, appErr := p.API.GetUser(userID)
if appErr != nil {
return appErr
}
customStatus := model.CustomStatus{
Emoji: "video_game",
Text: status,
Duration: "0",
}
customStatusJSON, err := json.Marshal(customStatus)
if err != nil {
return err
}
user.Props = map[string]string{
"custom_status": string(customStatusJSON),
}
if _, appErr := p.API.UpdateUser(user); appErr != nil {
return appErr
}
return nil
}
// MonitorGameStatus continuously monitors running processes and updates the user's status
func (p *Plugin) MonitorGameStatus(userID string) {
for {
select {
case <-p.stopChannel:
return
default:
game, err := p.GetActiveGame()
if err != nil {
p.API.LogError("Failed to scan processes", "error", err.Error())
continue
}
if game != "" {
if err := p.SetUserGameStatus(userID, game); err != nil {
p.API.LogError("Failed to set user game status", "error", err.Error())
}
}
time.Sleep(30 * time.Second)
}
}
}
// OnActivate is called when the plugin is activated
func (p *Plugin) OnActivate() error {
config := p.getConfiguration()
if config.EnableGameStatus {
p.stopChannel = make(chan struct{})
// Register the command for setting the game
command := &model.Command{
Trigger: "setgame",
AutoComplete: true,
AutoCompleteDesc: "Set the game you are currently playing",
AutoCompleteHint: "[game]",
}
if err := p.API.RegisterCommand(command); err != nil {
return err
}
// Register the HTTP handler for receiving process lists
http.HandleFunc("/plugins/com.mattermost.gamestatusupdate/processlist", p.ReceiveProcessList)
}
return nil
}
// OnDeactivate is called when the plugin is deactivated
func (p *Plugin) OnDeactivate() error {
close(p.stopChannel)
return nil
}
// HandleWebSocketEvent handles incoming WebSocket events.
func (p *Plugin) HandleWebSocketEvent(event model.WebSocketEvent) {
// Check if the event is of type user_status
if event.GetBroadcast().ChannelId == "user_status" {
var data struct {
UserID string `json:"user_id"`
}
// Accessing the data as a map
eventData := event.GetData()
// Convert to JSON
dataBytes, err := json.Marshal(eventData)
if err != nil {
p.API.LogError("Failed to marshal WebSocket event data", "error", err.Error())
return
}
// Unmarshal the JSON string to extract user ID
if err := json.Unmarshal(dataBytes, &data); err != nil {
p.API.LogError("Failed to unmarshal WebSocket event data", "error", err.Error())
return
}
// Continue with your logic to update the user's game status...
game, err := p.GetActiveGame()
if err != nil {
p.API.LogError("Failed to get active game", "error", err.Error())
return
}
// Update the user's game status
if game != "" {
if err := p.SetUserGameStatus(data.UserID, game); err != nil {
p.API.LogError("Failed to update user game status", "error", err.Error())
}
} else {
// Clear the user's game status if no game is detected
if err := p.SetUserGameStatus(data.UserID, ""); err != nil {
p.API.LogError("Failed to clear user game status", "error", err.Error())
}
}
}
}