Add initial extra info feature
Only the extra profile in profiles is shown, since it is the easiest to
implement.
Bug: twpowertools:93
Change-Id: Ife4f31ee056f74bd478702347d75044b024abf1e
diff --git a/src/contentScripts/communityConsole/extraInfo.js b/src/contentScripts/communityConsole/extraInfo.js
new file mode 100644
index 0000000..c572523
--- /dev/null
+++ b/src/contentScripts/communityConsole/extraInfo.js
@@ -0,0 +1,257 @@
+import {MDCTooltip} from '@material/tooltip';
+import {waitFor} from 'poll-until-promise';
+
+import {isOptionEnabled} from '../../common/optionsUtils.js';
+
+import {createExtBadge} from './utils/common.js';
+
+const kViewUnifiedUserResponseEvent = 'TWPT_ViewUnifiedUserResponse';
+
+const kAbuseCategories = [
+ ['1', 'Account'],
+ ['2', 'Display name'],
+ ['3', 'Avatar'],
+];
+const kAbuseViolationCategories = {
+ 0: 'NO_VIOLATION',
+ 1: 'COMMUNITY_POLICY_VIOLATION',
+ 2: 'LEGAL_VIOLATION',
+ 3: 'CSAI_VIOLATION',
+ 4: 'OTHER_VIOLATION',
+};
+const kAbuseViolationTypes = {
+ 0: 'UNSPECIFIED',
+ 23: 'ACCOUNT_DISABLED',
+ 55: 'ACCOUNT_HAS_SERVICES_DISABLED',
+ 35: 'ACCOUNT_HIJACKED',
+ 96: 'ACCOUNT_LEAKED_CREDENTIALS',
+ 92: 'ACCOUNT_NOT_SUPPORTED',
+ 81: 'ARTISTIC_NUDITY',
+ 66: 'BAD_BEHAVIOR_PATTERN',
+ 78: 'BAD_ENGAGEMENT_BEHAVIOR_PATTERN',
+ 79: 'BORDERLINE_HARASSMENT',
+ 80: 'BORDERLINE_HATE_SPEECH',
+ 38: 'BOTNET',
+ 32: 'BRANDING_VIOLATION',
+ 100: 'CAPITALIZING_TRAGIC_EVENTS',
+ 105: 'CLOAKING',
+ 49: 'COIN_MINING',
+ 7: 'COMMERCIAL_CONTENT',
+ 97: 'COPPA_REGULATED',
+ 57: 'COPYRIGHT_CIRCUMVENTION',
+ 8: 'COPYRIGHTED_CONTENT',
+ 58: 'COURT_ORDER',
+ 51: 'CSAI',
+ 94: 'CSAI_INSPECT',
+ 52: 'CSAI_CARTOON_HUMOR',
+ 53: 'CSAI_SOLICITATION',
+ 108: 'CSAI_NON_APPARENT',
+ 67: 'DANGEROUS',
+ 37: 'DATA_SCRAPING',
+ 86: 'DECEPTIVE_OAUTH_IMPLEMENTATION',
+ 46: 'DEFAMATORY_CONTENT',
+ 36: 'DELINQUENT_BILLING',
+ 30: 'DISRUPTION_ATTEMPT',
+ 112: 'DOMESTIC_INTERFERENCE',
+ 22: 'DOS',
+ 9: 'DUPLICATE_CONTENT',
+ 68: 'DUPLICATE_LOCAL_PAGE',
+ 121: 'NON_QUALIFYING_ORGANIZATION',
+ 115: 'EGREGIOUS_INTERACTION_WITH_MINOR',
+ 83: 'ENGAGEMENT_COLLUSION',
+ 41: 'EXPLOIT_ATTACKS',
+ 65: 'FAKE_USER',
+ 2: 'FRAUD',
+ 21: 'FREE_TRIAL_VIOLATION',
+ 43: 'GIBBERISH',
+ 101: 'FOREIGN_INTERFERENCE',
+ 59: 'GOVERNMENT_ORDER',
+ 10: 'GRAPHICAL_VIOLENCE',
+ 11: 'HARASSMENT',
+ 12: 'HATE_SPEECH',
+ 90: 'IDENTICAL_PRODUCT_NAME',
+ 60: 'ILLEGAL_DRUGS',
+ 13: 'IMPERSONATION',
+ 69: 'IMPERSONATION_WITH_PII',
+ 116: 'INAPPROPRIATE_INTERACTION_WITH_MINOR',
+ 45: 'INAPPROPRIATE_CONTENT_SPEECH',
+ 106: 'INTENTIONAL_THWARTING',
+ 27: 'INTRUSION_ATTEMPT',
+ 87: 'INVALID_API_USAGE',
+ 14: 'INVALID_CONTENT',
+ 20: 'INVALID_GCE_USAGE',
+ 120: 'INVALID_STORAGE_USAGE',
+ 15: 'INVALID_IMAGE_QUALITY',
+ 88: 'INVALID_API_PRIVACY_POLICY_DISCLOSURE',
+ 54: 'INVALID_USAGE_OF_IP_PROXYING',
+ 99: 'KEYWORD_STUFFING',
+ 61: 'LEGAL_COUNTERFEIT',
+ 62: 'LEGAL_EXPORT',
+ 63: 'LEGAL_PRIVACY',
+ 33: 'LEGAL_REVIEW',
+ 91: 'LEGAL_PROTECTED',
+ 70: 'LOW_QUALITY_CONTENT',
+ 93: 'LOW_REPUTATION_PHONE_NUMBER',
+ 6: 'MALICIOUS_SOFTWARE',
+ 40: 'MALWARE',
+ 113: 'MISLEADING',
+ 114: 'MISREP_OF_ID',
+ 89: 'MEMBER_OF_ABUSIVE_GCE_NETWORK',
+ 84: 'NON_CONSENSUAL_EXPLICIT_IMAGERY',
+ 1: 'NONE',
+ 102: 'OFF_TOPIC',
+ 31: 'OPEN_PROXY',
+ 28: 'PAYMENT_FRAUD',
+ 16: 'PEDOPHILIA',
+ 71: 'PERSONAL_INFORMATION_CONTENT',
+ 25: 'PHISHING',
+ 34: 'POLICY_REVIEW',
+ 17: 'PORNOGRAPHY',
+ 29: 'QUOTA_CIRCUMVENTION',
+ 72: 'QUOTA_EXCEEDED',
+ 73: 'REGULATED',
+ 24: 'REPEATED_POLICY_VIOLATION',
+ 104: 'RESOURCE_COMPROMISED',
+ 107: 'REWARD_PROGRAMS_ABUSE',
+ 74: 'ROGUE_PHARMA',
+ 82: 'ESCORT',
+ 75: 'SPAMMY_LOCAL_VERTICAL',
+ 39: 'SEND_EMAIL_SPAM',
+ 117: 'SEXTORTION',
+ 118: 'SEX_TRAFFICKING',
+ 44: 'SEXUALLY_EXPLICIT_CONTENT',
+ 3: 'SHARDING',
+ 95: 'SOCIAL_ENGINEERING',
+ 109: 'SUSPICIOUS',
+ 19: 'TRADEMARK_CONTENT',
+ 50: 'TRAFFIC_PUMPING',
+ 76: 'UNSAFE_RACY',
+ 103: 'UNUSUAL_ACTIVITY_ALERT',
+ 64: 'UNWANTED_CONTENT',
+ 26: 'UNWANTED_SOFTWARE',
+ 77: 'VIOLENT_EXTREMISM',
+ 119: 'UNAUTH_IMAGES_OF_MINORS',
+ 85: 'UNAUTHORIZED_SERVICE_RESELLING',
+ 98: 'CSAI_EXTERNAL',
+ 5: 'SPAM',
+ 4: 'UNSAFE',
+ 47: 'CHILD_PORNOGRAPHY_INCITATION',
+ 18: 'TERRORISM_SUPPORT',
+ 56: 'CSAI_WORST_OF_WORST',
+};
+
+export default class ExtraInfo {
+ constructor() {
+ this.lastProfile = {
+ body: {},
+ id: -1,
+ timestamp: 0,
+ };
+ this.setUpHandlers();
+ }
+
+ setUpHandlers() {
+ window.addEventListener(kViewUnifiedUserResponseEvent, e => {
+ if (e.detail.id < this.lastProfile.id) return;
+
+ this.lastProfile = {
+ body: e.detail.body,
+ id: e.detail.id,
+ timestamp: Date.now(),
+ };
+ });
+ }
+
+ // Add a pretty component which contains |info| to |node|.
+ addExtraInfoElement(info, node) {
+ // Don't create
+ if (info.length == 0) return;
+
+ let container = document.createElement('div');
+ container.classList.add('TWPT-extrainfo-container');
+
+ let badgeCell = document.createElement('div');
+ badgeCell.classList.add('TWPT-extrainfo-badge-cell');
+
+ let badge, badgeTooltip;
+ [badge, badgeTooltip] = createExtBadge();
+ badgeCell.append(badge);
+
+ let infoCell = document.createElement('div');
+ infoCell.classList.add('TWPT-extrainfo-info-cell');
+
+ for (const i of info) {
+ let iRow = document.createElement('div');
+ iRow.append(i);
+ infoCell.append(iRow);
+ }
+
+ container.append(badgeCell, infoCell);
+ node.append(container);
+ new MDCTooltip(badgeTooltip);
+ }
+
+ fieldInfo(field, value) {
+ let span = document.createElement('span');
+ span.append(document.createTextNode(field + ': '));
+
+ let valueEl = document.createElement('span');
+ valueEl.style.fontFamily = 'monospace';
+ valueEl.textContent = value;
+
+ span.append(valueEl);
+ return span;
+ }
+
+ // Profile functionality
+ injectAtProfile(card) {
+ waitFor(
+ () => {
+ let now = Date.now();
+ if (now - this.lastProfile.timestamp < 15 * 1000)
+ return Promise.resolve(this.lastProfile);
+ return Promise.reject('Didn\'t receive profile information');
+ },
+ {
+ interval: 500,
+ timeout: 15 * 1000,
+ })
+ .then(profile => {
+ let info = [];
+ const abuseViolationCategory = profile.body?.['1']?.['6'];
+ if (abuseViolationCategory) {
+ info.push(this.fieldInfo(
+ 'Abuse category',
+ kAbuseViolationCategories[abuseViolationCategory] ??
+ abuseViolationCategory));
+ }
+
+ const profileAbuse = profile.body?.['1']?.['1']?.['8'];
+
+ for (const [index, category] of kAbuseCategories) {
+ const violation = profileAbuse?.[index]?.['1']?.['1'];
+ if (violation) {
+ info.push(this.fieldInfo(
+ category + ' policy violation',
+ kAbuseViolationTypes[violation]));
+ }
+ }
+
+ const appealCount = profileAbuse?.['4'];
+ if (appealCount !== undefined)
+ info.push(this.fieldInfo('Number of appeals', appealCount));
+
+ this.addExtraInfoElement(info, card);
+ })
+ .catch(err => {
+ console.error(
+ 'extraInfo: error while injecting profile extra info: ', err);
+ });
+ }
+
+ injectAtProfileIfEnabled(card) {
+ isOptionEnabled('extrainfo').then(isEnabled => {
+ if (isEnabled) return this.injectAtProfile(card);
+ });
+ }
+}