Extension for x.com

A new extension

Size

7.0 KB

Version

1.0.1

Created

Dec 23, 2025

Updated

30 days ago

1// ==UserScript==
2// @name		Extension for x.com
3// @description		A new extension
4// @version		1.0.1
5// @match		https://*.x.com/*
6// @icon		https://abs.twimg.com/favicons/twitter.3.ico
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    // アフィリエイトIDのパターン(除外対象)
12    const affiliatePatterns = [
13        /af_id/i,
14        /affid/i,
15        /affiliate/i,
16        /voucher/i,
17        /aff_/i,
18        /partner_id/i,
19        /ref_id/i,
20        /tracking/i
21    ];
22
23    // 検索対象のIDパターン
24    const targetIdPatterns = [
25        /[?&]cid=([^&]+)/i,
26        /[?&]pid=([^&]+)/i,
27        /[?&]id=([^&]+)/i,
28        /[?&]product_id=([^&]+)/i,
29        /[?&]item_id=([^&]+)/i,
30        /\/cid[=\/]([^\/&?]+)/i,
31        /\/pid[=\/]([^\/&?]+)/i,
32        /\/id[=\/]([^\/&?]+)/i,
33    ];
34
35    // URLデコード処理
36    function decodeURL(url) {
37        try {
38            return decodeURIComponent(url);
39        } catch (e) {
40            console.log('[ID Extractor] デコードエラー:', e);
41            return url;
42        }
43    }
44
45    // URLパラメーターから実際のURLを抽出
46    function extractRealURL(url) {
47        const urlParams = [/[?&]lurl=([^&]+)/i, /[?&]url=([^&]+)/i, /[?&]link=([^&]+)/i, /[?&]target=([^&]+)/i];
48
49        for (let pattern of urlParams) {
50            const match = url.match(pattern);
51            if (match) {
52                const decoded = decodeURL(match[1]);
53                console.log('[ID Extractor] 実際のURLを抽出:', decoded);
54                return decoded;
55            }
56        }
57        return url;
58    }
59
60    // アフィリエイトIDかチェック
61    function isAffiliateId(paramName) {
62        return affiliatePatterns.some(pattern => pattern.test(paramName));
63    }
64
65    // IDを抽出
66    function extractID(text) {
67        if (!text) return null;
68        
69        const decodedText = decodeURL(text);
70        const realURL = extractRealURL(decodedText);
71
72        console.log('[ID Extractor] ID抽出を試行:', realURL.substring(0, 100));
73
74        for (let pattern of targetIdPatterns) {
75            const match = realURL.match(pattern);
76            if (match && match[1]) {
77                const id = match[1];
78                // アフィリエイトIDでないことを確認
79                const fullMatch = match[0];
80                if (!isAffiliateId(fullMatch)) {
81                    console.log('[ID Extractor] IDを発見:', id);
82                    return id;
83                }
84            }
85        }
86        return null;
87    }
88
89    // リンク要素を処理
90    function processLink(link) {
91        let extractedID = null;
92        const href = link.getAttribute('href');
93
94        console.log('[ID Extractor] リンクを処理中:', href);
95
96        // 1. リンクのテキストコンテンツから抽出(X.comのt.coリンクの場合、実際のURLがテキストに含まれる)
97        if (link.textContent && link.textContent.trim()) {
98            extractedID = extractID(link.textContent);
99            if (extractedID) {
100                console.log('[ID Extractor] テキストコンテンツからIDを抽出:', extractedID);
101            }
102        }
103
104        // 2. href属性から抽出
105        if (!extractedID && href) {
106            extractedID = extractID(href);
107            if (extractedID) {
108                console.log('[ID Extractor] href属性からIDを抽出:', extractedID);
109            }
110        }
111
112        // 3. alt属性から抽出(画像リンクの場合)
113        if (!extractedID) {
114            const img = link.querySelector('img');
115            if (img && img.alt) {
116                extractedID = extractID(img.alt);
117            }
118        }
119
120        // 4. data属性からも検索
121        if (!extractedID) {
122            for (let attr of link.attributes) {
123                if (attr.name.startsWith('data-')) {
124                    extractedID = extractID(attr.value);
125                    if (extractedID) break;
126                }
127            }
128        }
129
130        // IDが見つかった場合、DuckDuckGo検索リンクに書き換え
131        if (extractedID) {
132            const duckduckgoURL = `https://duckduckgo.com/?q=${encodeURIComponent(extractedID)}`;
133            link.setAttribute('href', duckduckgoURL);
134            link.setAttribute('data-original-href', href);
135            link.setAttribute('data-extracted-id', extractedID);
136            link.style.borderBottom = '2px solid #DE5833'; // 視覚的な識別用
137
138            // リンクテキストも更新(テキストがある場合)
139            if (link.textContent && link.textContent.trim()) {
140                const originalText = link.textContent;
141                link.setAttribute('data-original-text', originalText);
142                link.textContent = `🔍 ${extractedID} (DuckDuckGo検索)`;
143            }
144
145            console.log('[ID Extractor] リンクを書き換えました:', extractedID, '->', duckduckgoURL);
146            return true;
147        }
148
149        return false;
150    }
151
152    // すべてのリンクを処理
153    function processAllLinks() {
154        const links = document.querySelectorAll('a[href]');
155        let processedCount = 0;
156
157        console.log('[ID Extractor] リンクを処理中... 合計:', links.length);
158
159        links.forEach(link => {
160            // 既に処理済みのリンクはスキップ
161            if (link.hasAttribute('data-extracted-id')) {
162                return;
163            }
164
165            if (processLink(link)) {
166                processedCount++;
167            }
168        });
169
170        if (processedCount > 0) {
171            console.log(`[ID Extractor] ${processedCount}個のリンクを書き換えました`);
172        } else {
173            console.log('[ID Extractor] 書き換え対象のリンクが見つかりませんでした');
174        }
175    }
176
177    // デバウンス関数
178    function debounce(func, wait) {
179        let timeout;
180        return function executedFunction(...args) {
181            const later = () => {
182                clearTimeout(timeout);
183                func(...args);
184            };
185            clearTimeout(timeout);
186            timeout = setTimeout(later, wait);
187        };
188    }
189
190    // 初回実行(ページ読み込み後少し待つ)
191    setTimeout(() => {
192        console.log('[ID Extractor] 初回処理を開始');
193        processAllLinks();
194    }, 1000);
195
196    // 動的に追加されるコンテンツを監視(デバウンス付き)
197    const debouncedProcess = debounce(processAllLinks, 500);
198    
199    const observer = new MutationObserver((mutations) => {
200        let shouldProcess = false;
201
202        mutations.forEach((mutation) => {
203            if (mutation.addedNodes.length > 0) {
204                shouldProcess = true;
205            }
206        });
207
208        if (shouldProcess) {
209            debouncedProcess();
210        }
211    });
212
213    // 監視開始
214    if (document.body) {
215        observer.observe(document.body, {
216            childList: true,
217            subtree: true
218        });
219        console.log('[ID Extractor] DOM監視を開始しました');
220    }
221
222    console.log('[ID Extractor] スクリプトが起動しました');
223})();
Extension for x.com | Robomonkey