mirror of
https://github.com/aunefyren/wrapperr
synced 2025-01-10 00:38:41 +00:00
80720039e8
Sync details to user from every Tautulli server during user aggregation Prioritize active status to true if any Tautulli server says so Sync users before showing statistics and validating active state Show Tautulli servers on user page
339 lines
12 KiB
Go
339 lines
12 KiB
Go
package modules
|
|
|
|
import (
|
|
"aunefyren/wrapperr/files"
|
|
"aunefyren/wrapperr/models"
|
|
"aunefyren/wrapperr/utilities"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func TautulliTestConnection(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string) (bool, error) {
|
|
|
|
urlString, err := utilities.BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
|
|
if err != nil {
|
|
errString := strings.Replace(err.Error(), TautulliApiKey, "REDACTED", -1)
|
|
log.Println("Failed to build Tautulli connection URL. Error: " + errString)
|
|
return false, errors.New("Failed to build Tautulli connection URL.")
|
|
}
|
|
|
|
urlString = urlString + "api/v2/" + "?apikey=" + TautulliApiKey + "&cmd=status"
|
|
|
|
params := url.Values{}
|
|
payload := strings.NewReader(params.Encode())
|
|
|
|
req, err := http.NewRequest("GET", urlString, payload)
|
|
if err != nil {
|
|
errString := strings.Replace(err.Error(), TautulliApiKey, "REDACTED", -1)
|
|
log.Println("Failed to reach Tautulli server. Error: " + errString)
|
|
return false, errors.New("Failed to reach Tautulli server. Error: " + errString)
|
|
}
|
|
|
|
req.Header.Add("Accept", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
errString := strings.Replace(err.Error(), TautulliApiKey, "REDACTED", -1)
|
|
log.Println("Failed to reach Tautulli server. Error: " + errString)
|
|
return false, errors.New("Failed to reach Tautulli server. Error: " + errString)
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
body, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
errString := strings.Replace(err.Error(), TautulliApiKey, "REDACTED", -1)
|
|
log.Println("Failed to read Tautulli server response. Error: " + errString)
|
|
return false, errors.New("Failed to read Tautulli response. Error: " + errString)
|
|
} else if res.StatusCode != 200 {
|
|
errString := "Tautulli didn't respond with status code 200, got: " + res.Status + " instead."
|
|
reply := string(body)
|
|
replyString := strings.Replace(reply, TautulliApiKey, "REDACTED", -1)
|
|
log.Println("Failed to connect to Tautulli server. \n\nReply: " + replyString + ". +n\nError: " + errString)
|
|
return false, errors.New("Failed to connect to Tautulli server. \n\nReply: " + replyString + ". \n\nError: " + errString)
|
|
}
|
|
|
|
var body_reply models.TautulliStatusReply
|
|
err = json.Unmarshal(body, &body_reply)
|
|
if err != nil {
|
|
errString := strings.Replace(err.Error(), TautulliApiKey, "REDACTED", -1)
|
|
reply := string(body)
|
|
replyString := strings.Replace(reply, TautulliApiKey, "REDACTED", -1)
|
|
log.Println("Failed to parse Tautulli server response. \n\nReply: " + replyString + ". +n\nError: " + errString)
|
|
return false, errors.New("Failed to parse Tautulli server response. \n\nReply: " + replyString + ". \n\nError: " + errString)
|
|
}
|
|
|
|
var tautulli_status bool = false
|
|
|
|
if body_reply.Response.Result == "success" {
|
|
|
|
tautulli_status = true
|
|
|
|
} else if body_reply.Response.Result == "error" {
|
|
|
|
errString := strings.Replace(body_reply.Response.Message, TautulliApiKey, "REDACTED", -1)
|
|
log.Println("Tautulli server responded with an error. Error: " + errString)
|
|
return false, errors.New("Tautulli server responded with an error. Error: " + errString)
|
|
|
|
}
|
|
|
|
return tautulli_status, nil
|
|
}
|
|
|
|
func TautulliGetUserId(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string, PlexUser string) (userID int, userName string, userFriendlyName string, userEmail string, userActive bool, err error) {
|
|
userID = 0
|
|
userName = ""
|
|
userEmail = ""
|
|
userFriendlyName = ""
|
|
userActive = false
|
|
err = nil
|
|
|
|
body_reply, err := TautulliGetUsers(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot, TautulliApiKey)
|
|
if err != nil {
|
|
log.Println("Failed to get users from Tautulli. Error: " + err.Error())
|
|
return userID, userName, userFriendlyName, userEmail, userActive, errors.New("Failed to get users from Tautulli.")
|
|
}
|
|
|
|
for i := 0; i < len(body_reply.Response.Data); i++ {
|
|
if body_reply.Response.Data[i].UserID != 0 && (strings.ToLower(body_reply.Response.Data[i].Username) == strings.ToLower(PlexUser) || strings.ToLower(body_reply.Response.Data[i].Email) == strings.ToLower(PlexUser)) {
|
|
ActiveInt := body_reply.Response.Data[i].IsActive
|
|
if ActiveInt == 0 {
|
|
userActive = false
|
|
} else if ActiveInt == 1 {
|
|
userActive = true
|
|
} else {
|
|
return userID, userName, userFriendlyName, userEmail, userActive, errors.New("Invalid IsActive state in Tautulli.")
|
|
}
|
|
|
|
return body_reply.Response.Data[i].UserID, body_reply.Response.Data[i].Username, body_reply.Response.Data[i].FriendlyName, body_reply.Response.Data[i].Email, userActive, nil
|
|
}
|
|
}
|
|
|
|
log.Println("Could not find any user that matched the given Plex Identity: '" + PlexUser + "'.")
|
|
return userID, userName, userEmail, userFriendlyName, userActive, errors.New("Failed to find user.")
|
|
}
|
|
|
|
func TautulliGetUsers(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string) (usersReply models.TautulliGetUsersReply, err error) {
|
|
usersReply = models.TautulliGetUsersReply{}
|
|
err = nil
|
|
|
|
url_string, err := utilities.BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
|
|
if err != nil {
|
|
log.Println("Failed to build Tautulli connection URL. Error: " + err.Error())
|
|
return usersReply, errors.New("Failed to build Tautulli connection URL.")
|
|
}
|
|
|
|
url_string = url_string + "api/v2/" + "?apikey=" + TautulliApiKey + "&cmd=get_users"
|
|
|
|
params := url.Values{}
|
|
payload := strings.NewReader(params.Encode())
|
|
|
|
req, err := http.NewRequest("GET", url_string, payload)
|
|
if err != nil {
|
|
log.Println("Failed to reach Tautulli server. Error: " + err.Error())
|
|
return usersReply, errors.New("Failed to reach Tautulli server.")
|
|
}
|
|
|
|
req.Header.Add("Accept", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
log.Println("Failed to reach Tautulli server. Error: " + err.Error())
|
|
return usersReply, errors.New("Failed to reach Tautulli server.")
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
body, err := io.ReadAll(res.Body)
|
|
|
|
json.Unmarshal(body, &usersReply)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return usersReply, errors.New("Failed to parse Tautulli response.")
|
|
}
|
|
|
|
return usersReply, err
|
|
}
|
|
|
|
func TautulliDownloadStatistics(TautulliPort int, TautulliIP string, TautulliHttps bool, TautulliRoot string, TautulliApiKey string, TautulliLength int, Libraries string, Grouping string, StartDate string) ([]models.TautulliHistoryItem, error) {
|
|
|
|
url_string, err := utilities.BuildURL(TautulliPort, TautulliIP, TautulliHttps, TautulliRoot)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return []models.TautulliHistoryItem{}, errors.New("Failed to build Tautulli connection URL.")
|
|
}
|
|
|
|
url_string = url_string + "api/v2/" + "?apikey=" + TautulliApiKey + "&cmd=get_history&order_column=date&order_dir=desc&include_activity=0" + Libraries + "&grouping=" + Grouping + "&length=" + strconv.Itoa(TautulliLength) + "&start_date=" + StartDate
|
|
|
|
params := url.Values{}
|
|
payload := strings.NewReader(params.Encode())
|
|
|
|
req, err := http.NewRequest("GET", url_string, payload)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return []models.TautulliHistoryItem{}, errors.New("Failed to reach Tautulli server.")
|
|
}
|
|
|
|
req.Header.Add("Accept", "application/json")
|
|
|
|
res, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return []models.TautulliHistoryItem{}, errors.New("Failed to reach Tautulli server.")
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
body, err := io.ReadAll(res.Body)
|
|
|
|
var body_reply models.TautulliGetHistoryReply
|
|
json.Unmarshal(body, &body_reply)
|
|
if err != nil {
|
|
log.Println(err)
|
|
return []models.TautulliHistoryItem{}, errors.New("Failed to parse Tautulli response.")
|
|
}
|
|
|
|
return body_reply.Response.Data.Data, nil
|
|
|
|
}
|
|
|
|
func TautulliTestEveryServer() (err error) {
|
|
err = nil
|
|
|
|
config, err := files.GetConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr configuration. Error: " + err.Error())
|
|
return errors.New("Failed to load Wrapperr configuration.")
|
|
}
|
|
|
|
for i := 0; i < len(config.TautulliConfig); i++ {
|
|
log.Println("Checking Tautulli server '" + config.TautulliConfig[i].TautulliName + "'.")
|
|
tautulli_state, err := TautulliTestConnection(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey)
|
|
if err != nil {
|
|
log.Println("Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'. Error: " + err.Error())
|
|
return errors.New("Failed to reach Tautulli server '" + config.TautulliConfig[i].TautulliName + "'.")
|
|
} else if !tautulli_state {
|
|
log.Println("Failed to ping Tautulli server '" + config.TautulliConfig[i].TautulliName + "' before retrieving statistics.")
|
|
return errors.New("Failed to ping Tautulli server '" + config.TautulliConfig[i].TautulliName + "'.")
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func TautulliGetUsersFromEveryServer() (tautulliUsers []models.TautulliUserGroup, err error) {
|
|
err = nil
|
|
tautulliUsers = []models.TautulliUserGroup{}
|
|
|
|
config, err := files.GetConfig()
|
|
if err != nil {
|
|
log.Println("Failed to load Wrapperr configuration. Error: " + err.Error())
|
|
return tautulliUsers, errors.New("Failed to load Wrapperr configuration.")
|
|
}
|
|
|
|
for i := 0; i < len(config.TautulliConfig); i++ {
|
|
log.Println("Getting users from Tautulli server '" + config.TautulliConfig[i].TautulliName + "'.")
|
|
tautulliReply, err := TautulliGetUsers(config.TautulliConfig[i].TautulliPort, config.TautulliConfig[i].TautulliIP, config.TautulliConfig[i].TautulliHttps, config.TautulliConfig[i].TautulliRoot, config.TautulliConfig[i].TautulliApiKey)
|
|
if err != nil {
|
|
log.Println("Failed to get users from Tautulli. Error: " + err.Error())
|
|
return tautulliUsers, errors.New("Failed to get users from Tautulli.")
|
|
}
|
|
|
|
for _, user := range tautulliReply.Response.Data {
|
|
tautulliUserGroup := models.TautulliUserGroup{
|
|
TautulliUser: user,
|
|
TautulliServer: config.TautulliConfig[i].TautulliName,
|
|
}
|
|
tautulliUsers = append(tautulliUsers, tautulliUserGroup)
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func TautulliSyncUsersToWrapperr() (err error) {
|
|
err = nil
|
|
|
|
// Get all users from Tautulli
|
|
users, err := TautulliGetUsersFromEveryServer()
|
|
if err != nil {
|
|
log.Println("Failed to get users. Error: " + err.Error())
|
|
return errors.New("Failed to get users.")
|
|
}
|
|
|
|
// Create Wrapperr user objects array to fill
|
|
newUserArray := []models.WrapperrUser{}
|
|
|
|
// Create new Wrapperr user objects from Tautulli reply
|
|
for _, userGroup := range users {
|
|
var userMatch bool = false
|
|
var userIndex int = 0
|
|
for index, wrapperrUser := range newUserArray {
|
|
if userGroup.TautulliUser.UserID == wrapperrUser.UserID {
|
|
userMatch = true
|
|
userIndex = index
|
|
break
|
|
}
|
|
}
|
|
|
|
// User object is not in array, create and push to array
|
|
// If user object is in array, append new server and weigh active status
|
|
if !userMatch {
|
|
// Create bool from small int
|
|
var Active = false
|
|
if userGroup.TautulliUser.IsActive == 1 {
|
|
Active = true
|
|
} else if userGroup.TautulliUser.IsActive == 0 {
|
|
Active = false
|
|
} else {
|
|
log.Println("Failed to convert integer to bool. Error: " + err.Error())
|
|
return errors.New("Failed to convert integer to bool.")
|
|
}
|
|
|
|
// Create new object
|
|
wrapperrUser := models.WrapperrUser{
|
|
FriendlyName: userGroup.TautulliUser.FriendlyName,
|
|
User: userGroup.TautulliUser.Username,
|
|
UserID: userGroup.TautulliUser.UserID,
|
|
Email: userGroup.TautulliUser.Email,
|
|
TautulliServers: []string{userGroup.TautulliServer},
|
|
Active: Active,
|
|
Wrappings: []models.WrapperrHistoryEntry{},
|
|
}
|
|
|
|
// Push to array
|
|
newUserArray = append(newUserArray, wrapperrUser)
|
|
} else {
|
|
// If any server is "active", the user is active
|
|
if newUserArray[userIndex].Active == false && userGroup.TautulliUser.IsActive == 1 {
|
|
newUserArray[userIndex].Active = true
|
|
}
|
|
|
|
// Add new server
|
|
newUserArray[userIndex].TautulliServers = append(newUserArray[userIndex].TautulliServers, userGroup.TautulliServer)
|
|
}
|
|
}
|
|
|
|
// Update every new Wrapperr user with new object
|
|
for _, user := range newUserArray {
|
|
_, err = UsersGetUser(user.UserID)
|
|
if err != nil {
|
|
err = UsersSaveUserEntry(user)
|
|
if err != nil {
|
|
log.Println("Failed to save new user. Error: " + err.Error())
|
|
return errors.New("Failed to save new user.")
|
|
}
|
|
} else {
|
|
err = UsersUpdateUser(user.UserID, user.FriendlyName, user.User, user.Email, user.Active, user.TautulliServers)
|
|
if err != nil {
|
|
log.Println("Failed to update user. Error: " + err.Error())
|
|
return errors.New("Failed to update user.")
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|