File size: 7,957 Bytes
c92aa92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import axios from 'axios';
import {SearchResult} from '../../types.js';
import { config } from '../../config.js';
import {searchDuckDuckGo} from "../duckduckgo/index.js";
import {searchBrave} from "../brave/brave.js";
import * as cheerio from 'cheerio';

async function searchBingForLinuxDo(query: string, limit: number): Promise<SearchResult[]> {
    let allResults: SearchResult[] = [];
    let pn = 0;
    // Format query for URL
    const encodedQuery = encodeURIComponent(query);

    try {
        while (allResults.length < limit) {
            const response = await axios.get('https://cn.bing.com/search', {
                params: {
                    "q": query,
                    "qs": "n",
                    "sp": "-1",
                    "lq": "0",
                    "pq": query,
                    "sc": "1-26",
                    "sk": "",
                    "cvid": "83A1CA7981304D6D9DC6924C92C248C9",
                    "FPIG": "5A297328992C4FB8BEED24CB159DD092",
                    "first": 1 + pn * 10,
                    "FORM": "PERE",
                    "rdr": "1",
                    "rdrig": "5BE6670756A24EA5A4274EB2A47D978D"
                },
                headers: {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
                    "Connection": "keep-alive",
                    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                    "Accept-Encoding": "gzip, deflate, br",
                    "ect": "4g",
                    "sec-ch-ua": "\"Chromium\";v=\"112\", \"Google Chrome\";v=\"112\", \"Not:A-Brand\";v=\"99\"",
                    "sec-ch-ua-mobile": "?0",
                    "sec-ch-ua-full-version": "\"112.0.5615.50\"",
                    "sec-ch-ua-arch": "\"x86\"",
                    "sec-ch-ua-platform": "\"Windows\"",
                    "sec-ch-ua-platform-version": "\"15.0.0\"",
                    "sec-ch-ua-model": "\"\"",
                    "sec-ch-ua-bitness": "\"64\"",
                    "sec-ch-ua-full-version-list": "\"Chromium\";v=\"112.0.5615.50\", \"Google Chrome\";v=\"112.0.5615.50\", \"Not:A-Brand\";v=\"99.0.0.0\"",
                    "upgrade-insecure-requests": "1",
                    "sec-fetch-site": "same-origin",
                    "sec-fetch-mode": "navigate",
                    "sec-fetch-dest": "document",
                    "referer": `https://cn.bing.com/search?q=${encodedQuery}&qs=n&sp=-1&lq=0&pq=${encodedQuery}&sc=1-26&sk=&cvid=83A1CA7981304D6D9DC6924C92C248C9&FPIG=5A297328992C4FB8BEED24CB159DD092&first=11&FORM=PERE`,
                    "accept-language": "zh-CN,zh;q=0.9",
                    "Cookie": "MUID=296FAEFA1CB065372DBFB8DB1D6264B0; MUIDB=296FAEFA1CB065372DBFB8DB1D6264B0; _EDGE_S=F=1&SID=063A15C99EE5624608BF03E89F3763A1; _EDGE_V=1; SRCHD=AF=NOFORM; SRCHUID=V=2&GUID=5C95B3592BB9427D94A15ACCB93B2998&dmnchg=1; _Rwho=u=d&ts=2025-07-06; ipv6=hit=1751775967739&t=4; USRLOC=HS=1&ELOC=LAT=23.0211181640625|LON=113.73304748535156|N=%E4%B8%9C%E8%8E%9E%E5%B8%82%EF%BC%8C%E5%B9%BF%E4%B8%9C%E7%9C%81|ELT=4|; BFPRResults=FirstPageUrls=B0D24EBFA8A0B66D6B071D854B69DC44%2C20426A336B6E4CF57DE0D0DD3484549D%2C9BF59C5A66BB498707BDEE26787183B3%2C4BD56F463CDE1E98954C975164097F6F%2C0F9C9A380F681662D573A082BEE6F471%2C8ED94F5FBF7A57786DE0FECB10C3D613%2C23FB8C1D9A41145741724F52CB242134%2CBD2D8F53469372B7D64F7AEF3D7F642D%2C6D9A9E581F375A9E57BF5A9599101CD5%2C8FC7081B6D04C85C644E9E41D523F4A0&FPIG=5A297328992C4FB8BEED24CB159DD092; SRCHUSR=DOB=20250706&DS=1; _RwBf=r=0&ilt=10&ihpd=0&ispd=10&rc=30&rb=0&rg=200&pc=27&mtu=0&rbb=0&clo=0&v=10&l=2025-07-05T07:00:00.0000000Z&lft=0001-01-01T00:00:00.0000000&aof=0&ard=0001-01-01T00:00:00.0000000&rwdbt=0&rwflt=0&rwaul2=0&g=&o=2&p=&c=&t=0&s=0001-01-01T00:00:00.0000000+00:00&ts=2025-07-06T04:16:43.3231859+00:00&rwred=0&wls=&wlb=&wle=&ccp=&cpt=&lka=0&lkt=0&aad=0&TH=&cid=0&gb=; _SS=SID=063A15C99EE5624608BF03E89F3763A1&R=30&RB=0&GB=0&RG=200&RP=27; SRCHHPGUSR=SRCHLANG=zh-Hans&PV=15.0.0&BZA=0&BRW=XW&BRH=M&CW=1707&CH=809&SCW=1691&SCH=1685&DPR=1.5&UTC=480&PRVCW=1707&PRVCH=809&HV=1751775699&HVE=CfDJ8Inh5QCoSQBNls38F2rbEpRMaCs0odiEKdnprhQV_w0a8_NuQurksG4JGF0rBdUFR2yGWPBo6wf3uOSO2p1l-Y8XcVCroAmqnrOsA1QCo3OSPJl7M38jS2gXSuJk8XX-qNBkwJmrKmfmGnVmG3kTuHL1a5gIpkLuYYB9-DlPnVHAE8tfBgro5p7vESusk8fsWA&EXLTT=11&DM=0"
                }
            });

            const $ = cheerio.load(response.data);
            const results: SearchResult[] = [];

            $('#b_content').children()
                .find('#b_results').children()
                .each((i, element) => {
                    const titleElement = $(element).find('h2');
                    const linkElement = $(element).find('a');
                    const snippetElement = $(element).find('p').first();

                    if (titleElement.length && linkElement.length) {
                        const url = linkElement.attr('href');
                        if (url && url.startsWith('http') && url.includes('linux.do')) {
                            const sourceElement = $(element).find('.b_tpcn');
                            results.push({
                                title: titleElement.text(),
                                url: url,
                                description: snippetElement.text().trim() || '',
                                source: 'linux.do',
                                engine: 'bing'
                            });
                        }
                    }
                });

            allResults = allResults.concat(results);

            if (results.length === 0) {
                console.error('⚠️ No more results from Bing, ending early....');
                break;
            }

            pn += 1;
        }

        return allResults.slice(0, limit);
    } catch (error: any) {
        console.error('❌ Bing search failed:', error.message || error);
        return [];
    }
}


export async function searchLinuxDo(query: string, limit: number): Promise<SearchResult[]> {

    console.error(`πŸ” Searching linux.do with "${query}" using ${config.defaultSearchEngine} engine`);

    // Create the site-specific query
    const siteQuery = `site:linux.do ${query}`;

    let results: SearchResult[] = [];

    try {
        // Use the configured search engine
        if (config.defaultSearchEngine === 'duckduckgo') {
            results = await searchDuckDuckGo(siteQuery, limit);
        } else if (config.defaultSearchEngine === 'bing') {
            results = await searchBingForLinuxDo(siteQuery, limit);
        } else {
            // Use Brave which reliably supports site: operator
            results = await searchBrave(siteQuery, limit);
        }

        // If no results from configured engine, try Brave as fallback
        if (results.length === 0 && config.defaultSearchEngine !== 'brave') {
            console.error('πŸ”„ No results from configured engine, falling back to Brave...');
            results = await searchBrave(siteQuery, limit);
        }

        // Filter results to ensure they're from linux.do (including subdomains)
        const filteredResults = results.filter(result => {
            try {
                const url = new URL(result.url);
                return url.hostname === 'linux.do' || url.hostname.endsWith('.linux.do');
            } catch {
                return false;
            }
        });

        // Update source to be consistent
        filteredResults.forEach(result => {
            result.source = 'linux.do';
            // Keep the original engine info
        });

        return filteredResults.slice(0, limit);
    } catch (error: any) {
        console.error(`❌ Linux.do search failed using ${config.defaultSearchEngine}:`, error.message || error);
        return [];
    }

}