update: bump 'common' version to 0.1.20

This commit is contained in:
2026-08-11 21:18:44 +08:00
parent ddaca17173
commit a9c8644f03
3 changed files with 109 additions and 118 deletions
+13 -20
View File
@@ -191,15 +191,14 @@ import { default as $, LogRecord, Rpc2 } from '/';
let major = 0; let major = 0;
let minor = 0; let minor = 0;
let busy = false; let busy = false;
let save = false;
let status = null; let status = null;
let locale = null; let locale = null;
let unique = null; let unique = null;
let writable = null; let filehandle = null;
let filereader = null;
let subcategories = []; let subcategories = [];
window.addEventListener('beforeunload', (e) => { window.addEventListener('beforeunload', (e) => {
writable?.abort();
Rpc2.notify('exit'); Rpc2.notify('exit');
e.preventDefault(); e.preventDefault();
e.returnValue = ''; e.returnValue = '';
@@ -214,7 +213,7 @@ document.title += ` (${name})`;
async function handlePrimaryButtonClick() { async function handlePrimaryButtonClick() {
switch (status) { switch (status) {
case 'IDLE': case 'IDLE':
let PickerOptions = { [filehandle] = await showOpenFilePicker({
types: [ types: [
{ {
description: "Excel Spreadsheet", description: "Excel Spreadsheet",
@@ -223,17 +222,11 @@ async function handlePrimaryButtonClick() {
} }
} }
] ]
}; });
let [handle] = await showOpenFilePicker(PickerOptions); let chain = filehandle.getFile().then(f => Promise.all([f, f.arrayBuffer()]));
filereader = () => handle.getFile().then(file => new Promise((resolve, reject) => { let [file, buffer] = await chain.catch(e => alert('(ERROR) ' + new String(e ?? 'Failed to load file')));
let reader = new FileReader();
reader.onload = () => resolve([file, reader.result]);
reader.onerror = () => reject(reader.error);
reader.readAsArrayBuffer(file);
}));
let [file, buffer] = await filereader().catch(e => alert('(ERROR) ' + new String(e ?? 'Unable to read data'))); save = await handle.requestPermission({ mode: 'readwrite' }) === 'granted';
writable = await handle.createWritable().catch(() => null);
major = await Rpc2.invoke('loads', { params: [file.name, new Uint8Array(buffer).toBase64()] }); major = await Rpc2.invoke('loads', { params: [file.name, new Uint8Array(buffer).toBase64()] });
$.get('#fileLabel').innerText = file.name; $.get('#fileLabel').innerText = file.name;
$.get('#fromLabel').innerText = parameters.account; $.get('#fromLabel').innerText = parameters.account;
@@ -241,7 +234,7 @@ async function handlePrimaryButtonClick() {
break; break;
case 'READY': case 'READY':
let locale = $('#locale').value; let locale = $('#locale').value;
let options = { locale, subcategories, mapping: new Object(), writable: new Boolean(writable), start: 2, limit: null }; let options = { locale, subcategories, save, mapping: new Object(), start: 2, limit: null };
for (let element of $.all("input[type='number'].params")) { for (let element of $.all("input[type='number'].params")) {
parameters[element.id] = element.disabled ? null : element.valueAsNumber; parameters[element.id] = element.disabled ? null : element.valueAsNumber;
@@ -422,7 +415,6 @@ while (await new Promise(o => setTimeout(o, 1000, true))) {
case 'IDLE': case 'IDLE':
$('#send > span.text').innerText = 'Open'; $('#send > span.text').innerText = 'Open';
$('#send').classList.remove('pulse'); $('#send').classList.remove('pulse');
await writable?.abort().catch(() => void 0).finally(() => writable = null);
break; break;
case 'READY': case 'READY':
$('#send > span.text').innerText = 'Send'; $('#send > span.text').innerText = 'Send';
@@ -431,17 +423,18 @@ while (await new Promise(o => setTimeout(o, 1000, true))) {
$('#remainingLabel').innerText = ''; $('#remainingLabel').innerText = '';
break; break;
case 'CLOSING': case 'CLOSING':
if (writable) try { if (save) try {
$.all('#actions > button').forEach(e => e.disabled = true); $.all('#actions > button').forEach(e => e.disabled = true);
let data = await filereader().then(([_, b]) => Rpc2.invoke('merge', { params: [new Uint8Array(b).toBase64()] })); let writable = await filehandle.createWritable({ mode: 'exclusive' });
let data = await filehandle.getFile().arrayBuffer().then(b => Rpc2.invoke('merge', { params: [new Uint8Array(b).toBase64()] }));
await writable.write(Uint8Array.fromBase64(data)); await writable.write(Uint8Array.fromBase64(data));
await writable.close(); await writable.close();
await Rpc2.notify('close'); await Rpc2.notify('close');
} catch (e) { } catch (e) {
if (Error.isError(e) && e.name === 'NoModificationAllowedError') continue;
alert('(ERROR) ' + new String(e ?? 'File operation failed')); alert('(ERROR) ' + new String(e ?? 'File operation failed'));
await writable.abort().catch(() => void 0);
} }
writable = null; save = false;
continue; continue;
case 'RUNNING': case 'RUNNING':
$('#send > span.text').innerText = 'Pause'; $('#send > span.text').innerText = 'Pause';
+11 -13
View File
@@ -43,7 +43,7 @@ WEBMAIL = "https://email.ionos.fr/appsuite/#app=io.ox/mail&mailto=%s"
def main(driver: Chrome, logger = logging.getLogger('main')): def main(driver: Chrome, logger = logging.getLogger('main')):
timer = Timer() timer = Timer()
parameters = vars(args) parameters = vars(args)
sp = ServiceProvider.default() sp = ServiceProvider(ServiceProvider.Options(application='Mailer'))
class Status(Enum): class Status(Enum):
IDLE = 0 IDLE = 0
@@ -228,17 +228,17 @@ def main(driver: Chrome, logger = logging.getLogger('main')):
sleep(0.2); return True sleep(0.2); return True
class Timeout(Action): class Timeout(Action):
timer = Timer() clock = Timer()
@classmethod @classmethod
def prepare(cls): def prepare(cls):
cls.timer.clear() cls.clock.clear()
cls.timer.start() cls.clock.start()
return True return True
@classmethod @classmethod
def perform(cls): def perform(cls):
if cls.timer.delta() > parameters['timeout']: raise cls if cls.clock.delta() > parameters['timeout']: raise cls
sleep(0.2); return True sleep(0.2); return True
class Acknowledge(Action): class Acknowledge(Action):
@@ -329,14 +329,14 @@ def main(driver: Chrome, logger = logging.getLogger('main')):
def save(): def save():
try: try:
file = Path(parameters['directory']).joinpath(filename or f'Mailer-{datetime.now().strftime('%Y%m%d-%H%M%S-%f')}.xlsx').resolve() file = Path(parameters['directory']).joinpath(datetime.now().strftime('Mailer-%Y%m%d-%H%M%S-%f.xlsx')).resolve()
logger.info('Saving document at %s', str(file)) logger.info('Saving document at %s', str(file))
wb.save(file) wb.save(file)
except Exception as e: except Exception as e:
logger.error('Error while writing data', exc_info=e) logger.error('Error while writing data', exc_info=e)
exit = sp.pop('exit') exit = sp.pop('exit')
sp.set('exit', lambda: (save() if options.get('writable') and status.value >= 2 else None, exit())) sp.set('exit', lambda: (save() if options.get('save') and status.value >= 2 else None, exit()))
class MismatchedEmailSubject(Exception): pass class MismatchedEmailSubject(Exception): pass
class MismatchedEmailAddress(Exception): pass class MismatchedEmailAddress(Exception): pass
@@ -414,13 +414,11 @@ def main(driver: Chrome, logger = logging.getLogger('main')):
occurrence[code] += 1 occurrence[code] += 1
raise Skip() raise Skip()
match options.get('occurrence'): if (caps := options.get('occurrence')) is not None and caps > 0:
case o if o is None: if code is None:
pass
case o if o > 0 and code is None:
logger.error("Column '%s' cannot be used for spam control", column.code) logger.error("Column '%s' cannot be used for spam control", column.code)
raise Cancel() raise Cancel()
case o if o > 0 and occurrence[code] >= o: if occurrence[code] >= caps:
logger.info("[%d/%d] No Spam! Skipping '%s'", index-1, limit-1, email) logger.info("[%d/%d] No Spam! Skipping '%s'", index-1, limit-1, email)
raise Skip() raise Skip()
@@ -556,7 +554,7 @@ def main(driver: Chrome, logger = logging.getLogger('main')):
logger.error('RPC communication failed', exc_info=e) logger.error('RPC communication failed', exc_info=e)
sp.pop('exit')() sp.pop('exit')()
if options.get('writable'): if options.get('save'):
try: try:
logger.info('Saving document...') logger.info('Saving document...')
status = Status.CLOSING status = Status.CLOSING
BIN
View File
Binary file not shown.