from imapclient import IMAPClient import email from email.header import decode_header from pathlib import Path import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import Select from datetime import datetime, timedelta, date import pyautogui import time, sys import re import logging from telegram import Bot import asyncio import traceback from selenium.webdriver.common.action_chains import ActionChains # Настройка ведения журнала logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('/home/specit/email_processor.log'), logging.StreamHandler() ] ) # Конфигурация Telegram TELEGRAM_BOT_TOKEN = '5374522720:AAGoNUYCEyJ-7-bSAQPT7aV_W2GWcinnkQU' TELEGRAM_CHAT_ID = '394151541' # Конфигурация Email # EMAIL = 'seligenenko.a@maverik.ru' # PASSWORD = '9Jufd22Mwvzp07dvpELn' EMAIL = 'info@maverik.ru' PASSWORD = 'HWPwY9Ty7QHiusubV31x' IMAP_SERVER = 'mail.maverik.ru' SMTP_SERVER = 'mail.maverik.ru' FOLDER = 'INBOX/0СУБСИДИИ' SAVE_DIRECTORY = Path(r"/opt/autorun/auto_tkp/files") # SAVE_DIRECTORY = Path(r"\\192.168.0.13\obmen\ТКПСубсидии") # Web Конфигурация WEB_URL = 'https://www.tch.ru/ru-ru/Pages/Home.aspx' WEB_USERNAME = '12ROV_kornienko' WEB_PASSWORD = 'Rita20152015' WEB_PASSWORD = 'Tkmaverik2025*1' yesterday = date.today() - timedelta(days=2) # yesterday = date.today() async def send_telegram_notification(message): try: bot = Bot(token=TELEGRAM_BOT_TOKEN) await bot.send_message(chat_id=TELEGRAM_CHAT_ID, text=message) except Exception as e: logging.error(f"Субсидии:\nНе удалось отправить уведомление Telegram: {str(e)}") def handle_error(func): def wrapper(*args, **kwargs): try: return func(*args, **kwargs) except Exception as e: error_message = f"Субсидии:\nОшибка в {func.__name__}: {str(e)}\n{traceback.format_exc()}" logging.error(error_message) asyncio.run(send_telegram_notification(error_message)) raise return wrapper def connect_to_email(): server = IMAPClient(IMAP_SERVER, use_uid=True, ssl=True) server.login(EMAIL, PASSWORD) return server def extract_number_from_subject(subject): # Извлеките цифры из темы, например «субсидия 555 612086» или «субсидия555612086» numbers = re.findall(r'\d+', subject) if numbers and len(''.join(numbers)) > 9: # return ' '.join(numbers) # if len(''.join(numbers)) > 9: print(''.join(numbers)[-10:]) return ''.join(numbers)[-10:] return None def decode_email_header(header): """Декодировать заголовок электронной почты для обработки различных кодировок""" decoded_header = decode_header(header) result = [] for part, encoding in decoded_header: if isinstance(part, bytes): if encoding: result.append(part.decode(encoding)) else: result.append(part.decode('utf-8', errors='replace')) else: result.append(part) return ''.join(result) @handle_error def process_emails(): server = connect_to_email() server.select_folder(FOLDER) # Поиск непрочитанных сообщений # messages = server.search(['UNSEEN']) # Критерии поиска: непрочитанные сообщений перед текущей датой # messages = server.search(['UNSEEN', 'BEFORE', yesterday.strftime('%d-%b-%Y')]) # Критерии поиска: письма, полученные вчера. Ищем письма конкретно за вчерашний день messages = server.search(['ON', yesterday.strftime('%d-%b-%Y')]) processed_numbers = [] missing_files = [] error_extract_number = 0 multiple_attachments = [] # Список для хранения тем писем с несколькими вложениями for uid, message_data in server.fetch(messages, ['RFC822']).items(): time.sleep(3) email_message = email.message_from_bytes(message_data[b'RFC822']) # Расшифровать тему subject = decode_email_header(email_message['subject']) number = extract_number_from_subject(subject) if not number: # Отметить сообщение как непрочитанное, если в теме не найдено число server.remove_flags(uid, ['\\Seen']) error_extract_number += 1 continue # Количество вложений attachment_count = 0 for part in email_message.walk(): if part.get_content_maintype() == 'multipart': continue if part.get('Content-Disposition') is None: continue if part.get_content_disposition() == 'inline': continue attachment_count += 1 # Пропустить, если вложений несколько if attachment_count > 1: multiple_attachments.append(subject) server.remove_flags(uid, ['\\Seen']) # Отметить как непрочитанное continue has_attachment = False for part in email_message.walk(): if part.get_content_maintype() == 'multipart': continue if part.get('Content-Disposition') is None: continue if part.get_content_disposition() == 'inline': continue filename = part.get_filename() # Расшифровать имя файла, если оно закодировано filename = decode_email_header(filename) suffix = Path(filename).suffix if filename else '' print('Имя файла:', filename) if filename: # Сохранение файл с номером из темы new_filename = number + suffix filepath = SAVE_DIRECTORY / new_filename with open(filepath, 'wb') as f: f.write(part.get_payload(decode=True)) # processed_numbers.append(number) processed_numbers.append(new_filename) has_attachment = True break if not has_attachment: missing_files.append(number) server.logout() return processed_numbers, missing_files, error_extract_number, multiple_attachments @handle_error def upload_files_to_web(processed_numbers): if not processed_numbers: return 0, 0 disabled_passenger = [] # Пассажир инвалид driver = webdriver.Chrome() # Убедитесь, что ChromeDriver установлен. driver.get(WEB_URL) driver.set_window_size(1920, 1080) # Авторизоваться username_field = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.NAME, "ctl00$PlaceHolderMain$lf_Login$tb_Login")) ) password_field = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.NAME, "ctl00$PlaceHolderMain$lf_Login$lb_Pass")) ) username_field.send_keys(WEB_USERNAME) password_field.send_keys(WEB_PASSWORD) driver.find_element(By.XPATH, "//input[@type='submit']").click() # Перейдите к "Подтверждение документов" next_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CLASS_NAME, "a-login")) ) next_button.click() next_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.LINK_TEXT, "Управление сетью продажи, контроль и анализ данных, обмен электронными документами")) ) next_button.click() next_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.LINK_TEXT, "Стандартный вход по протоколу HTTPS")) ) next_button.click() next_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.LINK_TEXT, "Организация продаж")) ) next_button.click() next_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.LINK_TEXT, "Подтверждающие документы")) ) next_button.click() successful_uploads = [] failed_uploads = [] for number in processed_numbers: print('Обрабатывается: ', Path(number).stem) try: time.sleep(10) dropdown = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.TAG_NAME, "select")) ) select = Select(dropdown) select.select_by_visible_text("Отправлено в АВС") # Ввод номера number_field = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "input[data-bind='value: filter.ticketNo']")) ) number_field.clear() time.sleep(1) number_field.send_keys(Path(number).stem) current_date_obj = datetime.now().date() new_date_obj = current_date_obj - timedelta(days=9) new_date_str = new_date_obj.strftime("%d.%m.%Y") input_field = driver.find_element(By.CSS_SELECTOR, "input[data-bind='date: filter.modifyDateFrom']") input_field.clear() time.sleep(0.5) input_field.click() driver.execute_script("arguments[0].value = arguments[1];", input_field, new_date_str) # input_field.send_keys(new_date_str) time.sleep(4) form_button = driver.find_element(By.XPATH, "//button[contains(text(), 'Сформировать')]") # actions = ActionChains(driver) # actions.move_to_element(form_button).perform() # Перемещение курсора к элементу # actions.click().perform() # Клик form_button.click() # driver.execute_script("arguments[0].click();", form_button) print('Сформировать') time.sleep(10) edit_link = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "a.i-edit")) ) edit_link.click() try: # Скрытый родительский эелемент # driver.find_element(By.XPATH, "//td[contains(text(),'Справка об инвалидности')]") WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, "//td[contains(text(),'Справка об инвалидности')]/parent::tr[contains(@style, 'display: none')]")) ) # print('Инвалид: ', edit_link.text, edit_link.is_displayed()) # print('Инвалид: ', disabled.text, disabled.is_displayed(), number) # driver.back() # time.sleep(5) # continue except: # print('Закрываю') disabled_passenger.append(number) # time.sleep(5) driver.back() continue # pass # Загрузить файл upload_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-bind='visible: model.addButtonVisible, click: attach']")) ) upload_button.click() time.sleep(3) # file_path = os.path.join(SAVE_DIRECTORY, f"{number}.pdf") # file_input.send_keys(file_path) # pyautogui.typewrite(os.path.join(SAVE_DIRECTORY, f"{number}.pdf")) pyautogui.typewrite(str(SAVE_DIRECTORY / number)) time.sleep(1) pyautogui.press('enter') time.sleep(7) # Проверяется, подгружен ли файл try: present_element = WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, "a[data-bind='text: fileName, attr: { href: \\'/Attachments/Get?fileId=\\' + attachmentId() }']")) ) print(present_element) except: error_message = f"Ссылка на файл отсутствует." logging.error(error_message) pyautogui.press('esc') pyautogui.press('esc') failed_uploads.append(number) driver.back() continue send_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-bind='click: $parent.action, text: actionName, visible: actionName() != null']")) ) send_button.click() modal_button = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, "button[modal='true']")) ) modal_button.click() successful_uploads.append(number) time.sleep(2) except Exception as e: print(f"Не удалось загрузить файл для номера {number}: {str(e)}") failed_uploads.append(number) driver.quit() return successful_uploads, failed_uploads, disabled_passenger @handle_error def send_report_email(successful_uploads, failed_uploads, missing_files, error_extract_number, multiple_attachments, disabled_passenger): msg = MIMEMultipart() msg['From'] = EMAIL # msg['To'] = EMAIL msg['To'] = '109@maverik.ru' msg['Subject'] = 'Отчет по обработке субсидий' body = f""" Отчет по обработке субсидий: Успешно загружены файлы для номеров: {', '.join(successful_uploads) if successful_uploads else 'Нет'} Не удалось загрузить файлы для номеров: {', '.join(failed_uploads) if failed_uploads else 'Нет'} Отсутствуют файлы для номеров: {', '.join(missing_files) if missing_files else 'Нет'} Количество писем, в теме которых нет билета: {error_extract_number if error_extract_number else 'Нет'} Пропущены письма с несколькими вложениями: {', '.join(multiple_attachments) if multiple_attachments else 'Нет'} В билетах пассажир с инвалидностью: {', '.join(disabled_passenger) if disabled_passenger else 'Нет'} """ msg.attach(MIMEText(body, 'plain')) server = smtplib.SMTP(SMTP_SERVER) server.starttls() server.login(EMAIL, PASSWORD) server.send_message(msg) server.quit() def main(): # Создайте папку для сохранения, если она не существует SAVE_DIRECTORY.mkdir(parents=True, exist_ok=True) try: # Обработка электронных писем processed_numbers, missing_files, error_extract_number, multiple_attachments = process_emails() print(processed_numbers) # Загрузить файлы на ресурс successful_uploads, failed_uploads, disabled_passenger = upload_files_to_web(processed_numbers) # Отправить отчет по электронной почте send_report_email(successful_uploads, failed_uploads, missing_files, error_extract_number, multiple_attachments, disabled_passenger) logging.info("Субсидии:\nСкрипт успешно завершен") asyncio.run(send_telegram_notification("Субсидии:\nСкрипт успешно завершен")) except Exception as e: error_message = f"Субсидии:\nСкрипт не выполнен из-за ошибки: {str(e)}\n{traceback.format_exc()}" logging.error(error_message) asyncio.run(send_telegram_notification(error_message)) sys.exit(1) if __name__ == "__main__": main()