update: proper greetings

This commit is contained in:
2025-07-23 14:28:32 +08:00
parent 09465e141c
commit 55748542f2

View File

@@ -14,7 +14,8 @@ from selenium.webdriver.remote.webelement import WebElement
from tkinter import Tk, filedialog from tkinter import Tk, filedialog
from datetime import datetime, timedelta from datetime import datetime, timedelta
from nameparser import HumanName from zoneinfo import ZoneInfo
from nameparser import HumanName, config
parser = argparse.ArgumentParser(description="邮件批量发送脚本") parser = argparse.ArgumentParser(description="邮件批量发送脚本")
parser.add_argument('input', nargs='?') 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('-t', '--timeout', type=int, nargs='?', default=60)
parser.add_argument('-i', '--interval', type=int, nargs='?', default=10) parser.add_argument('-i', '--interval', type=int, nargs='?', default=10)
parser.add_argument('-m', '--max-occurrence', type=int, nargs='?', default=5) 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) parser.add_argument('-r', '--retry', type=int, nargs='?', default=3)
args = parser.parse_args() args = parser.parse_args()
@@ -38,6 +39,44 @@ sent = 0
errors = 0 errors = 0
warnings = 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(): def main():
if not args.input: if not args.input:
root = Tk() root = Tk()
@@ -107,9 +146,10 @@ def main():
except TimeoutException: continue except TimeoutException: continue
except: break except: break
def keyin(element: WebElement, value): def keyin(element: WebElement, string: str):
try: element.send_keys(value) for char in string:
except: driver.execute_script(f"arguments[0].value = arguments[1];", element, value) if driver.switch_to.active_element != element: click(element)
element.send_keys(char)
def ready(driver, predicate): def ready(driver, predicate):
try: try:
@@ -177,15 +217,28 @@ def main():
return 5 return 5
rate = 60 / (args.interval + 3) rate = 60 / (args.interval + 3)
command = None
timezone = ZoneInfo(args.timezone)
print(f'[信息] 当前发送速率 {round(rate, 2)} 封/分钟') print(f'[信息] 当前发送速率 {round(rate, 2)} 封/分钟')
print(f'[信息] 预计使用时间 {timedelta(minutes=limit / rate)}') print(f'[信息] 预计使用时间 {timedelta(minutes=limit / rate)}')
if rate > 8.33: print('[警告] 当前发送速率已超出限制 8.33 封/分钟') if rate > 8.33: print('[警告] 当前发送速率已超出限制 8.33 封/分钟')
key = input('[????] 是否确定发送?确定 (Y) / 取消 (N): ')
if key in ['Y', 'y']: print(f'[信息] 当前时区:{args.timezone}')
print('[信息] 已确定发送') print(f'[信息] 已读取问候语 {len(greetings)} 条:', end='\n\n')
else:
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('[信息] 已取消发送') print('[信息] 已取消发送')
exit() exit()
@@ -197,6 +250,10 @@ def main():
occurrences = {} occurrences = {}
while active and index < limit: while active and index < limit:
global warnings
global errors
global sent
current = index current = index
index += 1 index += 1
@@ -205,8 +262,12 @@ def main():
code = codes[current] code = codes[current]
mark = sents[current] mark = sents[current]
if not code: occurrence = [0] if not code:
else: occurrence = occurrences.setdefault(code, [0]) print(f'[警告] 最大允许重复次数已设置为 [{args.max_occurrence}], 但未提供有效唯一标识 (如客户编号)')
occurrence = [0]
warnings += 1
else:
occurrence = occurrences.setdefault(code, [0])
if mark is not None and str(mark).strip(): if mark is not None and str(mark).strip():
print(f'[信息] 已跳过项目 {recipient}') print(f'[信息] 已跳过项目 {recipient}')
@@ -215,7 +276,6 @@ def main():
if args.max_occurrence > 0 and occurrence[0] >= args.max_occurrence: if args.max_occurrence > 0 and occurrence[0] >= args.max_occurrence:
print(f'[警告] 收件人 {recipient} 所属组织出现次数已超出限制 {occurrence}') print(f'[警告] 收件人 {recipient} 所属组织出现次数已超出限制 {occurrence}')
global warnings
warnings += 1 warnings += 1
continue continue
@@ -227,12 +287,21 @@ def main():
ready(driver, lambda x: x.find_element(By.CSS_SELECTOR, ".io-ox-busy")) 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) 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 = 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): 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() parts.capitalize()
person = ' '.join(filter(None, [parts.title, parts.first or parts.middle or parts.last])) person = ' '.join(filter(None, [parts.title, parts.first or parts.middle or parts.last]))
action.send_keys(Keys.SPACE).send_keys(person) action.send_keys(Keys.SPACE).send_keys(person)
@@ -257,22 +326,20 @@ def main():
except TimeoutException: except TimeoutException:
sents[current] = '' sents[current] = ''
occurrence[0] += 1 occurrence[0] += 1
global sent
sent += 1 sent += 1
break break
errors += 1
message = alert.text.replace('\n', ' ') message = alert.text.replace('\n', ' ')
print(f'[警告] 来自网页:{message}') print(f'[警告] 来自网页:{message}')
global errors
errors += 1
# 关闭警告 # 关闭警告
click("div.io-ox-alert.io-ox-alert-error button[data-action='close']") 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"): 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']") click("div.modal-footer button[data-action='delete']")
break break