import json import time import re import os from imapclient import IMAPClient from http.server import HTTPServer, BaseHTTPRequestHandler from html.parser import HTMLParser from collections import deque print("Сервер запущен, начало работы...", flush=True) # === НАСТРОЙКИ ПОЧТЫ === IMAP_SERVER = "mail.gtn.ru" EMAIL = "M.Fedorov@gtn.ru" PASSWORD = "xkd-JZj-rRL-N2G" # Файлы STATE_FILE = "processed_uids.json" QUEUE_FILE = "leads_queue.json" # Очередь лидов MAX_QUEUE_SIZE = 50 leads_queue = deque(maxlen=MAX_QUEUE_SIZE) processed_uids = set() # ========== РАБОТА С ФАЙЛОМ ОЧЕРЕДИ ========== def save_queue_to_file(): with open(QUEUE_FILE, 'w', encoding='utf-8') as f: json.dump(list(leads_queue), f, ensure_ascii=False) print(f"💾 Очередь сохранена: {len(leads_queue)} лидов") def load_queue_from_file(): global leads_queue if os.path.exists(QUEUE_FILE): try: with open(QUEUE_FILE, 'r', encoding='utf-8') as f: saved_queue = json.load(f) leads_queue = deque(saved_queue, maxlen=MAX_QUEUE_SIZE) print(f"📂 Загружена очередь из файла: {len(leads_queue)} лидов") except Exception as e: print(f"Ошибка загрузки очереди: {e}") # ========== ЗАГРУЗКА ОБРАБОТАННЫХ UID ========== if os.path.exists(STATE_FILE): try: with open(STATE_FILE, 'r') as f: processed_uids = set(json.load(f)) print(f"📂 Загружено {len(processed_uids)} обработанных UID") except: pass def save_state(): with open(STATE_FILE, 'w') as f: json.dump(list(processed_uids), f) # ========== ПАРСИНГ HTML ========== class HTMLToTextParser(HTMLParser): def __init__(self): super().__init__() self.text = [] def handle_data(self, data): if data.strip(): self.text.append(data.strip()) def handle_starttag(self, tag, attrs): if tag in ['p', 'br', 'div', 'li', 'tr']: if self.text and self.text[-1] != '': self.text.append('') def get_text(self): return '\n'.join(self.text) def html_to_text(html): parser = HTMLToTextParser() parser.feed(html) text = parser.get_text() lines = [line.strip() for line in text.split('\n') if line.strip()] return '\n'.join(lines) def decode_subject(subject): if not subject: return "" if isinstance(subject, bytes): try: subject = subject.decode('utf-8') except: return str(subject) if subject.startswith('=?UTF-8?B?'): try: import base64 encoded = subject.split('?B?')[1].split('?=')[0] return base64.b64decode(encoded).decode('utf-8') except: return subject return subject def parse_email(subject, body_html): result = {"town": "", "phone": "", "email": "", "first_name": "", "surname": "", "second_name": "", "client_type": "", "addition": ""} text_body = html_to_text(body_html) plain_subject = decode_subject(subject) # Телефон phone_match = re.search(r"\+?7\d{10}|\+7\s\d{3}\s\d{3}\s\d{2}\s\d{2}|8\d{10}", text_body) if not phone_match: phone_match = re.search(r"Телефон[:\s]*([+\d\s]{10,20})", text_body, re.IGNORECASE) if phone_match: phone = re.sub(r'[^\d+]', '', phone_match.group(0)) if phone.startswith('8'): phone = '+7' + phone[1:] elif phone.isdigit() and len(phone) == 11: phone = '+' + phone result["phone"] = phone # Email email_match = re.search(r"Email[:\s]*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})", text_body, re.IGNORECASE) if email_match: result["email"] = email_match.group(1) # Имя name_match = re.search(r"Имя[:\s]*(.+)", text_body, re.IGNORECASE) if name_match: result["first_name"] = name_match.group(1).strip() # Фамилия surname_match = re.search(r"Фамилия[:\s]*(.+)", text_body, re.IGNORECASE) if surname_match: result["surname"] = surname_match.group(1).strip() # Отчество second_name_match = re.search(r"Отчество[:\s]*(.+)", text_body, re.IGNORECASE) if second_name_match: result["second_name"] = second_name_match.group(1).strip() # Тип клиента client_type_match = re.search(r"Физ/Юр\. лицо[:\s]*(.+)", text_body, re.IGNORECASE) if client_type_match: result["client_type"] = client_type_match.group(1).strip() # Город towns = ["Гатчина", "Коммунар", "Сиверский", "Вырица", "Тайцы", "Дружная Горка"] for town in towns: if town.lower() in text_body.lower(): result["town"] = town break # Всё остальное addition_lines = [f"Тема: {plain_subject}", "", text_body] result["addition"] = "\n".join(addition_lines) return result def fetch_new_emails(): print("Проверяю почту...", flush=True) global processed_uids, leads_queue try: with IMAPClient(IMAP_SERVER, ssl=True) as server: server.login(EMAIL, PASSWORD) server.select_folder("Подключения") messages = server.search(["ALL"]) if not messages: return 0 ALLOWED_SENDERS = ["no_reply@gtn.ru"] new_count = 0 for msg_id in messages: if msg_id in processed_uids: continue data = server.fetch([msg_id], ["ENVELOPE", "BODY[]"]) envelope = data[msg_id][b"ENVELOPE"] sender_email = "" if envelope.from_: from_addr = envelope.from_[0] mailbox = from_addr.mailbox host = from_addr.host if isinstance(mailbox, bytes): mailbox = mailbox.decode('utf-8') if isinstance(host, bytes): host = host.decode('utf-8') if mailbox and host: sender_email = f"{mailbox}@{host}" print(f"📨 Найдено письмо от {sender_email}, UID={msg_id}") allowed = False for pattern in ALLOWED_SENDERS: if pattern.startswith('@'): if sender_email.endswith(pattern): allowed = True break else: if sender_email.lower() == pattern.lower(): allowed = True break if not allowed: print(f"⏭️ Пропущено (отправитель не в списке)") processed_uids.add(msg_id) save_state() continue body_data = data[msg_id].get(b"BODY[]", b"") subject = envelope.subject if isinstance(subject, bytes): subject = subject.decode('utf-8', errors='ignore') elif subject is None: subject = "" body = body_data.decode("utf-8", errors="ignore") body_match = re.search(r"]*>(.*?)", body, re.DOTALL | re.IGNORECASE) html_content = body_match.group(1) if body_match else body lead = parse_email(subject, html_content) leads_queue.append(lead) save_queue_to_file() processed_uids.add(msg_id) save_state() new_count += 1 print(f"✅ Взят лид от {sender_email}, телефон: {lead['phone']}") return new_count except Exception as e: print(f"Ошибка IMAP: {e}") return 0 class LeadHandler(BaseHTTPRequestHandler): def do_GET(self): global leads_queue if self.path == "/lead": lead = leads_queue.popleft() if leads_queue else None save_queue_to_file() self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Access-Control-Allow-Origin", "*") self.end_headers() self.wfile.write(json.dumps({ "lead": lead, "queue_size": len(leads_queue) }, ensure_ascii=False).encode('utf-8')) elif self.path.startswith("/lead?index="): try: idx = int(self.path.split("=")[1]) if 0 <= idx < len(leads_queue): lead = leads_queue[idx] del leads_queue[idx] save_queue_to_file() else: lead = None self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Access-Control-Allow-Origin", "*") self.end_headers() self.wfile.write(json.dumps({ "lead": lead, "queue_size": len(leads_queue) }, ensure_ascii=False).encode('utf-8')) except: self.send_response(400) self.end_headers() elif self.path == "/queue_list": preview_list = [] for lead in leads_queue: preview = { "first_name": lead.get("first_name", ""), "phone": lead.get("phone", ""), "email": lead.get("email", "") } preview_list.append(preview) self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Access-Control-Allow-Origin", "*") self.end_headers() self.wfile.write(json.dumps({ "list": preview_list, "size": len(leads_queue) }, ensure_ascii=False).encode('utf-8')) elif self.path == "/queue": self.send_response(200) self.send_header("Content-Type", "application/json") self.send_header("Access-Control-Allow-Origin", "*") self.end_headers() self.wfile.write(json.dumps({"size": len(leads_queue)}).encode()) else: self.send_response(404) self.end_headers() def do_OPTIONS(self): self.send_response(200) self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Methods", "GET") self.end_headers() def log_message(self, format, *args): pass def main(): # Загружаем сохранённую очередь load_queue_from_file() print("🚀 Сервер запущен на http://0.0.0.0:8765") print(f"📁 Обработанных UID: {len(processed_uids)}") print(f"📋 В очереди: {len(leads_queue)} лидов") print("Ожидание писем...") import threading http_server = HTTPServer(("0.0.0.0", 8765), LeadHandler) thread = threading.Thread(target=http_server.serve_forever, daemon=True) thread.start() while True: new = fetch_new_emails() if new > 0: print(f"📬 Забрал {new} новое(ых) письмо(а). В очереди: {len(leads_queue)}") time.sleep(10) if __name__ == "__main__": print("Запуск main()...", flush=True) main()