mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-08 11:56:59 +00:00
Add task email output sender controls
This commit is contained in:
@@ -1712,8 +1712,15 @@ class TaskScheduler:
|
||||
|
||||
target = (output or "").strip()
|
||||
explicit = ""
|
||||
account_id = ""
|
||||
if target.startswith("email:"):
|
||||
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:
|
||||
explicit = target
|
||||
|
||||
@@ -1721,7 +1728,7 @@ class TaskScheduler:
|
||||
from routes.email_routes import _resolve_send_config
|
||||
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 ""
|
||||
if not to_addr:
|
||||
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) {
|
||||
if (!_EMAIL_ACCOUNT_ACTIONS.has(action)) return;
|
||||
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">
|
||||
<option value="session">Session</option>
|
||||
</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>
|
||||
<select id="task-form-model" class="task-form-input">
|
||||
@@ -1411,28 +1440,70 @@ function _showForm(existing, initTaskType, initTriggerType) {
|
||||
renderTriggerOpts();
|
||||
|
||||
// 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 => {
|
||||
const outputSel = document.getElementById('task-form-output');
|
||||
if (!outputSel || targets.length <= 1) return;
|
||||
outputSel.innerHTML = '';
|
||||
const existingEmailOutput = _parseTaskEmailOutputTarget(existing?.output_target || '');
|
||||
let matchedOutput = false;
|
||||
for (const t of targets) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = t.value;
|
||||
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;
|
||||
matchedOutput = true;
|
||||
}
|
||||
outputSel.appendChild(opt);
|
||||
}
|
||||
if (existing?.output_target && !matchedOutput) {
|
||||
if (existing?.output_target && !matchedOutput && !existingEmailOutput.enabled) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = existing.output_target;
|
||||
opt.textContent = existing.output_target.includes('@') ? `Email: ${existing.output_target}` : existing.output_target;
|
||||
opt.selected = true;
|
||||
outputSel.appendChild(opt);
|
||||
}
|
||||
outputSel.addEventListener('change', renderOutputExtra);
|
||||
renderOutputExtra();
|
||||
});
|
||||
|
||||
// Populate model dropdown from /api/models. Value is "endpoint_url::model"
|
||||
@@ -1517,7 +1588,13 @@ function _showForm(existing, initTaskType, initTriggerType) {
|
||||
// Save
|
||||
document.getElementById('task-form-save').addEventListener('click', async () => {
|
||||
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 = {
|
||||
task_type: taskType,
|
||||
|
||||
@@ -23427,6 +23427,8 @@ body:not(.welcome-ready) #welcome-screen {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.task-log-force-run {
|
||||
appearance: none;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
background: color-mix(in srgb, var(--fg) 7%, transparent);
|
||||
box-shadow: none;
|
||||
@@ -23434,7 +23436,9 @@ body:not(.welcome-ready) #welcome-screen {
|
||||
opacity: .82;
|
||||
margin-left: 7px;
|
||||
padding: 1px 6px 1px 4px;
|
||||
height: 18px;
|
||||
min-height: 16px;
|
||||
max-height: 18px;
|
||||
border-radius: 999px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user