-
-
Notifications
You must be signed in to change notification settings - Fork 86
Expand file tree
/
Copy pathschedulePosts.js
More file actions
159 lines (136 loc) · 5.12 KB
/
Copy pathschedulePosts.js
File metadata and controls
159 lines (136 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright (c) 2024-2026 nich (@nichxbt). Licensed under the Apache License, Version 2.0.
// scripts/schedulePosts.js
// Browser console script for scheduling posts using X/Twitter's native scheduler
// Paste in DevTools console on x.com
// by nichxbt
(() => {
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
// =============================================
// CONFIGURATION
// =============================================
const CONFIG = {
posts: [
{ text: 'Scheduled post!', date: '2026-03-01', time: '09:00' },
// { text: 'Another scheduled post', date: '2026-03-02', time: '14:30' },
],
dryRun: true, // SET FALSE TO ACTUALLY SCHEDULE
delay: 3000, // ms between scheduling each post
};
// =============================================
const results = { scheduled: [], failed: [] };
const typeIntoInput = async (input, text) => {
input.focus();
await sleep(100);
document.execCommand('insertText', false, text);
await sleep(200);
};
const scheduleOnePost = async (post, index, total) => {
const { text, date, time } = post;
console.log(`\n📝 [${index + 1}/${total}] Scheduling: "${text.substring(0, 50)}${text.length > 50 ? '...' : ''}"`);
console.log(` 📅 ${date} at ${time}`);
if (text.length > 280) {
console.error(` ❌ Text exceeds 280 chars (${text.length})`);
results.failed.push(post);
return;
}
try {
// Open compose dialog
const composeBtn = document.querySelector('[data-testid="SideNav_NewTweet_Button"]');
if (composeBtn) {
composeBtn.click();
await sleep(1500);
}
// Type the post text
const textInput = document.querySelector('[data-testid="tweetTextarea_0"]');
if (!textInput) {
console.error(' ❌ Compose box not found');
results.failed.push(post);
return;
}
await typeIntoInput(textInput, text);
await sleep(500);
// Click the schedule option
const schedBtn = document.querySelector('[data-testid="scheduleOption"]');
if (!schedBtn) {
console.error(' ❌ Schedule button not found (may require X Premium)');
results.failed.push(post);
return;
}
schedBtn.click();
await sleep(1000);
// Set date
const dateInput = document.querySelector('[data-testid="scheduledDateField"]');
if (dateInput) {
dateInput.click();
await sleep(300);
// Clear and type new date
dateInput.focus();
document.execCommand('selectAll', false, null);
document.execCommand('insertText', false, date);
await sleep(500);
} else {
console.warn(' ⚠️ Date picker not found — set date manually');
}
// Set time
const timeInput = document.querySelector('[data-testid="scheduledTimeField"]');
if (timeInput) {
timeInput.click();
await sleep(300);
timeInput.focus();
document.execCommand('selectAll', false, null);
document.execCommand('insertText', false, time);
await sleep(500);
} else {
console.warn(' ⚠️ Time picker not found — set time manually');
}
// Confirm the schedule
const confirmBtn = document.querySelector('[data-testid="scheduledConfirmationPrimaryAction"]');
if (confirmBtn) {
confirmBtn.click();
await sleep(1000);
results.scheduled.push(post);
console.log(` ✅ Scheduled for ${date} at ${time}`);
} else {
console.warn(' ⚠️ Confirm button not found — confirm manually');
results.failed.push(post);
}
// Wait before closing / next post
await sleep(1000);
} catch (e) {
console.error(` ❌ Error: ${e.message}`);
results.failed.push(post);
}
};
const run = async () => {
console.log('📅 SCHEDULE POSTS — XActions by nichxbt');
if (CONFIG.posts.length === 0) {
console.log('❌ No posts to schedule! Edit CONFIG.posts array.');
console.log('\nExample:');
console.log(' posts: [');
console.log(' { text: "Hello world!", date: "2026-03-01", time: "09:00" },');
console.log(' { text: "Another post!", date: "2026-03-02", time: "14:30" },');
console.log(' ]');
return;
}
// Sort by date+time
const sorted = [...CONFIG.posts].sort((a, b) =>
`${a.date}T${a.time}`.localeCompare(`${b.date}T${b.time}`)
);
console.log(`📋 Posts to schedule: ${sorted.length}`);
sorted.forEach((p, i) => {
console.log(` ${i + 1}. ${p.date} ${p.time} — "${p.text.substring(0, 50)}${p.text.length > 50 ? '...' : ''}"`);
});
if (CONFIG.dryRun) {
console.log('\n⚠️ DRY RUN MODE — Set CONFIG.dryRun = false to actually schedule');
return;
}
for (let i = 0; i < sorted.length; i++) {
await scheduleOnePost(sorted[i], i, sorted.length);
if (i < sorted.length - 1) await sleep(CONFIG.delay);
}
console.log('\n📊 RESULTS:');
console.log(` ✅ Scheduled: ${results.scheduled.length}`);
console.log(` ❌ Failed: ${results.failed.length}`);
};
run();
})();