hansaka1 commited on
Commit
da51c76
·
verified ·
1 Parent(s): e49d260

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -42
app.py CHANGED
@@ -2,12 +2,14 @@ import gradio as gr
2
  import pytesseract
3
  from pytesseract import Output
4
  from PIL import Image
 
5
 
6
  def validate_chat_origin(image):
7
  if image is None:
8
  return {"valid": False, "error": "No image uploaded"}
9
 
10
  try:
 
11
  data = pytesseract.image_to_data(image, output_type=Output.DICT)
12
  except Exception as e:
13
  return {"valid": False, "error": str(e)}
@@ -15,102 +17,128 @@ def validate_chat_origin(image):
15
  img_w, img_h = image.size
16
  center_x = img_w / 2
17
 
18
- # --- STEP 1: FIND THE HEADER BARRIER ---
19
- # We look for keywords that mark the END of the profile header.
20
- # The chat only starts BELOW these words.
21
- barrier_keywords = ["block", "add to contacts", "report", "business account", "joined in", "not a contact"]
 
 
 
 
 
22
 
 
 
 
 
23
  chat_start_y = 0
24
 
25
  n_boxes = len(data['text'])
26
  for i in range(n_boxes):
27
  text = data['text'][i].lower().strip()
28
- if not text: continue
 
 
29
 
30
- # If we find a barrier word, update the start position
31
- if any(k in text for k in barrier_keywords):
32
- # Set start Y to be 20px below this element
 
 
 
 
 
 
33
  element_bottom = data['top'][i] + data['height'][i]
 
 
34
  if element_bottom > chat_start_y:
35
- chat_start_y = element_bottom + 20
36
 
37
- # Fallback: If no "Block" button found (e.g. saved contact), skip top 20%
38
- if chat_start_y == 0:
 
 
 
39
  chat_start_y = img_h * 0.20
40
 
41
- # --- STEP 2: ANALYZE LINES ---
 
 
42
  valid_lines = []
43
 
44
- # Standard system words to ignore
45
- ignore_words = ["today", "yesterday", "messages", "calls", "encrypted", "security", "code", "changed"]
46
 
47
  for i in range(n_boxes):
48
  text = data['text'][i].strip()
49
  conf = int(data['conf'][i])
 
50
 
 
 
 
 
 
51
  if not text or conf < 30: continue
52
 
53
- # FILTER 1: Must be BELOW the Block/Profile section
54
- if data['top'][i] < chat_start_y: continue
55
-
56
- # FILTER 2: Ignore system words
57
  if any(w in text.lower() for w in ignore_words): continue
 
 
 
58
 
59
  valid_lines.append({
60
  "text": text,
61
  "left": data['left'][i],
62
- "top": data['top'][i],
63
  "width": data['width'][i],
64
- "height": data['height'][i]
65
  })
66
 
67
- # Sort by Top position
68
  valid_lines.sort(key=lambda x: x['top'])
69
 
70
  final_decision = None
71
  debug_log = []
72
- debug_log.append(f"Header Barrier set at Y={chat_start_y}")
73
 
74
  for line in valid_lines:
75
- # Calculate geometry
76
- box_center = line["left"] + (line["width"] / 2)
77
- deviation = abs(box_center - center_x)
78
-
79
- # --- CRITICAL FILTER: CENTER ALIGNMENT ---
80
- # The name "Nithish" or dates like "20:18" (if centered) are NOT messages.
81
- # If text is in the middle 20% of the screen, SKIP IT.
82
- if deviation < (img_w * 0.10):
83
- debug_log.append(f"Skipped CENTERED Text: '{line['text']}'")
84
  continue
85
 
86
- # --- DECISION: LEFT vs RIGHT ---
87
- # If we are here, the text is NOT centered. It must be a message.
88
 
89
- # Stranger messages align to the Left (Start < 20% of screen)
90
- if line["left"] < (img_w * 0.20):
91
  final_decision = True
92
- debug_log.append(f"✅ VALID (Stranger): '{line['text']}' is Left-Aligned.")
93
  break
94
 
95
- # Your messages align to the Right (Start > 40% of screen)
96
- # OR they are far enough right to not be a stranger.
97
- elif line["left"] > (img_w * 0.40):
98
  final_decision = False
99
- debug_log.append(f"❌ INVALID (Me): '{line['text']}' is Right-Aligned.")
100
  break
101
 
 
102
  if final_decision is True:
103
  return {"valid": True, "reason": "First message is from stranger.", "debug": debug_log}
104
  elif final_decision is False:
105
  return {"valid": False, "reason": "First message is from you.", "debug": debug_log}
106
  else:
107
- return {"valid": False, "reason": "No clear chat text found.", "debug": debug_log}
108
 
109
  iface = gr.Interface(
110
  fn=validate_chat_origin,
111
  inputs=gr.Image(type="pil"),
112
  outputs=gr.JSON(),
113
- title="WhatsApp Validator V10 (Barrier Logic)"
114
  )
115
 
116
  if __name__ == "__main__":
 
2
  import pytesseract
3
  from pytesseract import Output
4
  from PIL import Image
5
+ import re
6
 
7
  def validate_chat_origin(image):
8
  if image is None:
9
  return {"valid": False, "error": "No image uploaded"}
10
 
11
  try:
12
+ # Get OCR Data with bounding boxes
13
  data = pytesseract.image_to_data(image, output_type=Output.DICT)
14
  except Exception as e:
15
  return {"valid": False, "error": str(e)}
 
17
  img_w, img_h = image.size
18
  center_x = img_w / 2
19
 
20
+ # --- STEP 1: DEFINE HEADER BARRIERS ---
21
+ # We look for words that ALWAYS appear in the header/profile section.
22
+ # The true chat (Hi, Hello, etc.) will always be BELOW these.
23
+ barrier_keywords = [
24
+ "block", "add to contacts", "report", "business account",
25
+ "joined in", "not a contact", "encrypted", "end-to-end",
26
+ "security code", "waiting for this message", "invite link",
27
+ "group", "created this group", "you're both in", "spam"
28
+ ]
29
 
30
+ # Regex to catch phone numbers (header info) like +94 71...
31
+ phone_pattern = re.compile(r'\+\d{2,3}\s?\d{2}')
32
+
33
+ # Find the LOWEST header element to set our "Safe Start Line"
34
  chat_start_y = 0
35
 
36
  n_boxes = len(data['text'])
37
  for i in range(n_boxes):
38
  text = data['text'][i].lower().strip()
39
+ conf = int(data['conf'][i])
40
+
41
+ if not text or conf < 30: continue
42
 
43
+ # Check if this word is a known header keyword
44
+ is_header_word = any(k in text for k in barrier_keywords)
45
+
46
+ # Check if it looks like the profile phone number
47
+ is_phone = phone_pattern.search(text)
48
+
49
+ if is_header_word or is_phone:
50
+ # We found a header element!
51
+ # The chat must start BELOW this element.
52
  element_bottom = data['top'][i] + data['height'][i]
53
+
54
+ # Update our barrier to be the lowest one found so far
55
  if element_bottom > chat_start_y:
56
+ chat_start_y = element_bottom
57
 
58
+ # Add a safety buffer (25px) below the lowest header element
59
+ if chat_start_y > 0:
60
+ chat_start_y += 25
61
+ else:
62
+ # Fallback: If OCR missed 'Block'/'Encrypted', skip top 20% of screen
63
  chat_start_y = img_h * 0.20
64
 
65
+ # --- STEP 2: ANALYZE CHAT CONTENT ---
66
+ # Now we only look at text BELOW 'chat_start_y'
67
+
68
  valid_lines = []
69
 
70
+ # Words to ignore inside the chat area (System dates/info)
71
+ ignore_words = ["today", "yesterday", "unread", "messages", "calls"]
72
 
73
  for i in range(n_boxes):
74
  text = data['text'][i].strip()
75
  conf = int(data['conf'][i])
76
+ top = data['top'][i]
77
 
78
+ # FILTER A: Must be BELOW the calculated Header Barrier
79
+ if top < chat_start_y:
80
+ continue
81
+
82
+ # FILTER B: Ignore empty/low conf
83
  if not text or conf < 30: continue
84
 
85
+ # FILTER C: Ignore common system words
 
 
 
86
  if any(w in text.lower() for w in ignore_words): continue
87
+
88
+ # FILTER D: Ignore single symbols (OCR noise)
89
+ if len(text) == 1 and not text.isalnum(): continue
90
 
91
  valid_lines.append({
92
  "text": text,
93
  "left": data['left'][i],
94
+ "top": top,
95
  "width": data['width'][i],
96
+ "center": data['left'][i] + (data['width'][i] / 2)
97
  })
98
 
99
+ # Sort lines Top-to-Bottom
100
  valid_lines.sort(key=lambda x: x['top'])
101
 
102
  final_decision = None
103
  debug_log = []
104
+ debug_log.append(f"Header Barrier applied at Y={chat_start_y} (Ignored everything above)")
105
 
106
  for line in valid_lines:
107
+ # --- CHECK 1: CENTER ALIGNMENT ---
108
+ # If text is in the middle 15% of the screen, it's system text (Date/Info).
109
+ deviation = abs(line['center'] - center_x)
110
+ if deviation < (img_w * 0.15):
111
+ debug_log.append(f"Skipped CENTERED: '{line['text']}'")
 
 
 
 
112
  continue
113
 
114
+ # --- CHECK 2: LEFT vs RIGHT ---
115
+ # If we are here, text is NOT centered. It's a message.
116
 
117
+ # Stranger Limit: Starts within first 18% of screen
118
+ if line['left'] < (img_w * 0.18):
119
  final_decision = True
120
+ debug_log.append(f"✅ VALID (Stranger): '{line['text']}' found at Left Margin ({line['left']}px)")
121
  break
122
 
123
+ # Me Limit: Starts after 40% of screen (safe assumption for right-aligned)
124
+ elif line['left'] > (img_w * 0.40):
 
125
  final_decision = False
126
+ debug_log.append(f"❌ INVALID (Me): '{line['text']}' found at Right/Indented ({line['left']}px)")
127
  break
128
 
129
+ # --- FINAL OUTPUT ---
130
  if final_decision is True:
131
  return {"valid": True, "reason": "First message is from stranger.", "debug": debug_log}
132
  elif final_decision is False:
133
  return {"valid": False, "reason": "First message is from you.", "debug": debug_log}
134
  else:
135
+ return {"valid": False, "reason": "No valid chat messages found (Only system/header text).", "debug": debug_log}
136
 
137
  iface = gr.Interface(
138
  fn=validate_chat_origin,
139
  inputs=gr.Image(type="pil"),
140
  outputs=gr.JSON(),
141
+ title="WhatsApp Validator V11 (Dynamic Barrier)"
142
  )
143
 
144
  if __name__ == "__main__":