import Foundation enum RedditSubmitPrefillScript { static func make(prefill: RedditSubmitPrefill) -> String { let payload: [String: Any] = [ "body": prefill.body, "pollOptions": prefill.pollOptions, "pollDurationDays": prefill.pollDurationDays, ] guard let jsonData = try? JSONSerialization.data(withJSONObject: payload), let jsonString = String(data: jsonData, encoding: .utf8) else { return "" } return """ (function() { const config = \(jsonString); const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const queryAllDeep = (selector, root = document) => { const results = []; const visit = (node) => { results.push(...Array.from(node.querySelectorAll(selector))); Array.from(node.querySelectorAll('*')).forEach((element) => { if (element.shadowRoot) { visit(element.shadowRoot); } }); }; visit(root); return results; }; const clickPollTab = () => { const candidates = queryAllDeep('button, [role="tab"], [role="radio"], label, a, div[role="button"]'); const pollControl = candidates.find((element) => { const text = (element.textContent || '').trim().toLowerCase(); const label = (element.getAttribute('aria-label') || '').trim().toLowerCase(); return text === 'poll' || label === 'poll' || label.includes('poll post'); }); if (!pollControl) { return false; } pollControl.click(); return true; }; const setFieldValue = (field, value) => { if (!field || value == null) { return; } if (field.tagName === 'TEXTAREA' || field.tagName === 'INPUT') { field.value = value; } else if (field.isContentEditable) { field.textContent = value; } else { return; } field.dispatchEvent(new Event('input', { bubbles: true })); field.dispatchEvent(new Event('change', { bubbles: true })); }; const fillBody = () => { if (!config.body) { return; } const fields = queryAllDeep('textarea, div[contenteditable="true"], faceplate-textarea-input textarea, [data-testid*="body"] textarea'); const bodyField = fields.find((field) => { const placeholder = (field.getAttribute('placeholder') || '').toLowerCase(); const aria = (field.getAttribute('aria-label') || '').toLowerCase(); return placeholder.includes('body') || placeholder.includes('text') || aria.includes('body') || aria.includes('optional'); }) || fields[0]; if (bodyField) { setFieldValue(bodyField, config.body); } }; const pollOptionFields = () => { return queryAllDeep('input[type="text"], textarea').filter((field) => { const placeholder = (field.getAttribute('placeholder') || '').toLowerCase(); const aria = (field.getAttribute('aria-label') || '').toLowerCase(); const name = (field.getAttribute('name') || '').toLowerCase(); const testId = (field.getAttribute('data-testid') || '').toLowerCase(); return placeholder.includes('option') || aria.includes('option') || name.includes('poll') || testId.includes('poll-option'); }); }; const clickAddOption = () => { const buttons = queryAllDeep('button, [role="button"]'); const addButton = buttons.find((button) => /add.*option/i.test((button.textContent || '').trim())); if (addButton) { addButton.click(); return true; } return false; }; const fillPollOptions = async () => { for (let index = 0; index < config.pollOptions.length; index += 1) { let fields = pollOptionFields(); let attempts = 0; while (fields.length <= index && attempts < 4) { if (!clickAddOption()) { break; } await sleep(200); fields = pollOptionFields(); attempts += 1; } if (fields[index]) { setFieldValue(fields[index], config.pollOptions[index]); } } }; const selectDuration = () => { const label = config.pollDurationDays === 1 ? '1 day' : `${config.pollDurationDays} days`; const controls = queryAllDeep('button, [role="radio"], label, option, span, div[role="button"]'); const match = controls.find((control) => { const text = (control.textContent || '').trim().toLowerCase(); return text === label || text.startsWith(`${config.pollDurationDays} day`); }); if (match) { match.click(); } }; const applyPollPrefill = async () => { if (!clickPollTab()) { return false; } await sleep(500); fillBody(); await fillPollOptions(); selectDuration(); return true; }; return applyPollPrefill(); })(); """ } }