update: proper greetings
This commit is contained in:
109
邮件批量发送脚本.py
109
邮件批量发送脚本.py
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user