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