Skip to content

Commit 5c6aced

Browse files
committed
[chore] overhaul publishing step
- Converted to Typescript - More error handling - Made reusable workflow - Added a manual trigger
1 parent 8ad7819 commit 5c6aced

File tree

7 files changed

+722
-103
lines changed

7 files changed

+722
-103
lines changed

.github/scripts/publish_greasyfork.sh

Lines changed: 0 additions & 86 deletions
This file was deleted.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import { readFileSync } from 'fs';
2+
3+
async function main() {
4+
const greasyfork_user_email = process.env.GREASYFORK_USER_EMAIL;
5+
if (!greasyfork_user_email) {
6+
throw new Error('Environment variable "GREASYFORK_USER_EMAIL" must be set');
7+
}
8+
9+
const greasyfork_user_pass = process.env.GREASYFORK_USER_PASS;
10+
if (!greasyfork_user_pass) {
11+
throw new Error('Environment variable "GREASYFORK_USER_PASS" must be set');
12+
}
13+
14+
const greasyfork_script_id = process.env.GREASYFORK_SCRIPT_ID;
15+
if (!greasyfork_script_id) {
16+
throw new Error('Environment variable "GREASYFORK_SCRIPT_ID" must be set');
17+
}
18+
19+
const greasyfork_script_type = (() => {
20+
switch (process.env.GREASYFORK_SCRIPT_TYPE) {
21+
case 'public': return '1';
22+
case 'unlisted': return '2';
23+
case 'library': return '3';
24+
}
25+
})();
26+
if (!greasyfork_script_type) {
27+
throw new Error('Environment variable "GREASYFORK_SCRIPT_TYPE" must be set');
28+
}
29+
30+
const script_path_to_upload = process.env.SCRIPT_PATH_TO_UPLOAD;
31+
if (!script_path_to_upload) {
32+
throw new Error('Environment variable "SCRIPT_PATH_TO_UPLOAD" must be set');
33+
}
34+
35+
await publish_to_greasyfork(greasyfork_user_email, greasyfork_user_pass, greasyfork_script_id, greasyfork_script_type, script_path_to_upload);
36+
}
37+
38+
function extract_authenticity_token(text: string): string | null {
39+
const match = text.match(/name="csrf-token" content="([^"]+)"/);
40+
return match ? match[1] : null;
41+
}
42+
43+
class Cookies {
44+
static cookies: string;
45+
46+
static set(response: Response) {
47+
this.cookies = response.headers.getSetCookie().join('; ');
48+
}
49+
50+
static get() {
51+
return this.cookies;
52+
}
53+
}
54+
55+
async function publish_to_greasyfork(user_email: string, user_pass: string, script_id: string, script_type: string, file_path_to_upload: string) {
56+
const BASE_URL = 'https://greasyfork.org';
57+
58+
// "/en/search" appears to be the lightest page
59+
const LIGHTEST_PAGE_URL= `${BASE_URL}/en/search`;
60+
61+
// Get initial page to retrieve the initial tokens
62+
const initial_response = await fetch(LIGHTEST_PAGE_URL);
63+
64+
const initial_response_body = await initial_response.text();
65+
66+
Cookies.set(initial_response);
67+
let authenticity_token = extract_authenticity_token(initial_response_body);
68+
69+
if (!authenticity_token) {
70+
throw new Error('Could not retrieve initial authenticity token');
71+
}
72+
73+
const login_request_url= `${BASE_URL}/en/users/sign_in`;
74+
75+
const login_request_options: RequestInit = {
76+
method: 'POST',
77+
headers: { 'Cookie': Cookies.get() },
78+
body: new URLSearchParams({
79+
'authenticity_token': authenticity_token,
80+
'user[email]': user_email,
81+
'user[password]': user_pass,
82+
'user[remember_me]': '0',
83+
'commit': 'Log in',
84+
}),
85+
redirect: 'manual',
86+
};
87+
88+
// Log in to retrieve the final login tokens
89+
const login_response = await fetch(login_request_url, login_request_options);
90+
91+
Cookies.set(login_response);
92+
93+
if (login_response.ok) {
94+
const login_response_body = await login_response.text();
95+
if (login_response_body.includes('class="alert">Invalid')) {
96+
console.log('\x1b[1;31mFailed: Sign in\x1b[0m - Incorrect Email or password');
97+
} else {
98+
console.log(`\x1b[1;31mFailed: Sign in\x1b[0m - Unknown reason`);
99+
}
100+
process.exitCode = 1;
101+
return;
102+
} else if (login_response.status >= 300 && login_response.status < 400) {
103+
const redirect_response = await fetch(LIGHTEST_PAGE_URL, {
104+
headers: { 'Cookie': Cookies.get() },
105+
});
106+
107+
const redirect_response_body = await redirect_response.text();
108+
109+
Cookies.set(redirect_response);
110+
authenticity_token = extract_authenticity_token(redirect_response_body);
111+
} else {
112+
console.log(`\x1b[1;31mFailed: Sign in\x1b[0m - ${login_response.status} ${login_response.statusText}`);
113+
process.exitCode = 1;
114+
return;
115+
}
116+
117+
const script_file_blob = new Blob([readFileSync(file_path_to_upload)]);
118+
119+
const update_body = new FormData();
120+
update_body.set('authenticity_token', authenticity_token);
121+
update_body.set('script_version[code]', '');
122+
update_body.set('code_upload', script_file_blob);
123+
update_body.set('script_version[additional_info][0][attribute_default]', 'true');
124+
update_body.set('script_version[additional_info][0][value_markup]', 'html');
125+
update_body.set('script_version[additional_info][0][attribute_value]', '');
126+
update_body.set('script_version[attachments][]', '');
127+
update_body.set('script_version[changelog_markup]', 'html');
128+
update_body.set('script_version[changelog]', '');
129+
update_body.set('script[script_type]', script_type);
130+
update_body.set('script[adult_content_self_report]', '0');
131+
update_body.set('commit', 'Post new version');
132+
133+
const upload_request_url= `${BASE_URL}/en/scripts/${script_id}/versions`;
134+
135+
const upload_request_options: RequestInit = {
136+
method: 'POST',
137+
headers: { 'Cookie': Cookies.get() },
138+
body: update_body,
139+
};
140+
141+
const upload_response = await fetch(upload_request_url, upload_request_options);
142+
143+
const upload_response_body = await upload_response.text();
144+
145+
// Check if the upload was successful
146+
if (!upload_response_body.includes('id="install-area"')) {
147+
console.log(`\x1b[38;2;103;103;103m${upload_response_body}\x1b[0m`);
148+
if (upload_response_body.includes('validation-errors')) {
149+
console.log('\x1b[1;31mFailed: Publish to GreasyFork\x1b[0m - Validation errors were reported');
150+
} else {
151+
console.log('\x1b[1;31mFailed: Publish to GreasyFork\x1b[0m - Unknown reason');
152+
}
153+
154+
process.exitCode = 1;
155+
return;
156+
}
157+
}
158+
159+
main();

0 commit comments

Comments
 (0)