1const startUrl = "https://x.com/";
2const homeUrl = "https://x.com/home";
3const analytics_page = "https://x.com/i/account_analytics/audience";
4const DEFAULT_SLEEP = 200;
5const dayFrom = new Date(parseInt("{{timestamp_from}}"));
6const dayTo = new Date(parseInt("{{timestamp_to}}"));
7
8//Click Sign-in by Google button to trigger google init script and prepare it for user clicking
9function clickGoogleAuthButtonIfPresent() {
10 const googleAuthButton = document.querySelector(
11 '[data-testid*="google_placeholder_button"]',
12 );
13 if (googleAuthButton !== null) {
14 googleAuthButton.click();
15 }
16}
17
18function instructToSelectRange() {
19 const instruction = `Please select dates from ${dayFrom.toISOString().substring(0, 10)} to ${dayTo.toISOString().substring(0, 10)}`;
20 if (typeof displayInstruction === "function") {
21 displayInstruction({
22 message: instruction,
23 });
24 } else {
25 alert(instruction);
26 }
27}
28
29async function selectDateRange(dayFrom, dayTo) {
30 const datePickerButton = findDatePicker();
31 if (datePickerButton === null) {
32 console.warn("No date picker");
33 return false;
34 }
35 datePickerButton.click();
36 await sleep(DEFAULT_SLEEP);
37 const buttonFrom = await findButtonByTimestamp(dayFrom);
38 if (buttonFrom === null) {
39 console.warn("From button not found");
40 return false;
41 }
42 buttonFrom.click();
43 await sleep(DEFAULT_SLEEP);
44 buttonFrom.click();
45
46 const now = new Date();
47 if (
48 dayTo.getUTCMonth() == now.getUTCMonth() &&
49 dayTo.getUTCDate() == now.getUTCDate()
50 ) {
51 return true;
52 }
53
54 const buttonTo = await findButtonByTimestamp(dayTo);
55 if (buttonTo === null) {
56 console.warn("To button not found");
57 return false;
58 }
59 buttonTo.click();
60 console.warn("All ok!");
61 return true;
62}
63
64async function selectDates() {
65 console.warn("Selecting dates");
66 if (!(await selectDateRange(dayFrom, dayTo))) {
67 console.warn("Selecting dates failed, showing instruction");
68 instructToSelectRange();
69 }
70}
71
72window.onload = function () {
73 setTimeout(clickGoogleAuthButtonIfPresent, 100);
74
75 console.warn(
76 "JavaScriptNavigator: selecting action for " + window.location.href,
77 );
78 switch (window.location.href) {
79 case startUrl:
80 setInterval(() => {
81 if (window.location.href === homeUrl) {
82 window.location.href = analytics_page;
83 }
84 }, 1000);
85 break;
86 case homeUrl:
87 window.location.href = analytics_page;
88 break;
89 case analytics_page:
90 setTimeout(selectDates, 3000);
91 break;
92 default:
93 break;
94 }
95};
96
97function findByXPath(contextNode, xpath) {
98 return document.evaluate(
99 xpath,
100 contextNode,
101 null,
102 XPathResult.FIRST_ORDERED_NODE_TYPE,
103 ).singleNodeValue;
104}
105
106function findButtonByDayOfMonth(day, index = 1) {
107 return findByXPath(
108 document.body,
109 `//*[@id='react-day-picker-${index}']/../../table/tbody//td/button[text()=${day}]`,
110 );
111}
112
113function clickPrev() {
114 document.querySelector("[name='previous-month']").click();
115}
116
117function clickNext() {
118 document.querySelector("[name='next-month']").click();
119}
120
121async function findButtonByTimestamp(timestamp) {
122 const monthString = monthAndYear(timestamp);
123 while (monthString.trim() !== findMonth()) {
124 if (compareMonths(monthString, findMonth()) < 0) {
125 clickPrev();
126 } else {
127 const secondCalendar = document.querySelector("#react-day-picker-2");
128 if (secondCalendar) {
129 const targetDate = new Date(timestamp);
130 const { month } = parseMonthYear(findMonth());
131 if ((month + 1) % 12 === targetDate.getUTCMonth()) {
132 return findButtonByDayOfMonth(dayOfMonth(timestamp), 2);
133 } else {
134 clickNext();
135 }
136 } else {
137 clickNext();
138 }
139 }
140 await sleep(DEFAULT_SLEEP);
141 }
142 const day = dayOfMonth(timestamp);
143 return findButtonByDayOfMonth(day);
144}
145
146function dayOfMonth(timestamp) {
147 return timestamp.getUTCDate().toString();
148}
149
150function monthAndYear(timestamp) {
151 return new Intl.DateTimeFormat("en-US", {
152 timeZone: "UTC",
153 year: "numeric",
154 month: "long",
155 }).format(timestamp);
156}
157function findDatePicker() {
158 return document.querySelector('[aria-haspopup="dialog"]');
159}
160
161function findMonth() {
162 let xpath = `//*[@id="react-day-picker-1"]`;
163 const node = findByXPath(document.body, xpath);
164 return node ? node.textContent.trim() : "";
165}
166
167function parseMonthYear(monthYear) {
168 const monthNames = [
169 "January",
170 "February",
171 "March",
172 "April",
173 "May",
174 "June",
175 "July",
176 "August",
177 "September",
178 "October",
179 "November",
180 "December",
181 ];
182 const parts = monthYear.trim().split(" ");
183 const month = parts[0];
184 const year = parseInt(parts[1]);
185 const monthIndex = monthNames.indexOf(month);
186 return { year, month: monthIndex };
187}
188
189function compareMonths(wanted, selected) {
190 const wanted_parsed = parseMonthYear(wanted);
191 const selected_parsed = parseMonthYear(selected);
192
193 if (wanted_parsed.year !== selected_parsed.year) {
194 return wanted_parsed.year - selected_parsed.year;
195 }
196
197 return wanted_parsed.month - selected_parsed.month;
198}
199
200function sleep(ms) {
201 return new Promise((resolve) => setTimeout(resolve, ms));
202}
203