248 lines
9.3 KiB
Go
248 lines
9.3 KiB
Go
|
|
// contentControversy provides functions to calculate the controversy of a piece of reviewed content
|
|
// Controversy is a numeric rating of how much disagreement exists among moderators.
|
|
// Moderators can view the most controversial content to find moderators to ban or debate with
|
|
|
|
package contentControversy
|
|
|
|
// The content controversy calculation can be adjusted using several options:
|
|
|
|
// 1. Multiply moderator identity scores
|
|
// This will multiply the controversy by the sum of all reviewer identity scores, rather than by the number of moderators
|
|
// Toggling this on and off is useful to find controversial moderators who do and do not have high identity scores
|
|
// 2. Use enabled only or include banned moderators
|
|
// -This is useful to find moderators who have already been banned
|
|
// -We want as many moderators as possible to ban unruleful moderators, even if they have been banned already
|
|
// -This defends against an unruleful moderator increasing their score to evade a ban
|
|
// -It also defends against a powerful moderator disabling their profile, resulting in the unbanning of many unruleful moderators
|
|
// 3. TODO: Time range
|
|
// -This will enable only showing controversial content that was created during a certain time range
|
|
// -This will make it possible to only view content that was created recently or long ago
|
|
|
|
//TODO: Add filters
|
|
// Example:
|
|
// -Hide content that I have already reviewed
|
|
|
|
import "seekia/internal/contentMetadata"
|
|
import "seekia/internal/encoding"
|
|
import "seekia/internal/helpers"
|
|
import "seekia/internal/moderation/bannedModeratorConsensus"
|
|
import "seekia/internal/moderation/enabledModerators"
|
|
import "seekia/internal/moderation/moderatorScores"
|
|
import "seekia/internal/moderation/reviewStorage"
|
|
import "seekia/internal/network/appNetworkType/getAppNetworkType"
|
|
import "seekia/internal/network/backgroundDownloads"
|
|
|
|
import "errors"
|
|
|
|
//Outputs:
|
|
// -bool: Controversy is known (content metadata exists, required reviews are being downloaded, parameters exist)
|
|
// -int64: Content controversy rating
|
|
// -error
|
|
func GetContentControversyRating(contentHash []byte)(bool, int64, error){
|
|
|
|
contentType, err := helpers.GetContentTypeFromContentHash(contentHash)
|
|
if (err != nil){
|
|
contentHashHex := encoding.EncodeBytesToHexString(contentHash)
|
|
return false, 0, errors.New("GetContentControversyRating called with invalid contentHash: " + contentHashHex)
|
|
}
|
|
if (contentType != "Profile" && contentType != "Message"){
|
|
contentHashHex := encoding.EncodeBytesToHexString(contentHash)
|
|
return false, 0, errors.New("GetContentControversyRating called with invalid contentHash: Not profile/message: " + contentHashHex)
|
|
}
|
|
|
|
appNetworkType, err := getAppNetworkType.GetAppNetworkType()
|
|
if (err != nil) { return false, 0, err }
|
|
|
|
//TODO: Check if parameters exist
|
|
|
|
//TODO: Fix below to retrieve from settings
|
|
multiplyIdentityScores := true
|
|
includeBannedModerators := true
|
|
|
|
//Outputs:
|
|
// -bool: Required data exists (Metadata exists and downloading required reviews/moderator profiles)
|
|
// -map[[16]byte]int64: Approve advocates map (identity hash -> Time of approval)
|
|
// -map[[16]byte]int64: Ban advocates map (identity hash -> Time of ban)
|
|
// -error
|
|
getApproveAndBanAdvocatesMaps := func()(bool, map[[16]byte]int64, map[[16]byte]int64, error){
|
|
|
|
if (contentType == "Message"){
|
|
|
|
if (len(contentHash) != 26){
|
|
contentHashHex := encoding.EncodeBytesToHexString(contentHash)
|
|
return false, nil, nil, errors.New("GetContentTypeFromContentHash returning Message for different length contentHash: " + contentHashHex)
|
|
}
|
|
|
|
messageHash := [26]byte(contentHash)
|
|
|
|
metadataExists, _, messageNetworkType, _, messageInbox, messageCipherKeyHash, err := contentMetadata.GetMessageMetadata(messageHash)
|
|
if (err != nil) { return false, nil, nil, err }
|
|
if (metadataExists == false){
|
|
return false, nil, nil, nil
|
|
}
|
|
if (messageNetworkType != appNetworkType){
|
|
// We cannot determine controversy for content from a different networkType
|
|
return false, nil, nil, nil
|
|
}
|
|
|
|
downloadingRequiredContent, err := backgroundDownloads.CheckIfAppCanDetermineMessageVerdict(messageNetworkType, messageInbox, true, messageHash)
|
|
if (err != nil) { return false, nil, nil, err }
|
|
if (downloadingRequiredContent == false){
|
|
return true, nil, nil, nil
|
|
}
|
|
|
|
approveAdvocatesMap, banAdvocatesMap, err := reviewStorage.GetMessageVerdictMaps(messageHash, messageNetworkType, messageCipherKeyHash)
|
|
if (err != nil) { return false, nil, nil, err }
|
|
|
|
return true, approveAdvocatesMap, banAdvocatesMap, nil
|
|
}
|
|
|
|
// contentType is Profile
|
|
|
|
if (len(contentHash) != 28){
|
|
|
|
contentHashHex := encoding.EncodeBytesToHexString(contentHash)
|
|
return false, nil, nil, errors.New("GetContentTypeFromContentHash returning Profile for different length contentHash: " + contentHashHex)
|
|
}
|
|
|
|
profileHash := [28]byte(contentHash)
|
|
|
|
profileMetadataExists, _, profileNetworkType, profileIdentityHash, _, profileIsDisabled, _, profileAttributeHashesMap, err := contentMetadata.GetProfileMetadata(profileHash)
|
|
if (err != nil) { return false, nil, nil, err }
|
|
if (profileMetadataExists == false){
|
|
return false, nil, nil, nil
|
|
}
|
|
if (profileNetworkType != appNetworkType){
|
|
// We cannot determine controversy for content from a different networkType.
|
|
return false, nil, nil, nil
|
|
}
|
|
if (profileIsDisabled == true){
|
|
// Profile cannot be reviewed, content controversy is not applicable
|
|
return false, nil, nil, nil
|
|
}
|
|
|
|
downloadingRequiredReviews, err := backgroundDownloads.CheckIfAppCanDetermineIdentityVerdicts(profileIdentityHash)
|
|
if (err != nil){ return false, nil, nil, err }
|
|
if (downloadingRequiredReviews == false){
|
|
return false, nil, nil, nil
|
|
}
|
|
|
|
// We have to check for any moderators who banned an attribute after approving the full profile
|
|
// This would negate their full profile approval
|
|
|
|
attributeProfileHashesList := helpers.GetListOfMapValues(profileAttributeHashesMap)
|
|
|
|
approveAdvocatesMap, banAdvocatesMap, err := reviewStorage.GetProfileVerdictMaps(profileHash, profileNetworkType, true, attributeProfileHashesList)
|
|
if (err != nil) { return false, nil, nil, err }
|
|
|
|
return true, approveAdvocatesMap, banAdvocatesMap, nil
|
|
}
|
|
|
|
requiredDataExists, approveAdvocatesMap, banAdvocatesMap, err := getApproveAndBanAdvocatesMaps()
|
|
if (err != nil) { return false, 0, err }
|
|
if (requiredDataExists == false){
|
|
return false, 0, nil
|
|
}
|
|
|
|
if (len(approveAdvocatesMap) == 0 && len(banAdvocatesMap) == 0){
|
|
// No eligible reviewers exist.
|
|
// Thus, no controversy exists.
|
|
return true, 0, nil
|
|
}
|
|
|
|
//Outputs:
|
|
// -bool: Parameters and required data exists to determine moderator is banned status
|
|
// -float64: Moderators value (either number of moderators or a sum of identity scores)
|
|
// -error
|
|
getModeratorsValue := func(inputModeratorsMap map[[16]byte]int64)(bool, float64, error){
|
|
|
|
moderatorsValue := float64(0)
|
|
|
|
for identityHash, _ := range inputModeratorsMap{
|
|
|
|
moderatorIsEnabled, err := enabledModerators.CheckIfModeratorIsEnabled(true, identityHash, appNetworkType)
|
|
if (err != nil){ return false, 0, err }
|
|
if (moderatorIsEnabled == false){
|
|
continue
|
|
}
|
|
if (includeBannedModerators == false){
|
|
|
|
requiredDataExists, parametersExist, moderatorIsBanned, err := bannedModeratorConsensus.GetModeratorIsBannedStatus(true, identityHash, appNetworkType)
|
|
if (err != nil){ return false, 0, err }
|
|
if (requiredDataExists == false || parametersExist == false){
|
|
return false, 0, nil
|
|
}
|
|
if (moderatorIsBanned == true){
|
|
continue
|
|
}
|
|
}
|
|
|
|
if (multiplyIdentityScores == false){
|
|
moderatorsValue += 1
|
|
continue
|
|
}
|
|
|
|
scoreIsKnown, moderatorScore, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(identityHash)
|
|
if (err != nil) { return false, 0, err }
|
|
if (scoreIsKnown == false){
|
|
// Client has not downloaded moderator score, will do so automatically, skip for now.
|
|
continue
|
|
}
|
|
if (scoreIsSufficient == false){
|
|
// Moderator is not funded enough to make reviews
|
|
continue
|
|
}
|
|
moderatorsValue += moderatorScore
|
|
}
|
|
|
|
return true, moderatorsValue, nil
|
|
}
|
|
|
|
requiredDataExists, approveValue, err := getModeratorsValue(approveAdvocatesMap)
|
|
if (err != nil) { return false, 0, err }
|
|
if (requiredDataExists == false){
|
|
// We cannot determine content controversy
|
|
// Client should automatically download parameters in the background, and then content controversy will be calculable
|
|
return false, 0, nil
|
|
}
|
|
|
|
requiredDataExists, banValue, err := getModeratorsValue(banAdvocatesMap)
|
|
if (err != nil) { return false, 0, err }
|
|
if (requiredDataExists == false){
|
|
// We cannot determine content controversy
|
|
// Client should automatically download parameters in the background, and then content controversy will be calculable
|
|
return false, 0, nil
|
|
}
|
|
|
|
getValuesRatio := func()float64{
|
|
|
|
if (approveValue == banValue){
|
|
// Highest possible controversy
|
|
return 1
|
|
}
|
|
if (approveValue == 0 || banValue == 0){
|
|
// Lowest possible controversy
|
|
return 0
|
|
}
|
|
if (approveValue > banValue){
|
|
ratio := banValue/approveValue
|
|
return ratio
|
|
}
|
|
|
|
ratio := approveValue/banValue
|
|
return ratio
|
|
}
|
|
|
|
valuesRatio := getValuesRatio()
|
|
|
|
contentControversyFloat := valuesRatio * (approveValue + banValue)
|
|
|
|
contentControversyInt, err := helpers.FloorFloat64ToInt64(contentControversyFloat)
|
|
if (err != nil) { return false, 0, err }
|
|
|
|
return true, contentControversyInt, nil
|
|
}
|
|
|
|
|
|
|