1+ // ==UserScript==
2+ // @name add-dccbot-btn
3+ // @namespace https://github.com/luni/dccbot/
4+ // @website https://github.com/luni/dccbot/
5+ // @version 2025-02-12
6+ // @description Add Button for DCCbot to automate downloads.
7+ // @author luni
8+ // @match https://www.xdcc.eu/search.php*
9+ // @match https://nibl.co.uk/*
10+ // @downloadURL https://raw.githubusercontent.com/luni/dccbot/refs/heads/main/userscript/add-dccbot-btn.js
11+ // @connect *
12+ // @grant GM_xmlhttpRequest
13+ // @grant GM_registerMenuCommand
14+ // @grant GM_getValue
15+ // @grant GM_setValue
16+ // ==/UserScript==
17+
18+ ( function ( ) {
19+ 'use strict' ;
20+
21+ // Prompt user to set API endpoint
22+ GM_registerMenuCommand ( "Set API Endpoint" , function ( ) {
23+ const current = GM_getValue ( "dccbot_api" , "" ) ;
24+ const endpoint = prompt ( "Enter DCCBot API endpoint:" , current ) ;
25+ if ( endpoint !== null ) {
26+ GM_setValue ( "dccbot_api" , endpoint ) ;
27+ alert ( "API endpoint saved: " + endpoint ) ;
28+ }
29+ } ) ;
30+
31+ // Usage: fetch endpoint whenever needed
32+ const dccbot_api = GM_getValue ( "dccbot_api" , "" ) ;
33+ if ( ! dccbot_api ) {
34+ console . log ( "No API endpoint for DCCBot defined." ) ;
35+ return
36+ }
37+
38+ console . log ( "API Endpoint for DCCBot: " + dccbot_api ) ;
39+
40+ function get_element_by_xpath ( path , parent ) {
41+ return document . evaluate ( path , parent || document , null , XPathResult . FIRST_ORDERED_NODE_TYPE , null ) . singleNodeValue ;
42+ }
43+
44+ function get_elements_by_xpath ( xpath , parent ) {
45+ let results = [ ] , query = document . evaluate ( xpath , parent || document , null , XPathResult . ORDERED_NODE_SNAPSHOT_TYPE , null ) ;
46+ for ( let i = 0 , length = query . snapshotLength ; i < length ; ++ i ) { results . push ( query . snapshotItem ( i ) ) ; } ;
47+ return results ;
48+ }
49+
50+ function send_msg ( server , channel , user , message ) {
51+ GM_xmlhttpRequest ( {
52+ method : "POST" ,
53+ url : dccbot_api + '/msg' ,
54+ headers : {
55+ "Content-Type" : "application/json"
56+ } ,
57+ data : JSON . stringify ( {
58+ server : server ,
59+ channel : channel ,
60+ user : user ,
61+ message : message
62+ } ) ,
63+ onload : function ( response ) {
64+ console . log ( "[DCCBOT] API Reponse: " + response . responseText ) ;
65+ }
66+ } ) ;
67+ }
68+
69+ function add_button_xdcc_eu ( ) {
70+ document . getElementsByClassName ( 'container' ) [ 0 ] . style . maxWidth = "100%" ;
71+ document . getElementsByClassName ( 'container' ) [ 0 ] . style . width = "90%" ;
72+ document . getElementsByClassName ( 'twelve' ) [ 0 ] . style . marginTop = 0 ;
73+
74+ let all_results = [ ] ;
75+ for ( let x of document . getElementById ( 'table' ) . getElementsByTagName ( 'tbody' ) [ 0 ] . getElementsByTagName ( 'tr' ) ) {
76+ let d = [
77+ x . getElementsByTagName ( 'td' ) [ 1 ] . getElementsByTagName ( 'a' ) [ 0 ] . href . replace ( 'irc://' , '' ) . split ( '/' ) [ 0 ] ,
78+ ] ;
79+ for ( let y = 1 ; y < 4 ; y ++ ) {
80+ d . push ( x . getElementsByTagName ( 'td' ) [ y ] . textContent . trim ( ) ) ;
81+ }
82+ d [ 3 ] = d [ 3 ] . replace ( '#' , '' ) ;
83+ all_results . push ( d . join ( ';' ) )
84+ x . dataset . data = d . join ( ';' ) ;
85+ x . dataset . channel = d [ 1 ] ;
86+ x . dataset . server = d [ 0 ] ;
87+ x . dataset . bot = d [ 2 ] ;
88+ x . dataset . pack = d [ 3 ] ;
89+ let clmn = document . createElement ( 'td' ) ,
90+ btn = document . createElement ( 'button' ) ;
91+ btn . innerHTML = 'Down' ;
92+ clmn . appendChild ( btn ) ;
93+ x . appendChild ( clmn )
94+ btn . onclick = function ( e ) {
95+ e . preventDefault ( ) ;
96+ const d = this . parentNode . parentNode . dataset ;
97+ send_msg ( d . server , d . channel , d . bot , "xdcc send " + d . pack ) ;
98+ this . parentNode . parentNode . style . background = '#CECECE' ;
99+ return ;
100+ } ;
101+ }
102+
103+ document . getElementsByTagName ( 'h4' ) [ 0 ] . onclick = function ( e ) {
104+ document . getElementById ( 'msg' ) . innerHTML = '<textarea style="width:100%;" rows=8>' + all_results . join ( '\n' ) + '</textarea>' ;
105+ }
106+ }
107+
108+ function add_button_nibl ( ) {
109+ function send_to_dccbot ( e ) {
110+ e . preventDefault ( ) ;
111+ const botpack = this . dataset . botpack ,
112+ botname = this . dataset . botname ;
113+ send_msg ( "irc.rizon.net" , "#nibl" , botname , "xdcc send " + botpack ) ;
114+
115+ }
116+
117+ for ( let copy_btn of get_elements_by_xpath ( "//button[contains(@class, 'copy-data')]" ) ) {
118+ // const td=copy_btn.parentNode, new_btn=copy_btn.cloneNode();
119+ copy_btn . className = copy_btn . className . replace ( 'copy-data ' , '' ) ;
120+ copy_btn . innerHTML = 'Down' ;
121+ copy_btn . onclick = send_to_dccbot ;
122+ // td.appendChild(new_btn);
123+ }
124+
125+ const copy_batch_btn = document . getElementById ( 'copy-as-batch' ) ;
126+ copy_batch_btn . innerHTML = 'Download selected' ;
127+ copy_batch_btn . onclick = function ( e ) {
128+ e . preventDefault ( ) ;
129+ let bots = { } ;
130+ for ( const ckbox of get_elements_by_xpath ( "//input[@name='batch']" ) ) {
131+ if ( ckbox . checked ) {
132+ if ( ! bots [ ckbox . dataset . botname ] ) {
133+ bots [ ckbox . dataset . botname ] = [ ] ;
134+ }
135+ bots [ ckbox . dataset . botname ] . push ( ckbox . dataset . botpack ) ;
136+ }
137+ }
138+
139+ for ( const botname in bots ) {
140+ send_msg ( "irc.rizon.net" , "#nibl" , botname , "xdcc batch " + bots [ botname ] . join ( ',' ) ) ;
141+ }
142+ } ;
143+ }
144+
145+ const hostname = window . location . hostname ;
146+ if ( hostname == 'www.xdcc.eu' ) {
147+ add_button_xdcc_eu ( ) ;
148+ return ;
149+ }
150+ if ( hostname == 'nibl.co.uk' ) {
151+ add_button_nibl ( ) ;
152+ return ;
153+ }
154+ } ) ( ) ;
0 commit comments