569 lines
18 KiB
Go
569 lines
18 KiB
Go
|
|
// viewedHosts provides functions to generate and retrieve the viewedHosts list
|
|
// This list is used to browse hosts on the View Hosts page
|
|
|
|
package viewedHosts
|
|
|
|
import "seekia/internal/appMemory"
|
|
import "seekia/internal/encoding"
|
|
import "seekia/internal/helpers"
|
|
import "seekia/internal/identity"
|
|
import "seekia/internal/myDatastores/myList"
|
|
import "seekia/internal/myDatastores/myMap"
|
|
import "seekia/internal/mySettings"
|
|
import "seekia/internal/network/enabledHosts"
|
|
import "seekia/internal/profiles/viewableProfiles"
|
|
|
|
import "slices"
|
|
import "sync"
|
|
import "errors"
|
|
|
|
// This mutex will be locked whenever the viewed hosts list is being updated
|
|
var updatingViewedHostsMutex sync.Mutex
|
|
|
|
var viewedHostsListDatastore *myList.MyList
|
|
|
|
var viewedHostsFiltersMapDatastore *myMap.MyMap
|
|
|
|
// This function must be called whenever an app user signs in
|
|
func InitializeViewedHostsDatastores()error{
|
|
|
|
updatingViewedHostsMutex.Lock()
|
|
defer updatingViewedHostsMutex.Unlock()
|
|
|
|
newViewedHostsListDatastore, err := myList.CreateNewList("ViewedHosts")
|
|
if (err != nil) { return err }
|
|
|
|
newViewedHostsFiltersMapDatastore, err := myMap.CreateNewMap("ViewedHostsFilters")
|
|
if (err != nil) { return err }
|
|
|
|
viewedHostsListDatastore = newViewedHostsListDatastore
|
|
|
|
viewedHostsFiltersMapDatastore = newViewedHostsFiltersMapDatastore
|
|
|
|
return nil
|
|
}
|
|
|
|
|
|
func GetViewedHostsSortByAttribute()(string, error){
|
|
|
|
exists, currentAttribute, err := mySettings.GetSetting("ViewedHostsSortByAttribute")
|
|
if (err != nil) { return "", err }
|
|
if (exists == false){
|
|
return "BanAdvocates", nil
|
|
}
|
|
|
|
return currentAttribute, nil
|
|
}
|
|
|
|
func GetViewedHostsSortDirection()(string, error){
|
|
|
|
exists, sortDirection, err := mySettings.GetSetting("ViewedHostsSortDirection")
|
|
if (err != nil) { return "", err }
|
|
if (exists == false){
|
|
return "Descending", nil
|
|
}
|
|
if (sortDirection != "Ascending" && sortDirection != "Descending"){
|
|
return "", errors.New("MySettings contains invalid ViewedHostsSortDirection: " + sortDirection)
|
|
}
|
|
|
|
return sortDirection, nil
|
|
}
|
|
|
|
// Will need a refresh any time a new host profile is downloaded
|
|
func CheckIfViewedHostsNeedsRefresh()(bool, error){
|
|
|
|
exists, needsRefresh, err := mySettings.GetSetting("ViewedHostsNeedsRefreshYesNo")
|
|
if (err != nil) { return true, err }
|
|
if (exists == true && needsRefresh == "No") {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
|
|
func GetViewedHostsAreReadyStatus(networkType byte)(bool, error){
|
|
|
|
isValid := helpers.VerifyNetworkType(networkType)
|
|
if (isValid == false){
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
return false, errors.New("GetViewedHostsAreReadyStatus called with invalid networkType: " + networkTypeString)
|
|
}
|
|
|
|
exists, hostsGeneratedStatus, err := mySettings.GetSetting("ViewedHostsGeneratedStatus")
|
|
if (err != nil) { return false, err }
|
|
if (exists == false || hostsGeneratedStatus != "Yes"){
|
|
return false, nil
|
|
}
|
|
|
|
exists, hostsSortedStatus, err := mySettings.GetSetting("ViewedHostsSortedStatus")
|
|
if (err != nil) { return false, err }
|
|
if (exists == false || hostsSortedStatus != "Yes"){
|
|
return false, nil
|
|
}
|
|
|
|
exists, viewedHostsNetworkTypeString, err := mySettings.GetSetting("ViewedHostsNetworkType")
|
|
if (err != nil) { return false, err }
|
|
if (exists == false){
|
|
// This should not happen, because ViewedHostsNetworkType is created whenever viewedHosts are generated
|
|
return false, errors.New("mySettings missing ViewedHostsNetworkType when ViewedHostsGeneratedStatus exists.")
|
|
}
|
|
|
|
viewedHostsNetworkType, err := helpers.ConvertNetworkTypeStringToByte(viewedHostsNetworkTypeString)
|
|
if (err != nil) {
|
|
return false, errors.New("mySettings contains invalid ViewedHostsNetworkType: " + viewedHostsNetworkTypeString)
|
|
}
|
|
if (viewedHostsNetworkType != networkType){
|
|
// ViewedHosts were generated for a different networkType
|
|
// This should never happen, because we will always set ViewedHostsGeneratedStatus to No when we switch network types,
|
|
// and we will always call the GetViewedHostsAreReadyStatus function with the current appNetworkType
|
|
|
|
//TODO: Log this.
|
|
|
|
err := mySettings.SetSetting("ViewedHostsGeneratedStatus", "No")
|
|
if (err != nil) { return false, err }
|
|
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// Outputs:
|
|
// -bool: Viewed hosts list is ready
|
|
// -[][16]byte: Viewed host identity hashes list
|
|
// -error
|
|
func GetViewedHostsList(networkType byte)(bool, [][16]byte, error){
|
|
|
|
isValid := helpers.VerifyNetworkType(networkType)
|
|
if (isValid == false){
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
return false, nil, errors.New("GetViewedHostsList called with invalid networkType: " + networkTypeString)
|
|
}
|
|
|
|
areReady, err := GetViewedHostsAreReadyStatus(networkType)
|
|
if (err != nil) { return false, nil, err }
|
|
if (areReady == false){
|
|
return false, nil, nil
|
|
}
|
|
|
|
readyHostIdentityHashesList, err := viewedHostsListDatastore.GetList()
|
|
if (err != nil) { return false, nil, err }
|
|
|
|
viewedHostsList := make([][16]byte, 0, len(readyHostIdentityHashesList))
|
|
|
|
for _, hostIdentityHashString := range readyHostIdentityHashesList{
|
|
|
|
hostIdentityHash, identityType, err := identity.ReadIdentityHashString(hostIdentityHashString)
|
|
if (err != nil){
|
|
return false, nil, errors.New("viewedHostsListDatastore contains invalid identity hash: " + hostIdentityHashString)
|
|
}
|
|
if (identityType != "Host"){
|
|
return false, nil, errors.New("viewedHostsListDatastore contains non-Host identity hash: " + identityType)
|
|
}
|
|
|
|
viewedHostsList = append(viewedHostsList, hostIdentityHash)
|
|
}
|
|
|
|
return true, viewedHostsList, nil
|
|
}
|
|
|
|
// This function returns the number of viewed hosts
|
|
// It can be called before the list is ready (after being generated, but before being sorted)
|
|
func GetNumberOfGeneratedViewedHosts()(int, error){
|
|
|
|
currentViewedHostsList, err := viewedHostsListDatastore.GetList()
|
|
if (err != nil) { return 0, err }
|
|
|
|
lengthInt := len(currentViewedHostsList)
|
|
|
|
return lengthInt, nil
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Build encountered error
|
|
// -string: Error encountered
|
|
// -bool: Build is stopped (will be stopped if user went to different page)
|
|
// -bool: Viewed hosts are ready
|
|
// -float64: Progress status (0 - 1)
|
|
// -error
|
|
func GetViewedHostsBuildStatus(networkType byte)(bool, string, bool, bool, float64, error){
|
|
|
|
isValid := helpers.VerifyNetworkType(networkType)
|
|
if (isValid == false){
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
return false, "", false, false, 0, errors.New("GetViewedHostsBuildStatus called with invalid networkType: " + networkTypeString)
|
|
}
|
|
|
|
exists, encounteredError := appMemory.GetMemoryEntry("ViewedHostsBuildEncounteredError")
|
|
if (exists == false){
|
|
// No build exists. A build has not been started since Seekia was started
|
|
return false, "", false, false, 0, nil
|
|
}
|
|
if (encounteredError == "Yes"){
|
|
exists, errorEncountered := appMemory.GetMemoryEntry("ViewedHostsBuildError")
|
|
if (exists == false){
|
|
return false, "", false, false, 0, errors.New("Viewed Hosts build encountered error is yes, but no error exists.")
|
|
}
|
|
|
|
return true, errorEncountered, false, false, 0, nil
|
|
}
|
|
|
|
isStopped := CheckIfBuildViewedHostsIsStopped()
|
|
if (isStopped == true){
|
|
return false, "", true, false, 0, nil
|
|
}
|
|
|
|
hostsAreReadyBool, err := GetViewedHostsAreReadyStatus(networkType)
|
|
if (err != nil) { return false, "", false, false, 0, err }
|
|
if (hostsAreReadyBool == true){
|
|
|
|
return false, "", false, true, 1, nil
|
|
}
|
|
|
|
exists, currentPercentageString := appMemory.GetMemoryEntry("ViewedHostsReadyProgressStatus")
|
|
if (exists == false){
|
|
// No build exists. A build has not been started since Seekia was started
|
|
return false, "", false, false, 0, nil
|
|
}
|
|
|
|
currentPercentageFloat, err := helpers.ConvertStringToFloat64(currentPercentageString)
|
|
if (err != nil){
|
|
return false, "", false, false, 0, errors.New("ViewedHostsReadyProgressStatus is not a float: " + currentPercentageString)
|
|
}
|
|
|
|
return false, "", false, false, currentPercentageFloat, nil
|
|
}
|
|
|
|
func CheckIfBuildViewedHostsIsStopped()bool{
|
|
|
|
exists, buildStoppedStatus := appMemory.GetMemoryEntry("StopBuildViewedHostsYesNo")
|
|
if (exists == false || buildStoppedStatus != "No") {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
|
|
// This function will cancel the current build (if one is running)
|
|
// It will then start updating our viewed hosts
|
|
func StartUpdatingViewedHosts(networkType byte)error{
|
|
|
|
isValid := helpers.VerifyNetworkType(networkType)
|
|
if (isValid == false){
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
return errors.New("StartUpdatingViewedHosts called with invalid networkType: " + networkTypeString)
|
|
}
|
|
|
|
appMemory.SetMemoryEntry("StopBuildViewedHostsYesNo", "Yes")
|
|
|
|
// We wait for any existing build to stop
|
|
updatingViewedHostsMutex.Lock()
|
|
|
|
appMemory.SetMemoryEntry("ViewedHostsBuildEncounteredError", "No")
|
|
appMemory.SetMemoryEntry("ViewedHostsBuildError", "")
|
|
appMemory.SetMemoryEntry("ViewedHostsReadyProgressStatus", "0")
|
|
|
|
appMemory.SetMemoryEntry("StopBuildViewedHostsYesNo", "No")
|
|
|
|
updateViewedHosts := func()error{
|
|
|
|
getViewedHostsNeedToBeGeneratedStatus := func()(bool, error){
|
|
|
|
exists, hostsGeneratedStatus, err := mySettings.GetSetting("ViewedHostsGeneratedStatus")
|
|
if (err != nil) { return false, err }
|
|
if (exists == false || hostsGeneratedStatus != "Yes"){
|
|
return true, nil
|
|
}
|
|
|
|
exists, viewedHostsNetworkTypeString, err := mySettings.GetSetting("ViewedHostsNetworkType")
|
|
if (err != nil) { return false, err }
|
|
if (exists == false){
|
|
// This should not happen, because ViewedHostsNetworkType is set to Yes whenever viewed hosts are generated
|
|
return false, errors.New("ViewedHostsNetworkType missing when ViewedHostsGeneratedStatus exists.")
|
|
}
|
|
|
|
viewedHostsNetworkType, err := helpers.ConvertNetworkTypeStringToByte(viewedHostsNetworkTypeString)
|
|
if (err != nil) {
|
|
return false, errors.New("mySettings contains invalid ViewedHostsNetworkType: " + viewedHostsNetworkTypeString)
|
|
}
|
|
if (viewedHostsNetworkType != networkType){
|
|
// This should not happen, because ViewedHostsGeneratedStatus should be set to No whenever app network type is changed,
|
|
// and StartUpdatingViewedHosts should only be called with the current app network type.
|
|
return true, nil
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
viewedHostsNeedToBeGenerated, err := getViewedHostsNeedToBeGeneratedStatus()
|
|
if (err != nil) { return err }
|
|
if (viewedHostsNeedToBeGenerated == true){
|
|
|
|
err := mySettings.SetSetting("ViewedHostsSortedStatus", "No")
|
|
if (err != nil) { return err }
|
|
|
|
enabledHostsList, err := enabledHosts.GetEnabledHostsList(false, networkType)
|
|
if (err != nil) { return err }
|
|
|
|
numberOfEnabledHosts := len(enabledHostsList)
|
|
|
|
generatedHostIdentityHashesList := make([]string, 0)
|
|
|
|
for index, hostIdentityHash := range enabledHostsList{
|
|
|
|
hostPassesFilters, err := checkIfHostPassesFilters(hostIdentityHash)
|
|
if (err != nil) { return err }
|
|
if (hostPassesFilters == true){
|
|
|
|
hostIdentityHashString, identityType, err := identity.EncodeIdentityHashBytesToString(hostIdentityHash)
|
|
if (err != nil) {
|
|
hostIdentityHashHex := encoding.EncodeBytesToHexString(hostIdentityHash[:])
|
|
return errors.New("enabledHostsList contains invalid identity hash: " + hostIdentityHashHex)
|
|
}
|
|
if (identityType != "Host"){
|
|
return errors.New("enabledHostsList contains non-host identity hash: " + identityType)
|
|
}
|
|
|
|
generatedHostIdentityHashesList = append(generatedHostIdentityHashesList, hostIdentityHashString)
|
|
}
|
|
|
|
isStopped := CheckIfBuildViewedHostsIsStopped()
|
|
if (isStopped == true){
|
|
return nil
|
|
}
|
|
|
|
progressPercentage, err := helpers.ScaleNumberProportionally(true, index, 0, numberOfEnabledHosts-1, 0, 50)
|
|
if (err != nil) { return err }
|
|
|
|
progressFloat := float64(progressPercentage)/100
|
|
hostsReadyProgressPercentage := helpers.ConvertFloat64ToString(progressFloat)
|
|
|
|
appMemory.SetMemoryEntry("ViewedHostsReadyProgressStatus", hostsReadyProgressPercentage)
|
|
}
|
|
|
|
err = viewedHostsListDatastore.OverwriteList(generatedHostIdentityHashesList)
|
|
if (err != nil) { return err }
|
|
|
|
err = mySettings.SetSetting("ViewedHostsGeneratedStatus", "Yes")
|
|
if (err != nil) { return err }
|
|
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
|
|
err = mySettings.SetSetting("ViewedHostsNetworkType", networkTypeString)
|
|
if (err != nil) { return err }
|
|
}
|
|
|
|
appMemory.SetMemoryEntry("ViewedHostsReadyProgressStatus", "0.50")
|
|
|
|
isStopped := CheckIfBuildViewedHostsIsStopped()
|
|
if (isStopped == true){
|
|
return nil
|
|
}
|
|
|
|
hostsReadyStatus, err := GetViewedHostsAreReadyStatus(networkType)
|
|
if (err != nil) { return err }
|
|
if (hostsReadyStatus == false){
|
|
|
|
// Now we sort hosts
|
|
|
|
currentSortByAttribute, err := GetViewedHostsSortByAttribute()
|
|
if (err != nil) { return err }
|
|
currentSortDirection, err := GetViewedHostsSortDirection()
|
|
if (err != nil) { return err }
|
|
|
|
currentViewedHostsList, err := viewedHostsListDatastore.GetList()
|
|
if (err != nil) { return err }
|
|
|
|
// We use this map to make sure there are no duplicate hosts
|
|
// This should never happen, unless the user's stored list was edited or there is a bug
|
|
allHostsMap := make(map[[16]byte]struct{})
|
|
|
|
// Map structure: Host Identity Hash -> Sort By Attribute Value
|
|
hostAttributeValuesMap := make(map[string]float64)
|
|
|
|
maximumIndex := len(currentViewedHostsList) - 1
|
|
|
|
for index, hostIdentityHashString := range currentViewedHostsList{
|
|
|
|
hostIdentityHash, identityType, err := identity.ReadIdentityHashString(hostIdentityHashString)
|
|
if (err != nil){
|
|
return errors.New("currentViewedHostsList contains invalid identity hash: " + hostIdentityHashString)
|
|
}
|
|
if (identityType != "Host"){
|
|
return errors.New("currentViewedHostsList contains invalid non-Host identity: " + identityType)
|
|
}
|
|
|
|
_, exists := allHostsMap[hostIdentityHash]
|
|
if (exists == true){
|
|
return errors.New("currentViewedHostsList contains duplicate host identity hash.")
|
|
}
|
|
allHostsMap[hostIdentityHash] = struct{}{}
|
|
|
|
profileExists, _, attributeExists, attributeValue, err := viewableProfiles.GetAnyAttributeFromNewestViewableUserProfile(hostIdentityHash, networkType, currentSortByAttribute, true, true, true)
|
|
if (err != nil) { return err }
|
|
if (profileExists == true && attributeExists == true){
|
|
|
|
attributeValueFloat, err := helpers.ConvertStringToFloat64(attributeValue)
|
|
if (err != nil) {
|
|
return errors.New("Viewed hosts attribute cannot be converted to float.")
|
|
}
|
|
|
|
hostAttributeValuesMap[hostIdentityHashString] = attributeValueFloat
|
|
}
|
|
|
|
isStopped := CheckIfBuildViewedHostsIsStopped()
|
|
if (isStopped == true){
|
|
return nil
|
|
}
|
|
|
|
newScaledPercentageInt, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 50, 80)
|
|
if (err != nil) { return err }
|
|
|
|
newProgressFloat := float64(newScaledPercentageInt)/100
|
|
|
|
newProgressString := helpers.ConvertFloat64ToString(newProgressFloat)
|
|
|
|
appMemory.SetMemoryEntry("ViewedHostsReadyProgressStatus", newProgressString)
|
|
}
|
|
|
|
compareHostsFunction := func(identityHashA string, identityHashB string)int{
|
|
|
|
if (identityHashA == identityHashB){
|
|
panic("compareHostsFunction called with duplicate hosts.")
|
|
}
|
|
|
|
attributeValueA, attributeValueAExists := hostAttributeValuesMap[identityHashA]
|
|
|
|
attributeValueB, attributeValueBExists := hostAttributeValuesMap[identityHashB]
|
|
|
|
if (attributeValueAExists == false && attributeValueBExists == false){
|
|
|
|
// We don't know the attribute value for either host
|
|
// We sort hosts in unicode order
|
|
if (identityHashA < identityHashB){
|
|
return -1
|
|
}
|
|
return 1
|
|
|
|
} else if (attributeValueAExists == true && attributeValueBExists == false){
|
|
|
|
// We sort unknown attribute hosts to the back of the list
|
|
|
|
return -1
|
|
|
|
} else if (attributeValueAExists == false && attributeValueBExists == true){
|
|
|
|
return 1
|
|
}
|
|
|
|
// Both attribute values exist
|
|
|
|
if (attributeValueA == attributeValueB){
|
|
// We sort identity hashes in unicode order
|
|
if (identityHashA < identityHashB){
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
|
|
if (attributeValueA < attributeValueB){
|
|
|
|
if (currentSortDirection == "Ascending"){
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
if (currentSortDirection == "Ascending"){
|
|
return 1
|
|
}
|
|
return -1
|
|
}
|
|
|
|
slices.SortFunc(currentViewedHostsList, compareHostsFunction)
|
|
|
|
err = viewedHostsListDatastore.OverwriteList(currentViewedHostsList)
|
|
if (err != nil) { return err }
|
|
|
|
err = mySettings.SetSetting("ViewedHostsSortedStatus", "Yes")
|
|
if (err != nil) { return err }
|
|
}
|
|
|
|
appMemory.SetMemoryEntry("ViewedHostsReadyProgressStatus", "1")
|
|
|
|
err = mySettings.SetSetting("ViewedHostsNeedsRefreshYesNo", "No")
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
updateFunction := func(){
|
|
|
|
err := updateViewedHosts()
|
|
if (err != nil){
|
|
appMemory.SetMemoryEntry("ViewedHostsBuildEncounteredError", "Yes")
|
|
appMemory.SetMemoryEntry("ViewedHostsBuildError", err.Error())
|
|
}
|
|
|
|
updatingViewedHostsMutex.Unlock()
|
|
}
|
|
|
|
go updateFunction()
|
|
|
|
return nil
|
|
}
|
|
|
|
|
|
func checkIfHostPassesFilters(inputHostIdentityHash [16]byte)(bool, error){
|
|
|
|
//TODO
|
|
|
|
return true, nil
|
|
}
|
|
|
|
|
|
|
|
func GetNumberOfActiveHostFilters()(int, error){
|
|
|
|
numberOfActiveFilters := 0
|
|
|
|
//TODO
|
|
|
|
return numberOfActiveFilters, nil
|
|
}
|
|
|
|
|
|
func SetHostFilterOnOffStatus(filterName string, filterOnOffStatus bool)error{
|
|
|
|
filterOnOffStatusString := helpers.ConvertBoolToYesOrNoString(filterOnOffStatus)
|
|
|
|
err := viewedHostsFiltersMapDatastore.SetMapEntry(filterName, filterOnOffStatusString)
|
|
if (err != nil) { return err }
|
|
|
|
err = mySettings.SetSetting("ViewedHostsGeneratedStatus", "No")
|
|
if (err != nil) { return err }
|
|
|
|
return nil
|
|
}
|
|
|
|
|
|
func GetHostFilterOnOffStatus(filterName string)(bool, error){
|
|
|
|
filterStatusExists, currentFilterStatus, err := viewedHostsFiltersMapDatastore.GetMapEntry(filterName)
|
|
if (err != nil) { return false, err }
|
|
if (filterStatusExists == false){
|
|
return false, nil
|
|
}
|
|
|
|
filterOnOffStatusBool, err := helpers.ConvertYesOrNoStringToBool(currentFilterStatus)
|
|
if (err != nil) { return false, err }
|
|
|
|
return filterOnOffStatusBool, nil
|
|
}
|
|
|
|
|
|
|
|
|
|
|