seekia/internal/moderation/contentControversy/contentControversy.go

249 lines
9.3 KiB
Go
Raw Normal View History

// 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
}