Skip to content

Commit

Permalink
Merge pull request #8 from GLINCKER/develop
Browse files Browse the repository at this point in the history
Release v1.1.0
  • Loading branch information
thegdsks authored Aug 3, 2024
2 parents bc483ab + 0c47709 commit b8204a6
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 62 deletions.
Binary file modified .DS_Store
Binary file not shown.
18 changes: 10 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "glin-profanity",
"version": "1.0.1",
"version": "1.1.0",
"description": "Glin-Profanity is a lightweight and efficient npm package designed to detect and filter profane language in text inputs across multiple languages. Whether you’re building a chat application, a comment section, or any platform where user-generated content is involved, Glin-Profanity helps you maintain a clean and respectful environment.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -12,8 +12,12 @@
"commit": "git-cz",
"lint": "eslint ."
},
"keywords": [],
"author": "",
"repository": {
"type": "git",
"url": "https://github.com/GLINCKER/glin-profanity"
},
"keywords": ["glin-profanity","glincker","glin","profanity"],
"author": "gdsks",
"license": "ISC",
"dependencies": {
"react": "^18.3.1",
Expand All @@ -32,13 +36,11 @@
"@typescript-eslint/eslint-plugin": "^7.18.0",
"@typescript-eslint/parser": "^7.18.0",
"babel-loader": "^9.1.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.35.0",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.35.0",
"html-webpack-plugin": "^5.6.0",
"husky": "^8.0.3",
"prettier": "^3.3.3",
"prettier": "^3.3.3",
"typescript": "^5.5.4",
"webpack": "^5.93.0",
"webpack-cli": "^5.1.4",
Expand Down
112 changes: 103 additions & 9 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@ import { useProfanityChecker } from './hooks/useProfanityChecker';
const App: React.FC = () => {
const [text, setText] = useState('');
const [checkAllLanguages, setCheckAllLanguages] = useState(false);
const { result, checkText } = useProfanityChecker(
checkAllLanguages ? { allLanguages: true } : { languages: ['english', 'french'] }
);
const [caseSensitive, setCaseSensitive] = useState(false);
const [wordBoundaries, setWordBoundaries] = useState(true);
const [customWords, setCustomWords] = useState<string[]>([]);
const [uploadStatus, setUploadStatus] = useState<string>('');

const { result, checkText } = useProfanityChecker({
allLanguages: checkAllLanguages,
caseSensitive: caseSensitive,
wordBoundaries: wordBoundaries,
customWords: customWords,
});

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setText(e.target.value);
Expand All @@ -16,26 +24,112 @@ const App: React.FC = () => {
checkText(text);
};

const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
try {
const words = JSON.parse(event.target?.result as string);
if (Array.isArray(words)) {
setCustomWords(words);
setUploadStatus('Custom words loaded successfully.');
} else {
setUploadStatus('Invalid JSON format: expected an array of strings.');
}
} catch (error) {
setUploadStatus('Error parsing JSON: ' + error.message);
}
};
reader.readAsText(file);
}
};

const handleReset = () => {
setText('');
setCheckAllLanguages(false);
setCaseSensitive(false);
setWordBoundaries(true);
setCustomWords([]);
setUploadStatus('');
};

return (
<div>
<h1>Welcome to Glin-Profanity</h1>
<input type="text" value={text} onChange={handleChange} />
<button onClick={handleCheck}>Check Profanity</button>
<div>
<input
type="text"
value={text}
onChange={handleChange}
placeholder="Type text to check"
style={{ padding: '10px', fontSize: '16px', width: '300px', marginBottom: '10px' }}
/>
<button onClick={handleCheck} style={{ padding: '10px 20px', fontSize: '16px' }}>
Check Profanity
</button>
<button onClick={handleReset} style={{ padding: '10px 20px', fontSize: '16px', marginLeft: '10px' }}>
Reset
</button>
<div style={{ marginTop: '10px' }}>
<label>
<input
type="checkbox"
checked={checkAllLanguages}
onChange={(e) => setCheckAllLanguages(e.target.checked)}
style={{ marginRight: '10px' }}
/>
Check All Languages
</label>
</div>
<div style={{ marginTop: '10px' }}>
<label>
<input
type="checkbox"
checked={caseSensitive}
onChange={(e) => setCaseSensitive(e.target.checked)}
style={{ marginRight: '10px' }}
/>
Case Sensitive
</label>
</div>
<div style={{ marginTop: '10px' }}>
<label>
<input
type="checkbox"
checked={wordBoundaries}
onChange={(e) => setWordBoundaries(e.target.checked)}
style={{ marginRight: '10px' }}
/>
Word Boundaries
</label>
</div>
<div style={{ marginTop: '10px' }}>
<label>
Upload Custom Words JSON:
<input
type="file"
accept=".json"
onChange={handleFileUpload}
style={{ marginLeft: '10px' }}
/>
</label>
{uploadStatus && <p>{uploadStatus}</p>}
</div>
{customWords.length > 0 && (
<div style={{ marginTop: '10px' }}>
<h3>Custom Words:</h3>
<ul>
{customWords.map((word, index) => (
<li key={index}>{word}</li>
))}
</ul>
</div>
)}
{result && (
<div>
<p>Contains Profanity: {result.containsProfanity ? 'Yes' : 'No'}</p>
<div style={{ marginTop: '20px' }}>
<p>Contains Profanity: <strong>{result.containsProfanity ? 'Yes' : 'No'}</strong></p>
{result.containsProfanity && (
<p>Profane Words: {result.profaneWords.join(', ')}</p>
<p>Profane Words: <strong>{result.profaneWords.join(', ')}</strong></p>
)}
</div>
)}
Expand Down
32 changes: 23 additions & 9 deletions src/filters/Filter.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import dictionary from '../data/dictionary';
import { Language } from '../types/Language';
import { Language, CheckProfanityResult } from '../types/types';

interface CheckProfanityResult {
containsProfanity: boolean;
profaneWords: string[];
interface FilterConfig {
languages?: Language[];
allLanguages?: boolean;
caseSensitive?: boolean;
wordBoundaries?: boolean;
customWords?: string[];
}

class Filter {
private words: Set<string>;
private caseSensitive: boolean;
private wordBoundaries: boolean;

constructor(config?: { languages?: Language[]; allLanguages?: boolean }) {
constructor(config?: FilterConfig) {
let words: string[] = [];
this.caseSensitive = config?.caseSensitive ?? false;
this.wordBoundaries = config?.wordBoundaries ?? true;

if (config?.allLanguages) {
for (const lang in dictionary) {
Expand All @@ -27,12 +34,19 @@ class Filter {
});
}
}

if (config?.customWords) {
words = [...words, ...config.customWords];
}

this.words = new Set<string>(words);
}

isProfane(value: string): boolean {
const flags = this.caseSensitive ? 'g' : 'gi';
for (const word of this.words) {
const wordExp = new RegExp(`\\b${word.replace(/(\W)/g, '\\$1')}\\b`, 'gi');
const boundary = this.wordBoundaries ? '\\b' : '';
const wordExp = new RegExp(`${boundary}${word.replace(/(\W)/g, '\\$1')}${boundary}`, flags);
if (wordExp.test(value)) return true;
}
return false;
Expand All @@ -43,16 +57,16 @@ class Filter {
const profaneWords: string[] = [];

for (const word of words) {
if (this.words.has(word.toLowerCase())) {
if (this.isProfane(word)) {
profaneWords.push(word);
}
}

return {
containsProfanity: profaneWords.length > 0,
profaneWords
profaneWords,
};
}
}

export { Filter, CheckProfanityResult };
export { Filter };
30 changes: 26 additions & 4 deletions src/hooks/useProfanityChecker.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,44 @@
import { useState } from 'react';
import { Filter, CheckProfanityResult } from '../filters/Filter';
import { Language } from '../types/Language';
import { Filter } from '../filters/Filter';
import { CheckProfanityResult, Language } from '../types/types';

interface ProfanityCheckerConfig {
languages?: Language[];
allLanguages?: boolean;
caseSensitive?: boolean;
wordBoundaries?: boolean;
customWords?: string[];
replaceWith?: string;
severityLevels?: boolean;
customActions?: (result: CheckProfanityResult) => void;
}

export const useProfanityChecker = (config?: ProfanityCheckerConfig) => {
const [result, setResult] = useState<CheckProfanityResult | null>(null);
const filter = new Filter(config);

const checkText = (text: string) => {
setResult(filter.checkProfanity(text));
const checkResult = filter.checkProfanity(text);
setResult(checkResult);
if (config?.customActions) {
config.customActions(checkResult);
}
};

const checkTextAsync = async (text: string) => {
return new Promise<CheckProfanityResult>((resolve) => {
const checkResult = filter.checkProfanity(text);
setResult(checkResult);
if (config?.customActions) {
config.customActions(checkResult);
}
resolve(checkResult);
});
};

return {
result,
checkText
checkText,
checkTextAsync,
};
};
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { Filter, CheckProfanityResult } from './filters/Filter';
export { Filter } from './filters/Filter';
export { useProfanityChecker } from './hooks/useProfanityChecker';
export type { Language } from './types/Language';
export type { Language, CheckProfanityResult } from './types/types';
10 changes: 10 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
23 changes: 0 additions & 23 deletions src/types/Language.ts

This file was deleted.

30 changes: 30 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// src/types/types.ts
export interface CheckProfanityResult {
containsProfanity: boolean;
profaneWords: string[];
processedText?: string;
severityMap?: { [word: string]: number };
}
export type Language =
| 'arabic'
| 'chinese'
| 'czech'
| 'danish'
| 'english'
| 'esperanto'
| 'finnish'
| 'french'
| 'german'
| 'hindi'
| 'hungarian'
| 'italian'
| 'japanese'
| 'korean'
| 'norwegian'
| 'persian'
| 'polish'
| 'portuguese'
| 'russian'
| 'turkish'
| 'swedish'
| 'thai';
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "ES5",
"module": "CommonJS",
"target": "es2015",
"module": "commonjs",
"lib": [
"dom",
"es2015"
Expand All @@ -13,6 +13,7 @@
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true
},
Expand Down
Loading

0 comments on commit b8204a6

Please sign in to comment.