| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- 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();
- })();
- """
- }
- }
|