mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-14 12:48:03 +00:00
Add task email output sender controls
This commit is contained in:
@@ -1712,8 +1712,15 @@ class TaskScheduler:
|
|||||||
|
|
||||||
target = (output or "").strip()
|
target = (output or "").strip()
|
||||||
explicit = ""
|
explicit = ""
|
||||||
|
account_id = ""
|
||||||
if target.startswith("email:"):
|
if target.startswith("email:"):
|
||||||
explicit = target.split(":", 1)[1].strip()
|
explicit = target.split(":", 1)[1].strip()
|
||||||
|
if "|account=" in explicit:
|
||||||
|
explicit, account_id = explicit.split("|account=", 1)
|
||||||
|
explicit = explicit.strip()
|
||||||
|
account_id = account_id.strip()
|
||||||
|
if explicit == "self":
|
||||||
|
explicit = ""
|
||||||
elif "@" in target:
|
elif "@" in target:
|
||||||
explicit = target
|
explicit = target
|
||||||
|
|
||||||
@@ -1721,7 +1728,7 @@ class TaskScheduler:
|
|||||||
from routes.email_routes import _resolve_send_config
|
from routes.email_routes import _resolve_send_config
|
||||||
from routes.email_helpers import _send_smtp_message
|
from routes.email_helpers import _send_smtp_message
|
||||||
|
|
||||||
cfg = _resolve_send_config(owner=task.owner or "")
|
cfg = _resolve_send_config(account_id=account_id or None, owner=task.owner or "")
|
||||||
to_addr = explicit or cfg.get("from_address") or cfg.get("smtp_user") or ""
|
to_addr = explicit or cfg.get("from_address") or cfg.get("smtp_user") or ""
|
||||||
if not to_addr:
|
if not to_addr:
|
||||||
raise RuntimeError("No email recipient resolved for task output")
|
raise RuntimeError("No email recipient resolved for task output")
|
||||||
|
|||||||
+80
-3
@@ -254,6 +254,34 @@ function _taskPromptConfig(prompt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _parseTaskEmailOutputTarget(output) {
|
||||||
|
const raw = String(output || '').trim();
|
||||||
|
if (!raw) return { enabled: false, to: '', accountId: '' };
|
||||||
|
if (raw === 'email') return { enabled: true, to: '', accountId: '' };
|
||||||
|
if (/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(raw)) return { enabled: true, to: raw, accountId: '' };
|
||||||
|
if (!raw.startsWith('email:')) return { enabled: false, to: '', accountId: '' };
|
||||||
|
let payload = raw.slice('email:'.length).trim();
|
||||||
|
let accountId = '';
|
||||||
|
const marker = '|account=';
|
||||||
|
const markerIdx = payload.indexOf(marker);
|
||||||
|
if (markerIdx >= 0) {
|
||||||
|
accountId = payload.slice(markerIdx + marker.length).trim();
|
||||||
|
payload = payload.slice(0, markerIdx).trim();
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
enabled: true,
|
||||||
|
to: payload && payload !== 'self' ? payload : '',
|
||||||
|
accountId,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _buildTaskEmailOutputTarget(to, accountId) {
|
||||||
|
const cleanTo = String(to || '').trim();
|
||||||
|
const cleanAccount = String(accountId || '').trim();
|
||||||
|
const base = `email:${cleanTo || 'self'}`;
|
||||||
|
return cleanAccount ? `${base}|account=${cleanAccount}` : (cleanTo ? base : 'email');
|
||||||
|
}
|
||||||
|
|
||||||
async function _renderEmailActionOptions(action, existing, extra) {
|
async function _renderEmailActionOptions(action, existing, extra) {
|
||||||
if (!_EMAIL_ACCOUNT_ACTIONS.has(action)) return;
|
if (!_EMAIL_ACCOUNT_ACTIONS.has(action)) return;
|
||||||
const accounts = (await _fetchEmailAccountsForTasks()).filter(a => a && a.enabled !== false);
|
const accounts = (await _fetchEmailAccountsForTasks()).filter(a => a && a.enabled !== false);
|
||||||
@@ -1141,6 +1169,7 @@ function _showForm(existing, initTaskType, initTriggerType) {
|
|||||||
<select id="task-form-output" class="task-form-input">
|
<select id="task-form-output" class="task-form-input">
|
||||||
<option value="session">Session</option>
|
<option value="session">Session</option>
|
||||||
</select>
|
</select>
|
||||||
|
<div id="task-form-output-extra"></div>
|
||||||
|
|
||||||
<label class="task-form-label">Model <span style="opacity:0.5;font-weight:normal;font-size:10px;">(optional — overrides session default)</span></label>
|
<label class="task-form-label">Model <span style="opacity:0.5;font-weight:normal;font-size:10px;">(optional — overrides session default)</span></label>
|
||||||
<select id="task-form-model" class="task-form-input">
|
<select id="task-form-model" class="task-form-input">
|
||||||
@@ -1411,28 +1440,70 @@ function _showForm(existing, initTaskType, initTriggerType) {
|
|||||||
renderTriggerOpts();
|
renderTriggerOpts();
|
||||||
|
|
||||||
// Populate output targets
|
// Populate output targets
|
||||||
|
const renderOutputExtra = async () => {
|
||||||
|
const outputSel = document.getElementById('task-form-output');
|
||||||
|
const extra = document.getElementById('task-form-output-extra');
|
||||||
|
if (!outputSel || !extra) return;
|
||||||
|
const currentTo = document.getElementById('task-form-output-email-to')?.value;
|
||||||
|
const currentAccountId = document.getElementById('task-form-output-email-account')?.value;
|
||||||
|
extra.innerHTML = '';
|
||||||
|
if (outputSel.value !== 'email') return;
|
||||||
|
const parsed = _parseTaskEmailOutputTarget(existing?.output_target || '');
|
||||||
|
if (currentTo != null) parsed.to = currentTo;
|
||||||
|
if (currentAccountId != null) parsed.accountId = currentAccountId;
|
||||||
|
const accounts = (await _fetchEmailAccountsForTasks()).filter(a => a && a.enabled !== false);
|
||||||
|
const options = [
|
||||||
|
`<option value="" ${parsed.accountId ? '' : 'selected'}>Default sending account</option>`,
|
||||||
|
...accounts.map(a => {
|
||||||
|
const id = String(a.id || '');
|
||||||
|
const label = a.name || a.from_address || a.imap_user || id.slice(0, 8);
|
||||||
|
const suffix = a.is_default ? ' (default)' : '';
|
||||||
|
return `<option value="${_escHtml(id)}" ${id === parsed.accountId ? 'selected' : ''}>${_escHtml(label + suffix)}</option>`;
|
||||||
|
}),
|
||||||
|
].join('');
|
||||||
|
extra.innerHTML = `
|
||||||
|
<div class="task-form-output-email" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:8px;margin-top:6px;">
|
||||||
|
<label>
|
||||||
|
<span class="task-form-label" style="margin-top:0;">From</span>
|
||||||
|
<select id="task-form-output-email-account" class="task-form-input">${options}</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span class="task-form-label" style="margin-top:0;">To</span>
|
||||||
|
<input id="task-form-output-email-to" class="task-form-input" type="email" value="${_escHtml(parsed.to)}" placeholder="Me / selected account" />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="memory-desc" style="font-size:10px;margin-top:3px;">Leave To blank to send to the selected account’s own address.</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
_fetchOutputTargets().then(targets => {
|
_fetchOutputTargets().then(targets => {
|
||||||
const outputSel = document.getElementById('task-form-output');
|
const outputSel = document.getElementById('task-form-output');
|
||||||
if (!outputSel || targets.length <= 1) return;
|
if (!outputSel || targets.length <= 1) return;
|
||||||
outputSel.innerHTML = '';
|
outputSel.innerHTML = '';
|
||||||
|
const existingEmailOutput = _parseTaskEmailOutputTarget(existing?.output_target || '');
|
||||||
let matchedOutput = false;
|
let matchedOutput = false;
|
||||||
for (const t of targets) {
|
for (const t of targets) {
|
||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
opt.value = t.value;
|
opt.value = t.value;
|
||||||
opt.textContent = t.label;
|
opt.textContent = t.label;
|
||||||
if (existing?.output_target === t.value) {
|
if (existingEmailOutput.enabled && t.value === 'email') {
|
||||||
|
opt.selected = true;
|
||||||
|
matchedOutput = true;
|
||||||
|
} else if (!existingEmailOutput.enabled && existing?.output_target === t.value) {
|
||||||
opt.selected = true;
|
opt.selected = true;
|
||||||
matchedOutput = true;
|
matchedOutput = true;
|
||||||
}
|
}
|
||||||
outputSel.appendChild(opt);
|
outputSel.appendChild(opt);
|
||||||
}
|
}
|
||||||
if (existing?.output_target && !matchedOutput) {
|
if (existing?.output_target && !matchedOutput && !existingEmailOutput.enabled) {
|
||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
opt.value = existing.output_target;
|
opt.value = existing.output_target;
|
||||||
opt.textContent = existing.output_target.includes('@') ? `Email: ${existing.output_target}` : existing.output_target;
|
opt.textContent = existing.output_target.includes('@') ? `Email: ${existing.output_target}` : existing.output_target;
|
||||||
opt.selected = true;
|
opt.selected = true;
|
||||||
outputSel.appendChild(opt);
|
outputSel.appendChild(opt);
|
||||||
}
|
}
|
||||||
|
outputSel.addEventListener('change', renderOutputExtra);
|
||||||
|
renderOutputExtra();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Populate model dropdown from /api/models. Value is "endpoint_url::model"
|
// Populate model dropdown from /api/models. Value is "endpoint_url::model"
|
||||||
@@ -1517,7 +1588,13 @@ function _showForm(existing, initTaskType, initTriggerType) {
|
|||||||
// Save
|
// Save
|
||||||
document.getElementById('task-form-save').addEventListener('click', async () => {
|
document.getElementById('task-form-save').addEventListener('click', async () => {
|
||||||
const nameEl = document.getElementById('task-form-name');
|
const nameEl = document.getElementById('task-form-name');
|
||||||
const outputTarget = document.getElementById('task-form-output')?.value || 'session';
|
const outputSelValue = document.getElementById('task-form-output')?.value || 'session';
|
||||||
|
let outputTarget = outputSelValue;
|
||||||
|
if (outputSelValue === 'email') {
|
||||||
|
const to = document.getElementById('task-form-output-email-to')?.value || '';
|
||||||
|
const accountId = document.getElementById('task-form-output-email-account')?.value || '';
|
||||||
|
outputTarget = _buildTaskEmailOutputTarget(to, accountId);
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
task_type: taskType,
|
task_type: taskType,
|
||||||
|
|||||||
@@ -23427,6 +23427,8 @@ body:not(.welcome-ready) #welcome-screen {
|
|||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
.task-log-force-run {
|
.task-log-force-run {
|
||||||
|
appearance: none;
|
||||||
|
box-sizing: border-box;
|
||||||
border: 0;
|
border: 0;
|
||||||
background: color-mix(in srgb, var(--fg) 7%, transparent);
|
background: color-mix(in srgb, var(--fg) 7%, transparent);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
@@ -23434,7 +23436,9 @@ body:not(.welcome-ready) #welcome-screen {
|
|||||||
opacity: .82;
|
opacity: .82;
|
||||||
margin-left: 7px;
|
margin-left: 7px;
|
||||||
padding: 1px 6px 1px 4px;
|
padding: 1px 6px 1px 4px;
|
||||||
|
height: 18px;
|
||||||
min-height: 16px;
|
min-height: 16px;
|
||||||
|
max-height: 18px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
Reference in New Issue
Block a user