172 lines
5.5 KiB
Go
172 lines
5.5 KiB
Go
|
|
||
|
// mateCriteria provides functions to read/create profile criterias for server requests
|
||
|
// These are used to filter mate profiles from a host using the user's desires
|
||
|
// They have their own encoding
|
||
|
|
||
|
package mateCriteria
|
||
|
|
||
|
import "seekia/internal/desires/mateDesires"
|
||
|
import "seekia/internal/encoding"
|
||
|
import "seekia/internal/profiles/calculatedAttributes"
|
||
|
import "seekia/internal/profiles/readProfiles"
|
||
|
|
||
|
import messagepack "github.com/vmihailenco/msgpack/v5"
|
||
|
|
||
|
import "strings"
|
||
|
import "errors"
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Criteria is valid
|
||
|
// -bool: Profile fulfills criteria
|
||
|
// -error
|
||
|
func CheckIfMateProfileFulfillsCriteria(allowParameters bool, profileVersion int, rawProfileMap map[int]messagepack.RawMessage, criteriaBytes []byte)(bool, bool, error){
|
||
|
|
||
|
profileIsDisabled, _, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(rawProfileMap, "Disabled")
|
||
|
if (err != nil) { return false, false, err }
|
||
|
if (profileIsDisabled == true){
|
||
|
return true, false, nil
|
||
|
}
|
||
|
|
||
|
exists, profileType, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(rawProfileMap, "ProfileType")
|
||
|
if (err != nil) { return false, false, err }
|
||
|
if (exists == false){
|
||
|
return false, false, errors.New("CheckIfMateProfileFulfillsCriteria called profile missing ProfileType.")
|
||
|
}
|
||
|
if (profileType != "Mate"){
|
||
|
return false, false, errors.New("CheckIfMateProfileFulfillsCriteria called with non-mate profileType: " + profileType)
|
||
|
}
|
||
|
|
||
|
getAnyProfileAttributeFunction, err := calculatedAttributes.GetRetrieveAnyProfileAttributeIncludingCalculatedFunction(profileVersion, rawProfileMap)
|
||
|
if (err != nil) { return false, false, err }
|
||
|
|
||
|
criteriaMap := make(map[string]string)
|
||
|
|
||
|
err = encoding.DecodeMessagePackBytes(false, criteriaBytes, &criteriaMap)
|
||
|
if (err != nil) { return false, false, err }
|
||
|
|
||
|
getAnyDesireFunction := func(desireName string)(bool, string, error){
|
||
|
|
||
|
desireValue, exists := criteriaMap[desireName]
|
||
|
if (exists == false){
|
||
|
return false, "", nil
|
||
|
}
|
||
|
return true, desireValue, nil
|
||
|
}
|
||
|
|
||
|
allDesiresList := mateDesires.GetAllDesiresList(false)
|
||
|
|
||
|
for _, desireName := range allDesiresList{
|
||
|
|
||
|
if (desireName == "HasMessagedMe" ||
|
||
|
desireName == "IHaveMessaged" ||
|
||
|
desireName == "HasRejectedMe" ||
|
||
|
desireName == "IsLiked" ||
|
||
|
desireName == "IsIgnored" ||
|
||
|
desireName == "IsMyContact" ||
|
||
|
desireName == "Distance" ||
|
||
|
desireName == "OffspringProbabilityOfAnyMonogenicDisease"){
|
||
|
// These desires are not used in criteria
|
||
|
continue
|
||
|
}
|
||
|
if (desireName == "Wealth"){
|
||
|
// Criteria does not use the Wealth desire. It instead uses the WealthInGold desire
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
desireIsFilterAll := strings.HasSuffix(desireName, "_FilterAll")
|
||
|
if (desireIsFilterAll == true){
|
||
|
// We use these to convey if a desire is a FilterAll desire
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
desireIsRequireResponse := strings.HasSuffix(desireName, "_RequireResponse")
|
||
|
if (desireIsRequireResponse == true){
|
||
|
// We use these to convey if a desire is a ResponseRequired desire
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if (allowParameters == false){
|
||
|
|
||
|
// We do not allow reliance on parameters to determine if the profile passes criteria
|
||
|
// This will only be false when we are trying to determine if a host is malicious
|
||
|
// A host's parameters might be different than our own
|
||
|
// Without this bool, we could falsely believe that a host was malicious
|
||
|
// The host could serve us a profile that fulfills our criteria when using their parameters,
|
||
|
// while our parameters would result in the profile not fulfilling our criteria
|
||
|
|
||
|
if (desireName == "WealthInGold"){
|
||
|
// Calculating this requires reliance on the currency exchange rate parameters
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
desireAllowsResponseRequired, attributeNameToCheck := mateDesires.CheckIfDesireAllowsRequireResponse(desireName)
|
||
|
if (desireAllowsResponseRequired == true){
|
||
|
|
||
|
// We check to see if the user requires a response
|
||
|
|
||
|
getRequireResponseBool := func()bool{
|
||
|
requireResponseStatus, exists := criteriaMap[desireName + "_RequireResponse"]
|
||
|
if (exists == true && requireResponseStatus == "Yes"){
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
requireResponseBool := getRequireResponseBool()
|
||
|
|
||
|
profileContainsAttribute, _, _, err := getAnyProfileAttributeFunction(attributeNameToCheck)
|
||
|
if (err != nil) { return false, false, err }
|
||
|
if (profileContainsAttribute == false){
|
||
|
|
||
|
// Profile is missing a response
|
||
|
|
||
|
if (requireResponseBool == true){
|
||
|
// Criteria requires a response
|
||
|
return true, false, nil
|
||
|
}
|
||
|
// Criteria does not require a response
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
getFilterAllBool := func()bool{
|
||
|
filterAllStatus, exists := criteriaMap[desireName + "_FilterAll"]
|
||
|
if (exists == true && filterAllStatus == "Yes"){
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
filterAllBool := getFilterAllBool()
|
||
|
if (filterAllBool == false){
|
||
|
// User passes this desire
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
anyDesireExists, desireIsValid, userResponseExists, userFulfillsDesire, err := mateDesires.CheckIfMateProfileFulfillsDesire(desireName, getAnyDesireFunction, getAnyProfileAttributeFunction)
|
||
|
if (err != nil) { return false, false, err }
|
||
|
if (anyDesireExists == false){
|
||
|
continue
|
||
|
}
|
||
|
if (desireIsValid == false){
|
||
|
// Criteria is invalid. Requestor must be malicious (or a bug in Seekia).
|
||
|
return false, false, nil
|
||
|
}
|
||
|
if (userResponseExists == false){
|
||
|
// We already checked for this earlier
|
||
|
return false, false, errors.New("CheckIfMateProfileFulfillsDesire says user has no response, but we already checked.")
|
||
|
}
|
||
|
if (userFulfillsDesire == false){
|
||
|
return true, false, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Profile passed all desires
|
||
|
|
||
|
return true, true, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
|