chandugeesala0 commited on
Commit
cfcda37
·
verified ·
1 Parent(s): 03d8be2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +113 -145
app.py CHANGED
@@ -1,10 +1,10 @@
1
  #!/usr/bin/env python3
2
  """
3
- Email Validator API - Hugging Face Spaces Edition (Fixed)
4
- Uses working validation logic from test_email.py
5
  """
6
 
7
- from flask import Flask, request, jsonify
8
  import socket
9
  import time
10
  import uuid
@@ -13,13 +13,11 @@ import dns.resolver
13
  import smtplib
14
  import logging
15
 
16
- app = Flask(__name__)
17
  logging.basicConfig(level=logging.INFO)
18
 
19
  FROM_ADDRESS = 'verifier@yourdomain.com'
20
  SMTP_TIMEOUT = 15
21
  RATE_LIMIT_SECONDS = 1.0
22
- MAX_BATCH_SIZE = 100
23
 
24
  # Domain cache
25
  domain_cache = {}
@@ -80,11 +78,11 @@ def smtp_probe_recipient(mx_host: str, from_addr: str, recipient: str):
80
  pass
81
  return False, None, None, 'SMTPRecipientsRefused'
82
 
83
- except (smtplib.SMTPConnectError, smtplib.SMTPServerDisconnected, socket.timeout, ConnectionRefusedError, OSError) as net_err:
84
- return False, None, None, f'NETWORK_ERROR: {net_err}'
85
 
86
- except Exception as ex:
87
- return False, None, None, f'ERROR: {ex}'
88
 
89
  def decide_outcome(email, mx_host, rcpt_code, catch_all_flag, probe_error):
90
  """Determine if email is valid or invalid"""
@@ -94,39 +92,33 @@ def decide_outcome(email, mx_host, rcpt_code, catch_all_flag, probe_error):
94
  if not mx_host:
95
  return 'INVALID', 'UNDELIVERABLE_NO_MX'
96
 
97
- if probe_error:
98
  return 'INVALID', 'PROBE_FAILED_NETWORK'
99
 
100
- if rcpt_code is None:
101
- return 'INVALID', 'PROBE_FAILED_NETWORK'
102
-
103
- # 5xx = definitive rejection
104
  if 500 <= rcpt_code <= 599:
105
  return 'INVALID', 'MAILBOX_DOES_NOT_EXIST'
106
 
107
- # 2xx = accepted
108
  if 200 <= rcpt_code <= 299:
109
  if catch_all_flag:
110
  return 'VALID', 'UNKNOWN_CATCHALL'
111
  else:
112
  return 'VALID', 'MAILBOX_EXISTS'
113
 
114
- # 4xx = temporary error
115
  if 400 <= rcpt_code <= 499:
116
  return 'INVALID', 'TEMPORARY_ERROR'
117
 
118
  return 'INVALID', 'UNKNOWN_STATUS'
119
 
120
- def probe_email(email: str, domain_cache: Dict):
121
- """Probe a single email and return validation result"""
122
  email = email.strip().lower()
123
 
124
  if not is_valid_format(email):
125
  return {
126
- 'email': email,
127
- 'status': 'INVALID',
128
- 'reason': 'INVALID_FORMAT',
129
- 'smtp_code': None
130
  }
131
 
132
  domain = email.split('@')[1].lower()
@@ -140,10 +132,10 @@ def probe_email(email: str, domain_cache: Dict):
140
 
141
  if not hosts:
142
  return {
143
- 'email': email,
144
- 'status': 'INVALID',
145
- 'reason': 'UNDELIVERABLE_NO_MX',
146
- 'smtp_code': None
147
  }
148
 
149
  mx_host = hosts[0]
@@ -158,8 +150,6 @@ def probe_email(email: str, domain_cache: Dict):
158
  catch_all = False
159
  if ok and code and 200 <= code <= 299:
160
  catch_all = True
161
- elif not ok and err and 'NETWORK_ERROR' in str(err):
162
- catch_all = None
163
 
164
  domain_cache[domain]['catch_all'] = catch_all
165
  time.sleep(RATE_LIMIT_SECONDS)
@@ -177,135 +167,113 @@ def probe_email(email: str, domain_cache: Dict):
177
  probe_error=(not ok or err is not None)
178
  )
179
 
 
 
 
 
180
  return {
181
- 'email': email,
182
- 'status': status,
183
- 'reason': reason,
184
- 'smtp_code': code
185
  }
186
 
187
- @app.route('/', methods=['GET'])
188
- def home():
189
- """API Documentation"""
190
- return jsonify({
191
- 'name': 'Email Validator API',
192
- 'version': '2.0',
193
- 'description': 'Validates emails using DNS MX records and SMTP probing',
194
- 'endpoints': {
195
- '/validate': {
196
- 'method': 'POST',
197
- 'description': 'Validate a single email',
198
- 'example': {'email': 'test@gmail.com'},
199
- },
200
- '/batch-validate': {
201
- 'method': 'POST',
202
- 'description': 'Validate multiple emails',
203
- 'example': {'emails': ['test1@gmail.com', 'test2@yahoo.com']}
204
- }
205
- }
206
- })
 
 
 
 
 
207
 
208
- @app.route('/validate', methods=['POST'])
209
- def validate():
210
- """Validate a single email"""
211
- try:
212
- data = request.get_json()
213
-
214
- if not data or 'email' not in data:
215
- return jsonify({
216
- 'error': 'Missing email parameter',
217
- 'example': {'email': 'test@example.com'}
218
- }), 400
 
219
 
220
- email = data['email']
221
 
222
- if not isinstance(email, str):
223
- return jsonify({'error': 'Email must be a string'}), 400
 
224
 
225
- result = probe_email(email, domain_cache)
 
 
226
 
227
- return jsonify({
228
- 'email': result['email'],
229
- 'valid': result['status'] == 'VALID',
230
- 'status': result['status'],
231
- 'reason': result['reason'],
232
- 'smtp_code': result['smtp_code']
233
- }), 200
234
 
235
- except Exception as e:
236
- logging.error(f"Error in /validate: {str(e)}")
237
- return jsonify({
238
- 'error': 'Internal server error',
239
- 'message': str(e)
240
- }), 500
241
-
242
- @app.route('/batch-validate', methods=['POST'])
243
- def batch_validate():
244
- """Validate multiple emails"""
245
- try:
246
- data = request.get_json()
247
 
248
- if not data or 'emails' not in data:
249
- return jsonify({
250
- 'error': 'Missing emails parameter',
251
- 'example': {'emails': ['test@gmail.com', 'user@yahoo.com']}
252
- }), 400
253
 
254
- emails = data['emails']
255
-
256
- if not isinstance(emails, list):
257
- return jsonify({'error': 'Emails must be a list'}), 400
 
 
 
 
 
258
 
259
- if len(emails) > MAX_BATCH_SIZE:
260
- return jsonify({'error': f'Maximum {MAX_BATCH_SIZE} emails per request'}), 400
 
 
261
 
262
- results = []
263
- valid_count = 0
 
 
264
 
265
- for email in emails:
266
- if not isinstance(email, str):
267
- results.append({
268
- 'email': str(email),
269
- 'valid': False,
270
- 'error': 'Invalid type'
271
- })
272
- continue
273
-
274
- result = probe_email(email, domain_cache)
275
- is_valid = result['status'] == 'VALID'
276
-
277
- results.append({
278
- 'email': result['email'],
279
- 'valid': is_valid,
280
- 'status': result['status'],
281
- 'reason': result['reason']
282
- })
283
-
284
- if is_valid:
285
- valid_count += 1
286
-
287
- return jsonify({
288
- 'total': len(emails),
289
- 'valid_count': valid_count,
290
- 'invalid_count': len(emails) - valid_count,
291
- 'results': results
292
- }), 200
293
-
294
- except Exception as e:
295
- logging.error(f"Error in /batch-validate: {str(e)}")
296
- return jsonify({
297
- 'error': 'Internal server error',
298
- 'message': str(e)
299
- }), 500
300
-
301
- @app.route('/health', methods=['GET'])
302
- def health():
303
- """Health check endpoint"""
304
- return jsonify({
305
- 'status': 'healthy',
306
- 'service': 'Email Validator API',
307
- 'version': '2.0'
308
- }), 200
309
 
310
- if __name__ == '__main__':
311
- app.run(host='0.0.0.0', port=7860, debug=False)
 
1
  #!/usr/bin/env python3
2
  """
3
+ Email Validator - Hugging Face Spaces (Gradio Version)
4
+ Works perfectly on Hugging Face because Gradio is native
5
  """
6
 
7
+ import gradio as gr
8
  import socket
9
  import time
10
  import uuid
 
13
  import smtplib
14
  import logging
15
 
 
16
  logging.basicConfig(level=logging.INFO)
17
 
18
  FROM_ADDRESS = 'verifier@yourdomain.com'
19
  SMTP_TIMEOUT = 15
20
  RATE_LIMIT_SECONDS = 1.0
 
21
 
22
  # Domain cache
23
  domain_cache = {}
 
78
  pass
79
  return False, None, None, 'SMTPRecipientsRefused'
80
 
81
+ except (smtplib.SMTPConnectError, smtplib.SMTPServerDisconnected, socket.timeout, ConnectionRefusedError, OSError):
82
+ return False, None, None, 'NETWORK_ERROR'
83
 
84
+ except Exception:
85
+ return False, None, None, 'ERROR'
86
 
87
  def decide_outcome(email, mx_host, rcpt_code, catch_all_flag, probe_error):
88
  """Determine if email is valid or invalid"""
 
92
  if not mx_host:
93
  return 'INVALID', 'UNDELIVERABLE_NO_MX'
94
 
95
+ if probe_error or rcpt_code is None:
96
  return 'INVALID', 'PROBE_FAILED_NETWORK'
97
 
 
 
 
 
98
  if 500 <= rcpt_code <= 599:
99
  return 'INVALID', 'MAILBOX_DOES_NOT_EXIST'
100
 
 
101
  if 200 <= rcpt_code <= 299:
102
  if catch_all_flag:
103
  return 'VALID', 'UNKNOWN_CATCHALL'
104
  else:
105
  return 'VALID', 'MAILBOX_EXISTS'
106
 
 
107
  if 400 <= rcpt_code <= 499:
108
  return 'INVALID', 'TEMPORARY_ERROR'
109
 
110
  return 'INVALID', 'UNKNOWN_STATUS'
111
 
112
+ def validate_email_function(email: str) -> Dict:
113
+ """Validate a single email"""
114
  email = email.strip().lower()
115
 
116
  if not is_valid_format(email):
117
  return {
118
+ 'Email': email,
119
+ 'Status': 'INVALID',
120
+ 'Reason': 'Invalid format',
121
+ 'Valid': 'No'
122
  }
123
 
124
  domain = email.split('@')[1].lower()
 
132
 
133
  if not hosts:
134
  return {
135
+ 'Email': email,
136
+ 'Status': 'INVALID',
137
+ 'Reason': 'No MX records',
138
+ 'Valid': 'No'
139
  }
140
 
141
  mx_host = hosts[0]
 
150
  catch_all = False
151
  if ok and code and 200 <= code <= 299:
152
  catch_all = True
 
 
153
 
154
  domain_cache[domain]['catch_all'] = catch_all
155
  time.sleep(RATE_LIMIT_SECONDS)
 
167
  probe_error=(not ok or err is not None)
168
  )
169
 
170
+ is_valid = status == 'VALID'
171
+ status_display = '✅ VALID' if is_valid else '❌ INVALID'
172
+ valid_text = 'Yes' if is_valid else 'No'
173
+
174
  return {
175
+ 'Email': email,
176
+ 'Status': status_display,
177
+ 'Reason': reason,
178
+ 'Valid': valid_text
179
  }
180
 
181
+ def validate_batch(emails_text: str) -> str:
182
+ """Validate multiple emails from text input"""
183
+ emails = [e.strip() for e in emails_text.split('\n') if e.strip()]
184
+
185
+ if not emails:
186
+ return "Please enter at least one email"
187
+
188
+ results = []
189
+ valid_count = 0
190
+
191
+ for email in emails:
192
+ result = validate_email_function(email)
193
+ results.append(result)
194
+ if result['Valid'] == 'Yes':
195
+ valid_count += 1
196
+
197
+ # Format output
198
+ output = f"**Total: {len(emails)} | Valid: {valid_count} | Invalid: {len(emails) - valid_count}**\n\n"
199
+ output += "| Email | Status | Reason | Valid |\n"
200
+ output += "|-------|--------|--------|-------|\n"
201
+
202
+ for r in results:
203
+ output += f"| {r['Email']} | {r['Status']} | {r['Reason']} | {r['Valid']} |\n"
204
+
205
+ return output
206
 
207
+ # Create Gradio Interface
208
+ with gr.Blocks(title="Email Validator") as demo:
209
+ gr.Markdown("# 📧 Email Validator API")
210
+ gr.Markdown("Validate emails using DNS MX records and SMTP probing")
211
+
212
+ with gr.Tab("Single Email"):
213
+ with gr.Row():
214
+ email_input = gr.Textbox(
215
+ label="Email Address",
216
+ placeholder="Enter email to validate",
217
+ value="test@gmail.com"
218
+ )
219
 
220
+ validate_btn = gr.Button("Validate", variant="primary", size="lg")
221
 
222
+ with gr.Row():
223
+ email_output = gr.Textbox(label="Email", interactive=False)
224
+ status_output = gr.Textbox(label="Status", interactive=False)
225
 
226
+ with gr.Row():
227
+ reason_output = gr.Textbox(label="Reason", interactive=False)
228
+ valid_output = gr.Textbox(label="Valid", interactive=False)
229
 
230
+ validate_btn.click(
231
+ fn=validate_email_function,
232
+ inputs=email_input,
233
+ outputs=[email_output, status_output, reason_output, valid_output]
234
+ )
 
 
235
 
236
+ with gr.Tab("Batch Validation"):
237
+ with gr.Row():
238
+ batch_input = gr.Textbox(
239
+ label="Emails (one per line)",
240
+ placeholder="test1@gmail.com\ntest2@yahoo.com\ninvalid@test.com",
241
+ lines=10
242
+ )
 
 
 
 
 
243
 
244
+ batch_btn = gr.Button("Validate Batch", variant="primary", size="lg")
245
+ batch_output = gr.Markdown(label="Results")
 
 
 
246
 
247
+ batch_btn.click(
248
+ fn=validate_batch,
249
+ inputs=batch_input,
250
+ outputs=batch_output
251
+ )
252
+
253
+ with gr.Tab("API Docs"):
254
+ gr.Markdown("""
255
+ ## API Endpoints
256
 
257
+ ### Single Email Validation
258
+ - **URL:** `/api/validate`
259
+ - **Method:** POST
260
+ - **Payload:** `{"email": "test@gmail.com"}`
261
 
262
+ ### Batch Validation
263
+ - **URL:** `/api/batch`
264
+ - **Method:** POST
265
+ - **Payload:** `{"emails": ["test1@gmail.com", "test2@yahoo.com"]}`
266
 
267
+ ## Response
268
+ ```
269
+ {
270
+ "Email": "test@gmail.com",
271
+ "Status": "✅ VALID",
272
+ "Reason": "MAILBOX_EXISTS",
273
+ "Valid": "Yes"
274
+ }
275
+ ```
276
+ """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
 
278
+ if __name__ == "__main__":
279
+ demo.launch(server_name="0.0.0.0", server_port=7860, share=False)