diff --git a/邮件批量发送脚本.py b/邮件批量发送脚本.py index aa102f0..3dbf287 100644 --- a/邮件批量发送脚本.py +++ b/邮件批量发送脚本.py @@ -14,7 +14,8 @@ from selenium.webdriver.remote.webelement import WebElement from tkinter import Tk, filedialog from datetime import datetime, timedelta -from nameparser import HumanName +from zoneinfo import ZoneInfo +from nameparser import HumanName, config parser = argparse.ArgumentParser(description="邮件批量发送脚本") parser.add_argument('input', nargs='?') @@ -28,7 +29,7 @@ parser.add_argument('-p', '--password', type=str, required=True) parser.add_argument('-t', '--timeout', type=int, nargs='?', default=60) parser.add_argument('-i', '--interval', type=int, nargs='?', default=10) parser.add_argument('-m', '--max-occurrence', type=int, nargs='?', default=5) -parser.add_argument('-H', '--hello', type=str, nargs='?', default='') +parser.add_argument('-T', '--timezone', type=str, nargs='?', default='Europe/Berlin') parser.add_argument('-r', '--retry', type=int, nargs='?', default=3) args = parser.parse_args() @@ -38,6 +39,44 @@ sent = 0 errors = 0 warnings = 0 +greetings = [ + { + "locale": "**", + "default": "不使用问候语", + "registry": [] + }, + { + "locale": "en", + "default": "Hello", + "registry": ["Good morning", "Good afternoon", "Good evening"] + }, + { + "locale": "fr", + "default": "Bonjour", + "registry": [None, "Bon après-midi", "Bonsoir"] + }, + { + "locale": "de", + "default": "Hallo", + "registry": ["Guten Morgen", "Guten Tag", "Guten Abend"] + }, + { + "locale": "it", + "default": "Ciao", + "registry": ["Buongiorno", "Buon pomeriggio", "Buonasera"] + }, + { + "locale": "es", + "default": "Hola", + "registry": ["Buenos días", "Buenas tardes", None] + }, + { + "locale": "pt", + "default": "Olá", + "registry": ["Bom dia", "Boa tarde", None] + } +] + def main(): if not args.input: root = Tk() @@ -107,9 +146,10 @@ def main(): except TimeoutException: continue except: break - def keyin(element: WebElement, value): - try: element.send_keys(value) - except: driver.execute_script(f"arguments[0].value = arguments[1];", element, value) + def keyin(element: WebElement, string: str): + for char in string: + if driver.switch_to.active_element != element: click(element) + element.send_keys(char) def ready(driver, predicate): try: @@ -177,15 +217,28 @@ def main(): return 5 rate = 60 / (args.interval + 3) + command = None + timezone = ZoneInfo(args.timezone) + print(f'[信息] 当前发送速率 {round(rate, 2)} 封/分钟') print(f'[信息] 预计使用时间 {timedelta(minutes=limit / rate)}') - if rate > 8.33: print('[警告] 当前发送速率已超出限制 8.33 封/分钟') - key = input('[????] 是否确定发送?确定 (Y) / 取消 (N): ') - if key in ['Y', 'y']: - print('[信息] 已确定发送') - else: + print(f'[信息] 当前时区:{args.timezone}') + print(f'[信息] 已读取问候语 {len(greetings)} 条:', end='\n\n') + + for index, item in enumerate(greetings): + print(f'\t[{index}] {item.get('locale')}', end=' ') + print('- %s' % (', '.join(filter(None, item.get('registry'))) or item.get('default'))) + + while command is None: + match input('\n[????] 请选择 (留空取消操作): ').strip(): + case keys if not keys: + command = -1 + case keys if keys.isdigit(): + number = int(keys) + if number < len(greetings): command = number + if command < 0: print('[信息] 已取消发送') exit() @@ -197,6 +250,10 @@ def main(): occurrences = {} while active and index < limit: + global warnings + global errors + global sent + current = index index += 1 @@ -205,8 +262,12 @@ def main(): code = codes[current] mark = sents[current] - if not code: occurrence = [0] - else: occurrence = occurrences.setdefault(code, [0]) + if not code: + print(f'[警告] 最大允许重复次数已设置为 [{args.max_occurrence}], 但未提供有效唯一标识 (如客户编号)') + occurrence = [0] + warnings += 1 + else: + occurrence = occurrences.setdefault(code, [0]) if mark is not None and str(mark).strip(): print(f'[信息] 已跳过项目 {recipient}') @@ -215,7 +276,6 @@ def main(): if args.max_occurrence > 0 and occurrence[0] >= args.max_occurrence: print(f'[警告] 收件人 {recipient} 所属组织出现次数已超出限制 {occurrence}') - global warnings warnings += 1 continue @@ -227,12 +287,21 @@ def main(): ready(driver, lambda x: x.find_element(By.CSS_SELECTOR, ".io-ox-busy")) locate("div.io-ox-mail-compose-window iframe", condition=EC.frame_to_be_available_and_switch_to_it) - if hello := str(args.hello): + if command > 0 and (selection := greetings[command]): + match datetime.now(timezone).hour: + case hour if 6 <= hour < 12: registry = selection.get('registry')[0] + case hour if 12 <= hour < 18: registry = selection.get('registry')[1] + case hour if 18 <= hour < 21: registry = selection.get('registry')[2] + case _: registry = None + action = ActionChains(driver, 5000) - action.send_keys(hello) + action.send_keys(registry or selection.get('default')) if name is not None and (name := str(name).strip()) and not contains_non_latin_alphabet(name): - parts = HumanName(name) + const = config.CONSTANTS + const.titles.add('M.') + + parts = HumanName(name, const) parts.capitalize() person = ' '.join(filter(None, [parts.title, parts.first or parts.middle or parts.last])) action.send_keys(Keys.SPACE).send_keys(person) @@ -257,22 +326,20 @@ def main(): except TimeoutException: sents[current] = '✔' occurrence[0] += 1 - global sent sent += 1 break + errors += 1 message = alert.text.replace('\n', ' ') print(f'[警告] 来自网页:{message}') - global errors - errors += 1 - # 关闭警告 click("div.io-ox-alert.io-ox-alert-error button[data-action='close']") while mails := driver.find_elements(By.CSS_SELECTOR, "div.io-ox-mail-compose-window"): # 关闭过期邮件 - click("button[data-action='close']", parent=mails[0]) + try: click("button[data-action='close']", parent=mails[0]) + except: continue # 删除过期邮件 click("div.modal-footer button[data-action='delete']") break