backend-manager 5.7.6 → 5.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/CLAUDE.md +1 -0
- package/PROGRESS.md +29 -5
- package/docs/ghostii.md +127 -0
- package/package.json +3 -1
- package/src/manager/events/cron/daily/ghostii-auto-publisher.js +287 -28
- package/src/manager/libraries/content/feed-parser.js +198 -0
- package/src/manager/libraries/content/ghostii.js +38 -17
- package/src/manager/libraries/email/data/disposable-domains.json +78 -1
- package/src/manager/libraries/email/generators/lib/structure.js +2 -2
- package/templates/backend-manager-config.json +25 -3
- package/test/helpers/content/feed-parser.js +528 -0
- package/test/helpers/content/ghostii-auto-publisher.js +342 -0
- package/test/helpers/content/ghostii-feed-integration.js +399 -0
- package/test/helpers/content/ghostii-write-article.js +243 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feed Parser — RSS 2.0, Atom 1.0, and JSON Feed parser + article content extractor.
|
|
3
|
+
*
|
|
4
|
+
* Parses standard syndication feeds into a normalized item array and extracts
|
|
5
|
+
* readable article text from URLs. Used by the ghostii-auto-publisher cron to
|
|
6
|
+
* select individual articles from third-party feeds for AI-assisted content
|
|
7
|
+
* generation.
|
|
8
|
+
*
|
|
9
|
+
* @module feed-parser
|
|
10
|
+
*/
|
|
11
|
+
const { XMLParser } = require('fast-xml-parser');
|
|
12
|
+
const cheerio = require('cheerio');
|
|
13
|
+
const fetch = require('wonderful-fetch');
|
|
14
|
+
|
|
15
|
+
const MAX_CONTENT_LENGTH = 1024 * 14;
|
|
16
|
+
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parse an RSS 2.0, Atom 1.0, or JSON Feed string into a normalized item array.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} text - Raw feed text (XML or JSON).
|
|
22
|
+
* @returns {{ items: FeedItem[] }} Normalized feed items. Empty array on unparseable input.
|
|
23
|
+
*
|
|
24
|
+
* @typedef {object} FeedItem
|
|
25
|
+
* @property {string} id - Stable unique identifier (guid / atom:id / url).
|
|
26
|
+
* @property {string} title - Item title.
|
|
27
|
+
* @property {string} url - Link to the full article.
|
|
28
|
+
* @property {string} summary - Short excerpt (max 500 chars, HTML stripped).
|
|
29
|
+
* @property {string} content - Full inline content if available (max 14KB, HTML stripped).
|
|
30
|
+
* @property {string} publishedAt - Publication date string (ISO or RFC-822).
|
|
31
|
+
*/
|
|
32
|
+
function parseFeed(text) {
|
|
33
|
+
if (!text || typeof text !== 'string') {
|
|
34
|
+
return { items: [] };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const clean = text.replace(/^/, '').trim();
|
|
38
|
+
if (!clean) {
|
|
39
|
+
return { items: [] };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// JSON Feed
|
|
43
|
+
try {
|
|
44
|
+
const json = JSON.parse(clean);
|
|
45
|
+
if (json && Array.isArray(json.items)) {
|
|
46
|
+
return { items: normalizeJsonFeed(json.items) };
|
|
47
|
+
}
|
|
48
|
+
return { items: [] };
|
|
49
|
+
} catch (e) {
|
|
50
|
+
// Not JSON — try XML
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// XML (RSS 2.0 / Atom 1.0)
|
|
54
|
+
try {
|
|
55
|
+
const parser = new XMLParser({
|
|
56
|
+
ignoreAttributes: false,
|
|
57
|
+
attributeNamePrefix: '@_',
|
|
58
|
+
isArray: (name) => ['item', 'entry'].includes(name),
|
|
59
|
+
});
|
|
60
|
+
const doc = parser.parse(clean);
|
|
61
|
+
|
|
62
|
+
if (doc.rss?.channel) {
|
|
63
|
+
return { items: normalizeRss(doc.rss.channel) };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (doc.feed) {
|
|
67
|
+
return { items: normalizeAtom(doc.feed) };
|
|
68
|
+
}
|
|
69
|
+
} catch (e) {
|
|
70
|
+
// Unparseable XML
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { items: [] };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* JSON Feed (jsonfeed.org) + rss.app format normalizer.
|
|
78
|
+
*/
|
|
79
|
+
function normalizeJsonFeed(items) {
|
|
80
|
+
return (items || []).map((item) => ({
|
|
81
|
+
id: String(item.id || item.url || ''),
|
|
82
|
+
title: String(item.title || ''),
|
|
83
|
+
url: String(item.url || item.link || ''),
|
|
84
|
+
summary: String(item.summary || item.content_text || '').slice(0, 500),
|
|
85
|
+
content: String(item.content_html || item.content_text || '').slice(0, MAX_CONTENT_LENGTH),
|
|
86
|
+
publishedAt: String(item.date_published || item.date_modified || ''),
|
|
87
|
+
})).filter((item) => item.id || item.url);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* RSS 2.0 normalizer — handles <item> with guid, title, link, description, content:encoded, pubDate.
|
|
92
|
+
*/
|
|
93
|
+
function normalizeRss(channel) {
|
|
94
|
+
const items = channel.item || [];
|
|
95
|
+
|
|
96
|
+
return items.map((item) => {
|
|
97
|
+
const link = Array.isArray(item.link) ? item.link[0] : (item.link || '');
|
|
98
|
+
const guid = item.guid?.['#text'] || item.guid || '';
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
id: String(guid || link),
|
|
102
|
+
title: String(item.title || ''),
|
|
103
|
+
url: String(link),
|
|
104
|
+
summary: stripHtml(String(item.description || '')).slice(0, 500),
|
|
105
|
+
content: stripHtml(String(item['content:encoded'] || item.description || '')).slice(0, MAX_CONTENT_LENGTH),
|
|
106
|
+
publishedAt: String(item.pubDate || ''),
|
|
107
|
+
};
|
|
108
|
+
}).filter((item) => item.id || item.url);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Atom 1.0 normalizer — handles <entry> with id, title, link href, summary, content, published.
|
|
113
|
+
*/
|
|
114
|
+
function normalizeAtom(feed) {
|
|
115
|
+
const entries = feed.entry || [];
|
|
116
|
+
|
|
117
|
+
return entries.map((entry) => {
|
|
118
|
+
const links = Array.isArray(entry.link) ? entry.link : [entry.link].filter(Boolean);
|
|
119
|
+
const altLink = links.find((l) => l?.['@_rel'] === 'alternate' || !l?.['@_rel']);
|
|
120
|
+
const url = altLink?.['@_href'] || links[0]?.['@_href'] || '';
|
|
121
|
+
|
|
122
|
+
const title = entry.title?.['#text'] || entry.title || '';
|
|
123
|
+
const summary = entry.summary?.['#text'] || entry.summary || '';
|
|
124
|
+
const content = entry.content?.['#text'] || entry.content || '';
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
id: String(entry.id || url),
|
|
128
|
+
title: String(title),
|
|
129
|
+
url: String(url),
|
|
130
|
+
summary: stripHtml(String(summary)).slice(0, 500),
|
|
131
|
+
content: stripHtml(String(content)).slice(0, MAX_CONTENT_LENGTH),
|
|
132
|
+
publishedAt: String(entry.published || entry.updated || ''),
|
|
133
|
+
};
|
|
134
|
+
}).filter((item) => item.id || item.url);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Fetch a URL and extract the readable article text from its HTML via Cheerio.
|
|
139
|
+
*
|
|
140
|
+
* Priority: `<article>` → `<main>` → `<body>`. Removes non-content elements
|
|
141
|
+
* (scripts, styles, nav, ads, forms, widgets) before extracting text.
|
|
142
|
+
* Truncates to 14KB.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} url - The article URL to fetch.
|
|
145
|
+
* @returns {Promise<string>} Extracted plain text (empty string on failure).
|
|
146
|
+
*/
|
|
147
|
+
async function extractArticleContent(url) {
|
|
148
|
+
if (!url) {
|
|
149
|
+
return '';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const res = await fetch(url, {
|
|
153
|
+
timeout: 30000,
|
|
154
|
+
tries: 2,
|
|
155
|
+
response: 'raw',
|
|
156
|
+
headers: { 'User-Agent': USER_AGENT },
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const html = await res.text();
|
|
160
|
+
if (!html) {
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return extractTextFromHtml(html).slice(0, MAX_CONTENT_LENGTH);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Extract readable text from an HTML string using Cheerio.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} html - Raw HTML string.
|
|
171
|
+
* @returns {string} Clean plain text.
|
|
172
|
+
*/
|
|
173
|
+
function extractTextFromHtml(html) {
|
|
174
|
+
const $ = cheerio.load(html);
|
|
175
|
+
|
|
176
|
+
// Remove non-content elements
|
|
177
|
+
$('script, style, noscript, nav, footer, header, aside, form, button, svg, iframe, figure, figcaption, [role="navigation"], [role="banner"], [role="complementary"], [aria-hidden="true"], .ad, .ads, .advertisement, .social-share, .related-articles, .sidebar').remove();
|
|
178
|
+
|
|
179
|
+
// Extract from the most specific content container
|
|
180
|
+
const $content = $('article').length ? $('article')
|
|
181
|
+
: $('main').length ? $('main')
|
|
182
|
+
: $('body');
|
|
183
|
+
|
|
184
|
+
const text = $content.text().replace(/\s+/g, ' ').trim();
|
|
185
|
+
return text;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Strip all HTML tags from a string and normalize whitespace.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} html - HTML string to clean.
|
|
192
|
+
* @returns {string} Plain text.
|
|
193
|
+
*/
|
|
194
|
+
function stripHtml(html) {
|
|
195
|
+
return html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
module.exports = { parseFeed, extractArticleContent, extractTextFromHtml, stripHtml };
|
|
@@ -30,33 +30,54 @@ const powertools = require('node-powertools');
|
|
|
30
30
|
* @param {object} args.brand - Public brand config ({ brand: { url, ... }, github: { ... } })
|
|
31
31
|
* @param {string} args.description - The article brief / prompt content
|
|
32
32
|
* @param {string[]} [args.links] - Optional links to inject into the article body
|
|
33
|
+
* @param {string} [args.sourceContent] - Reference article text for source-based generation
|
|
34
|
+
* @param {object} [args.overrides] - Per-entry overrides for Ghostii API params
|
|
33
35
|
* @returns {Promise<object>} Ghostii's generic article response:
|
|
34
36
|
* { title, description, body, json, headerImageUrl, images, categories, keywords, links, outline }
|
|
35
37
|
* where `json` is the structured block array ([{ name, content }]) that
|
|
36
38
|
* blocksToPost() consumes to build BEM's post shape.
|
|
37
39
|
*/
|
|
38
|
-
function writeArticle({ brand, description, links }) {
|
|
39
|
-
|
|
40
|
+
async function writeArticle({ brand, description, links, sourceContent, overrides }) {
|
|
41
|
+
const o = overrides || {};
|
|
42
|
+
|
|
43
|
+
const body = {
|
|
44
|
+
backendManagerKey: process.env.BACKEND_MANAGER_KEY,
|
|
45
|
+
keywords: o.keywords || [''],
|
|
46
|
+
description: description,
|
|
47
|
+
insertLinks: o.insertLinks ?? true,
|
|
48
|
+
research: o.research ?? true,
|
|
49
|
+
insertImages: o.insertImages ?? true,
|
|
50
|
+
length: o.length || 'long',
|
|
51
|
+
maxLinks: o.maxLinks || 6,
|
|
52
|
+
headerImageUrl: o.headerImageUrl || 'unsplash',
|
|
53
|
+
url: brand.brand.url,
|
|
54
|
+
sectionQuantity: o.sectionQuantity || powertools.random(3, 6, { mode: 'gaussian' }),
|
|
55
|
+
feedUrl: o.feedUrl || `${brand.brand.url}/feeds/posts.json`,
|
|
56
|
+
links: links || [],
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
if (sourceContent) {
|
|
60
|
+
body.sourceContent = sourceContent;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const start = Date.now();
|
|
64
|
+
|
|
65
|
+
// Original URL: https://api.ghostii.ai/write/article
|
|
66
|
+
// Firebase Hosting rewrites have a hard 60s proxy timeout that causes 502s
|
|
67
|
+
// on slow AI pipelines. The raw Cloud Functions URL uses the function's own
|
|
68
|
+
// 5-minute timeout instead.
|
|
69
|
+
const result = await fetch('https://us-central1-ghostii.cloudfunctions.net/writeGlobal/write/article', {
|
|
40
70
|
method: 'post',
|
|
41
71
|
timeout: 180000,
|
|
42
72
|
tries: 1,
|
|
43
73
|
response: 'json',
|
|
44
|
-
body:
|
|
45
|
-
backendManagerKey: process.env.BACKEND_MANAGER_KEY,
|
|
46
|
-
keywords: [''],
|
|
47
|
-
description: description,
|
|
48
|
-
insertLinks: true,
|
|
49
|
-
research: true,
|
|
50
|
-
insertImages: true,
|
|
51
|
-
length: 'long',
|
|
52
|
-
maxLinks: 6,
|
|
53
|
-
headerImageUrl: 'unsplash',
|
|
54
|
-
url: brand.brand.url,
|
|
55
|
-
sectionQuantity: powertools.random(3, 6, { mode: 'gaussian' }),
|
|
56
|
-
feedUrl: `${brand.brand.url}/feeds/posts.json`,
|
|
57
|
-
links: links || [],
|
|
58
|
-
},
|
|
74
|
+
body: body,
|
|
59
75
|
});
|
|
76
|
+
|
|
77
|
+
const duration = Date.now() - start;
|
|
78
|
+
console.log(`[ghostii] writeArticle() completed in ${duration}ms (${(duration / 1000).toFixed(1)}s)`);
|
|
79
|
+
|
|
80
|
+
return result;
|
|
60
81
|
}
|
|
61
82
|
|
|
62
83
|
/**
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
[
|
|
2
2
|
"0-mail.com",
|
|
3
3
|
"0-mailer.dynv6.net",
|
|
4
|
+
"000-webmail.myhome-server.de",
|
|
4
5
|
"000000000000000.theworkpc.com",
|
|
5
6
|
"000000000000000000.dynv6.net",
|
|
6
7
|
"000webmail.ddnss.de",
|
|
8
|
+
"000webmail.ddnss.org",
|
|
9
|
+
"000webmail.dyn-ip24.de",
|
|
10
|
+
"000webmail.dyn.ddnss.de",
|
|
11
|
+
"000webmail.dyn.home-webserver.de",
|
|
12
|
+
"000webmail.dyndns.ddnss.de",
|
|
13
|
+
"000webmail.dyndns1.de",
|
|
14
|
+
"000webmail.home-webserver.de",
|
|
7
15
|
"01022.hk",
|
|
8
16
|
"01130.hk",
|
|
9
17
|
"027168.com",
|
|
@@ -417,6 +425,7 @@
|
|
|
417
425
|
"aeonpsi.com",
|
|
418
426
|
"aerionx25k.io.vn",
|
|
419
427
|
"aeronyx.cfd",
|
|
428
|
+
"aesel.me",
|
|
420
429
|
"aetheron.cfd",
|
|
421
430
|
"afarek.com",
|
|
422
431
|
"afeeyah.store",
|
|
@@ -686,6 +695,7 @@
|
|
|
686
695
|
"asia-hq.jo3.org",
|
|
687
696
|
"asimarif.com",
|
|
688
697
|
"ask-mail.com",
|
|
698
|
+
"asleepity.com",
|
|
689
699
|
"asorent.com",
|
|
690
700
|
"aspireviastudios.org",
|
|
691
701
|
"ass.pp.ua",
|
|
@@ -922,6 +932,7 @@
|
|
|
922
932
|
"beddly.com",
|
|
923
933
|
"beefmilk.com",
|
|
924
934
|
"beelsil.com",
|
|
935
|
+
"beganment.com",
|
|
925
936
|
"begemail.com",
|
|
926
937
|
"behatifoundation.org",
|
|
927
938
|
"bekasi.me",
|
|
@@ -933,6 +944,7 @@
|
|
|
933
944
|
"benipaula.org",
|
|
934
945
|
"benphim.com",
|
|
935
946
|
"benphim.net",
|
|
947
|
+
"benshelf.com",
|
|
936
948
|
"bepureme.com",
|
|
937
949
|
"beribase.ru",
|
|
938
950
|
"beribaza.ru",
|
|
@@ -992,6 +1004,7 @@
|
|
|
992
1004
|
"biyug.com",
|
|
993
1005
|
"biz.st",
|
|
994
1006
|
"bizisstance.com",
|
|
1007
|
+
"bjorwi.rest",
|
|
995
1008
|
"blackgoldagency.ru",
|
|
996
1009
|
"blackmarket.to",
|
|
997
1010
|
"bladesmail.net",
|
|
@@ -1048,6 +1061,7 @@
|
|
|
1048
1061
|
"botgetlink.com",
|
|
1049
1062
|
"botgetlink.net",
|
|
1050
1063
|
"botnet.my.id",
|
|
1064
|
+
"boukshilf.com",
|
|
1051
1065
|
"boun.cr",
|
|
1052
1066
|
"bouncr.com",
|
|
1053
1067
|
"box-mail.ru",
|
|
@@ -1072,6 +1086,7 @@
|
|
|
1072
1086
|
"brandallday.net",
|
|
1073
1087
|
"brashbeat.online",
|
|
1074
1088
|
"brasx.org",
|
|
1089
|
+
"breaite.com",
|
|
1075
1090
|
"breakthru.com",
|
|
1076
1091
|
"brefmail.com",
|
|
1077
1092
|
"brennendesreich.de",
|
|
@@ -1083,6 +1098,7 @@
|
|
|
1083
1098
|
"brightfuturescharity.org.uk",
|
|
1084
1099
|
"bristolstatehousefoundation.org",
|
|
1085
1100
|
"broadbandninja.com",
|
|
1101
|
+
"brokenion.com",
|
|
1086
1102
|
"brownjasen.ccwu.cc",
|
|
1087
1103
|
"bskyx.fun",
|
|
1088
1104
|
"bsnow.net",
|
|
@@ -1188,6 +1204,7 @@
|
|
|
1188
1204
|
"capcutpro.dev",
|
|
1189
1205
|
"capitalistdilemma.com",
|
|
1190
1206
|
"captus.cyou",
|
|
1207
|
+
"caqcut.top",
|
|
1191
1208
|
"car101.pro",
|
|
1192
1209
|
"carbtc.net",
|
|
1193
1210
|
"carpin.org",
|
|
@@ -1279,6 +1296,7 @@
|
|
|
1279
1296
|
"chibakenma.ml",
|
|
1280
1297
|
"chickenkiller.com",
|
|
1281
1298
|
"chielo.com",
|
|
1299
|
+
"childrenth.com",
|
|
1282
1300
|
"childsavetrust.org",
|
|
1283
1301
|
"chilkat.com",
|
|
1284
1302
|
"chilloliandfii.space",
|
|
@@ -1441,6 +1459,7 @@
|
|
|
1441
1459
|
"cotigz.com",
|
|
1442
1460
|
"courriel.fr.nf",
|
|
1443
1461
|
"courrieltemporaire.com",
|
|
1462
|
+
"cousinment.com",
|
|
1444
1463
|
"coza.ro",
|
|
1445
1464
|
"cozilla.pw",
|
|
1446
1465
|
"cpc.cx",
|
|
@@ -1865,6 +1884,7 @@
|
|
|
1865
1884
|
"durandinterstellar.com",
|
|
1866
1885
|
"duskmail.com",
|
|
1867
1886
|
"dusrui.com",
|
|
1887
|
+
"dustinry.com",
|
|
1868
1888
|
"duya25446.top",
|
|
1869
1889
|
"dv2.host",
|
|
1870
1890
|
"dvdpit.com",
|
|
@@ -2119,6 +2139,7 @@
|
|
|
2119
2139
|
"epurcf.org",
|
|
2120
2140
|
"eqiluxspam.ga",
|
|
2121
2141
|
"equivara.cfd",
|
|
2142
|
+
"erahods.com",
|
|
2122
2143
|
"ereplyzy.com",
|
|
2123
2144
|
"ericjohnson.ml",
|
|
2124
2145
|
"eripo.net",
|
|
@@ -2130,6 +2151,7 @@
|
|
|
2130
2151
|
"escapehatchapp.com",
|
|
2131
2152
|
"esemay.com",
|
|
2132
2153
|
"esgeneri.com",
|
|
2154
|
+
"eshimod.com",
|
|
2133
2155
|
"esiix.com",
|
|
2134
2156
|
"esprity.com",
|
|
2135
2157
|
"estate-invest.fr",
|
|
@@ -2174,6 +2196,7 @@
|
|
|
2174
2196
|
"exdonuts.com",
|
|
2175
2197
|
"exelica.com",
|
|
2176
2198
|
"exespay.com",
|
|
2199
|
+
"exhaycle.com",
|
|
2177
2200
|
"exile.my.id",
|
|
2178
2201
|
"existiert.net",
|
|
2179
2202
|
"exitbit.com",
|
|
@@ -2182,11 +2205,13 @@
|
|
|
2182
2205
|
"exmab.com",
|
|
2183
2206
|
"expiredtoaster.org",
|
|
2184
2207
|
"explodemail.com",
|
|
2208
|
+
"expltain.com",
|
|
2185
2209
|
"express.net.ua",
|
|
2186
2210
|
"extracurricularsociety.com",
|
|
2187
2211
|
"extraku.net",
|
|
2188
2212
|
"extraku.shop",
|
|
2189
2213
|
"extremail.ru",
|
|
2214
|
+
"extrset.com",
|
|
2190
2215
|
"exweme.com",
|
|
2191
2216
|
"ey-hungry.top",
|
|
2192
2217
|
"eycfhb.com",
|
|
@@ -2352,6 +2377,7 @@
|
|
|
2352
2377
|
"fkainc.com",
|
|
2353
2378
|
"fknm8.mobi",
|
|
2354
2379
|
"flaimenet.ir",
|
|
2380
|
+
"flavourity.com",
|
|
2355
2381
|
"flayeraaron.eu.cc",
|
|
2356
2382
|
"flayerhu.eu.cc",
|
|
2357
2383
|
"fleckens.hu",
|
|
@@ -3207,6 +3233,7 @@
|
|
|
3207
3233
|
"illistnoise.com",
|
|
3208
3234
|
"illnessth.com",
|
|
3209
3235
|
"illubd.com",
|
|
3236
|
+
"iloveion.com",
|
|
3210
3237
|
"ilovespam.com",
|
|
3211
3238
|
"im5z.com",
|
|
3212
3239
|
"imagic.wiki",
|
|
@@ -3579,6 +3606,7 @@
|
|
|
3579
3606
|
"khtextile.com",
|
|
3580
3607
|
"kiani.com",
|
|
3581
3608
|
"kidaroa.com",
|
|
3609
|
+
"kiecchn.com",
|
|
3582
3610
|
"kiki287666.xyz",
|
|
3583
3611
|
"killmail.com",
|
|
3584
3612
|
"killmail.net",
|
|
@@ -3770,6 +3798,7 @@
|
|
|
3770
3798
|
"lez.se",
|
|
3771
3799
|
"lgxscreen.com",
|
|
3772
3800
|
"lhsdv.com",
|
|
3801
|
+
"liadhene.com",
|
|
3773
3802
|
"liamcyrus.com",
|
|
3774
3803
|
"liang-tts.shop",
|
|
3775
3804
|
"liangganxiaopu.bond",
|
|
@@ -3780,6 +3809,7 @@
|
|
|
3780
3809
|
"lifetimefriends.info",
|
|
3781
3810
|
"lifetotech.com",
|
|
3782
3811
|
"lifetreechurch.co.uk",
|
|
3812
|
+
"liggegi.com",
|
|
3783
3813
|
"lightv.online",
|
|
3784
3814
|
"ligsb.com",
|
|
3785
3815
|
"likeageek.fr.nf",
|
|
@@ -4170,6 +4200,7 @@
|
|
|
4170
4200
|
"malioter.pro",
|
|
4171
4201
|
"mama3.org",
|
|
4172
4202
|
"mamabood.com",
|
|
4203
|
+
"mameganteng.store",
|
|
4173
4204
|
"mamulenok.ru",
|
|
4174
4205
|
"managedisabled.online",
|
|
4175
4206
|
"mandraghen.cf",
|
|
@@ -4189,6 +4220,7 @@
|
|
|
4189
4220
|
"martenson-mail.mywire.org",
|
|
4190
4221
|
"martenson-mail.ooguy.com",
|
|
4191
4222
|
"martenson-mail.theworkpc.com",
|
|
4223
|
+
"masbahlil.xyz",
|
|
4192
4224
|
"mask03.ru",
|
|
4193
4225
|
"maskmy.id",
|
|
4194
4226
|
"masonline.info",
|
|
@@ -4647,6 +4679,7 @@
|
|
|
4647
4679
|
"naslazhdai.ru",
|
|
4648
4680
|
"natange.org.uk",
|
|
4649
4681
|
"nationalgardeningclub.com",
|
|
4682
|
+
"natuanal.com",
|
|
4650
4683
|
"nautonk.com",
|
|
4651
4684
|
"navalcadets.com",
|
|
4652
4685
|
"naverapp.com",
|
|
@@ -4989,6 +5022,7 @@
|
|
|
4989
5022
|
"oshietechan.link",
|
|
4990
5023
|
"osifixtech.com.mx",
|
|
4991
5024
|
"oskarstalbergcitygenerator.com",
|
|
5025
|
+
"ospirun.com",
|
|
4992
5026
|
"ostahie.com",
|
|
4993
5027
|
"ostinmail.com",
|
|
4994
5028
|
"osxofulk.com",
|
|
@@ -5015,6 +5049,7 @@
|
|
|
5015
5049
|
"oxfo.edu.pl",
|
|
5016
5050
|
"oxmail.homes",
|
|
5017
5051
|
"oxopoha.com",
|
|
5052
|
+
"oyisam.my",
|
|
5018
5053
|
"ozatvn.com",
|
|
5019
5054
|
"ozm.fr",
|
|
5020
5055
|
"ozsaip.com",
|
|
@@ -5236,6 +5271,7 @@
|
|
|
5236
5271
|
"primabananen.net",
|
|
5237
5272
|
"prin.be",
|
|
5238
5273
|
"printz.site",
|
|
5274
|
+
"prisonity.com",
|
|
5239
5275
|
"privacy-mail.top",
|
|
5240
5276
|
"privacy.net",
|
|
5241
5277
|
"privacyshield.cc",
|
|
@@ -5271,6 +5307,7 @@
|
|
|
5271
5307
|
"protempmail.com",
|
|
5272
5308
|
"protonza.com",
|
|
5273
5309
|
"prout.be",
|
|
5310
|
+
"proveity.com",
|
|
5274
5311
|
"proxy-gateway.net",
|
|
5275
5312
|
"proxymail.eu",
|
|
5276
5313
|
"proxyparking.com",
|
|
@@ -5283,6 +5320,7 @@
|
|
|
5283
5320
|
"psnator.com",
|
|
5284
5321
|
"psoxs.com",
|
|
5285
5322
|
"ptct.net",
|
|
5323
|
+
"ptncereio.com",
|
|
5286
5324
|
"ptsculure.com",
|
|
5287
5325
|
"puabook.com",
|
|
5288
5326
|
"puglieisi.com",
|
|
@@ -5321,6 +5359,7 @@
|
|
|
5321
5359
|
"qd28max.eu.cc",
|
|
5322
5360
|
"qejjyl.com",
|
|
5323
5361
|
"qentic.shop",
|
|
5362
|
+
"qeurtor.com",
|
|
5324
5363
|
"qhgs.com",
|
|
5325
5364
|
"qiang66.ccwu.cc",
|
|
5326
5365
|
"qibl.at",
|
|
@@ -5372,6 +5411,7 @@
|
|
|
5372
5411
|
"rabitex.com",
|
|
5373
5412
|
"racaho.com",
|
|
5374
5413
|
"rackabzar.com",
|
|
5414
|
+
"racovor.com",
|
|
5375
5415
|
"rael.us",
|
|
5376
5416
|
"raetp9.com",
|
|
5377
5417
|
"rainbowly.ml",
|
|
@@ -5510,6 +5550,7 @@
|
|
|
5510
5550
|
"rotaniliam.com",
|
|
5511
5551
|
"rotomails.co.uk",
|
|
5512
5552
|
"rotomails.com",
|
|
5553
|
+
"rouflav.com",
|
|
5513
5554
|
"route64.de",
|
|
5514
5555
|
"routerboardvietnam.com",
|
|
5515
5556
|
"rover.info",
|
|
@@ -5531,6 +5572,7 @@
|
|
|
5531
5572
|
"rtskiya.xyz",
|
|
5532
5573
|
"rudymail.ml",
|
|
5533
5574
|
"ruinique.tech",
|
|
5575
|
+
"rukiaops.me",
|
|
5534
5576
|
"rulersonline.com",
|
|
5535
5577
|
"rumgel.com",
|
|
5536
5578
|
"runi.ca",
|
|
@@ -5541,6 +5583,7 @@
|
|
|
5541
5583
|
"ruu.kr",
|
|
5542
5584
|
"ruutukf.com",
|
|
5543
5585
|
"rvb.ro",
|
|
5586
|
+
"rvneous.com",
|
|
5544
5587
|
"rwstatus.com",
|
|
5545
5588
|
"rwxsxnomo.top",
|
|
5546
5589
|
"rxs9.cn",
|
|
@@ -5588,6 +5631,8 @@
|
|
|
5588
5631
|
"sanim.net",
|
|
5589
5632
|
"sanstr.com",
|
|
5590
5633
|
"saovangtiles.site",
|
|
5634
|
+
"sappisi.com",
|
|
5635
|
+
"sarawakreport.com",
|
|
5591
5636
|
"sast.ro",
|
|
5592
5637
|
"sasukiez.shop",
|
|
5593
5638
|
"satisfyme.club",
|
|
@@ -5716,8 +5761,10 @@
|
|
|
5716
5761
|
"shotmail.ru",
|
|
5717
5762
|
"showslow.de",
|
|
5718
5763
|
"shrib.com",
|
|
5764
|
+
"shuelder.com",
|
|
5719
5765
|
"shut.name",
|
|
5720
5766
|
"shut.ws",
|
|
5767
|
+
"siap-sepuh.com",
|
|
5721
5768
|
"siberpay.com",
|
|
5722
5769
|
"sicmg.com",
|
|
5723
5770
|
"sidelka-mytischi.ru",
|
|
@@ -5865,6 +5912,7 @@
|
|
|
5865
5912
|
"sonjj.edu.pl",
|
|
5866
5913
|
"sonny.tk",
|
|
5867
5914
|
"sonphuongthinh.com",
|
|
5915
|
+
"sonrusu.com",
|
|
5868
5916
|
"sonshi.cf",
|
|
5869
5917
|
"soodmail.com",
|
|
5870
5918
|
"soodomail.com",
|
|
@@ -6291,6 +6339,7 @@
|
|
|
6291
6339
|
"thanhnhontienbao.com",
|
|
6292
6340
|
"thanksnospam.info",
|
|
6293
6341
|
"thankyou2010.com",
|
|
6342
|
+
"thaotri.com",
|
|
6294
6343
|
"thatim.info",
|
|
6295
6344
|
"thc.st",
|
|
6296
6345
|
"thdfyrg.top",
|
|
@@ -6334,6 +6383,7 @@
|
|
|
6334
6383
|
"thg.cc.cd",
|
|
6335
6384
|
"thichanthit.com",
|
|
6336
6385
|
"thichmmo.com",
|
|
6386
|
+
"thiefness.com",
|
|
6337
6387
|
"thietbivanphong.asia",
|
|
6338
6388
|
"thirifara.com",
|
|
6339
6389
|
"thisisnotmyrealemail.com",
|
|
@@ -6510,6 +6560,7 @@
|
|
|
6510
6560
|
"trash-mail.gq",
|
|
6511
6561
|
"trash-mail.ml",
|
|
6512
6562
|
"trash-mail.tk",
|
|
6563
|
+
"trash-mail.webredirect.org",
|
|
6513
6564
|
"trash-me.com",
|
|
6514
6565
|
"trash2009.com",
|
|
6515
6566
|
"trash2010.com",
|
|
@@ -6586,8 +6637,10 @@
|
|
|
6586
6637
|
"tuamaeaquelaursa.com",
|
|
6587
6638
|
"tubeemail.com",
|
|
6588
6639
|
"tumroc.net",
|
|
6640
|
+
"tunthuta.com",
|
|
6589
6641
|
"tuofs.com",
|
|
6590
6642
|
"tupmail.com",
|
|
6643
|
+
"turkeyth.com",
|
|
6591
6644
|
"turnimon.com",
|
|
6592
6645
|
"turoid.com",
|
|
6593
6646
|
"turual.com",
|
|
@@ -6624,6 +6677,7 @@
|
|
|
6624
6677
|
"ucche.us",
|
|
6625
6678
|
"ucupdong.ml",
|
|
6626
6679
|
"uddinfoundation.org",
|
|
6680
|
+
"udingclin.com",
|
|
6627
6681
|
"uemail99.com",
|
|
6628
6682
|
"ufacturing.com",
|
|
6629
6683
|
"ufokeuabmail.com",
|
|
@@ -6654,6 +6708,7 @@
|
|
|
6654
6708
|
"undeadbank.com",
|
|
6655
6709
|
"underseagolf.com",
|
|
6656
6710
|
"undo.it",
|
|
6711
|
+
"unfairship.com",
|
|
6657
6712
|
"unicodeworld.com",
|
|
6658
6713
|
"unids.com",
|
|
6659
6714
|
"unimark.org",
|
|
@@ -6661,6 +6716,7 @@
|
|
|
6661
6716
|
"unit7lahaina.com",
|
|
6662
6717
|
"unitedcharitiesosm.org.uk",
|
|
6663
6718
|
"universall.me",
|
|
6719
|
+
"unlikeyth.com",
|
|
6664
6720
|
"unmail.ru",
|
|
6665
6721
|
"unratito.com",
|
|
6666
6722
|
"uofzcpg9ftykob.asia",
|
|
@@ -6952,6 +7008,7 @@
|
|
|
6952
7008
|
"web-library.net",
|
|
6953
7009
|
"web-mail.pp.ua",
|
|
6954
7010
|
"web2mailco.com",
|
|
7011
|
+
"webcamness.com",
|
|
6955
7012
|
"webclub.infos.st",
|
|
6956
7013
|
"webcontact-france.eu",
|
|
6957
7014
|
"webemail.me",
|
|
@@ -7237,6 +7294,7 @@
|
|
|
7237
7294
|
"ying-ge.com",
|
|
7238
7295
|
"yingzi321.shop",
|
|
7239
7296
|
"yjlwcbrf.top",
|
|
7297
|
+
"ymonthl.com",
|
|
7240
7298
|
"ynagjie-66.cc",
|
|
7241
7299
|
"ynmrealty.com",
|
|
7242
7300
|
"yodx.ro",
|
|
@@ -7405,5 +7463,24 @@
|
|
|
7405
7463
|
"zyvenix.cfd",
|
|
7406
7464
|
"zzi.us",
|
|
7407
7465
|
"zzrgg.com",
|
|
7408
|
-
"zzz.com"
|
|
7466
|
+
"zzz.com",
|
|
7467
|
+
"zzzzzzzzzzzzzz-1090.dynv6.net",
|
|
7468
|
+
"zzzzzzzzzzzzzz-2092.dynv6.net",
|
|
7469
|
+
"zzzzzzzzzzzzzz-277.dynv6.net",
|
|
7470
|
+
"zzzzzzzzzzzzzz-3294.dynv6.net",
|
|
7471
|
+
"zzzzzzzzzzzzzz-3535.dynv6.net",
|
|
7472
|
+
"zzzzzzzzzzzzzz-3928.dynv6.net",
|
|
7473
|
+
"zzzzzzzzzzzzzz-3940.dynv6.net",
|
|
7474
|
+
"zzzzzzzzzzzzzz-4283.dynv6.net",
|
|
7475
|
+
"zzzzzzzzzzzzzz-5055.dynv6.net",
|
|
7476
|
+
"zzzzzzzzzzzzzz-5246.dynv6.net",
|
|
7477
|
+
"zzzzzzzzzzzzzz-546.dynv6.net",
|
|
7478
|
+
"zzzzzzzzzzzzzz-7619.dynv6.net",
|
|
7479
|
+
"zzzzzzzzzzzzzz-7911.dynv6.net",
|
|
7480
|
+
"zzzzzzzzzzzzzz-8050.dynv6.net",
|
|
7481
|
+
"zzzzzzzzzzzzzz-8079.dynv6.net",
|
|
7482
|
+
"zzzzzzzzzzzzzz-8874.dynv6.net",
|
|
7483
|
+
"zzzzzzzzzzzzzz-969.dynv6.net",
|
|
7484
|
+
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.loseyourip.com",
|
|
7485
|
+
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.ooguy.com"
|
|
7409
7486
|
]
|
|
@@ -230,7 +230,7 @@ function buildClassicSystemPrompt(brand, config) {
|
|
|
230
230
|
'- If a source mentions a third-party platform, product, or company by name (e.g., LinkedIn, YouTube, Apple), THAT is fine — those are subjects of the news, not the source. Name them freely.',
|
|
231
231
|
'',
|
|
232
232
|
'CITATIONS:',
|
|
233
|
-
'- If the source contains hard data — specific statistics, percentages, dollar amounts, dates, study results
|
|
233
|
+
'- If the source contains hard data — specific statistics, percentages, dollar amounts, dates, study results — paraphrase them before including in the body. Never copy exact figures from the source. Round numbers, change units or scale, reframe percentages as ratios or fractions, and vary phrasing. The meaning and magnitude must stay accurate but the expression must be different enough that it cannot be traced back to the source (e.g. "$7.5 billion" becomes "north of $7 billion"; "60%" becomes "close to six in ten"; "47,000 of 500,000" becomes "fewer than 50,000 of a planned half-million").',
|
|
234
234
|
'- Then add a corresponding entry to the `citations` array with:',
|
|
235
235
|
' - note: the cited fact (e.g. "Crosscheck AI flagged 12,000 impersonation attempts in beta")',
|
|
236
236
|
' - source: a neutral attribution that does NOT name the source publication (e.g. "Reported by LinkedIn product team, May 2026", "Per company beta data", "Industry research, Q2 2026")',
|
|
@@ -251,7 +251,7 @@ function buildClassicSystemPrompt(brand, config) {
|
|
|
251
251
|
'- citations: array of { note, source } for any hard data referenced. Empty array if none.',
|
|
252
252
|
'',
|
|
253
253
|
'STYLE:',
|
|
254
|
-
'- Do NOT copy source text verbatim. Synthesize and rewrite in your voice.',
|
|
254
|
+
'- Do NOT copy source text verbatim. Paraphrase all facts, figures, and phrasing. Synthesize and rewrite in your voice.',
|
|
255
255
|
'- Do NOT use emojis, hashtags, or "guru" language unless brand instructions say otherwise.',
|
|
256
256
|
'- Respond with valid JSON only — no markdown fences, no preamble.',
|
|
257
257
|
].filter(Boolean).join('\n');
|