diff --git a/browser-extension/plugin/package-lock.json b/browser-extension/plugin/package-lock.json index 5dd5a5da..15136f03 100644 --- a/browser-extension/plugin/package-lock.json +++ b/browser-extension/plugin/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "1.6.2", "concurrently": "8.2.2", + "dexie": "^4.0.11", "dom-to-image": "2.6.0", "dom-to-image-improved": "2.8.0", "dom-to-image-more": "3.2.0", @@ -5092,6 +5093,11 @@ "integrity": "sha512-nEzHZteIUZfGCZtTiS1fRpC8UZmsfD1SiyPvaUNvS13dvKf666OAm8YTi0+Ca3n1nLEyu49Cy4+dPWpaHFJk9g==", "dev": true }, + "node_modules/dexie": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.11.tgz", + "integrity": "sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==" + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", diff --git a/browser-extension/plugin/package.json b/browser-extension/plugin/package.json index 34b78467..2a98f232 100644 --- a/browser-extension/plugin/package.json +++ b/browser-extension/plugin/package.json @@ -43,6 +43,7 @@ "dependencies": { "axios": "1.6.2", "concurrently": "8.2.2", + "dexie": "^4.0.11", "dom-to-image": "2.6.0", "dom-to-image-improved": "2.8.0", "dom-to-image-more": "3.2.0", diff --git a/browser-extension/plugin/src/api/public-slurs.js b/browser-extension/plugin/src/api/public-slurs.js new file mode 100644 index 00000000..659508fc --- /dev/null +++ b/browser-extension/plugin/src/api/public-slurs.js @@ -0,0 +1,24 @@ +import axios from 'axios'; +import config from '../config'; + +const { API_URL } = config; + +export async function getPublicSlurs() { + try { + const res = await axios.get(`${API_URL}/api/dataset/slur/1`); + + return res.data.slurs; + } catch (error) { + throw error; + } +} + +export async function getPublicSlursMetadata() { + try { + const res = await axios.get(`${API_URL}/api/dataset/slurmetadata/1`); + + return res.data.slurs_metadata; + } catch (error) { + throw error; + } +} \ No newline at end of file diff --git a/browser-extension/plugin/src/background.js b/browser-extension/plugin/src/background.js index 2885e1c6..c48e095b 100644 --- a/browser-extension/plugin/src/background.js +++ b/browser-extension/plugin/src/background.js @@ -65,4 +65,4 @@ userBrowserContextMenus.onClicked.addListener((info, tab) => { default: console('unexpected action'); } -}); +}); \ No newline at end of file diff --git a/browser-extension/plugin/src/chrome.js b/browser-extension/plugin/src/chrome.js index 0fd5808f..724207a1 100644 --- a/browser-extension/plugin/src/chrome.js +++ b/browser-extension/plugin/src/chrome.js @@ -70,34 +70,45 @@ const set = (key, value) => { }); }; -function sendMessage(type) { - if (type == 'updateData') { - userBrowserTabs.query( - { active: true, currentWindow: true }, - function (tabs) { - userBrowserTabs.sendMessage( - tabs[0].id, - { type: 'updateData' }, - function (response) { - console.log(response); - } - ); - } - ); - } +function sendMessage(type, data = null) { + return new Promise((resolve, reject) => { + const message = { type }; + if (data) { + message.data = data; + } + + if ( + type === 'updateData' || + type === 'fetchPersonalSlurs' || + type === 'syncApprovedCrowdsourcedSlurs' + ) { + userBrowserTabs.query( + { active: true, currentWindow: true }, + function (tabs) { + userBrowserTabs.sendMessage( + tabs[0].id, + message, + function (response) { + resolve(response); + } + ); + } + ); + } else { + reject(new Error('Unsupported message type')); + } + }); } function addListener(type, func, response) { - chrome.runtime.onMessage.addListener(async function ( - message, - sender, - sendResponse - ) { - if (message.type === type) { - func(); - sendResponse(response); + chrome.runtime.onMessage.addListener( + async function (message, sender, sendResponse) { + if (message.type === type) { + func(); + sendResponse(response); + } } - }); + ); } export default { diff --git a/browser-extension/plugin/src/content-script.js b/browser-extension/plugin/src/content-script.js index cab02a1d..4739704c 100644 --- a/browser-extension/plugin/src/content-script.js +++ b/browser-extension/plugin/src/content-script.js @@ -5,10 +5,19 @@ import transform_v2 from './transform-v2'; import { log } from './logger'; import repository from './repository'; const { getUserData, getPreferenceData, setPreferenceData } = repository; -import { updateSlurList } from './slur-replace'; import transformGeneral from './transform-general'; import Api from './ui-components/pages/Api'; +import { + initializeSlurs, + getSlursBySource, + addSlur, + deleteSlur, + slurExists, + bulkAddSlurs, + convertSlurMetadataFromDBtoJSON +} from './slur-store'; import { createCrowdsourceSlur } from './api/crowdsource-slurs'; +import { getPublicSlurs } from './api/public-slurs'; const { createSlurAndCategory } = Api; @@ -79,85 +88,141 @@ function processPage(newUrl) { * eg : When a user clicks on a tweet on their home timeline, they * go from the home page to the user status page. */ -chrome.runtime.onMessage.addListener(async function (request) { - if (request.type === 'updateData') { - console.log('data changed. time to update slurs'); - const preference = await getPreferenceData(); - console.log(preference); - if (preference.slurList != undefined) { - updateSlurList(preference.slurList); - processPage(location.href); - } - return true; - } - if (request.message === 'URL_CHANGED') { - const newUrl = request.url; - log('Url Changed', newUrl); - processPage(location.href); - return true; - } - if (request.type === 'SLUR_ADDED') { - const slur = request.slur; - log('slur added from bg', slur); - const pref = await getPreferenceData(); - let slurList; - - const user = await getUserData(); - // console.log('USER in content-script', user); - const crowdsourceData = { - label: slur, - }; - - // Adding Slur to Prefrences - if (!pref || !pref.slurList) { - slurList = slur; - } else { - slurList = pref.slurList; - if (!slurList || slurList === '') { - slurList = slur; - } else { - slurList += `,${slur}`; +chrome.runtime.onMessage.addListener( + async function (request, sender, sendResponse) { + if (request.type === 'updateData') { + try { + const newSlurs = request.data; + console.log('New slurs received:', newSlurs); + // fetch exisiting slurs + const existingSlurs = (await getSlursBySource('personal')).map( + (slur) => slur.word + ); + // Add new slurs to the database + for (const slur of newSlurs) { + if (!existingSlurs.includes(slur)) { + await addSlur(slur, 'personal'); + } + } + // Remove slurs from the database that no longer exist in the new list + for (const slur of existingSlurs) { + if (!newSlurs.includes(slur)) { + await deleteSlur(slur, 'personal'); + } + } + processPage(location.href); + } catch (error) { + console.error('Error during updating slur list:', error); } + return true; } - try { - await setPreferenceData({ ...pref, slurList }); - } catch (error) { - console.error('error updating pref data', error); + if (request.type === 'fetchPersonalSlurs') { + try { + const personalSlurs = await getSlursBySource('personal'); + const slurArr = personalSlurs.map((slur) => slur.word); + console.log('Sending slurs back to pref:', slurArr); + return slurArr; + } catch (error) { + console.error( + 'Error fetching personal slurs in content script:', + error + ); + // sendResponse({ success: false, error: error.message }); + } } + if (request.type === 'syncApprovedCrowdsourcedSlurs') { + const source = 'public_crowdsourced_slurs'; + try { + const publicSlurs = await getPublicSlurs(); + const publicSlursArray = publicSlurs.map((slur) => slur.label); + // console.log(publicSlursArray); - //Crowdsourcing Slur - try { - // await createSlurAndCategory(user.accessToken, crowdsourceData); - await createCrowdsourceSlur(crowdsourceData, user.token) - console.log('finsihed POST req'); - window.alert(`Slur word "${slur}" added to Uli`); - } catch (error) { - console.log(error); + const filteredSlurs = []; + for (const slur of publicSlursArray) { + const exists = await slurExists(slur, source); + if (!exists) { + filteredSlurs.push(slur); + } + } + // If there are slurs to add, bulk add them to the database + if (filteredSlurs.length > 0) { + await bulkAddSlurs(filteredSlurs, source); + } else { + console.log('No new slurs to add.'); + } + } catch (error) { + console.error('Error fetch public crowsrouced slurs'); + } } + if (request.message === 'URL_CHANGED') { + const newUrl = request.url; + log('Url Changed', newUrl); + processPage(location.href); + return true; + } + if (request.type === 'SLUR_ADDED') { + const slur = request.slur; + log('slur added from bg', slur); + let slurList; - return true; - } + const user = await getUserData(); + // console.log('USER in content-script', user); + const crowdsourceData = { + label: slur + }; + + // Adding Slur to IndexedDB + try { + const exists = await slurExists(slur, 'personal'); + + if (!exists) { + await addSlur(slur, 'personal'); + log('Slur added to IndexedDB:', slur); + } else { + log('Slur already exists in IndexedDB, skipping:', slur); + } + } catch (error) { + console.error('Error handling SLUR_ADDED request:', error); + } + + //Crowdsourcing Slur + try { + // await createSlurAndCategory(user.accessToken, crowdsourceData); + await createCrowdsourceSlur(crowdsourceData, user.token); + console.log('finsihed POST req'); + window.alert(`Slur word "${slur}" added to Uli`); + } catch (error) { + console.log(error); + } + + return true; + } - if (request.type === 'ULI_ENABLE_SLUR_REPLACEMENT') { - console.log('Toggle change event received', request.payload); - if (!request.payload) { - clearInterval(mainLoadedChecker); + if (request.type === 'ULI_ENABLE_SLUR_REPLACEMENT') { + console.log('Toggle change event received', request.payload); + if (!request.payload) { + clearInterval(mainLoadedChecker); + } } } -}); +); window.addEventListener( 'load', async () => { console.log('content loaded'); const pref = await getPreferenceData(); - const { enableSlurReplacement , enableSlurMetadata } = pref; + const { enableSlurReplacement, enableSlurMetadata } = pref; + + // Initialize Slurs on content load + await initializeSlurs(); + if (enableSlurMetadata) { let body = document.getElementsByTagName('body'); let first_body = body[0]; - transformGeneral.processNewlyAddedNodesGeneral2(first_body); - } - else if (enableSlurReplacement) { + const jsonData = await convertSlurMetadataFromDBtoJSON(); + transformGeneral.processNewlyAddedNodesGeneral2(first_body, jsonData); + } else if (enableSlurReplacement) { processPage(location.href); } }, diff --git a/browser-extension/plugin/src/slur-replace.js b/browser-extension/plugin/src/slur-replace.js index 45dc17aa..9b420704 100644 --- a/browser-extension/plugin/src/slur-replace.js +++ b/browser-extension/plugin/src/slur-replace.js @@ -1,5 +1,4 @@ -import repository from './repository'; -const { getPreferenceData } = repository; +import { getAllSlurs } from './slur-store'; const PUNCTUATIONS = [' ', '.', ',', ';', '#', '"']; @@ -18,53 +17,53 @@ function generateMask(word, offset, string) { } } -let slurList = - 'जिहादी|छक्का|छिनाल|रंडी|रण्डी|रांड|रंडीखाना|रण्डी रोना|लुल्ली|गांड|गा#|कुतिया|कुत्ती|बत्तमीज़|कुल्टा|हरामजादी|साली|चो#|चुदाई|ma ki chui|मा के भोसड़े|भोस्डीके|भोछडी वाला |लोड़ू|बहन चोद|मादरचोद|लानती|छुतीये|चूतिये |चूत|लौड़ा|लौड़े|चरित्रहीन |लिब्राण्डू|नंगी पुंगी|पागल औरत |बाज़ारू औरत|बलात्कार|बदसूरत|मुजरा|जाहिल औरत|औरत-ए-जाहिल|भोसड़ीwala|चंडाल चौकड़ी|म्लेच्छा|सूअर|सूअर की औलाद|दोगली|🏹🏹|पनौती|हरामी|गधी|बुरखा धत्त|बुल्ली |कलमुंही |पिछवाड़ा|काम वाली बाई|पैर की जूती|नाल|गंदी नाली|हगना|सुल्ली|हिज़रापंती|naachne waali|तवाइफ़|सौ टका टंच माल|किन्नर|गद्दार|चमचा|चमची|आतंकवादी|मुलिया|Katwa|चाटुकार|बहन की लोड़ी|चुस्लिम|चुस्लामि|चुसल्मान|चूस|भीमटा|भीमटी|बैल बुद्धि|हलाला|भद्दी औरत|भांड औरत|भाड़े का टट्टू|दो कौड़ी की औरत|घटिया औरत|बेहूदा औरत|चालू औरत|झूठी औरत|मर क्यों नहीं जाती|नल्ली|भूतनी के|चूत के बाल|मादरजात|भड़वा|चूची|टट्टी|गटर पैदाइश|मुँह मैं ले|मूत|नाजायज़|कटा लुंड|काला टेंट|जूता खायेगी|बुरखे वाली|काली कलूटी|काले तवे|मोटी भैंस|देहातन|देहाती औरत|गणिका|हबशी|ओला हु उबर|ABLANARI|AblaNari|ablanari|chakka|jihidis|jihadis|jihadi|Jihidis|Jihadis|jihadi|zehadi|jehadan|jihadinon|Chakko|chakki|chaka|Chinal|Randi|ramdi|Randie|randya|randikhana|r&d-khana|randi ke beej|Lulli|Gasti|Meetha|Halwa|Gud|Gaandu|Gaand|Gandiaal|Dheela Lun@|lodu|kutiya|kutti|Chudail|Badchalan|Battameez|kulta|haramjadi|dyan|saali|sali|chod|chodu bhagat|chudai|chooda|chuda|Bhdsk|2BHK|Bhosi ke|bsdk|bhonsdi ke|bhosad|bhosdiwale|maa ka bhosra|Lodu|bhenchod|Madarchod|Maderchod|mcp|mc|Lanti|chutiye|chutiya|Chut|hutiye|chutie|chutia|chut ke dhakkan|chut marli|chutan|<3da|Lavde|Gandu|Rakhail|librandu|chal phut|nangi poongi|pagal aurat|bazaru|bazari aurat|ola hi uber hai|balatkar|Ugly|Mujra|mujra|jaahil aurat|Mulli|hilana|hilaogi|Mlechcha|Suar|suar ki aulad|doghli|Panauti|panooti|harami|gadhi|रनडwa|🅱️ulli|kalmuhi|pichwada|jhadu|bai|kaam wali bai|pair ki jutti|naali|hagna|tukde tukde gang|Sulli|नाचने वाली|Tawaif|sau taka tunch maal|Skirt waali bai|Dhimmi hood|Dhimmihood|izzlam|gaddar|chamcha|chamchi|aatankwadi|Mulliya|Uncut|chatukar|Bahan Ke loudi|Kachra|Chuslim|chuslami|Chusalmans|chus|Bhimta|bheem-meem walas|bail budhi|Budhdhi|हलाला|bhadi aurat|bhanndh aurat|bhadi ka tattu|2 Kaudi ki aurat|Gatiya|Ghatiya aurat|behuda aurat|chalu aurat|jhuti aurat|Kaali aurat|Kaali bhaand|marr kyun nahi jaati|nalli|dimaag se paidal|bhootni|bhootni ke|choot ke baal|madarjaat|bhadva|bhadvi|bhandve|chuchi|tatti|maa ka boba chusu|mooh|munh mein le|mutth|najayaz paidaish|najayaz aulaad|Gutter ki paidaish|kata Lund|kala tent|joota khayegi|burkhe waali|ladki kahin ka|victim card|Aurat card|kali kalutti|Kale tawe|naali saaf kar|moti bhains|sukkhi haddi|Pataka|choodiyan pehen lo|abba ka naam|Ganika|gaand phadna|chewtypa|atrocuty_act|RandiKutiya|sulli|Rice bags|ola u uber|lovejihad|dull-it|toxic aunty|Presstitutes|libtard|bimbo|slims|Black Pepper|faggot|Sissy|whore|chrislamocommies|piddilover|Dynast Sycophants|Deshdrohi Chinese|Pak agents|Chinese Corona|Chinks|chinky|Feminazi|Mulli|R@ndi|halala|Half M|Scumreds|scumbags|burnol|anti national tukde|pheminist|dented-painted|Muzlim|Buzlim|Izzlam|pissfull|Simp|Bitch| Ms |sekoolar|sickular|sc0undrel|R@pe|R@p3|Characterless woman|Drama Queen|Ferrorists|Cunt|Slut|pussy|ugly|stupid|promiscuous|crazy|fat|fag|homo|hoe|motherfucker|sisterfucker|bastard|b@st@rd|bint|dyke|gash|muslimette|muttah|scag|gender nigger|assfucker|boobs|boobies|Melons|lesbain|moslem|nasty|redlight|nymph|piss|pimp|poop|pube|puke|retarded|slave|sissy|ola uh uber|pu55i|pu55y|mothafuck|mothafucka|mothafuckaz|mothafucked|mothafucker|mothafuckin|mothafucking |mothafuckings|motherfuck|motherfucked|motherfucker|motherfuckin|motherfucking|motherfuckings|lesbain|lesbayn|lesbian|lesbin|lesbo|nastyslut|nastywhore|nastybitch|nastyho|#முட்டாஉபி|#பெரியாராவது_மயிராவது|#பாலியல்_ஜல்சா_கட்சி|பொம்பள பொருக்கி|#ங்கோத்தா|கோத்தா|#கோத்தா|#கொம்மா|தாயோளி|தேவ்டியா பையா|தேவ்டியா|#பொட்டை|#சாமான்|சூத்து|லெஸ்பியன்|ஊம்பு|புண்ட|#திருட்டு_பள்ளன்|ஐட்டம்|அயிட்டம்|சாமான்|கூதி|ஆட்டக்காரி|வேசை|வேச|பொதுச் சொத்து|ஊர் மேய்றது|நடத்தை கெட்டது|பொட்டை|க்ரோஸ்ஸி|தாயோளி|குஜ்ஜிலீஸ்|மாங்கா|கோழி|முலை|பறத்தாயோலி|ஓக்க|தேவடியா மவன்|தேவடியா பசங்களா|புண்டை|புண்ட|பொட்டை நாய்|வாயில பூல விடுவேன்|தேவிடியா புண்`ட|புண்டை சைடு|உங்கம்மாவை ஓக்க|தேவிடியாளுக்கு பொறந்தவன்|சூத்தடி|ஒன்பது|பொன்ஸ்|ஆப்ப மாமி|கம்பு துண்டு|கல்லு|ஆம்புள கள்ளன்|அலி|அரவாணி|பின்துவாரி|பொடியன் மாஸ்டர்|டிகி|குரும்ப|அத்தை|December 23, 2021|#ஓத்த|Sunflowerண்டை|Sunflowerண்டை|லூசு கூ|#OSISORU|#thevdiyaa|#thevdiya|#gommala|#Pundamavane|#pundai|#otha|Koodhi|pottai|Potta Alith|Aththai|athai|loosu|fuck|cunt'; - -slurList = slurList.split('|'); -slurList = slurList.sort((a,b) => b.length - a.length); -slurList = slurList.join('|'); -let expression = new RegExp(`(?:${slurList})`, 'gi'); - +// Initialize the expression +let expression; +let expressionStatic; // These are words that are not replaced if part of 'slurList' let missedSlurListStatic = 'லூசு|கூFire'; -missedSlurListStatic = missedSlurListStatic.split('|'); -missedSlurListStatic = missedSlurListStatic.sort((a,b) => b.length - a.length); -missedSlurListStatic = missedSlurListStatic.join('|'); -let expressionStatic = new RegExp(`(?:${missedSlurListStatic})`, 'gi'); - // These are words that are not replaced if part of 'slurList' and contain escape character let missedEscapedSlurListStatic = 'choo$iya'; -(async () => { - const preference = await getPreferenceData(); - console.log(preference); - if (preference.slurList != undefined) { - updateSlurList(preference.slurList); +// Function to initialize/update the expression's +async function initializeExpressions() { + try { + // Fetch all words + const allSlurWords = await getAllSlurs(); + const slurList = allSlurWords.map((wordObj) => wordObj.word); + console.log("Slur words fetched from DB", slurList.length); + + // Sort and join slur list + const sortedSlurList = slurList.sort((a, b) => b.length - a.length).join('|'); + expression = new RegExp(`(?:${sortedSlurList})`, 'gi'); + + const sortedMissedList = missedSlurListStatic.split('|').sort((a, b) => b.length - a.length).join('|'); + expressionStatic = new RegExp(`(?:${sortedMissedList})`, 'gi'); + + console.log('Expressions initialized with slurs.'); + } catch (error) { + console.error('Error initializing expressions:', error); } +} + +// Initialize expressions on module load +(async () => { + await initializeExpressions(); })(); export function replaceSlur(sentence) { + // Ensure expressions are initialized + if (!expression || !expressionStatic) { + throw new Error('Expressions not initialized. Call initializeExpressions first.'); + } + sentence = sentence.replace(expression, generateMask); sentence = sentence.replace(expressionStatic, generateMask); sentence = sentence.replace(missedEscapedSlurListStatic, generateMask); + return sentence; } -export function updateSlurList(newSlurs) { - const list = newSlurs.split(',').join('|'); - slurList = slurList + '|' + list; - - slurList = slurList.split('|'); - slurList = slurList.sort((a,b) => b.length - a.length); - slurList = slurList.join('|'); - expression = new RegExp(`(?:${slurList})`, 'gi'); - missedSlurListStatic = missedSlurListStatic.split('|'); - missedSlurListStatic = missedSlurListStatic.sort((a,b) => b.length - a.length); - missedSlurListStatic = missedSlurListStatic.join('|'); - expressionStatic = new RegExp(`(?:${missedSlurListStatic})`, 'gi'); - } - function testSlurReplace() { // inputs space-separated slurList to get masked output diff --git a/browser-extension/plugin/src/slur-store.js b/browser-extension/plugin/src/slur-store.js new file mode 100644 index 00000000..caf352c5 --- /dev/null +++ b/browser-extension/plugin/src/slur-store.js @@ -0,0 +1,208 @@ +import Dexie from 'dexie'; +import mainSlurList from './slurlist-main'; +import { getPublicSlursMetadata } from './api/public-slurs'; + +const db = new Dexie('SlurWordsDatabase'); + +// Define database schema +db.version(1).stores({ + words: '++id, word, source', + words_metadata: '++id, label, level_of_severity, meaning, categories, language, timestamp' +}); + +// Function to add a word to the database +export async function addSlur(word, source) { + try { + const id = await db.words.add({ + word: word, + timestamp: new Date().toISOString(), + source: source + }); + return id; + } catch (error) { + console.error(`Error adding word to database: ${error}`); + throw error; + } +} + +// Function to get all words +export async function getAllSlurs() { + try { + const words = await db.words.toArray(); + return words; + } catch (error) { + console.error(`Error getting all words from database: ${error}`); + throw error; + } +} + +// Function to get all words by source +export async function getSlursBySource(source) { + try { + if (!source || typeof source !== 'string') { + throw new Error('Source must be a valid string'); + } + const words = await db.words.where('source').equals(source).toArray(); + return words; + } catch (error) { + console.error(`Error getting words by source "${source}":`, error); + throw error; + } +} + +// Function to bulk add words to the database +export async function bulkAddSlurs(wordsArray, source) { + if (!Array.isArray(wordsArray) || typeof source !== 'string') { + throw new Error('Invalid input: wordsArray must be an array and source must be a string'); + } + + // Prepare word objects with source and timestamp + const wordObjects = wordsArray.map((word) => ({ + word: word, + timestamp: new Date().toISOString(), + source: source + })); + + try { + await db.words.bulkAdd(wordObjects); + console.log(`${wordObjects.length} words added from source: ${source}`); + } catch (error) { + console.error(`Error adding words from source "${source}":`, error); + throw error; + } +} + +// Function to delete a single slur +export async function deleteSlur(word, source) { + try { + const slurToDelete = await db.words + .where('source') + .equals(source) + .and((slur) => slur.word === word) + .first(); // Fetch only the first matching slur + + if (slurToDelete) { + await db.words.delete(slurToDelete.id); + console.log(`Deleted slur "${word}" from source "${source}".`); + } else { + console.log(`Slur "${word}" not found for source "${source}".`); + } + } catch (error) { + console.error(`Error deleting slur "${word}" from source "${source}":`, error); + } +} + +// Function to check if a slur exists in the database +export async function slurExists(word, source) { + try { + const count = await db.words + .where('source') + .equals(source) + .filter((slur) => slur.word === word) + .count(); + return count > 0; // Returns true if the slur exists, else false + } catch (error) { + console.error(`Error checking if slur exists: ${error}`); + throw error; + } +} + +// Function to bulk add slur metadata to the database +export async function bulkAddSlurMetadata(metadataArray) { + if (!Array.isArray(metadataArray)) { + throw new Error('Invalid input: metadataArray must be an array'); + } + + // Prepare metadata objects for the database + const timestamp = new Date().toISOString(); + const metadataObjects = metadataArray.map((metadata) => ({ + label: metadata.label, + level_of_severity: metadata.level_of_severity, + meaning: metadata.meaning, + categories: metadata.categories, + language: metadata.language, + timestamp: timestamp, + })); + + try { + await db.words_metadata.bulkAdd(metadataObjects); + console.log(`${metadataObjects.length} slur metadata added.`); + } catch (error) { + console.error('Error adding slur metadata:', error); + throw error; + } +} + +// Function to fetch all slur metadata from the database +export async function getAllSlurMetadata() { + try { + const slurMetadata = await db.words_metadata.toArray(); + return slurMetadata; + } catch (error) { + console.error('Error fetching slur metadata:', error); + throw error; + } +} + +export async function convertSlurMetadataFromDBtoJSON() { + try { + const slurMetadata = await getAllSlurMetadata(); + + // Format the data into the desired structure + let jsonData = []; + jsonData = slurMetadata.map(slur => { + return { + [slur.label]: { + "Level of Severity": slur.level_of_severity, + "Casual": slur.casual ? "Yes" : "No", + "Appropriated": slur.appropriated ? "Yes" : "No", + "If, Appropriated, Is it by Community or Others?": slur.appropriation_context || "", + "What Makes it Problematic?": slur.meaning || "", + "Categories": slur.categories + } + }; + }); + + console.log("Formatted jsonData:", jsonData); + return jsonData; + } catch (error) { + console.error('Error fetching or formatting slur metadata:', error); + } +} + +export async function initializeSlurs() { + console.log('Initializing Indexed database'); + + try { + // Check if any words with source "hard_coded" already exist + const hardCodedWordCount = await db.words + .where('source') + .equals('hard_coded') + .count(); + + if (hardCodedWordCount > 0) { + console.log('Hard-coded slurs already exist in the database. Skipping initialization.'); + return; + } + + const mainSlurListArray = mainSlurList.split('|'); + // Index hard-coded slurs into the database + if (mainSlurListArray.length > 0) { + await bulkAddSlurs(mainSlurListArray, 'hard_coded'); + console.log(`Indexed ${mainSlurListArray.length} hard-coded slurs into the database.`); + } else { + console.log('No slurs found in the JSON file.'); + } + + // Fetch and store public slurs metadata + try { + const publicSlursMetadata = await getPublicSlursMetadata(); + // console.log('Public Slurs Metadata:', publicSlursMetadata); + await bulkAddSlurMetadata(publicSlursMetadata); + } catch (error) { + console.error('Error fetching and adding public slurs metadata:', error); + } + } catch (error) { + console.error('Error during database initialization:', error); + } +} diff --git a/browser-extension/plugin/src/slurlist-main.js b/browser-extension/plugin/src/slurlist-main.js new file mode 100644 index 00000000..252698ba --- /dev/null +++ b/browser-extension/plugin/src/slurlist-main.js @@ -0,0 +1,4 @@ +const mainSlurList = + 'जिहादी|छक्का|छिनाल|रंडी|रण्डी|रांड|रंडीखाना|रण्डी रोना|लुल्ली|गांड|गा#|कुतिया|कुत्ती|बत्तमीज़|कुल्टा|हरामजादी|साली|चो#|चुदाई|ma ki chui|मा के भोसड़े|भोस्डीके|भोछडी वाला |लोड़ू|बहन चोद|मादरचोद|लानती|छुतीये|चूतिये |चूत|लौड़ा|लौड़े|चरित्रहीन |लिब्राण्डू|नंगी पुंगी|पागल औरत |बाज़ारू औरत|बलात्कार|बदसूरत|मुजरा|जाहिल औरत|औरत-ए-जाहिल|भोसड़ीwala|चंडाल चौकड़ी|म्लेच्छा|सूअर|सूअर की औलाद|दोगली|🏹🏹|पनौती|हरामी|गधी|बुरखा धत्त|बुल्ली |कलमुंही |पिछवाड़ा|काम वाली बाई|पैर की जूती|नाल|गंदी नाली|हगना|सुल्ली|हिज़रापंती|naachne waali|तवाइफ़|सौ टका टंच माल|किन्नर|गद्दार|चमचा|चमची|आतंकवादी|मुलिया|Katwa|चाटुकार|बहन की लोड़ी|चुस्लिम|चुस्लामि|चुसल्मान|चूस|भीमटा|भीमटी|बैल बुद्धि|हलाला|भद्दी औरत|भांड औरत|भाड़े का टट्टू|दो कौड़ी की औरत|घटिया औरत|बेहूदा औरत|चालू औरत|झूठी औरत|मर क्यों नहीं जाती|नल्ली|भूतनी के|चूत के बाल|मादरजात|भड़वा|चूची|टट्टी|गटर पैदाइश|मुँह मैं ले|मूत|नाजायज़|कटा लुंड|काला टेंट|जूता खायेगी|बुरखे वाली|काली कलूटी|काले तवे|मोटी भैंस|देहातन|देहाती औरत|गणिका|हबशी|ओला हु उबर|ABLANARI|AblaNari|ablanari|chakka|jihidis|jihadis|jihadi|Jihidis|Jihadis|jihadi|zehadi|jehadan|jihadinon|Chakko|chakki|chaka|Chinal|Randi|ramdi|Randie|randya|randikhana|r&d-khana|randi ke beej|Lulli|Gasti|Meetha|Halwa|Gud|Gaandu|Gaand|Gandiaal|Dheela Lun@|lodu|kutiya|kutti|Chudail|Badchalan|Battameez|kulta|haramjadi|dyan|saali|sali|chod|chodu bhagat|chudai|chooda|chuda|Bhdsk|2BHK|Bhosi ke|bsdk|bhonsdi ke|bhosad|bhosdiwale|maa ka bhosra|Lodu|bhenchod|Madarchod|Maderchod|mcp|mc|Lanti|chutiye|chutiya|Chut|hutiye|chutie|chutia|chut ke dhakkan|chut marli|chutan|<3da|Lavde|Gandu|Rakhail|librandu|chal phut|nangi poongi|pagal aurat|bazaru|bazari aurat|ola hi uber hai|balatkar|Ugly|Mujra|mujra|jaahil aurat|Mulli|hilana|hilaogi|Mlechcha|Suar|suar ki aulad|doghli|Panauti|panooti|harami|gadhi|रनडwa|🅱️ulli|kalmuhi|pichwada|jhadu|bai|kaam wali bai|pair ki jutti|naali|hagna|tukde tukde gang|Sulli|नाचने वाली|Tawaif|sau taka tunch maal|Skirt waali bai|Dhimmi hood|Dhimmihood|izzlam|gaddar|chamcha|chamchi|aatankwadi|Mulliya|Uncut|chatukar|Bahan Ke loudi|Kachra|Chuslim|chuslami|Chusalmans|chus|Bhimta|bheem-meem walas|bail budhi|Budhdhi|हलाला|bhadi aurat|bhanndh aurat|bhadi ka tattu|2 Kaudi ki aurat|Gatiya|Ghatiya aurat|behuda aurat|chalu aurat|jhuti aurat|Kaali aurat|Kaali bhaand|marr kyun nahi jaati|nalli|dimaag se paidal|bhootni|bhootni ke|choot ke baal|madarjaat|bhadva|bhadvi|bhandve|chuchi|tatti|maa ka boba chusu|mooh|munh mein le|mutth|najayaz paidaish|najayaz aulaad|Gutter ki paidaish|kata Lund|kala tent|joota khayegi|burkhe waali|ladki kahin ka|victim card|Aurat card|kali kalutti|Kale tawe|naali saaf kar|moti bhains|sukkhi haddi|Pataka|choodiyan pehen lo|abba ka naam|Ganika|gaand phadna|chewtypa|atrocuty_act|RandiKutiya|sulli|Rice bags|ola u uber|lovejihad|dull-it|toxic aunty|Presstitutes|libtard|bimbo|slims|Black Pepper|faggot|Sissy|whore|chrislamocommies|piddilover|Dynast Sycophants|Deshdrohi Chinese|Pak agents|Chinese Corona|Chinks|chinky|Feminazi|Mulli|R@ndi|halala|Half M|Scumreds|scumbags|burnol|anti national tukde|pheminist|dented-painted|Muzlim|Buzlim|Izzlam|pissfull|Simp|Bitch| Ms |sekoolar|sickular|sc0undrel|R@pe|R@p3|Characterless woman|Drama Queen|Ferrorists|Cunt|Slut|pussy|ugly|stupid|promiscuous|crazy|fat|fag|homo|hoe|motherfucker|sisterfucker|bastard|b@st@rd|bint|dyke|gash|muslimette|muttah|scag|gender nigger|assfucker|boobs|boobies|Melons|lesbain|moslem|nasty|redlight|nymph|piss|pimp|poop|pube|puke|retarded|slave|sissy|ola uh uber|pu55i|pu55y|mothafuck|mothafucka|mothafuckaz|mothafucked|mothafucker|mothafuckin|mothafucking |mothafuckings|motherfuck|motherfucked|motherfucker|motherfuckin|motherfucking|motherfuckings|lesbain|lesbayn|lesbian|lesbin|lesbo|nastyslut|nastywhore|nastybitch|nastyho|#முட்டாஉபி|#பெரியாராவது_மயிராவது|#பாலியல்_ஜல்சா_கட்சி|பொம்பள பொருக்கி|#ங்கோத்தா|கோத்தா|#கோத்தா|#கொம்மா|தாயோளி|தேவ்டியா பையா|தேவ்டியா|#பொட்டை|#சாமான்|சூத்து|லெஸ்பியன்|ஊம்பு|புண்ட|#திருட்டு_பள்ளன்|ஐட்டம்|அயிட்டம்|சாமான்|கூதி|ஆட்டக்காரி|வேசை|வேச|பொதுச் சொத்து|ஊர் மேய்றது|நடத்தை கெட்டது|பொட்டை|க்ரோஸ்ஸி|தாயோளி|குஜ்ஜிலீஸ்|மாங்கா|கோழி|முலை|பறத்தாயோலி|ஓக்க|தேவடியா மவன்|தேவடியா பசங்களா|புண்டை|புண்ட|பொட்டை நாய்|வாயில பூல விடுவேன்|தேவிடியா புண்`ட|புண்டை சைடு|உங்கம்மாவை ஓக்க|தேவிடியாளுக்கு பொறந்தவன்|சூத்தடி|ஒன்பது|பொன்ஸ்|ஆப்ப மாமி|கம்பு துண்டு|கல்லு|ஆம்புள கள்ளன்|அலி|அரவாணி|பின்துவாரி|பொடியன் மாஸ்டர்|டிகி|குரும்ப|அத்தை|December 23, 2021|#ஓத்த|Sunflowerண்டை|Sunflowerண்டை|லூசு கூ|#OSISORU|#thevdiyaa|#thevdiya|#gommala|#Pundamavane|#pundai|#otha|Koodhi|pottai|Potta Alith|Aththai|athai|loosu|fuck|cunt'; + +export default mainSlurList; diff --git a/browser-extension/plugin/src/transform-general.js b/browser-extension/plugin/src/transform-general.js index 731637e3..a7c5178b 100644 --- a/browser-extension/plugin/src/transform-general.js +++ b/browser-extension/plugin/src/transform-general.js @@ -2,7 +2,6 @@ import { replaceSlur } from './slur-replace'; import { log } from './logger'; import repository from './repository'; const { getPreferenceData } = repository; -const jsonData = require('../../api-server/assets/slur_metadata.json') // Traverse dom nodes to find leaf node that are text nodes and process function bft(node) { @@ -50,18 +49,19 @@ function setCaretPosition(element, offset) { sel.addRange(range); } -const processNewlyAddedNodesGeneral2 = function (firstBody) { - let targetWords = [] ; +const processNewlyAddedNodesGeneral2 = function (firstBody, jsonData) { + let targetWords = []; jsonData.forEach(slur => { const slurWord = Object.keys(slur)[0]; - targetWords.push(slurWord) ; + targetWords.push(slurWord); targetWords.push(slurWord.charAt(0).toUpperCase() + slurWord.slice(1)); - }) - let uliStore = [] - getAllTextNodes(firstBody, uliStore) - abc = locateSlur(uliStore, targetWords) - addMetaData(targetWords) -} + }); + + let uliStore = []; + getAllTextNodes(firstBody, uliStore); + abc = locateSlur(uliStore, targetWords); + addMetaData(targetWords, jsonData); +}; const processNewlyAddedNodesGeneral = function (firstBody) { log('processing new nodes'); @@ -150,7 +150,7 @@ function locateSlur(uliStore, targetWords) { const className = `icon-container-${targetWord}`; const slurClass = `slur-container-${targetWord}` const parts = tempParent.innerHTML.split(targetWord); - console.log("PARTS: ",parts) + // console.log("PARTS: ",parts) const replacedHTML = parts.join(`${targetWord}`); tempParent.innerHTML = replacedHTML slurPresentInTempParent = true; @@ -164,20 +164,20 @@ function locateSlur(uliStore, targetWords) { textnode.replaceWith(tempParent) } - console.log("TEMPParent: ",tempParent) + // console.log("TEMPParent: ",tempParent) } return uliStore; } -function addMetaData(targetWords) { +function addMetaData(targetWords, jsonData) { // console.log(targetWords) targetWords.forEach(targetWord => { const className = `slur-container-${targetWord}` const elements = Array.from(document.querySelectorAll(`.${className}`)) - console.log("ELEMENTS are: ",elements) + // console.log("ELEMENTS are: ",elements) elements.forEach(element => { - console.log("ELements InnerHTML:",element.innerHTML) + // console.log("ELements InnerHTML:",element.innerHTML) // element.innerHTML = element.innerHTML.replace(/]*>/g, '') @@ -268,15 +268,15 @@ function addMetaData(targetWords) { sup.appendChild(span) - console.log("Element first child",element.children[0]) - console.log("Element last child",element.children[element.children.length-1]) - console.log("SUP: ",sup) - console.log("ELEMENT IS: ",element) - console.log("ELEMENT INNERHTML: ",element.innerHTML) + // console.log("Element first child",element.children[0]) + // console.log("Element last child",element.children[element.children.length-1]) + // console.log("SUP: ",sup) + // console.log("ELEMENT IS: ",element) + // console.log("ELEMENT INNERHTML: ",element.innerHTML) element.append(span) - console.log("ELEMENT AFTER IS: ",element) + // console.log("ELEMENT AFTER IS: ",element) // element.append(img) let slur = element.children[0] slur.style.backgroundColor="#ffde2155" @@ -284,7 +284,7 @@ function addMetaData(targetWords) { slur.style.cursor = "pointer" let metabox = element.children[1] - console.log("METABOX IS: ",metabox) + // console.log("METABOX IS: ",metabox) let spans = element.children[0].children[1] // const svgs = element.children[0].children[0]; const svgs = element.children[element.children.length-1]; diff --git a/browser-extension/plugin/src/ui-components/pages/App.jsx b/browser-extension/plugin/src/ui-components/pages/App.jsx index d3fe2034..6a2ba42b 100644 --- a/browser-extension/plugin/src/ui-components/pages/App.jsx +++ b/browser-extension/plugin/src/ui-components/pages/App.jsx @@ -219,4 +219,4 @@ export function App() { ); -} +} \ No newline at end of file diff --git a/browser-extension/plugin/src/ui-components/pages/Preferences.jsx b/browser-extension/plugin/src/ui-components/pages/Preferences.jsx index cc65fb03..d4068fba 100644 --- a/browser-extension/plugin/src/ui-components/pages/Preferences.jsx +++ b/browser-extension/plugin/src/ui-components/pages/Preferences.jsx @@ -9,7 +9,8 @@ import { Button, Select, CheckBox, - RadioButtonGroup + RadioButtonGroup, + Tip } from 'grommet'; // import { HelpCircle } from 'react-feather'; import Api from '../pages/Api'; @@ -21,7 +22,7 @@ const { savePreference } = Api; import { UserContext, NotificationContext } from '../atoms/AppContext'; import { userBrowserTabs } from '../../browser-compat'; import { Link, Outlet, useNavigate } from 'react-router-dom'; -import { FormClose, FormPreviousLink, LinkPrevious } from 'grommet-icons'; +import { FormClose, FormPreviousLink, LinkPrevious, Sync } from 'grommet-icons'; const { setPreferenceData, getPreferenceData } = repository; const defaultValue = {}; @@ -221,7 +222,7 @@ export function PreferencesHome() { type: 'message', message: t('message_ok_saved') }); - browserUtils.sendMessage('updateData', undefined); + // browserUtils.sendMessage('updateData', undefined); } catch (err) { showNotification({ type: 'error', @@ -236,10 +237,33 @@ export function PreferencesHome() { i18n.changeLanguage(langNameMap[option]); } + async function handleSyncApprovedSlurs() { + try { + browserUtils.sendMessage('syncApprovedCrowdsourcedSlurs', undefined); + + console.log('Sync message sent to content-script.js'); + } catch (error) { + console.error('Error syncing approved slurs:', error); + } + } + return ( - {t('language')} + + {t('language')} +