enotkrutoy commited on
Commit
c3a4d04
·
verified ·
1 Parent(s): 2e6bfdc

Deploy Gradio app with multiple files

Browse files
Files changed (4) hide show
  1. app.py +457 -0
  2. config.py +44 -0
  3. requirements.txt +37 -0
  4. utils.py +83 -0
app.py ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import random
4
+ import string
5
+ import re
6
+ import asyncio
7
+ import aiohttp
8
+ import smtplib
9
+ import dns.resolver
10
+ from email.utils import parseaddr
11
+ import time
12
+ from datetime import datetime
13
+ import json
14
+ from typing import List, Dict, Tuple, Optional
15
+ import threading
16
+ from concurrent.futures import ThreadPoolExecutor
17
+ import csv
18
+
19
+ # Конфигурация
20
+ CONFIG = {
21
+ "domains": ["gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "example.com"],
22
+ "common_names": ["john", "jane", "mike", "sarah", "david", "lisa", "robert", "emma"],
23
+ "smtp_servers": {
24
+ "gmail.com": ("smtp.gmail.com", 587),
25
+ "yahoo.com": ("smtp.mail.yahoo.com", 587),
26
+ "hotmail.com": ("smtp-mail.outlook.com", 587),
27
+ "outlook.com": ("smtp-mail.outlook.com", 587),
28
+ }
29
+ }
30
+
31
+ class EmailValidator:
32
+ def __init__(self):
33
+ self.session = None
34
+ self.results_cache = {}
35
+
36
+ async def __aenter__(self):
37
+ self.session = aiohttp.ClientSession(
38
+ timeout=aiohttp.ClientTimeout(total=10),
39
+ connector=aiohttp.TCPConnector(limit=100)
40
+ )
41
+ return self
42
+
43
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
44
+ if self.session:
45
+ await self.session.close()
46
+
47
+ def validate_format(self, email: str) -> bool:
48
+ """Проверка формата email"""
49
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
50
+ return re.match(pattern, email) is not None
51
+
52
+ def check_mx_record(self, domain: str) -> bool:
53
+ """Проверка MX записи домена"""
54
+ try:
55
+ records = dns.resolver.resolve(domain, 'MX')
56
+ return len(records) > 0
57
+ except:
58
+ return False
59
+
60
+ async def check_smtp_connection(self, email: str) -> Dict[str, any]:
61
+ """Проверка SMTP соединения"""
62
+ domain = email.split('@')[1]
63
+ if domain not in CONFIG["smtp_servers"]:
64
+ return {"exists": "unknown", "method": "smtp_not_supported"}
65
+
66
+ smtp_host, smtp_port = CONFIG["smtp_servers"][domain]
67
+
68
+ try:
69
+ with smtplib.SMTP(smtp_host, smtp_port, timeout=10) as server:
70
+ server.starttls()
71
+ # Проверка существования email через VRFY (если поддерживается)
72
+ try:
73
+ response = server.verify(email)
74
+ if response.startswith("250"):
75
+ return {"exists": "valid", "method": "smtp_vrfy"}
76
+ except:
77
+ pass
78
+
79
+ # Альтернативная проверка через RCPT TO
80
+ server.mail("test@example.com")
81
+ code, message = server.rcpt(email)
82
+ if code == 250:
83
+ return {"exists": "valid", "method": "smtp_rcpt"}
84
+ else:
85
+ return {"exists": "invalid", "method": "smtp_rcpt"}
86
+
87
+ except Exception as e:
88
+ return {"exists": "error", "method": "smtp_error", "error": str(e)}
89
+
90
+ async def check_disposable_email(self, email: str) -> bool:
91
+ """Проверка на временный email"""
92
+ domain = email.split('@')[1]
93
+ disposable_domains = [
94
+ "10minutemail.com", "guerrillamail.com", "mailinator.com",
95
+ "tempmail.org", "yopmail.com", "throwaway.email"
96
+ ]
97
+ return domain in disposable_domains
98
+
99
+ async def validate_email(self, email: str) -> Dict[str, any]:
100
+ """Комплексная проверка email"""
101
+ if email in self.results_cache:
102
+ return self.results_cache[email]
103
+
104
+ result = {
105
+ "email": email,
106
+ "format_valid": False,
107
+ "domain_valid": False,
108
+ "mx_valid": False,
109
+ "smtp_valid": "unknown",
110
+ "is_disposable": False,
111
+ "timestamp": datetime.now().isoformat()
112
+ }
113
+
114
+ # Проверка формата
115
+ result["format_valid"] = self.validate_format(email)
116
+ if not result["format_valid"]:
117
+ result["overall_status"] = "invalid"
118
+ self.results_cache[email] = result
119
+ return result
120
+
121
+ # Проверка домена
122
+ domain = email.split('@')[1]
123
+ result["domain_valid"] = len(domain) > 0 and '.' in domain
124
+
125
+ # Проверка MX записи
126
+ result["mx_valid"] = self.check_mx_record(domain)
127
+
128
+ # Проверка на временный email
129
+ result["is_disposable"] = await self.check_disposable_email(email)
130
+
131
+ # SMTP проверка (асинхронная)
132
+ smtp_result = await self.check_smtp_connection(email)
133
+ result["smtp_valid"] = smtp_result["exists"]
134
+ result["smtp_method"] = smtp_result["method"]
135
+
136
+ # Определение общего статуса
137
+ if result["smtp_valid"] == "valid":
138
+ result["overall_status"] = "valid"
139
+ elif result["smtp_valid"] == "invalid":
140
+ result["overall_status"] = "invalid"
141
+ elif result["is_disposable"]:
142
+ result["overall_status"] = "disposable"
143
+ elif not result["mx_valid"]:
144
+ result["overall_status"] = "invalid_domain"
145
+ else:
146
+ result["overall_status"] = "unknown"
147
+
148
+ self.results_cache[email] = result
149
+ return result
150
+
151
+ class EmailGenerator:
152
+ @staticmethod
153
+ def generate_single(domain: str = None, name_pattern: str = "random") -> str:
154
+ """Генерация одного email"""
155
+ if domain is None:
156
+ domain = random.choice(CONFIG["domains"])
157
+
158
+ if name_pattern == "random":
159
+ name = random.choice(CONFIG["common_names"])
160
+ elif name_pattern == "numbers":
161
+ name = random.choice(CONFIG["common_names"]) + str(random.randint(1, 999))
162
+ else:
163
+ # Случайная строка
164
+ name = ''.join(random.choices(string.ascii_lowercase, k=8))
165
+
166
+ return f"{name}@{domain}"
167
+
168
+ @staticmethod
169
+ def generate_batch(count: int, domain: str = None, name_pattern: str = "random") -> List[str]:
170
+ """Генерация списка email"""
171
+ emails = []
172
+ for _ in range(count):
173
+ emails.append(EmailGenerator.generate_single(domain, name_pattern))
174
+ return emails
175
+
176
+ def generate_emails(count: int, domain: str, name_pattern: str) -> List[str]:
177
+ """Генерация email для Gradio"""
178
+ try:
179
+ count = int(count)
180
+ if count <= 0 or count > 1000:
181
+ return ["Ошибка: количество должно быть от 1 до 1000"]
182
+
183
+ domain = domain if domain else None
184
+ emails = EmailGenerator.generate_batch(count, domain, name_pattern)
185
+ return emails
186
+ except Exception as e:
187
+ return [f"Ошибка генерации: {str(e)}"]
188
+
189
+ async def validate_emails_batch(emails: List[str], progress=gr.Progress()) -> Tuple[List[Dict], str]:
190
+ """Проверка списка email"""
191
+ if not emails:
192
+ return [], "Нет email для проверки"
193
+
194
+ results = []
195
+ async with EmailValidator() as validator:
196
+ tasks = []
197
+ for i, email in enumerate(emails):
198
+ if email and '@' in email:
199
+ tasks.append(validator.validate_email(email.strip()))
200
+
201
+ # Обработка с прогресс-баром
202
+ total = len(tasks)
203
+ for i, task in enumerate(asyncio.as_completed(tasks)):
204
+ try:
205
+ result = await task
206
+ results.append(result)
207
+ progress((i + 1) / total, f"Проверено {i + 1}/{total} email")
208
+ except Exception as e:
209
+ results.append({
210
+ "email": emails[i],
211
+ "error": str(e),
212
+ "overall_status": "error"
213
+ })
214
+
215
+ return results, f"Проверено {len(results)} email"
216
+
217
+ def validate_emails_sync(emails: List[str]) -> Tuple[List[Dict], str]:
218
+ """Синхронная обертка для асинхронной проверки"""
219
+ loop = asyncio.new_event_loop()
220
+ asyncio.set_event_loop(loop)
221
+ try:
222
+ return loop.run_until_complete(validate_emails_batch(emails))
223
+ finally:
224
+ loop.close()
225
+
226
+ def export_results(results: List[Dict], format_type: str) -> str:
227
+ """Экспорт результатов"""
228
+ if not results:
229
+ return "Нет результатов для экспорта"
230
+
231
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
232
+
233
+ try:
234
+ if format_type == "CSV":
235
+ filename = f"email_validation_{timestamp}.csv"
236
+ with open(filename, 'w', newline='', encoding='utf-8') as csvfile:
237
+ if results and "error" not in results[0]:
238
+ fieldnames = ["email", "overall_status", "format_valid",
239
+ "domain_valid", "mx_valid", "smtp_valid",
240
+ "is_disposable", "timestamp"]
241
+ else:
242
+ fieldnames = ["email", "error", "overall_status"]
243
+
244
+ writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
245
+ writer.writeheader()
246
+ writer.writerows(results)
247
+
248
+ return f"Экспортировано в {filename}"
249
+
250
+ elif format_type == "JSON":
251
+ filename = f"email_validation_{timestamp}.json"
252
+ with open(filename, 'w', encoding='utf-8') as jsonfile:
253
+ json.dump(results, jsonfile, indent=2, ensure_ascii=False)
254
+
255
+ return f"Экспортировано в {filename}"
256
+
257
+ elif format_type == "Excel":
258
+ filename = f"email_validation_{timestamp}.xlsx"
259
+ df = pd.DataFrame(results)
260
+ df.to_excel(filename, index=False)
261
+ return f"Экспортировано в {filename}"
262
+
263
+ except Exception as e:
264
+ return f"Ошибка экспорта: {str(e)}"
265
+
266
+ def get_statistics(results: List[Dict]) -> Dict:
267
+ """Получение статистики"""
268
+ if not results:
269
+ return {}
270
+
271
+ stats = {
272
+ "total": len(results),
273
+ "valid": 0,
274
+ "invalid": 0,
275
+ "disposable": 0,
276
+ "unknown": 0,
277
+ "error": 0
278
+ }
279
+
280
+ for result in results:
281
+ status = result.get("overall_status", "error")
282
+ if status in stats:
283
+ stats[status] += 1
284
+
285
+ return stats
286
+
287
+ def create_interface():
288
+ """Создание интерфейса"""
289
+ with gr.Blocks(
290
+ title="Email Generator & Validator",
291
+ theme=gr.themes.Soft(),
292
+ css="""
293
+ .header {
294
+ text-align: center;
295
+ margin-bottom: 2rem;
296
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
297
+ padding: 2rem;
298
+ border-radius: 10px;
299
+ color: white;
300
+ }
301
+ .stats-box {
302
+ background: #f8f9fa;
303
+ padding: 1rem;
304
+ border-radius: 8px;
305
+ margin: 1rem 0;
306
+ }
307
+ .success { color: #28a745; font-weight: bold; }
308
+ .error { color: #dc3545; font-weight: bold; }
309
+ .warning { color: #ffc107; font-weight: bold; }
310
+ """
311
+ ) as demo:
312
+
313
+ with gr.Row():
314
+ gr.HTML("""
315
+ <div class="header">
316
+ <h1>📧 Email Generator & Validator</h1>
317
+ <p>Генерация и проверка email адресов с расширенной функциональностью</p>
318
+ <p><a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: white;">Built with anycoder</a></p>
319
+ </div>
320
+ """)
321
+
322
+ with gr.Tabs():
323
+ # Таб 1: Генерация email
324
+ with gr.TabItem("🔧 Генерация Email"):
325
+ with gr.Row():
326
+ with gr.Column(scale=2):
327
+ gr.Markdown("### Настройки генерации")
328
+ count_input = gr.Number(
329
+ label="Количество email",
330
+ value=10,
331
+ minimum=1,
332
+ maximum=1000,
333
+ step=1
334
+ )
335
+ domain_input = gr.Dropdown(
336
+ label="Домен (оставьте пустым для случайного)",
337
+ choices=CONFIG["domains"] + [""],
338
+ value=""
339
+ )
340
+ pattern_input = gr.Radio(
341
+ label="Шаблон имени",
342
+ choices=[
343
+ ("Случайное имя", "random"),
344
+ ("Имя + цифры", "numbers"),
345
+ ("Случайная строка", "random_string")
346
+ ],
347
+ value="random"
348
+ )
349
+ generate_btn = gr.Button("🚀 Сгенерировать", variant="primary")
350
+
351
+ with gr.Column(scale=3):
352
+ generated_emails = gr.Textbox(
353
+ label="Сгенерированные email",
354
+ lines=10,
355
+ placeholder="Здесь появятся сгенерированные email..."
356
+ )
357
+
358
+ generate_btn.click(
359
+ fn=generate_emails,
360
+ inputs=[count_input, domain_input, pattern_input],
361
+ outputs=generated_emails
362
+ )
363
+
364
+ # Таб 2: Валидация email
365
+ with gr.TabItem("✅ Валидация Email"):
366
+ with gr.Row():
367
+ with gr.Column():
368
+ gr.Markdown("### Ввод email для проверки")
369
+ email_input = gr.Textbox(
370
+ label="Email адреса (каждый с новой строки)",
371
+ lines=10,
372
+ placeholder="Введите email адреса для проверки..."
373
+ )
374
+ validate_btn = gr.Button("🔍 Проверить", variant="primary")
375
+
376
+ with gr.Column():
377
+ gr.Markdown("### Результаты проверки")
378
+ validation_results = gr.Dataframe(
379
+ label="Результаты",
380
+ headers=["Email", "Статус", "Формат", "Домен", "MX", "SMTP"],
381
+ datatype=["str", "str", "bool", "bool", "bool", "str"],
382
+ interactive=False
383
+ )
384
+
385
+ validate_btn.click(
386
+ fn=validate_emails_sync,
387
+ inputs=email_input,
388
+ outputs=[validation_results, gr.Textbox(visible=False)]
389
+ )
390
+
391
+ # Таб 3: Статистика и экспорт
392
+ with gr.TabItem("📊 Статистика и Экспорт"):
393
+ with gr.Row():
394
+ with gr.Column():
395
+ gr.Markdown("### Статистика проверки")
396
+ stats_display = gr.JSON(label="Статистика")
397
+
398
+ gr.Markdown("### Экспорт результатов")
399
+ format_choice = gr.Radio(
400
+ label="Формат экспорта",
401
+ choices=["CSV", "JSON", "Excel"],
402
+ value="CSV"
403
+ )
404
+ export_btn = gr.Button("💾 Экспортировать", variant="secondary")
405
+ export_status = gr.Textbox(label="Статус экспорта")
406
+
407
+ with gr.Column():
408
+ gr.Markdown("### Детальные результаты")
409
+ detailed_results = gr.Dataframe(
410
+ label="Все результаты",
411
+ interactive=False
412
+ )
413
+
414
+ # Функции обновления
415
+ def update_stats_and_details(emails_list):
416
+ results, _ = validate_emails_sync(emails_list.split('\n') if emails_list else [])
417
+ stats = get_statistics(results)
418
+
419
+ # Форматирование результатов для отображения
420
+ formatted_results = []
421
+ for r in results:
422
+ formatted_results.append([
423
+ r.get("email", ""),
424
+ r.get("overall_status", ""),
425
+ r.get("format_valid", False),
426
+ r.get("domain_valid", False),
427
+ r.get("mx_valid", False),
428
+ r.get("smtp_valid", "unknown")
429
+ ])
430
+
431
+ return stats, formatted_results, results
432
+
433
+ # Привязка событий
434
+ email_input.change(
435
+ fn=update_stats_and_details,
436
+ inputs=email_input,
437
+ outputs=[stats_display, detailed_results, gr.State()]
438
+ )
439
+
440
+ def export_from_state(results, format_type):
441
+ return export_results(results, format_type)
442
+
443
+ export_btn.click(
444
+ fn=export_from_state,
445
+ inputs=[gr.State(), format_choice],
446
+ outputs=export_status
447
+ )
448
+
449
+ return demo
450
+
451
+ if __name__ == "__main__":
452
+ demo = create_interface()
453
+ demo.launch(
454
+ server_name="0.0.0.0",
455
+ server_port=7860,
456
+ share=False
457
+ )
config.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Конфигурация приложения
2
+ import os
3
+
4
+ # Настройки генерации
5
+ DOMAINS = [
6
+ "gmail.com", "yahoo.com", "hotmail.com", "outlook.com",
7
+ "example.com", "test.com", "demo.com"
8
+ ]
9
+
10
+ COMMON_NAMES = [
11
+ "john", "jane", "mike", "sarah", "david", "lisa",
12
+ "robert", "emma", "alex", "maria", "james", "anna"
13
+ ]
14
+
15
+ # SMTP серверы для проверки
16
+ SMTP_SERVERS = {
17
+ "gmail.com": ("smtp.gmail.com", 587),
18
+ "yahoo.com": ("smtp.mail.yahoo.com", 587),
19
+ "hotmail.com": ("smtp-mail.outlook.com", 587),
20
+ "outlook.com": ("smtp-mail.outlook.com", 587),
21
+ }
22
+
23
+ # Список временных email сервисов
24
+ DISPOSABLE_DOMAINS = [
25
+ "10minutemail.com", "guerrillamail.com", "mailinator.com",
26
+ "tempmail.org", "yopmail.com", "throwaway.email",
27
+ "temp-mail.org", "maildrop.cc", "getairmail.com"
28
+ ]
29
+
30
+ # Настройки приложения
31
+ APP_CONFIG = {
32
+ "max_emails_per_batch": 1000,
33
+ "timeout_seconds": 10,
34
+ "max_concurrent_checks": 50,
35
+ "cache_results": True,
36
+ "export_formats": ["CSV", "JSON", "Excel"]
37
+ }
38
+
39
+ # Настройки UI
40
+ UI_CONFIG = {
41
+ "theme": "soft",
42
+ "title": "Email Generator & Validator",
43
+ "description": "Профессиональный инструмент для генерации и проверки email адресов"
44
+ }
requirements.txt ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ pandas>=1.5.0
3
+ aiohttp>=3.8.0
4
+ dnspython>=2.3.0
5
+ openpyxl>=3.1.0
6
+ asyncio-throttle>=1.0.2
7
+ python-dotenv>=1.0.0
8
+
9
+ Приложение включает:
10
+
11
+ 1. **Генерация email**:
12
+ - Настраиваемое количество (1-1000)
13
+ - Выбор домена или случайный
14
+ - Различные шаблоны имен
15
+ - Валидация формата при генерации
16
+
17
+ 2. **Проверка email**:
18
+ - Форматная валидация
19
+ - Проверка MX записей DNS
20
+ - SMTP проверка (VRFY/RCPT TO)
21
+ - Определение временных email
22
+ - Асинхронная обработка
23
+
24
+ 3. **Статистика и экспорт**:
25
+ - Детальная статистика проверки
26
+ - Экспорт в CSV, JSON, Excel
27
+ - Визуализация результатов
28
+ - Фильтрация и сортировка
29
+
30
+ 4. **Улучшения**:
31
+ - Асинхронная обработка больших списков
32
+ - Кэширование результатов
33
+ - Обработка ошибок сети
34
+ - Прогресс-бар для длительных операций
35
+ - Адаптивный интерфейс
36
+
37
+ Приложение готово к использованию и может быть развернуто на Hugging Face Spaces или локально.
utils.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import random
3
+ import string
4
+ from typing import List, Dict, Optional
5
+
6
+ def is_valid_email_format(email: str) -> bool:
7
+ """Проверка базового формата email"""
8
+ if not email or '@' not in email:
9
+ return False
10
+
11
+ pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
12
+ return re.match(pattern, email) is not None
13
+
14
+ def extract_domain(email: str) -> Optional[str]:
15
+ """Извлечение домена из email"""
16
+ if '@' in email:
17
+ return email.split('@')[1].lower()
18
+ return None
19
+
20
+ def generate_username(pattern: str = "random") -> str:
21
+ """Генерация имени пользователя"""
22
+ if pattern == "random":
23
+ names = ["john", "jane", "mike", "sarah", "david", "lisa", "robert", "emma"]
24
+ return random.choice(names)
25
+ elif pattern == "numbers":
26
+ names = ["john", "jane", "mike", "sarah", "david", "lisa", "robert", "emma"]
27
+ return random.choice(names) + str(random.randint(1, 999))
28
+ else:
29
+ return ''.join(random.choices(string.ascii_lowercase, k=8))
30
+
31
+ def clean_email_list(emails: str) -> List[str]:
32
+ """Очистка списка email от дубликатов и пустых строк"""
33
+ email_list = []
34
+ for email in emails.split('\n'):
35
+ email = email.strip()
36
+ if email and email not in email_list:
37
+ email_list.append(email)
38
+ return email_list
39
+
40
+ def format_validation_results(results: List[Dict]) -> List[List]:
41
+ """Форматирование результатов для отображения в таблице"""
42
+ formatted = []
43
+ for result in results:
44
+ formatted.append([
45
+ result.get("email", ""),
46
+ result.get("overall_status", "unknown"),
47
+ "✓" if result.get("format_valid", False) else "✗",
48
+ "✓" if result.get("domain_valid", False) else "✗",
49
+ "✓" if result.get("mx_valid", False) else "✗",
50
+ result.get("smtp_valid", "unknown")
51
+ ])
52
+ return formatted
53
+
54
+ def get_status_color(status: str) -> str:
55
+ """Получение цвета для статуса"""
56
+ colors = {
57
+ "valid": "#28a745",
58
+ "invalid": "#dc3545",
59
+ "disposable": "#ffc107",
60
+ "unknown": "#6c757d",
61
+ "error": "#dc3545",
62
+ "invalid_domain": "#dc3545"
63
+ }
64
+ return colors.get(status, "#6c757d")
65
+
66
+ def generate_sample_emails(count: int = 5) -> List[str]:
67
+ """Генерация примеров email для демонстрации"""
68
+ samples = [
69
+ "john.doe@gmail.com",
70
+ "invalid-email",
71
+ "test@nonexistentdomain12345.com",
72
+ "user@10minutemail.com",
73
+ "admin@github.com"
74
+ ]
75
+ return samples[:count]
76
+
77
+ def calculate_validation_time(start_time: float, end_time: float) -> str:
78
+ """Расчет времени валидации"""
79
+ duration = end_time - start_time
80
+ if duration < 1:
81
+ return f"{int(duration * 1000)} мс"
82
+ else:
83
+ return f"{duration:.2f} сек"