@@ -134,7 +134,7 @@ export class ScriptDAO extends Repo<Script> {
134134 } ) ;
135135 }
136136
137- public findByNameAndNamespace ( name : string , namespace ? : string ) {
137+ public findByNameAndNamespace ( name : string , namespace : string ) {
138138 return this . findOne ( ( key , value ) => {
139139 return value . name === name && ( ! namespace || value . namespace === namespace ) ;
140140 } ) ;
@@ -151,6 +151,99 @@ export class ScriptDAO extends Repo<Script> {
151151 return value . origin === origin && value . subscribeUrl === suburl ;
152152 } ) ;
153153 }
154+
155+ public async searchExistingScript ( targetScript : Script , toCheckScriptInfoEqual : boolean = true ) : Promise < Script [ ] > {
156+ const removeScriptNameFromURL = ( url : string ) => {
157+ // https://scriptcat.org/scripts/code/{id}/{scriptname}.user.js (单匹配)
158+ if ( url . startsWith ( "https://scriptcat.org/scripts/code/" ) && url . endsWith ( ".js" ) ) {
159+ const idx1 = url . indexOf ( "/" , "https://scriptcat.org/scripts/code/" . length ) ;
160+ const idx2 = url . indexOf ( "/" , idx1 + 1 ) ;
161+ if ( idx1 > 0 && idx2 < 0 ) {
162+ const idx3 = url . indexOf ( "." , idx1 + 1 ) ;
163+ return url . substring ( 0 , idx1 + 1 ) + "*" + url . substring ( idx3 ) ;
164+ }
165+ }
166+ // https://update.greasyfork.org/scripts/{id}/{scriptname}.user.js (单匹配)
167+ if ( url . startsWith ( "https://update.greasyfork.org/scripts/" ) && url . endsWith ( ".js" ) ) {
168+ const idx1 = url . indexOf ( "/" , "https://update.greasyfork.org/scripts/" . length ) ;
169+ const idx2 = url . indexOf ( "/" , idx1 + 1 ) ;
170+ if ( idx1 > 0 && idx2 < 0 ) {
171+ const idx3 = url . indexOf ( "." , idx1 + 1 ) ;
172+ return url . substring ( 0 , idx1 + 1 ) + "*" + url . substring ( idx3 ) ;
173+ }
174+ }
175+ // https://openuserjs.org/install/{username}/{scriptname}.user.js (复数匹配)
176+ if ( url . startsWith ( "https://openuserjs.org/install/" ) && url . endsWith ( ".js" ) ) {
177+ const idx1 = url . indexOf ( "/" , "https://openuserjs.org/install/" . length ) ;
178+ const idx2 = url . indexOf ( "/" , idx1 + 1 ) ;
179+ if ( idx1 > 0 && idx2 < 0 ) {
180+ const idx3 = url . indexOf ( "." , idx1 + 1 ) ;
181+ return url . substring ( 0 , idx1 + 1 ) + "*" + url . substring ( idx3 ) ;
182+ }
183+ }
184+ return url ;
185+ } ;
186+ const valEqual = ( val1 : any , val2 : any ) => {
187+ if ( val1 && val2 && Array . isArray ( val1 ) && Array . isArray ( val2 ) ) {
188+ if ( val1 . length !== val2 . length ) return false ;
189+ if ( val1 . length < 2 ) {
190+ return val1 [ 0 ] === val2 [ 0 ] ;
191+ }
192+ // 無視次序
193+ const s = new Set ( [ ...val1 , ...val2 ] ) ;
194+ if ( s . size !== val1 . length ) return false ;
195+ return true ;
196+ }
197+ return val1 === val2 ;
198+ } ;
199+ const isScriptInfoEqual = ( script1 : Script , script2 : Script ) => {
200+ // @author , @copyright, @license 應該不會改
201+ if ( ! valEqual ( script1 . metadata . author , script2 . metadata . author ) ) return false ;
202+ if ( ! valEqual ( script1 . metadata . copyright , script2 . metadata . copyright ) ) return false ;
203+ if ( ! valEqual ( script1 . metadata . license , script2 . metadata . license ) ) return false ;
204+ // @grant , @connect 應該不會改
205+ if ( ! valEqual ( script1 . metadata . grant , script2 . metadata . grant ) ) return false ;
206+ if ( ! valEqual ( script1 . metadata . connect , script2 . metadata . connect ) ) return false ;
207+ // @match @include 應該不會改
208+ if ( ! valEqual ( script1 . metadata . match , script2 . metadata . match ) ) return false ;
209+ if ( ! valEqual ( script1 . metadata . include , script2 . metadata . include ) ) return false ;
210+ return true ;
211+ } ;
212+
213+ const { metadata, origin } = targetScript ;
214+
215+ if ( origin && ! metadata ?. updateurl ?. [ 0 ] && ! metadata ?. downloadurl ?. [ 0 ] ) {
216+ // scriptcat
217+ const targetOrigin = removeScriptNameFromURL ( origin ) ;
218+ return this . find ( ( key , entry ) => {
219+ if ( ! entry . origin ) return false ;
220+ const entryOrigin = removeScriptNameFromURL ( entry . origin ) ;
221+ if ( targetOrigin !== entryOrigin ) return false ;
222+ if ( toCheckScriptInfoEqual && ! isScriptInfoEqual ( targetScript , entry ) ) return false ;
223+ return true ;
224+ } ) ;
225+ } else if ( origin && ( metadata ?. updateurl ?. [ 0 ] || metadata ?. downloadurl ?. [ 0 ] ) ) {
226+ // greasyfork
227+
228+ const targetOrigin = removeScriptNameFromURL ( origin ) ;
229+ const targetUpdateURL = removeScriptNameFromURL ( metadata ?. updateurl ?. [ 0 ] || "" ) ;
230+ const targetDownloadURL = removeScriptNameFromURL ( metadata ?. downloadurl ?. [ 0 ] || "" ) ;
231+ return this . find ( ( key , entry ) => {
232+ if ( ! entry . origin ) return false ;
233+ const entryOrigin = removeScriptNameFromURL ( entry . origin ) ;
234+ if ( targetOrigin !== entryOrigin ) return false ;
235+
236+ const entryUpdateURL = removeScriptNameFromURL ( entry . metadata ?. updateurl ?. [ 0 ] || "" ) ;
237+ const entryDownloadURL = removeScriptNameFromURL ( entry . metadata ?. downloadurl ?. [ 0 ] || "" ) ;
238+
239+ if ( targetUpdateURL !== entryUpdateURL || targetDownloadURL !== entryDownloadURL ) return false ;
240+ if ( toCheckScriptInfoEqual && ! isScriptInfoEqual ( targetScript , entry ) ) return false ;
241+ return true ;
242+ } ) ;
243+ } else {
244+ return [ ] ;
245+ }
246+ }
154247}
155248
156249// 为了防止脚本代码数据量过大,单独存储脚本代码
0 commit comments