660 lines
26 KiB
Go
660 lines
26 KiB
Go
|
|
// myUnreviewed provides functions to get message, profile, and attribute hashes the user has not yet reviewed
|
|
|
|
package myUnreviewed
|
|
|
|
// Each content to review has a priority
|
|
// Content that has been reviewed by the fewest moderators will have the highest priority
|
|
// Content that has been sufficiently reviewed by other moderators will have a lower priority
|
|
// Unreviewed content will be retained by the client until it expires from the network
|
|
|
|
//TODO: Add filter for ProfileLanguage, so user will only review content they understand
|
|
//TODO: Add higher priority for reported profiles?
|
|
//TODO: Add limit where if it has found enough options to review, break
|
|
// -This means we will only need to search through a maximum number of contents before we settle on one that is the highest priority
|
|
// -It may not be the highest priority out of all contents, but it will be the highest priority out of a subset of contents
|
|
// -This would make the retrieval faster
|
|
//TODO: Extract errantProfiles from identity reviews and treat them like profile reports
|
|
//TODO: Add a toggle to consider/not consider profiles/messages reviewed if we have banned the author
|
|
//TODO: Build a cache to store the highest priority unreviewed content hashes
|
|
// -This way we only have to repeat the process after the highest priority unreviewed content has been reviewed
|
|
|
|
import "seekia/internal/moderation/myReviews"
|
|
import "seekia/internal/moderation/verifiedVerdict"
|
|
import "seekia/internal/moderation/mySkippedContent"
|
|
import "seekia/internal/moderation/myHiddenContent"
|
|
import "seekia/internal/profiles/profileStorage"
|
|
import "seekia/internal/profiles/readProfiles"
|
|
import "seekia/internal/messaging/chatMessageStorage"
|
|
import "seekia/internal/myRanges"
|
|
import "seekia/internal/badgerDatabase"
|
|
import "seekia/internal/helpers"
|
|
|
|
import "strings"
|
|
import "errors"
|
|
import "slices"
|
|
|
|
//Outputs:
|
|
// -bool: Any unreviewed message hash exists
|
|
// -[26]byte: Message Hash
|
|
// -error
|
|
func GetMyHighestPriorityUnreviewedMessageHash(networkType byte, imageOrText string)(bool, [26]byte, error){
|
|
|
|
isValid := helpers.VerifyNetworkType(networkType)
|
|
if (isValid == false){
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
return false, [26]byte{}, errors.New("GetMyHighestPriorityUnreviewedMessageHash called with invalid networkType: " + networkTypeString)
|
|
}
|
|
|
|
if (imageOrText != "Image" && imageOrText != "Text"){
|
|
return false, [26]byte{}, errors.New("GetMyHighestPriorityUnreviewedMessageHash called with invalid imageOrText: " + imageOrText)
|
|
}
|
|
|
|
allReviewedMessageHashesList, err := badgerDatabase.GetAllReviewedMessageHashes()
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
|
|
allReportedMessageHashesList, err := badgerDatabase.GetAllReportedMessageHashes()
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
|
|
allMessageHashesList := helpers.CombineTwoListsAndAvoidDuplicates(allReviewedMessageHashesList, allReportedMessageHashesList)
|
|
|
|
myModeratorIdentityExists, myReviewedMessageHashesList, err := myReviews.GetMyReviewedMessageHashesList(networkType)
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
if (myModeratorIdentityExists == false) {
|
|
return false, [26]byte{}, errors.New("GetMyHighestPriorityUnreviewedMessageHash called when my moderator identity does not exist.")
|
|
}
|
|
|
|
myIdentityExists, myBannedIdentityHashesList, err := myReviews.GetMyBannedIdentityHashesList(networkType)
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
if (myIdentityExists == false) {
|
|
return false, [26]byte{}, errors.New("My moderator identity not found after being found already.")
|
|
}
|
|
|
|
leastReviewedMessageFound := false
|
|
var leastReviewedMessageHash [26]byte
|
|
leastReviewedMessageNumberOfReviews := 0
|
|
|
|
// We will only serve skipped messages if all messages remaining to review are skipped
|
|
// This bool will be true, unless we encounter a non-skipped message
|
|
// If we do, we will overwrite the existing least reviewed messageHash and reject all skipped messages going forward
|
|
allMessagesAreSkipped := true
|
|
|
|
for _, messageHash := range allMessageHashesList{
|
|
|
|
iHaveReviewed := slices.Contains(myReviewedMessageHashesList, messageHash)
|
|
if (iHaveReviewed == true){
|
|
continue
|
|
}
|
|
|
|
messageMetadataIsKnown, _, messageNetworkType, messageInbox, _, downloadingRequiredReviews, parametersExist, _, numberApprove, numberBan, _, _, _, _, err := verifiedVerdict.GetVerifiedMessageVerdict(messageHash)
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
if (messageMetadataIsKnown == false){
|
|
// We do not have the message downloaded. It is not eligible for review
|
|
continue
|
|
}
|
|
if (messageNetworkType != networkType){
|
|
// Message belongs to a different networkType.
|
|
continue
|
|
}
|
|
if (downloadingRequiredReviews == false){
|
|
// Message is not within our moderation range. Skip message
|
|
continue
|
|
}
|
|
if (parametersExist == false){
|
|
// Moderation parameters need to be downloaded to moderate content. Stop searching for messages.
|
|
break
|
|
}
|
|
moderatorModeEnabled, isInMyModerationRange, err := myRanges.CheckIfMessageInboxIsWithinMyModerationRange(messageInbox)
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
if (moderatorModeEnabled == false){
|
|
// Mode must have been disabled during generation.
|
|
// No messages to review.
|
|
return false, [26]byte{}, nil
|
|
}
|
|
if (isInMyModerationRange == false){
|
|
// Message is outside of our moderation range. Skip it.
|
|
continue
|
|
}
|
|
|
|
messageExists, cipherKeyFound, messageIsDecryptable, _, senderIdentityHash, messageCommunication, err := chatMessageStorage.GetDecryptedMessageForModeration(messageHash)
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
if (messageExists == false){
|
|
// We cannot review messages which we do not have downloaded
|
|
continue
|
|
}
|
|
if (cipherKeyFound == false){
|
|
// We do not have a cipher key
|
|
// We cannot view the message contents
|
|
continue
|
|
}
|
|
if (messageIsDecryptable == false){
|
|
// We cannot decrypt the message
|
|
// Message author must be malicious
|
|
// We should ban these kinds of messages automatically, and ban anyone who approves them
|
|
continue
|
|
}
|
|
|
|
identityIsBanned := slices.Contains(myBannedIdentityHashesList, senderIdentityHash)
|
|
if (identityIsBanned == true){
|
|
// We already banned the author of the message
|
|
// Review is not necessary
|
|
continue
|
|
}
|
|
|
|
getImageOrTextStatus := func()string{
|
|
isImage := strings.HasPrefix(messageCommunication, ">!>Photo=")
|
|
if (isImage == true){
|
|
return "Image"
|
|
}
|
|
return "Text"
|
|
}
|
|
|
|
imageOrTextStatus := getImageOrTextStatus()
|
|
if (imageOrTextStatus != imageOrText){
|
|
// Message is not the imageOrText that we are interested in.
|
|
continue
|
|
}
|
|
|
|
messageIsHidden, _, _, err := myHiddenContent.CheckIfMessageIsHidden(messageHash)
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
if (messageIsHidden == true){
|
|
// The moderator has hidden this message.
|
|
continue
|
|
}
|
|
|
|
messageIsSkipped, _, _, err := mySkippedContent.CheckIfMessageIsSkipped(messageHash)
|
|
if (err != nil) { return false, [26]byte{}, err }
|
|
if (messageIsSkipped == false){
|
|
if (allMessagesAreSkipped == true){
|
|
|
|
// This is the first non-skipped message we have encountered
|
|
// This may not be the first message we have encountered
|
|
// Thus, we must clear any previously found content
|
|
leastReviewedMessageFound = false
|
|
leastReviewedMessageHash = [26]byte{}
|
|
leastReviewedMessageNumberOfReviews = 0
|
|
|
|
allMessagesAreSkipped = false
|
|
}
|
|
} else {
|
|
if (allMessagesAreSkipped == false){
|
|
// We have some non-skipped content to review, so we will reject all skipped content
|
|
continue
|
|
}
|
|
}
|
|
|
|
numberOfReviewers := numberApprove + numberBan
|
|
|
|
if (numberOfReviewers == 0 && messageIsSkipped == false){
|
|
// Message is high priority, it has no reviews. We stop searching.
|
|
return true, messageHash, nil
|
|
}
|
|
|
|
if (leastReviewedMessageFound == false || numberOfReviewers < leastReviewedMessageNumberOfReviews){
|
|
leastReviewedMessageFound = true
|
|
leastReviewedMessageHash = messageHash
|
|
leastReviewedMessageNumberOfReviews = numberOfReviewers
|
|
}
|
|
}
|
|
|
|
if (leastReviewedMessageFound == false){
|
|
// Nothing left to review.
|
|
return false, [26]byte{}, nil
|
|
}
|
|
|
|
return true, leastReviewedMessageHash, nil
|
|
}
|
|
|
|
|
|
// This function only returns full profiles that have been banned or reported, without reference to the specific attribute
|
|
// To approve these profiles, moderators must approve the full profile, which requires viewing all attributes of the profile, including canonical ones
|
|
// For profiles whose reviewed attributes have been specified, use GetMyHighestPriorityUnreviewedProfileAttributeHash instead
|
|
//Outputs:
|
|
// -bool: Unreviewed profile exists
|
|
// -[28]byte: Highest priority unreviewed profile hash
|
|
// -error
|
|
func GetMyHighestPriorityUnreviewedProfileHash(profileType string, networkType byte)(bool, [28]byte, error){
|
|
|
|
if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
|
|
return false, [28]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileHash called with invalid profileType: " + profileType)
|
|
}
|
|
|
|
isValid := helpers.VerifyNetworkType(networkType)
|
|
if (isValid == false){
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
return false, [28]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileHash called with invalid networkType: " + networkTypeString)
|
|
}
|
|
|
|
// For each profile, we must determine if we have reviewed it
|
|
// If we have approved/banned the entire profile, then it is considered reviewed
|
|
// If we have banned any attribute of the profile, then it is considered reviewed
|
|
|
|
moderatorIdentityExists, myReviewedProfileHashesMap, err := myReviews.GetMyReviewedProfileHashesMap(profileType, networkType)
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
if (moderatorIdentityExists == false) {
|
|
return false, [28]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileHash called when my moderator identity does not exist.")
|
|
}
|
|
|
|
myIdentityExists, myBannedIdentityHashesList, err := myReviews.GetMyBannedIdentityHashesList(networkType)
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
if (myIdentityExists == false) {
|
|
return false, [28]byte{}, errors.New("My moderator identity not found after being found already.")
|
|
}
|
|
|
|
allReviewedProfileHashesList, err := badgerDatabase.GetAllReviewedProfileHashes()
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
|
|
allReportedProfileHashesList, err := badgerDatabase.GetAllReportedProfileHashes()
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
|
|
allProfileHashesList := helpers.CombineTwoListsAndAvoidDuplicates(allReviewedProfileHashesList, allReportedProfileHashesList)
|
|
|
|
leastReviewedProfileHashFound := false
|
|
var leastReviewedProfileHash [28]byte
|
|
leastReviewedProfileHashReviewersCount := 0
|
|
|
|
// We will only serve skipped profiles if all remaining profiles to review are skipped
|
|
// This bool will be true, unless we encounter a non-skipped profile
|
|
// If we do, we will overwrite the existing least reviewed profileHash and reject all skipped profiles going forward
|
|
allProfilesAreSkipped := true
|
|
|
|
for _, profileHash := range allProfileHashesList{
|
|
|
|
_, iHaveReviewed := myReviewedProfileHashesMap[profileHash]
|
|
if (iHaveReviewed == true){
|
|
// We have already reviewed the profile, or banned one of its attributes
|
|
continue
|
|
}
|
|
|
|
profileIsHidden, _, _, err := myHiddenContent.CheckIfProfileIsHidden(profileHash)
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
if (profileIsHidden == true){
|
|
// The moderator has hidden this profile.
|
|
continue
|
|
}
|
|
|
|
exists, _, err := profileStorage.GetStoredProfile(profileHash)
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
if (exists == false){
|
|
// We need the profile bytes to review the profile
|
|
continue
|
|
}
|
|
|
|
profileIsDisabled, profileMetadataIsKnown, _, profileNetworkType, profileAuthor, _, downloadingRequiredReviews, parametersExist, _, approveAdvocatesCount, banAdvocatesCount, _, _, _, _, _, _, _, _, fullProfileBanAdvocatesCount, _, _, err := verifiedVerdict.GetVerifiedProfileVerdict(profileHash)
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
if (profileIsDisabled == true){
|
|
// No review is needed. Skip
|
|
continue
|
|
}
|
|
if (profileMetadataIsKnown == false){
|
|
// We do not have the profile downloaded, we cannot review it.
|
|
// It must have been deleted after GetAllProfileHashes step.
|
|
continue
|
|
}
|
|
if (profileNetworkType != networkType){
|
|
// Profile belongs to a different networkType.
|
|
continue
|
|
}
|
|
if (downloadingRequiredReviews == false){
|
|
// Message is not within our moderation range. Skip profile
|
|
continue
|
|
}
|
|
if (parametersExist == false){
|
|
// Moderation parameters need to be downloaded to moderate content. Stop searching for profiles.
|
|
return false, [28]byte{}, nil
|
|
}
|
|
|
|
profileIsReported := slices.Contains(allReportedProfileHashesList, profileHash)
|
|
|
|
if (fullProfileBanAdvocatesCount == 0 && profileIsReported == false){
|
|
// Nobody has banned/reported the full profile.
|
|
// This means that the profile can be reviewed by reviewing attributes only
|
|
// We skip this profile
|
|
continue
|
|
}
|
|
|
|
moderatorModeEnabled, isWithinMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyModerationRange(profileAuthor)
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
if (moderatorModeEnabled == false){
|
|
// Mode must have been disabled after previous check. Return no profiles to review.
|
|
return false, [28]byte{}, nil
|
|
}
|
|
if (isWithinMyRange == false){
|
|
// Profile is outside of our moderation range, skip it.
|
|
continue
|
|
}
|
|
|
|
iHaveBannedIdentity := slices.Contains(myBannedIdentityHashesList, profileAuthor)
|
|
if (iHaveBannedIdentity == true){
|
|
// We have banned the profile author
|
|
// Reviewing the profile is not necessary
|
|
continue
|
|
}
|
|
|
|
profileIsSkipped, _, _, err := mySkippedContent.CheckIfProfileIsSkipped(profileHash)
|
|
if (err != nil) { return false, [28]byte{}, err }
|
|
if (profileIsSkipped == false){
|
|
if (allProfilesAreSkipped == true){
|
|
|
|
// This is the first non-skipped profile we have encountered
|
|
// This may not be the first profile we have encountered
|
|
// Thus, we must clear any previously found profiles
|
|
leastReviewedProfileHashFound = false
|
|
leastReviewedProfileHash = [28]byte{}
|
|
leastReviewedProfileHashReviewersCount = 0
|
|
|
|
allProfilesAreSkipped = false
|
|
}
|
|
} else {
|
|
if (allProfilesAreSkipped == false){
|
|
// We have some non-skipped content to review, so we will reject all skipped content
|
|
continue
|
|
}
|
|
}
|
|
|
|
numberOfEligibleReviewers := approveAdvocatesCount + banAdvocatesCount
|
|
|
|
if (approveAdvocatesCount == 0 && profileIsSkipped == false){
|
|
|
|
// This profile has been either been reported or banned, and has no approvals by anyone eligible
|
|
// We consider this the highest priority kind of profile.
|
|
// Stop searching and return this profile
|
|
return true, profileHash, nil
|
|
}
|
|
|
|
if (leastReviewedProfileHashFound == false || leastReviewedProfileHashReviewersCount < numberOfEligibleReviewers){
|
|
leastReviewedProfileHashFound = true
|
|
leastReviewedProfileHash = profileHash
|
|
leastReviewedProfileHashReviewersCount = numberOfEligibleReviewers
|
|
}
|
|
}
|
|
|
|
if (leastReviewedProfileHashFound == false){
|
|
// No profiles to review
|
|
return false, [28]byte{}, nil
|
|
}
|
|
|
|
return true, leastReviewedProfileHash, nil
|
|
}
|
|
|
|
|
|
//Outputs:
|
|
// -bool: Any unreviewed profile attribute hash exists
|
|
// -[27]byte: Profile attribute hash
|
|
// -[28]byte: Profile hash that contained this attribute (there may be many, we will return the first one we encounter)
|
|
// -[16]byte: Profile identity hash that authored the profile
|
|
// -error
|
|
func GetMyHighestPriorityUnreviewedProfileAttributeHash(profileType string, attributeIdentifier int, networkType byte)(bool, [27]byte, [28]byte, [16]byte, error){
|
|
|
|
if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileAttributeHash called with invalid profileType: " + profileType)
|
|
}
|
|
|
|
isValid := helpers.VerifyNetworkType(networkType)
|
|
if (isValid == false){
|
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileAttributeHash called with invalid networkType: " + networkTypeString)
|
|
}
|
|
|
|
myIdentityExists, myReviewedProfileHashesMap, err := myReviews.GetMyReviewedProfileHashesMap(profileType, networkType)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (myIdentityExists == false) {
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileAttributeHash called when my moderator identity does not exist.")
|
|
}
|
|
|
|
myIdentityExists, myReviewedAttributeHashesMap, err := myReviews.GetMyReviewedProfileAttributeHashesMap(networkType)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (myIdentityExists == false) {
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileAttributeHash called when my moderator identity does not exist.")
|
|
}
|
|
|
|
myIdentityExists, myBannedIdentityHashesList, err := myReviews.GetMyBannedIdentityHashesList(networkType)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (myIdentityExists == false) {
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, errors.New("GetMyHighestPriorityUnreviewedProfileAttributeHash called when my moderator identity does not exist.")
|
|
}
|
|
|
|
allReportedAttributeHashesList, err := badgerDatabase.GetAllReportedProfileAttributeHashes()
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
|
|
allProfileHashesList, err := badgerDatabase.GetAllProfileHashes(profileType)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
|
|
// This map will store the attribute hashes we have already checked
|
|
// We need this because the same attribute may exist in multiple profiles
|
|
encounteredAttributeHashesMap := make(map[[27]byte]struct{})
|
|
|
|
// We use the below variables to find the least reviewed (highest priority) attribute hash
|
|
leastReviewedAttributeHashFound := false
|
|
var leastReviewedAttributeHash [27]byte
|
|
var leastReviewedAttributeProfileHash [28]byte
|
|
var leastReviewedAttributeIdentityHash [16]byte
|
|
leastReviewedAttributeHashReviewsCount := 0
|
|
|
|
// We will only serve skipped attributes if all remaining attributes to review are skipped
|
|
// This bool will be true, unless we encounter a non-skipped attribute
|
|
// If we do, we will overwrite the existing least reviewed attributeHash and reject all skipped attributes going forward
|
|
allAttributesAreSkipped := true
|
|
|
|
for _, profileHash := range allProfileHashesList{
|
|
|
|
_, iHaveReviewed := myReviewedProfileHashesMap[profileHash]
|
|
if (iHaveReviewed == true){
|
|
// We have already reviewed this profile.
|
|
// We don't need to review any of its component attributes
|
|
continue
|
|
}
|
|
|
|
exists, _, err := profileStorage.GetStoredProfile(profileHash)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (exists == false){
|
|
// We need the profile bytes to review the profile
|
|
// Profile must have been deleted after we called GetAllProfileHashes
|
|
continue
|
|
}
|
|
|
|
profileIsDisabled, profileMetadataIsKnown, _, profileNetworkType, profileAuthor, profileAttributeHashesMap, downloadingRequiredReviews, parametersExist, _, _, _, _, _, attributeApproveAdvocateCountsMap, attributeBanAdvocateCountsMap, _, _, _, _, _, _, allAttributeBanAdvocatesMap, err := verifiedVerdict.GetVerifiedProfileVerdict(profileHash)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (profileIsDisabled == true){
|
|
// No review is needed. Skip
|
|
continue
|
|
}
|
|
if (profileMetadataIsKnown == false){
|
|
// We do not have the profile downloaded, we cannot review it.
|
|
// It must have been deleted after GetAllProfileHashes step.
|
|
continue
|
|
}
|
|
if (profileNetworkType != networkType){
|
|
// Profile belongs to a different networkType.
|
|
continue
|
|
}
|
|
if (downloadingRequiredReviews == false){
|
|
// Profile is not within our moderation range. Skip message
|
|
continue
|
|
}
|
|
if (parametersExist == false){
|
|
// Moderation parameters need to be downloaded to moderate content. Stop searching for profile attributes.
|
|
break
|
|
}
|
|
moderatorModeEnabled, isWithinMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyModerationRange(profileAuthor)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (moderatorModeEnabled == false){
|
|
// Mode must have been disabled after previous check. Return no profiles to review.
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, nil
|
|
}
|
|
if (isWithinMyRange == false){
|
|
// Profile is outside of our moderation range, skip it.
|
|
continue
|
|
}
|
|
|
|
iHaveBannedIdentity := slices.Contains(myBannedIdentityHashesList, profileAuthor)
|
|
if (iHaveBannedIdentity == true){
|
|
// We have already banned the profile author
|
|
continue
|
|
}
|
|
|
|
attributeHash, exists := profileAttributeHashesMap[attributeIdentifier]
|
|
if (exists == false){
|
|
// This profile does not have the attribute which we want to review
|
|
continue
|
|
}
|
|
|
|
_, hasBeenEncountered := encounteredAttributeHashesMap[attributeHash]
|
|
if (hasBeenEncountered == true){
|
|
// We already dealt with this attribute
|
|
continue
|
|
}
|
|
|
|
encounteredAttributeHashesMap[attributeHash] = struct{}{}
|
|
|
|
_, iHaveReviewed = myReviewedAttributeHashesMap[attributeHash]
|
|
if (iHaveReviewed == true){
|
|
// We have already reviewed the attribute.
|
|
continue
|
|
}
|
|
|
|
attributeIsHidden, _, _, err := myHiddenContent.CheckIfAttributeIsHidden(attributeHash)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (attributeIsHidden == true){
|
|
// The moderator has hidden this attribute.
|
|
continue
|
|
}
|
|
|
|
// This will tell us if any moderators, banned or not, have banned the attribute
|
|
checkIfAnyAttributeBanAdvocatesExist := func()bool{
|
|
|
|
allAttributeBanAdvocatesList, exists := allAttributeBanAdvocatesMap[attributeIdentifier]
|
|
if (exists == false){
|
|
return false
|
|
}
|
|
if (len(allAttributeBanAdvocatesList) == 0){
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
anyAttributeBanAdvocatesExist := checkIfAnyAttributeBanAdvocatesExist()
|
|
|
|
if (anyAttributeBanAdvocatesExist == false){
|
|
|
|
attributeIsReported := slices.Contains(allReportedAttributeHashesList, attributeHash)
|
|
|
|
if (attributeIsReported == false){
|
|
|
|
// Nobody has banned/reported this attribute
|
|
|
|
if (profileType != "Mate"){
|
|
// We do not need to review attributes that are not banned for non-mate profiles
|
|
continue
|
|
}
|
|
|
|
attributeProfileType, attributeIsCanonical, err := readProfiles.ReadAttributeHashMetadata(attributeHash)
|
|
if (err != nil){ return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (attributeProfileType != profileType){
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, errors.New("GetVerifiedProfileVerdict returning attribute hash with different profileType than author.")
|
|
}
|
|
|
|
if (attributeIsCanonical == true){
|
|
// We don't need to review it, even if profileType == Mate
|
|
continue
|
|
}
|
|
|
|
// We see if the user has a newer profile
|
|
// If they do, we don't need to review this profile
|
|
|
|
anyProfileExists, _, newestProfileHash, _, _, _, err := profileStorage.GetNewestUserProfile(profileAuthor, networkType)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (anyProfileExists == false){
|
|
// The user's profile must have been deleted after we performed GetAllProfileHashes
|
|
continue
|
|
}
|
|
if (profileHash != newestProfileHash){
|
|
// There is a newer profile by this same Mate user
|
|
// We don't need to review the current profile, because it is not banned by anyone, and none of its attributes are banned
|
|
// We will review the newer profile instead
|
|
continue
|
|
}
|
|
|
|
// We still need to review this attribute, because it is a non-canonical mate attribute
|
|
}
|
|
}
|
|
|
|
// The attribute is worthy of review
|
|
// We see if it is less reviewed than our current leastReviewedAttributeHash
|
|
|
|
// This will tell us the number of eligible attribute approve advocates
|
|
getAttributeApproveAdvocatesCount := func()int{
|
|
|
|
attributeApproveAdvocatesCount, exists := attributeApproveAdvocateCountsMap[attributeIdentifier]
|
|
if (exists == false){
|
|
return 0
|
|
}
|
|
return attributeApproveAdvocatesCount
|
|
}
|
|
|
|
// This will tell us the number of eligible attribute ban advocates
|
|
getAttributeBanAdvocatesCount := func()int{
|
|
|
|
attributeBanAdvocatesCount, exists := attributeBanAdvocateCountsMap[attributeIdentifier]
|
|
if (exists == false){
|
|
return 0
|
|
}
|
|
return attributeBanAdvocatesCount
|
|
}
|
|
|
|
attributeApproveAdvocatesCount := getAttributeApproveAdvocatesCount()
|
|
attributeBanAdvocatesCount := getAttributeBanAdvocatesCount()
|
|
|
|
numberOfReviewers := attributeApproveAdvocatesCount + attributeBanAdvocatesCount
|
|
|
|
attributeIsSkipped, _, _, err := mySkippedContent.CheckIfAttributeIsSkipped(attributeHash)
|
|
if (err != nil) { return false, [27]byte{}, [28]byte{}, [16]byte{}, err }
|
|
if (attributeIsSkipped == false){
|
|
if (allAttributesAreSkipped == true){
|
|
|
|
// This is the first non-skipped attribute we have encountered
|
|
// It may not be the first attribute we have encountered
|
|
// Thus, we must clear any previously found attributes
|
|
leastReviewedAttributeHashFound = false
|
|
leastReviewedAttributeHash = [27]byte{}
|
|
leastReviewedAttributeProfileHash = [28]byte{}
|
|
leastReviewedAttributeIdentityHash = [16]byte{}
|
|
leastReviewedAttributeHashReviewsCount = 0
|
|
|
|
allAttributesAreSkipped = false
|
|
}
|
|
} else {
|
|
if (allAttributesAreSkipped == false){
|
|
// We have some non-skipped attributes, so we will reject all skipped attributes
|
|
continue
|
|
}
|
|
}
|
|
|
|
if (attributeApproveAdvocatesCount == 0 && attributeIsSkipped == false){
|
|
// This is the highest priority kind of profile attribute
|
|
// It has no approvers, and needs to be reviewed
|
|
// We stop searching and return the attribute
|
|
return true, attributeHash, profileHash, profileAuthor, nil
|
|
}
|
|
|
|
if (leastReviewedAttributeHashFound == false || numberOfReviewers < leastReviewedAttributeHashReviewsCount){
|
|
leastReviewedAttributeHashFound = true
|
|
leastReviewedAttributeHash = attributeHash
|
|
leastReviewedAttributeProfileHash = profileHash
|
|
leastReviewedAttributeIdentityHash = profileAuthor
|
|
leastReviewedAttributeHashReviewsCount = numberOfReviewers
|
|
}
|
|
}
|
|
|
|
if (leastReviewedAttributeHashFound == true){
|
|
return true, leastReviewedAttributeHash, leastReviewedAttributeProfileHash, leastReviewedAttributeIdentityHash, nil
|
|
}
|
|
|
|
// Nothing left to review
|
|
return false, [27]byte{}, [28]byte{}, [16]byte{}, nil
|
|
}
|
|
|
|
|
|
|
|
|
|
|