Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
|
|
| 14 |
class ArabicContentModerator:
|
| 15 |
"""
|
| 16 |
Arabic Story Content Moderation Model using Deepseek API
|
| 17 |
-
Checks for cultural violations and inappropriate content
|
| 18 |
"""
|
| 19 |
|
| 20 |
def __init__(self, deepseek_api_key: str = None):
|
|
@@ -34,150 +34,124 @@ class ArabicContentModerator:
|
|
| 34 |
"Content-Type": "application/json"
|
| 35 |
}
|
| 36 |
|
| 37 |
-
#
|
| 38 |
self.moderation_prompt = """
|
| 39 |
-
أنت ناقد أدبي محترف متخصص في الأدب العربي والثقافة الإسلامية
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
2. **الفسق**:
|
| 156 |
-
- "اتبع شهواتك"، "الحرام لذيذ"، "العيب حلو".
|
| 157 |
-
3. **تشجيع الجريمة**:
|
| 158 |
-
- "السرقة سهلة"، "اضرب بقوة"، "اخفِ الدليل".
|
| 159 |
-
|
| 160 |
-
#### ثامنًا: التعبيرات الساخرة المحظورة
|
| 161 |
-
1. **سخرية من الدين**:
|
| 162 |
-
- "ههه دين"، "خرافات المسلمين"، "دين الصحراء".
|
| 163 |
-
2. **سخرية من الثقافة**:
|
| 164 |
-
- "تخلف عربي"، "عادات قديمة"، "جهل موروث".
|
| 165 |
-
3. **سخرية من القيم**:
|
| 166 |
-
- "شرف مزيف"، "أخلاق منافقة"، "قيم كاذبة".
|
| 167 |
-
|
| 168 |
-
#### تاسعًا: النصوص المشبوهة في السياق
|
| 169 |
-
1. **التلميحات الخفية**:
|
| 170 |
-
- "فعل بها شيئًا"، "لعب معها"، "استمتع بها".
|
| 171 |
-
2. **التعبيرات الغامضة**:
|
| 172 |
-
- "اقترب منها كثيرًا"، "جلسا معًا في الظلام".
|
| 173 |
-
|
| 174 |
-
### تعليمات الإجابة:
|
| 175 |
-
- إذا كان النص قصة أدبية أصيلة مكتوبة بالفصحى، يحتوي على عناصر السرد (شخصيات، حبكة، حوار، وصف)، يعكس قيمًا إيجابية، وخالٍ من أي انتهاك للمعايير أعلاه، أجب بـ **"true"** فقط.
|
| 176 |
-
- إذا انتهك النص أي معيار (غير أدبي، عامية، شتائم، محتوى جنسي، عنف، تحريض، سخرية، إلخ)، أو كان خبرًا، تعليقًا، أو مادة دراسية، أجب بـ **"no"** فقط.
|
| 177 |
-
- الإجابة يجب أن تكون **"true"** أو **"no"** فقط، بدون نص إضافي.
|
| 178 |
-
- راجع النص بدقة لتجنب رفض نص سليم أو قبول نص مخالف.
|
| 179 |
-
|
| 180 |
-
### النص المطلوب مراجعته:
|
| 181 |
"""
|
| 182 |
|
| 183 |
def _call_deepseek_api(self, story_content: str) -> Dict[str, Any]:
|
|
@@ -196,7 +170,7 @@ class ArabicContentModerator:
|
|
| 196 |
"messages": [
|
| 197 |
{
|
| 198 |
"role": "system",
|
| 199 |
-
"content": "أنت ناقد أدبي محترف متخصص في الأدب العربي والثقافة الإسلامية. مهمتك مراجعة النصوص بدقة للتأكد من التزامها بالقيم الدينية والثقافية العربية الإسلامية وكونها قصص
|
| 200 |
},
|
| 201 |
{
|
| 202 |
"role": "user",
|
|
@@ -204,7 +178,7 @@ class ArabicContentModerator:
|
|
| 204 |
}
|
| 205 |
],
|
| 206 |
"max_tokens": 10,
|
| 207 |
-
"temperature": 0.0,
|
| 208 |
"stream": False
|
| 209 |
}
|
| 210 |
|
|
@@ -225,7 +199,7 @@ class ArabicContentModerator:
|
|
| 225 |
logger.error(f"Exception calling Deepseek API: {str(e)}")
|
| 226 |
return {"error": str(e)}
|
| 227 |
|
| 228 |
-
def _validate_story_format(self, story_content: str) ->
|
| 229 |
"""
|
| 230 |
Enhanced validation of story format and content
|
| 231 |
|
|
@@ -233,54 +207,51 @@ class ArabicContentModerator:
|
|
| 233 |
story_content: Story content to validate
|
| 234 |
|
| 235 |
Returns:
|
| 236 |
-
|
| 237 |
"""
|
| 238 |
if not story_content or not isinstance(story_content, str):
|
| 239 |
-
return False
|
| 240 |
|
| 241 |
-
# Check minimum length
|
| 242 |
if len(story_content.strip()) < 50:
|
| 243 |
-
return False
|
| 244 |
|
| 245 |
-
# Check for Arabic characters
|
| 246 |
arabic_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
|
| 247 |
arabic_chars = len(arabic_pattern.findall(story_content))
|
| 248 |
|
| 249 |
-
|
| 250 |
-
|
|
|
|
| 251 |
|
| 252 |
-
# Quick pre-screening for obvious violations
|
| 253 |
content_lower = story_content.lower()
|
| 254 |
|
| 255 |
-
# Check for
|
| 256 |
-
|
| 257 |
-
r'أعلنت\s*(?:الحكومة|السلطات|وزارة)', # News
|
| 258 |
-
r'(?:برأيي|أعتقد\s*أن|من\s*وجهة\s*نظري)', # Opinions
|
| 259 |
-
r'(?:تعريف|في\s*هذا\s*الفصل|يشرح\s*الدرس)', # Study materials
|
| 260 |
-
r'أسباب\s*(?:النجاح|الفشل|المشكلة)\s*:\s*\d+', # Lists
|
| 261 |
-
r'(?:اشترِ|تسوق|منتج\s*جديد|خصم\s*\d+%)' # Advertisements
|
| 262 |
-
]
|
| 263 |
-
|
| 264 |
-
for pattern in non_literary_patterns:
|
| 265 |
-
if re.search(pattern, content_lower):
|
| 266 |
-
return False, "النص غير أدبي (أخبار، تعليقات، مواد دراسية، إعلانات)"
|
| 267 |
-
|
| 268 |
-
# Check for profanity and religious violations
|
| 269 |
-
violation_patterns = [
|
| 270 |
# Sexual profanity
|
| 271 |
-
r'\b
|
|
|
|
|
|
|
|
|
|
| 272 |
|
| 273 |
-
# Religious violations
|
| 274 |
-
r'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
]
|
| 276 |
|
| 277 |
-
for pattern in
|
| 278 |
if re.search(pattern, content_lower):
|
| 279 |
-
|
| 280 |
-
return False, "انتهاك ديني أو طائفي"
|
| 281 |
-
return False, "ألفاظ نابية أو محتوى غير أخلاقي"
|
| 282 |
|
| 283 |
-
return True
|
| 284 |
|
| 285 |
def moderate_story(self, story_content: str) -> Dict[str, Any]:
|
| 286 |
"""
|
|
@@ -293,13 +264,11 @@ class ArabicContentModerator:
|
|
| 293 |
Dictionary with moderation result
|
| 294 |
"""
|
| 295 |
# Enhanced validation
|
| 296 |
-
|
| 297 |
-
if not is_valid:
|
| 298 |
return {
|
| 299 |
"approved": False,
|
| 300 |
"response": "no",
|
| 301 |
-
"reason":
|
| 302 |
-
"violation_type": "format_validation" if "غير أدبي" in validation_reason or "قصير" in validation_reason or "عربي غير كافٍ" in validation_reason else "content_violation",
|
| 303 |
"timestamp": datetime.now().isoformat()
|
| 304 |
}
|
| 305 |
|
|
@@ -315,7 +284,6 @@ class ArabicContentModerator:
|
|
| 315 |
"approved": False,
|
| 316 |
"response": "no",
|
| 317 |
"reason": "خطأ في خدمة المراجعة",
|
| 318 |
-
"violation_type": "api_error",
|
| 319 |
"error": api_response["error"],
|
| 320 |
"timestamp": datetime.now().isoformat()
|
| 321 |
}
|
|
@@ -324,10 +292,10 @@ class ArabicContentModerator:
|
|
| 324 |
# Extract the moderation decision
|
| 325 |
ai_response = api_response.get("choices", [{}])[0].get("message", {}).get("content", "").strip().lower()
|
| 326 |
|
| 327 |
-
# Clean the response
|
| 328 |
ai_response = re.sub(r'[^\w]', '', ai_response)
|
| 329 |
|
| 330 |
-
# Determine if content is approved
|
| 331 |
approved = ai_response == "true"
|
| 332 |
response_value = "true" if approved else "no"
|
| 333 |
|
|
@@ -340,27 +308,9 @@ class ArabicContentModerator:
|
|
| 340 |
}
|
| 341 |
|
| 342 |
if not approved:
|
| 343 |
-
result["reason"] = "المحتوى ينتهك القواعد المجتمعية أو الثقافية أو الدينية، أو أنه ليس قصة أدبية
|
| 344 |
-
result["violation_type"] = "content_violation"
|
| 345 |
-
# Categorize violation
|
| 346 |
-
content_lower = cleaned_content.lower()
|
| 347 |
-
if any(re.search(pattern, content_lower) for pattern in [
|
| 348 |
-
r'(?:الله\s*(?:كذاب|وهم|غبي)|لعن\s*الله|تبا\s*لله|يلعن\s*رب|القرآن\s*(?:كذب|مزور|خرافات)|محمد\s*(?:كذاب|دجال)|لعنة\s*على\s*محمد|أبو\s*بكر\s*منافق|عمر\s*ظالم|علي\s*مخادع|الصحابة\s*كذابون|الصلاة\s*مضيعة|رمضان\s*تخلف|الكعبة\s*(?:صنم|حجر)|المسيح\s*كذاب|الإنجيل\s*محرف|موسى\s*دجال|التوراة\s*مزورة|لا\s*إله|الجنة\s*وهم|النار\s*خيال|القيامة\s*كذبة|لعنة\s*الله|بيوت\s*خرافة|السنة\s*نواصب|الشيعة\s*رافضة|الصوفية\s*مشركون|الوهابيون\s*كفار)'
|
| 349 |
-
]):
|
| 350 |
-
result["violation_type"] = "religious_violation"
|
| 351 |
-
elif any(re.search(pattern, content_lower) for pattern in [
|
| 352 |
-
r'\b(?:كس|زب|نيك|منيوك|شرموط|قحب|لحس|مص|طيز|بزاز|تبن|نعل|كوس|أير|منايك|خرا|كداب|عرص|خول|حيوان)\b'
|
| 353 |
-
]):
|
| 354 |
-
result["violation_type"] = "profanity_violation"
|
| 355 |
-
elif any(re.search(pattern, content_lower) for pattern in [
|
| 356 |
-
r'أعلنت\s*(?:الحكومة|السلطات|وزارة)|(?:برأيي|أعتقد\s*أن|من\s*وجهة\s*نظري)|(?:تعريف|في\s*هذا\s*الفصل|يشرح\s*الدرس)|أسباب\s*(?:النجاح|الفشل|المشكلة)\s*:\s*\d+|(?:اشترِ|تسوق|منتج\s*جديد|خصم\s*\d+%)'
|
| 357 |
-
]):
|
| 358 |
-
result["violation_type"] = "non_literary_content"
|
| 359 |
-
elif arabic_chars < len(cleaned_content.strip()) * 0.8:
|
| 360 |
-
result["violation_type"] = "non_classical_arabic"
|
| 361 |
else:
|
| 362 |
-
result["reason"] = "المحتوى مقبول ويلتزم بالمعايير ال
|
| 363 |
-
result["violation_type"] = None
|
| 364 |
|
| 365 |
logger.info(f"Moderation completed: {response_value} for content of length {len(cleaned_content)}")
|
| 366 |
return result
|
|
@@ -371,15 +321,15 @@ class ArabicContentModerator:
|
|
| 371 |
"approved": False,
|
| 372 |
"response": "no",
|
| 373 |
"reason": "خطأ في معالجة نتيجة المراجعة",
|
| 374 |
-
"violation_type": "processing_error",
|
| 375 |
"error": str(e),
|
| 376 |
"timestamp": datetime.now().isoformat()
|
| 377 |
}
|
| 378 |
|
|
|
|
| 379 |
# Flask application
|
| 380 |
app = Flask(__name__)
|
| 381 |
|
| 382 |
-
# Initialize the moderator
|
| 383 |
try:
|
| 384 |
moderator = ArabicContentModerator()
|
| 385 |
logger.info("Arabic Content Moderator initialized successfully")
|
|
@@ -394,27 +344,31 @@ def home():
|
|
| 394 |
"service": "مراجع المحتوى الأدبي العربي المحسن",
|
| 395 |
"service_en": "Enhanced Arabic Literary Content Moderator",
|
| 396 |
"version": "2.0.0",
|
| 397 |
-
"description": "AI-powered professional literary critic for
|
| 398 |
-
"description_ar": "ناقد أدبي محترف مدعوم بالذكاء الاصطناعي للقصص العربية القصيرة
|
| 399 |
"endpoints": {
|
| 400 |
"/health": "Health check",
|
| 401 |
"/moderate": "POST - Moderate single story",
|
| 402 |
"/moderate/batch": "POST - Moderate multiple stories"
|
| 403 |
},
|
| 404 |
"features": [
|
| 405 |
-
"
|
| 406 |
-
"
|
| 407 |
-
"
|
| 408 |
-
"
|
| 409 |
-
"
|
| 410 |
-
"
|
| 411 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 412 |
],
|
| 413 |
"usage": {
|
| 414 |
"moderate": {
|
| 415 |
"method": "POST",
|
| 416 |
"payload": {"story_content": "Arabic story text"},
|
| 417 |
-
"response": {"approved": "boolean", "response": "true/no"
|
| 418 |
}
|
| 419 |
},
|
| 420 |
"status": "healthy" if moderator else "service unavailable"
|
|
@@ -445,7 +399,6 @@ def moderate_content():
|
|
| 445 |
"approved": true/false,
|
| 446 |
"response": "true"/"no",
|
| 447 |
"reason": "reason in Arabic",
|
| 448 |
-
"violation_type": "string or null",
|
| 449 |
"timestamp": "ISO timestamp"
|
| 450 |
}
|
| 451 |
"""
|
|
@@ -454,8 +407,7 @@ def moderate_content():
|
|
| 454 |
"error": "خدمة المراجعة غير متوفرة - لم يتم تكوين مفتاح API",
|
| 455 |
"error_en": "Moderation service not available - API key not configured",
|
| 456 |
"approved": False,
|
| 457 |
-
"response": "no"
|
| 458 |
-
"violation_type": "service_unavailable"
|
| 459 |
}), 500
|
| 460 |
|
| 461 |
try:
|
|
@@ -466,8 +418,7 @@ def moderate_content():
|
|
| 466 |
"error": "محتوى القصة مفقود في الطلب",
|
| 467 |
"error_en": "Missing story_content in request",
|
| 468 |
"approved": False,
|
| 469 |
-
"response": "no"
|
| 470 |
-
"violation_type": "invalid_request"
|
| 471 |
}), 400
|
| 472 |
|
| 473 |
story_content = data['story_content']
|
|
@@ -482,7 +433,6 @@ def moderate_content():
|
|
| 482 |
"error_en": "Internal server error",
|
| 483 |
"approved": False,
|
| 484 |
"response": "no",
|
| 485 |
-
"violation_type": "server_error",
|
| 486 |
"details": str(e)
|
| 487 |
}), 500
|
| 488 |
|
|
@@ -499,8 +449,7 @@ def moderate_batch():
|
|
| 499 |
if not moderator:
|
| 500 |
return jsonify({
|
| 501 |
"error": "خدمة المراجعة غير متوفرة - لم يتم تكوين مفتاح API",
|
| 502 |
-
"error_en": "Moderation service not available - API key not configured"
|
| 503 |
-
"violation_type": "service_unavailable"
|
| 504 |
}), 500
|
| 505 |
|
| 506 |
try:
|
|
@@ -509,16 +458,14 @@ def moderate_batch():
|
|
| 509 |
if not data or 'stories' not in data:
|
| 510 |
return jsonify({
|
| 511 |
"error": "مصفوفة القصص مفقودة في الطلب",
|
| 512 |
-
"error_en": "Missing stories array in request"
|
| 513 |
-
"violation_type": "invalid_request"
|
| 514 |
}), 400
|
| 515 |
|
| 516 |
stories = data['stories']
|
| 517 |
if not isinstance(stories, list):
|
| 518 |
return jsonify({
|
| 519 |
"error": "القصص يجب أن تكون في شكل مصفوفة",
|
| 520 |
-
"error_en": "Stories must be an array"
|
| 521 |
-
"violation_type": "invalid_request"
|
| 522 |
}), 400
|
| 523 |
|
| 524 |
results = []
|
|
@@ -550,11 +497,10 @@ def moderate_batch():
|
|
| 550 |
return jsonify({
|
| 551 |
"error": "خطأ داخلي في الخادم",
|
| 552 |
"error_en": "Internal server error",
|
| 553 |
-
"violation_type": "server_error",
|
| 554 |
"details": str(e)
|
| 555 |
}), 500
|
| 556 |
|
| 557 |
if __name__ == '__main__':
|
| 558 |
# For local testing
|
| 559 |
-
port = int(os.
|
| 560 |
app.run(host='0.0.0.0', port=port, debug=False)
|
|
|
|
| 14 |
class ArabicContentModerator:
|
| 15 |
"""
|
| 16 |
Arabic Story Content Moderation Model using Deepseek API
|
| 17 |
+
Checks for cultural violations and inappropriate content
|
| 18 |
"""
|
| 19 |
|
| 20 |
def __init__(self, deepseek_api_key: str = None):
|
|
|
|
| 34 |
"Content-Type": "application/json"
|
| 35 |
}
|
| 36 |
|
| 37 |
+
# Enhanced professional literary critic moderation prompt with comprehensive examples
|
| 38 |
self.moderation_prompt = """
|
| 39 |
+
أنت ناقد أدبي محترف متخصص في الأدب العربي والثقافة الإسلامية. مهمتك مراجعة النصوص للتأكد من أنها قصص أدبية حقيقية وأنها تلتزم بالقيم الثقافية والدينية العربية الإسلامية.
|
| 40 |
+
|
| 41 |
+
معايير المراجعة الصارمة مع أمثلة شاملة:
|
| 42 |
+
|
| 43 |
+
أولاً - التحقق من طبيعة النص الأدبي:
|
| 44 |
+
- يجب أن يكون النص قصة أدبية حقيقية وليس خبراً صحفياً أو تعليقاً أو مادة دراسية
|
| 45 |
+
- يجب أن يحتوي على عناصر السرد: الشخصيات، الحبكة، الحوار، الوصف
|
| 46 |
+
- لا يُقبل النصوص الإخبارية مثل: "أعلنت الحكومة اليوم..." أو "وفقاً للتقارير..."
|
| 47 |
+
- لا يُقبل التعليقات الشخصية مثل: "أعتقد أن..." أو "من وجهة نظري..."
|
| 48 |
+
|
| 49 |
+
ثانياً - المحتوى الديني والثقافي (فحص دقيق) - أمثلة محظورة:
|
| 50 |
+
|
| 51 |
+
أ) الاستهزاء والسب المباشر للإسلام:
|
| 52 |
+
▪ سب الله تعالى: "الله كذاب"، "لعن الله"، "تبا لله"، "يلعن رب العالمين"، "الله غبي"، "أبو الله"
|
| 53 |
+
▪ سب القرآن: "القرآن كذب"، "كتاب مزور"، "خرافات القرآن"، "هراء قرآني"، "قرآن مؤلف"
|
| 54 |
+
▪ سب الرسول محمد: "محمد كذاب"، "الرسول دجال"، "نبي كاذب"، "أبو محمد"، "لعنة على محمد"
|
| 55 |
+
▪ سب الصحابة: "أبو بكر منافق"، "عمر ظالم"، "علي مخادع"، "الصحابة كذابون"
|
| 56 |
+
▪ سب الأئمة والعلماء: "الإمام جاهل"، "الشيخ محتال"، "العلماء مضللون"
|
| 57 |
+
|
| 58 |
+
ب) الاستهزاء بالشعائر الإسلامية:
|
| 59 |
+
▪ الصلاة: "الصلاة مضيعة وقت"، "الصلاة رقص"، "السجود ذل"، "الوضوء قذارة"
|
| 60 |
+
▪ الصيام: "الصيام تعذيب"، "رمضان تخلف"، "الصوم جوع اختياري"، "إفطار مبكر"
|
| 61 |
+
▪ الحج: "الحج مجرد طواف حول حجر"، "الكعبة صنم"، "الحج سياحة دينية"، "طقوس وثنية"
|
| 62 |
+
▪ الزكاة: "الزكاة سرقة"، "ضريبة دينية"، "استغلال الفقراء"، "نصب باسم الدين"
|
| 63 |
+
|
| 64 |
+
ج) سب وانتقاد الأديان الأخرى:
|
| 65 |
+
▪ المسيحية: "المسيح كذاب"، "الإنجيل محرف"، "المسيحيون كفار"، "الصليب لعنة"، "مريم زانية"
|
| 66 |
+
▪ اليهودية: "موسى دجال"، "التوراة مزورة"، "اليهود ملعونون"، "بني إسرائيل خونة"
|
| 67 |
+
▪ الهندوسية: "الهندوس وثنيون"، "آلهتم أصنام"، "البقرة إلههم"، "طقوس شيطانية"
|
| 68 |
+
▪ البوذية: "بوذا شيطان"، "البوذية ضلال"، "عبادة التماثيل"، "ديانة كاذبة"
|
| 69 |
+
|
| 70 |
+
د) التجديف والكفر المباشر:
|
| 71 |
+
▪ إنكار وجود الله: "لا إله"، "الله وهم"، "لا خالق"، "الكون بلا رب"، "الله خرافة"
|
| 72 |
+
▪ إنكار الآخرة: "الجنة وهم"، "النار خيال"، "القيامة كذبة"، "لا بعث ولا نشور"
|
| 73 |
+
▪ إنكار الأنبياء: "لا أنبياء"، "الرسل دجالون"، "الوحي خرافة"، "النبوة كذب"
|
| 74 |
+
|
| 75 |
+
ه) السب بالألفاظ الدينية المحرمة:
|
| 76 |
+
▪ تحريف أسماء الله: "اللع"، "الرحمن الغض��ان"، "العزيز الذليل"، "الحكيم الجاهل"
|
| 77 |
+
▪ اللعن الديني: "لعنة الله عليك"، "الله يلعنك"، "ملعون من رب العالمين"
|
| 78 |
+
▪ القسم الكاذب: "والله كذاب"، "أقسم بالله زوراً"، "حلفان كاذب"
|
| 79 |
+
|
| 80 |
+
و) الاستهزاء بالرموز الدينية:
|
| 81 |
+
▪ المساجد: "بيوت خرافة"، "مساجد ضلال"، "مراكز غسيل مخ"، "مقرات إرهاب"
|
| 82 |
+
▪ الكعبة: "حجر أسود"، "صنم مكة"، "وثن العرب"، "مكعب خرافي"
|
| 83 |
+
▪ المصحف: "كتاب خرافات"، "ورق بلا قيمة"، "كلام فارغ"، "هراء مقدس"
|
| 84 |
+
|
| 85 |
+
ز) السب الطائفي والمذهبي:
|
| 86 |
+
▪ ضد السنة: "السنة نواصب"، "أهل السنة مجسمة"، "السلفيون إرهابيون"، "الوهابيون كفار"
|
| 87 |
+
▪ ضد الشيعة: "الشيعة رافضة"، "أهل البدع"، "عبدة القبور"، "الشيعة مجوس"
|
| 88 |
+
▪ ضد الصوفية: "الصوفية مشركون"، "دراويش ضالون"، "عبدة الأولياء"، "طرق شيطانية"
|
| 89 |
+
|
| 90 |
+
ح) التكفير والتضليل:
|
| 91 |
+
▪ تكفير المسلمين: "المسلمون كفار"، "أهل القبلة مشركون"، "المؤمنون ضالون"
|
| 92 |
+
▪ تضليل الطوائف: "كلهم على باطل"، "كل الأديان كذب"، "كل المذاهب ضلال"
|
| 93 |
+
|
| 94 |
+
ثالثاً - الألفاظ النابية والسب بجميع اللهجات العربية:
|
| 95 |
+
▪ المشرقية: "كس أختك"، "منيوك"، "تبن أم..."، "نعل أبوك"، "أبو شرموطة"
|
| 96 |
+
▪ المغربية/الجزائرية: "قحبة"، "نيك"، "زبي في..."، "كحلة"، "شرموطة"، "طبون أمك"
|
| 97 |
+
▪ المصرية: "كسم اللي خلفك"، "ابن الوسخة"، "ابن الشرموطة"، "نيكني"، "كسك"
|
| 98 |
+
▪ الخليجية: "منيج"، "أبوي فيك"، "خرا عليك"، "تبن ريلك"، "منايك"
|
| 99 |
+
▪ العراقية: "كوس أمك"، "منيوج"، "ابن الجحش"، "خرا"، "تبن أبوك"
|
| 100 |
+
▪ الشامية: "كسك"، "نايك"، "أير فيك"، "ابن الشرموطة"، "منيوك أختك"
|
| 101 |
+
▪ السودانية: "كداب"، "خايب"، "حيوان"، "ابن الكلب"، "عرص"
|
| 102 |
+
▪ اليمنية: "لحس"، "منيك"، "قحبة"، "كسك"، "عاهرة"
|
| 103 |
+
|
| 104 |
+
رابعاً - المحتوى الجنسي والأخلاقي (رقابة صارمة) - أمثلة محظورة:
|
| 105 |
+
▪ وصف الأعضاء التناسلية: "زب"، "كس"، "طيز"، "صدر"، "بزاز"، "فرج"
|
| 106 |
+
▪ الأفعال الجنسية: "نيك"، "لحس"، "مص"، "دخل فيها"، "ركب عليها"، "مارس الجنس"
|
| 107 |
+
▪ الإثارة الجنسية: "مثير"، "ساخن"، "شهوة"، "رغبة جنسية"، "انتصاب"، "هيجان"
|
| 108 |
+
▪ العلاقات المحرمة: "حبيبي"، "عشيقة"، "خليلة"، "صديق بفوائد"، "متعة"
|
| 109 |
+
▪ الشذوذ الجنسي: "مثلي"، "لوطي"، "سحاقية"، "شاذ"، "خنثى"
|
| 110 |
+
|
| 111 |
+
خامساً - العنف والمحتوى المؤذي - أمثلة محظورة:
|
| 112 |
+
▪ العنف المفرط: "قطع رأسه"، "مزق جسده"، "عذبه حتى الموت"، "سحله"
|
| 113 |
+
▪ تمجيد القتل: "القتل شرف"، "الانتقام واجب"، "الدم يغسل العار"
|
| 114 |
+
▪ إيذاء النفس: "اقتل نفسك"، "انتحر"، "اجرح نفسك"، "أذي نفسك"
|
| 115 |
+
|
| 116 |
+
سادساً - المحتوى السياسي والاجتماعي الحساس - أمثلة محظورة:
|
| 117 |
+
▪ التحريض الطائفي: "السنة كفار"، "الشيعة مرتدون"، "الصوفية ضالون"
|
| 118 |
+
▪ التحريض العرقي: "العرب متخلفون"، "الأكراد خونة"، "البربر همج"
|
| 119 |
+
▪ إثارة الفتنة: "اقتلوا الطائفة الأخرى"، "احرقوا مساجدهم"، "دمروا مقدساتهم"
|
| 120 |
+
|
| 121 |
+
سابعاً - السب والشتائم الاجتماعية بجميع اللهجات:
|
| 122 |
+
▪ سب الأمهات: "أم الخبيث"، "أمك شرموطة"، "تبن أم اللي خلفك"، "كس أم..."
|
| 123 |
+
▪ سب الآباء: "أبوك حيوان"، "تبن أبوك"، "أبو الوسخ"، "نعل أبو..."
|
| 124 |
+
▪ سب الأخوات: "أختك عاهرة"، "كس أختك"، "أختك قحبة"، "نيك أختك"
|
| 125 |
+
▪ الشتائم العامة: "ابن الكلب"، "ابن الحرام"، "عرص"، "خول"، "معفن"، "وسخ"، "نذل"، "حقير"، "خنزير"، "كلب"، "حمار"، "بهيمة"
|
| 126 |
+
|
| 127 |
+
ثامناً - تعبيرات الاستخفاف والسخرية المحظورة:
|
| 128 |
+
▪ من الدين: "ههههه دين"، "دين الصحراء"، "خرافات المسلمين"
|
| 129 |
+
▪ من التقاليد: "عادات بالية"، "تخلف عربي"، "جهل الأجداد"
|
| 130 |
+
▪ من القيم: "شرف كاذب"، "أخلاق مصطنعة"، "قيم منافقة"
|
| 131 |
+
|
| 132 |
+
تاسعاً - الكلمات المبتذلة والعامية المرفوضة:
|
| 133 |
+
▪ ألفاظ مبتذلة: "تفو"، "يع"، "قرف"، "وسخ"، "قذر"، "نتن"، "عفن"
|
| 134 |
+
▪ تعبيرات جنسية مخفية: "عمل شيء"، "فعل به الفاحشة"، "لعب معها"، "استمتع بها"
|
| 135 |
+
|
| 136 |
+
عاشراً - التحريض والدعوة للمخالفات:
|
| 137 |
+
▪ المخدرات: "خذ حبة"، "اشرب خمر"، "جرب الحشيش"، "المخدرات رائعة"
|
| 138 |
+
▪ الفسق: "اتبع شهواتك"، "لا تلتزم بالحلال"، "الحرام لذيذ"، "العيب حلو"
|
| 139 |
+
|
| 140 |
+
أحد عشر - العبارات المشبوهة في السياق:
|
| 141 |
+
▪ التلميحات الجنسية: "اقترب منها أكثر"، "لمسها بحنان"، "قبلها بشغف"
|
| 142 |
+
▪ العنف المقنع: "علمه درساً لن ينساه"، "جعله يندم"، "أوجعه"
|
| 143 |
+
|
| 144 |
+
إثنا عشر - التعبيرات المسيئة للمرأة:
|
| 145 |
+
▪ تحقير المرأة: "المرأة ناقصة عقل"، "النساء عورة"، "المرأة فتنة"، "البنت عار"
|
| 146 |
+
|
| 147 |
+
انتبه: هذه الأمثلة للتوضيح فقط. أي نص يحتوي على مثل هذه التعبيرات أو ما يشبهها أو يحمل نفس المعنى بأي لهجة عربية يجب رفضه فوراً.
|
| 148 |
+
|
| 149 |
+
تعليمات الإجابة:
|
| 150 |
+
- إذا كان النص قصة أدبية حقيقية وملتزمة بجميع المعايير وخالية من جميع الأمثلة المحظورة أعلاه، أجب بـ "true" فقط
|
| 151 |
+
- إذا انتهك النص أي معيار أو احتوى على أي من الأمثلة المحظورة أو ما يشبهها، أجب بـ "no" فقط
|
| 152 |
+
- يجب أن تكون إجابتك "true" أو "no" فقط بدون أي نص إضافي
|
| 153 |
+
|
| 154 |
+
النص المطلوب مراجعته:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
"""
|
| 156 |
|
| 157 |
def _call_deepseek_api(self, story_content: str) -> Dict[str, Any]:
|
|
|
|
| 170 |
"messages": [
|
| 171 |
{
|
| 172 |
"role": "system",
|
| 173 |
+
"content": "أنت ناقد أدبي محترف متخصص في الأدب العربي والثقافة الإسلامية. مهمتك مراجعة النصوص بدقة شديدة للتأكد من التزامها بالقيم الدينية والثقافية العربية الإسلامية ومن كونها قصصاً أدبية حقيقية."
|
| 174 |
},
|
| 175 |
{
|
| 176 |
"role": "user",
|
|
|
|
| 178 |
}
|
| 179 |
],
|
| 180 |
"max_tokens": 10,
|
| 181 |
+
"temperature": 0.0, # Changed to 0.0 for more consistent results
|
| 182 |
"stream": False
|
| 183 |
}
|
| 184 |
|
|
|
|
| 199 |
logger.error(f"Exception calling Deepseek API: {str(e)}")
|
| 200 |
return {"error": str(e)}
|
| 201 |
|
| 202 |
+
def _validate_story_format(self, story_content: str) -> bool:
|
| 203 |
"""
|
| 204 |
Enhanced validation of story format and content
|
| 205 |
|
|
|
|
| 207 |
story_content: Story content to validate
|
| 208 |
|
| 209 |
Returns:
|
| 210 |
+
Boolean indicating if format is valid
|
| 211 |
"""
|
| 212 |
if not story_content or not isinstance(story_content, str):
|
| 213 |
+
return False
|
| 214 |
|
| 215 |
+
# Check minimum length (at least 50 characters for a meaningful story)
|
| 216 |
if len(story_content.strip()) < 50:
|
| 217 |
+
return False
|
| 218 |
|
| 219 |
+
# Check for Arabic characters (must have substantial Arabic content)
|
| 220 |
arabic_pattern = re.compile(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]')
|
| 221 |
arabic_chars = len(arabic_pattern.findall(story_content))
|
| 222 |
|
| 223 |
+
# Arabic characters should be at least 30% of total characters
|
| 224 |
+
if arabic_chars < len(story_content.strip()) * 0.3:
|
| 225 |
+
return False
|
| 226 |
|
| 227 |
+
# Quick pre-screening for obvious violations (before API call)
|
| 228 |
content_lower = story_content.lower()
|
| 229 |
|
| 230 |
+
# Check for common profanity patterns across dialects AND religious violations
|
| 231 |
+
profanity_patterns = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 232 |
# Sexual profanity
|
| 233 |
+
r'\bكس\b', r'\bزب\b', r'\bنيك\b', r'\bمنيوك\b', r'\bشرموط\b',
|
| 234 |
+
r'\bقحب\b', r'\bلحس\b', r'\bمص\b', r'\bطيز\b', r'\bبزاز\b',
|
| 235 |
+
r'\bتبن\b', r'\bنعل\b', r'\bكوس\b', r'\bأير\b', r'\bمنايك\b',
|
| 236 |
+
r'\bخرا\b', r'\bكداب\b', r'\bعرص\b', r'\bخول\b', r'\bحيوان\b',
|
| 237 |
|
| 238 |
+
# Religious violations and blasphemy
|
| 239 |
+
r'الله كذاب', r'لعن الله', r'تبا لله', r'يلعن رب', r'الله غبي',
|
| 240 |
+
r'القرآن كذب', r'كتاب مزور', r'خرافات القرآن', r'قرآن مؤلف',
|
| 241 |
+
r'محمد كذاب', r'الرسول دجال', r'نبي كاذب', r'لعنة على محمد',
|
| 242 |
+
r'أبو بكر منافق', r'عمر ظالم', r'علي مخادع', r'الصحابة كذابون',
|
| 243 |
+
r'الصلاة مضيعة', r'رمضان تخلف', r'الحج طقوس', r'الكعبة صنم',
|
| 244 |
+
r'المسيح كذاب', r'الإنجيل محرف', r'موسى دجال', r'التوراة مزورة',
|
| 245 |
+
r'لا إله', r'الله وهم', r'الجنة وهم', r'النار خيال', r'القيامة كذبة',
|
| 246 |
+
r'لعنة الله', r'الله يلعنك', r'ملعون من رب', r'بيوت خرافة',
|
| 247 |
+
r'السنة نواصب', r'الشيعة رافضة', r'الصوفية مشركون', r'الوهابيون كفار'
|
| 248 |
]
|
| 249 |
|
| 250 |
+
for pattern in profanity_patterns:
|
| 251 |
if re.search(pattern, content_lower):
|
| 252 |
+
return False
|
|
|
|
|
|
|
| 253 |
|
| 254 |
+
return True
|
| 255 |
|
| 256 |
def moderate_story(self, story_content: str) -> Dict[str, Any]:
|
| 257 |
"""
|
|
|
|
| 264 |
Dictionary with moderation result
|
| 265 |
"""
|
| 266 |
# Enhanced validation
|
| 267 |
+
if not self._validate_story_format(story_content):
|
|
|
|
| 268 |
return {
|
| 269 |
"approved": False,
|
| 270 |
"response": "no",
|
| 271 |
+
"reason": "فشل في التحقق من صحة تنسيق القصة أو عدم وجود محتوى عربي كافٍ",
|
|
|
|
| 272 |
"timestamp": datetime.now().isoformat()
|
| 273 |
}
|
| 274 |
|
|
|
|
| 284 |
"approved": False,
|
| 285 |
"response": "no",
|
| 286 |
"reason": "خطأ في خدمة المراجعة",
|
|
|
|
| 287 |
"error": api_response["error"],
|
| 288 |
"timestamp": datetime.now().isoformat()
|
| 289 |
}
|
|
|
|
| 292 |
# Extract the moderation decision
|
| 293 |
ai_response = api_response.get("choices", [{}])[0].get("message", {}).get("content", "").strip().lower()
|
| 294 |
|
| 295 |
+
# Clean the response (remove any extra whitespace or characters)
|
| 296 |
ai_response = re.sub(r'[^\w]', '', ai_response)
|
| 297 |
|
| 298 |
+
# Determine if content is approved (be more strict)
|
| 299 |
approved = ai_response == "true"
|
| 300 |
response_value = "true" if approved else "no"
|
| 301 |
|
|
|
|
| 308 |
}
|
| 309 |
|
| 310 |
if not approved:
|
| 311 |
+
result["reason"] = "المحتوى ينتهك القواعد المجتمعية أو الثقافية أو الدينية، أو أنه ليس قصة أدبية حقيقية"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
else:
|
| 313 |
+
result["reason"] = "المحتوى مقبول ويلتزم بالمعايير المطلوبة"
|
|
|
|
| 314 |
|
| 315 |
logger.info(f"Moderation completed: {response_value} for content of length {len(cleaned_content)}")
|
| 316 |
return result
|
|
|
|
| 321 |
"approved": False,
|
| 322 |
"response": "no",
|
| 323 |
"reason": "خطأ في معالجة نتيجة المراجعة",
|
|
|
|
| 324 |
"error": str(e),
|
| 325 |
"timestamp": datetime.now().isoformat()
|
| 326 |
}
|
| 327 |
|
| 328 |
+
|
| 329 |
# Flask application
|
| 330 |
app = Flask(__name__)
|
| 331 |
|
| 332 |
+
# Initialize the moderator (API key will be set via environment variable)
|
| 333 |
try:
|
| 334 |
moderator = ArabicContentModerator()
|
| 335 |
logger.info("Arabic Content Moderator initialized successfully")
|
|
|
|
| 344 |
"service": "مراجع المحتوى الأدبي العربي المحسن",
|
| 345 |
"service_en": "Enhanced Arabic Literary Content Moderator",
|
| 346 |
"version": "2.0.0",
|
| 347 |
+
"description": "AI-powered professional literary critic for Arabic short stories with comprehensive dialect-aware moderation",
|
| 348 |
+
"description_ar": "ناقد أدبي محترف مدعوم بالذكاء الاصطناعي للقصص العربية القصيرة مع مراجعة شاملة لجميع اللهجات العربية",
|
| 349 |
"endpoints": {
|
| 350 |
"/health": "Health check",
|
| 351 |
"/moderate": "POST - Moderate single story",
|
| 352 |
"/moderate/batch": "POST - Moderate multiple stories"
|
| 353 |
},
|
| 354 |
"features": [
|
| 355 |
+
"Comprehensive profanity detection across all Arabic dialects",
|
| 356 |
+
"Enhanced religious and cultural compliance checking with specific examples",
|
| 357 |
+
"Professional literary criticism standards",
|
| 358 |
+
"Strict content type validation (stories only)",
|
| 359 |
+
"Multi-dialect offensive content detection (Gulf, Levantine, Egyptian, Maghrebi, Iraqi, Sudanese, Yemeni)",
|
| 360 |
+
"Comprehensive religious blasphemy detection (Islam, Christianity, Judaism, other faiths)",
|
| 361 |
+
"Advanced sectarian and inter-religious conflict prevention",
|
| 362 |
+
"Takfir and religious defamation blocking",
|
| 363 |
+
"Advanced pre-screening before API calls",
|
| 364 |
+
"Arabic language purity validation",
|
| 365 |
+
"Context-aware violation detection"
|
| 366 |
],
|
| 367 |
"usage": {
|
| 368 |
"moderate": {
|
| 369 |
"method": "POST",
|
| 370 |
"payload": {"story_content": "Arabic story text"},
|
| 371 |
+
"response": {"approved": "boolean", "response": "true/no"}
|
| 372 |
}
|
| 373 |
},
|
| 374 |
"status": "healthy" if moderator else "service unavailable"
|
|
|
|
| 399 |
"approved": true/false,
|
| 400 |
"response": "true"/"no",
|
| 401 |
"reason": "reason in Arabic",
|
|
|
|
| 402 |
"timestamp": "ISO timestamp"
|
| 403 |
}
|
| 404 |
"""
|
|
|
|
| 407 |
"error": "خدمة المراجعة غير متوفرة - لم يتم تكوين مفتاح API",
|
| 408 |
"error_en": "Moderation service not available - API key not configured",
|
| 409 |
"approved": False,
|
| 410 |
+
"response": "no"
|
|
|
|
| 411 |
}), 500
|
| 412 |
|
| 413 |
try:
|
|
|
|
| 418 |
"error": "محتوى القصة مفقود في الطلب",
|
| 419 |
"error_en": "Missing story_content in request",
|
| 420 |
"approved": False,
|
| 421 |
+
"response": "no"
|
|
|
|
| 422 |
}), 400
|
| 423 |
|
| 424 |
story_content = data['story_content']
|
|
|
|
| 433 |
"error_en": "Internal server error",
|
| 434 |
"approved": False,
|
| 435 |
"response": "no",
|
|
|
|
| 436 |
"details": str(e)
|
| 437 |
}), 500
|
| 438 |
|
|
|
|
| 449 |
if not moderator:
|
| 450 |
return jsonify({
|
| 451 |
"error": "خدمة المراجعة غير متوفرة - لم يتم تكوين مفتاح API",
|
| 452 |
+
"error_en": "Moderation service not available - API key not configured"
|
|
|
|
| 453 |
}), 500
|
| 454 |
|
| 455 |
try:
|
|
|
|
| 458 |
if not data or 'stories' not in data:
|
| 459 |
return jsonify({
|
| 460 |
"error": "مصفوفة القصص مفقودة في الطلب",
|
| 461 |
+
"error_en": "Missing stories array in request"
|
|
|
|
| 462 |
}), 400
|
| 463 |
|
| 464 |
stories = data['stories']
|
| 465 |
if not isinstance(stories, list):
|
| 466 |
return jsonify({
|
| 467 |
"error": "القصص يجب أن تكون في شكل مصفوفة",
|
| 468 |
+
"error_en": "Stories must be an array"
|
|
|
|
| 469 |
}), 400
|
| 470 |
|
| 471 |
results = []
|
|
|
|
| 497 |
return jsonify({
|
| 498 |
"error": "خطأ داخلي في الخادم",
|
| 499 |
"error_en": "Internal server error",
|
|
|
|
| 500 |
"details": str(e)
|
| 501 |
}), 500
|
| 502 |
|
| 503 |
if __name__ == '__main__':
|
| 504 |
# For local testing
|
| 505 |
+
port = int(os.environment.get('PORT', 7860))
|
| 506 |
app.run(host='0.0.0.0', port=port, debug=False)
|