// M-Team 适配脚本 (mteam) // 站点类型: mtorrent // API 基地址: https://api.m-team.io (优先) / https://api.m-team.cc (备用) final String API_BASE = "https://api.m-team.io" // ==================== PAR 校验入口 ==================== // content: 访问结果页面内容(JSON 字符串) // url: 访问结果的 URL // 返回: [pageName:"xx", fields:[k:v,...]] 或 [pageName:"xx", extracted:[[...]], message:"..."] Map parse(String content, String url) { def slurp = new groovy.json.JsonSlurper() def root = slurp.parseText(content) // ── 用户信息 ── if (url.contains('/api/member/profile')) { def d = root?.data if (!d) return [pageName: '用户信息', message: 'data 为空'] return buildProfile(d) } // ── 搜索 ── if (url.contains('/api/torrent/search')) { def items = root?.data?.data ?: [] if (items.isEmpty()) return [pageName: '搜索', message: '无结果'] return buildSearchResults(items) } // ── 未匹配 ── return [pageName: '未知接口', message: "未配置解析: ${url}"] } private Map buildProfile(d) { return [ pageName: '用户信息', fields: [ id: d.id?.toString(), username: d.username?.toString(), uploaded: utils.formatSize(d.uploaded as long), downloaded: utils.formatSize(d.downloaded as long), bonus: d.bonus?.toString(), seeding: d.seeding?.toString(), seedingSize: utils.formatSize(d.seedingSize as long), invite: d.memberCount?.invite?.toString(), ] ] } private Map buildSearchResults(List items) { def rows = items.collect { it -> def st = it.status ?: [:] return [ title: it.name ?: '', size: utils.formatSize((it.size ?: 0) as long), seeders: st.seeders ?: 0, leechers: st.leechers ?: 0, grabs: st.timesCompleted ?: 0, discount: st.discount ?: 'NORMAL', category: it.category ?: '', ] } return [pageName: '搜索', extracted: rows, message: "共 ${rows.size()} 条"] } // ==================== mediabot 运行时入口 ==================== Map sync(PluginContext ctx) { if (!ctx.site.authorization) return [message: "未配置 Token", loginRequired: true] try { def resp = apiGet(ctx, "/api/member/profile") if (resp.statusCode() == 403 || resp.statusCode() == 401) return [message: "Token 无效", loginRequired: true] if (resp.statusCode() != 200) return [message: "HTTP ${resp.statusCode()}", siteUnavailable: true] def json = new groovy.json.JsonSlurper().parseText(resp.body()) if (json.message != "SUCCESS" || !json.data) return [message: "响应格式异常"] def d = json.data return [ upload: d.uploaded ? (d.uploaded as long) * 1073741824 : null, download: d.downloaded ? (d.downloaded as long) * 1073741824 : null, bonus: d.bonus ? d.bonus as double : null, seeding: d.seeding ? d.seeding as int : null, seedingSize: d.seedingSize? (d.seedingSize as long) * 1073741824: null, ] } catch (e) { return [message: "同步失败: ${e.message}"] } } Map sign(PluginContext ctx) { return [message: "M-Team 不支持签到"] } List search(PluginContext ctx, Map params) { if (!ctx.site.authorization) return [] try { def keyword = params.keyword ?: "" def page = (params.page ?: 1) as int def body = groovy.json.JsonOutput.toJson([ keyword: keyword, pageNumber: page, pageSize: 50, visible: 1 ]) def resp = apiPost(ctx, "/api/torrent/search", body) if (resp.statusCode() != 200) return [] def json = new groovy.json.JsonSlurper().parseText(resp.body()) def list = json?.data?.data ?: [] return list.collect { item -> def st = item.status ?: [:] return [ title: item.name ?: "", detailUrl: "${ctx.siteUrl}detail/${item.id ?: ''}", size: (item.size ?: 0) as long, seeders: (st.seeders ?: 0) as int, leechers: (st.leechers ?: 0) as int, grabs: (st.timesCompleted ?: 0) as int, category: item.category ?: "", free: st.discount == "FREE" || st.discount == "_2X_FREE", ] } } catch (e) { return [] } } // ==================== 工具方法 ==================== private def apiGet(PluginContext ctx, String path) { return ctx.httpClient.send( java.net.http.HttpRequest.newBuilder() .uri(new java.net.URI(API_BASE + path)) .timeout(java.time.Duration.ofSeconds(path.contains('profile') ? 10 : 20)) .header("User-Agent", "Mozilla/5.0") .header("x-api-key", ctx.site.authorization) .header("Origin", ctx.siteUrl) .POST(java.net.http.HttpRequest.BodyPublishers.noBody()) .build(), java.net.http.HttpResponse.BodyHandlers.ofString()) } private def apiPost(PluginContext ctx, String path, String body) { return ctx.httpClient.send( java.net.http.HttpRequest.newBuilder() .uri(new java.net.URI(API_BASE + path)) .timeout(java.time.Duration.ofSeconds(20)) .header("User-Agent", "Mozilla/5.0") .header("x-api-key", ctx.site.authorization) .header("Content-Type", "application/json") .POST(java.net.http.HttpRequest.BodyPublishers.ofString(body)) .build(), java.net.http.HttpResponse.BodyHandlers.ofString()) }