Files
My-scripts/Auto lead/server.py
T

382 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
from datetime import datetime
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 = [
"Гатчина", "Коммунар", "Сиверский", "Вырица", "Тайцы", "Дружная Горка",
"Белогорка", "Большая Вруда", "Большие Колпаны", "Большое Рейзино",
"Вайя", "Вайялово", "Войсковицы", "Волосово", "Высокоключевой",
"Елизаветино (пл. Дружбы)", "Елизаветино (Басова, Горная и др.)", "Елизаветино, Парковая 3",
"Ивановка", "Калитино", "Каложицы", "Кикерино", "Кобринское", "Курковицы",
"Куровицы", "Лампово", "Лесное", "Лукаши", "Малое Верево", "Малые Колпаны",
"Меньково", "Мыза-Ивановка", "КП Надежда", "Никольское", "Новый Свет",
"Новый Учхоз", "Парицы", "Пригородный", "Пригородный - Ленэнерго", "Пудость",
"Рабитицы", "Рождествено", "Суйда", "Сумино", "Старосиверский", "Сяськелево",
"Тойворово", "Торфяное", "Шпаньково"
]
for town in towns:
if town.lower() in text_body.lower():
result["town"] = town
break
# Дата и время
result["received_at"] = datetime.now().strftime("%d.%m %H:%M")
# Формируем addition (исключая распарсенные данные)
cleaned_body = text_body
if result["first_name"]:
cleaned_body = cleaned_body.replace(result["first_name"], "")
if result["surname"]:
cleaned_body = cleaned_body.replace(result["surname"], "")
if result["second_name"]:
cleaned_body = cleaned_body.replace(result["second_name"], "")
if result["phone"]:
cleaned_body = cleaned_body.replace(result["phone"], "")
phone_clean = result["phone"].replace("+7", "").replace("+", "")
if phone_clean:
cleaned_body = cleaned_body.replace(phone_clean, "")
if result["email"]:
cleaned_body = cleaned_body.replace(result["email"], "")
# Чистим лишние строки
lines = [line.strip() for line in cleaned_body.split('\n') if line.strip()]
cleaned_body = '\n'.join(lines)
addition_lines = [f"Тема: {plain_subject}", "", cleaned_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[^>]*>(.*?)</body>", 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
# Обработка статических .user.js файлов
if self.path.endswith(".user.js"):
try:
filename = self.path.split('/')[-1]
filepath = f"/home/yasu/scripts/{filename}"
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
self.send_response(200)
self.send_header("Content-Type", "application/javascript")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
self.wfile.write(content.encode('utf-8'))
return
except FileNotFoundError:
self.send_response(404)
self.end_headers()
return
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", ""),
"received_at": lead.get("received_at", "")
}
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()