import { Injectable } from '@nestjs/common'; import * as http from 'node:http'; import * as https from 'node:https'; export type QqbotPluginHttpClientRequest = { body?: Buffer | string; context?: string; failureMessage?: (statusCode: number) => string; headers?: Record; invalidJsonMessage?: string; method?: string; timeoutMessage?: string; timeoutMs?: number; url: string | URL; }; @Injectable() export class QqbotPluginHttpClientService { async requestJson(input: QqbotPluginHttpClientRequest): Promise { const body = await this.requestText(input); try { return JSON.parse(body) as T; } catch { throw new Error( input.invalidJsonMessage || `${input.context || '插件 HTTP 接口'}返回不是合法 JSON`, ); } } requestText(input: QqbotPluginHttpClientRequest): Promise { const url = input.url instanceof URL ? input.url : new URL(input.url); const method = input.method || 'GET'; const timeoutMs = input.timeoutMs || 8000; const context = input.context || '插件 HTTP 接口'; return new Promise((resolve, reject) => { const client = url.protocol === 'http:' ? http : https; const request = client.request( url, { headers: { Accept: 'application/json', 'User-Agent': 'kt-template-online-api/qqbot-plugin', ...(input.headers || {}), }, method, timeout: timeoutMs, }, (response) => { let responseBody = ''; response.setEncoding('utf8'); response.on('data', (chunk) => { responseBody += chunk; }); response.on('end', () => { const statusCode = response.statusCode || 500; if (statusCode >= 400) { reject( new Error( input.failureMessage?.(statusCode) || `${context}请求失败:${statusCode}`, ), ); return; } resolve(responseBody); }); }, ); request.on('timeout', () => { request.destroy( new Error(input.timeoutMessage || `${context}请求超时`), ); }); request.on('error', reject); if (input.body) request.write(input.body); request.end(); }); } }