Skip to content

Commit 2443d8f

Browse files
authored
fix: StorySeedling (#1857)
* Story seedling beginning * Font decoding * Bump version number * Remove spam * Remove story seedling ad * Live update nonce * add error for captcha * JSON instead of text compare * Fix proxy crashing on error * Fix captcha error * Fix updatedNonce check * Compression headers should only be forwarded by proxy's that keep the content compressed * Revert index.ts change
1 parent a416801 commit 2443d8f

2 files changed

Lines changed: 79 additions & 30 deletions

File tree

plugins/english/StorySeedling.ts

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { CheerioAPI, load } from 'cheerio';
22
import { Plugin } from '@/types/plugin';
3-
import { fetchApi } from '@libs/fetch';
3+
import { fetchApi, fetchText } from '@libs/fetch';
44
import { NovelStatus } from '@libs/novelStatus';
5-
import { defaultCover } from '@libs/defaultCover';
65

76
class StorySeedlingPlugin implements Plugin.PluginBase {
87
id = 'storyseedling';
98
name = 'StorySeedling';
109
icon = 'src/en/storyseedling/icon.png';
1110
site = 'https://storyseedling.com/';
12-
version = '1.0.3';
11+
version = '1.0.4';
12+
nonce: string | undefined;
1313

1414
async getCheerio(url: string, search: boolean): Promise<CheerioAPI> {
1515
const r = await fetchApi(url);
@@ -162,24 +162,66 @@ class StorySeedlingPlugin implements Plugin.PluginBase {
162162
return novel as Plugin.SourceNovel;
163163
}
164164

165-
async parseChapter(chapterPath: string): Promise<string> {
165+
async updateNonce(chapterPath: string) {
166166
const $ = await this.getCheerio(this.site + chapterPath, false);
167+
this.nonce = $('div.mb-4:has(h1.text-xl) > div')
168+
.attr('x-data')
169+
?.match(/loadChapter\('.+?', '(.+?)'\)/)[1];
170+
}
167171

168-
const xdata = $('div[ax-load][x-data]').attr('x-data');
169-
170-
const t = $('div.justify-center > div.mb-4');
171-
let chapterText = t.html() || '';
172-
173-
if (xdata) {
174-
chapterText =
175-
chapterText +
176-
"\n\n Error parsing chapter: Turnstile detected. Advise just reading in web view until there's a fix.";
177-
// const listXdata = xdata?.split("'");
178-
// const dataNovelId = listXdata[1];
179-
// const dataNovelN = listXdata[3];
172+
async parseChapter(chapterPath: string): Promise<string> {
173+
const updatedNonce = !this.nonce;
174+
if (!this.nonce) await this.updateNonce(chapterPath);
175+
const text = await fetchApi(this.site + chapterPath + '/content', {
176+
method: 'POST',
177+
headers: {
178+
'referrer': this.site + chapterPath + '/',
179+
'x-nonce': this.nonce,
180+
},
181+
body: JSON.stringify({ 'captcha_response': '' }),
182+
}).then(r => r.text());
183+
let textJson;
184+
try {
185+
textJson = JSON.parse(text);
186+
} catch (_) {
187+
//not json :fire: we have chapter
188+
}
189+
if (textJson && !textJson.success) {
190+
if (textJson.message === 'Invalid security.') {
191+
if (updatedNonce) {
192+
throw new Error(`Failed to find code!`);
193+
}
194+
this.nonce = '';
195+
return await this.parseChapter(chapterPath);
196+
}
197+
if (textJson.captcha) {
198+
throw new Error(
199+
`Failed to bypass turnstile captcha (read in webview until it stops ig)`,
200+
);
201+
}
180202
}
203+
let html = text
204+
.replace(/cls[a-f0-9]+/g, '')
205+
.split('')
206+
.map(char => {
207+
const code = char.charCodeAt(0);
208+
const offset = code > 12123 ? 12027 : 12033;
209+
const decoded = code - offset;
210+
return decoded >= 32 && decoded <= 126
211+
? String.fromCharCode(decoded)
212+
: char;
213+
})
214+
.join('');
215+
let $ = load(html);
216+
217+
$('span').text((_, txt) =>
218+
txt.toLowerCase().includes('storyseedling') ||
219+
txt.toLowerCase().includes('story seedling')
220+
? ''
221+
: txt,
222+
);
181223

182-
return chapterText;
224+
return $.html();
183225
}
184226

185227
async searchNovels(

proxy.ts

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,7 @@ const settings: ServerSetting = {
2121
'sec-fetch-dest',
2222
'pragma',
2323
],
24-
disAllowResponseHeaders: [
25-
'link',
26-
'set-cookie',
27-
'set-cookie2',
28-
'content-encoding',
29-
'content-length',
30-
],
24+
disAllowResponseHeaders: ['link', 'set-cookie', 'set-cookie2'],
3125
useUserAgent: true,
3226
};
3327

@@ -173,7 +167,11 @@ const proxyRequest: Connect.SimpleHandleFunction = (req, res) => {
173167
.then(([res2, text]) => {
174168
res.statusCode = res2.status;
175169
res2.headers.forEach((val, key) => {
176-
if (!settings.disAllowResponseHeaders.includes(key)) {
170+
if (
171+
!settings.disAllowResponseHeaders.includes(key) &&
172+
key !== 'content-encoding' &&
173+
key !== 'content-length'
174+
) {
177175
res.setHeader(key, val);
178176
}
179177
});
@@ -186,11 +184,20 @@ const proxyRequest: Connect.SimpleHandleFunction = (req, res) => {
186184
res.end();
187185
});
188186
} else if (settings.fetchMode === FetchMode.PROXY) {
189-
proxy.web(req, res, {
190-
target: _url.origin,
191-
selfHandleResponse: true,
192-
followRedirects: true,
193-
});
187+
proxy.web(
188+
req,
189+
res,
190+
{
191+
target: _url.origin,
192+
selfHandleResponse: true,
193+
followRedirects: true,
194+
},
195+
err => {
196+
console.error(err);
197+
res.statusCode = 500;
198+
res.end();
199+
},
200+
);
194201
}
195202
};
196203

0 commit comments

Comments
 (0)