import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:http/http.dart' as http; import '../http_client.dart'; import '../http_util.dart'; import '../models.dart'; import '../sse.dart'; import 'base_provider.dart'; /// ChatGPT-Next-Web / Free2GPT clones (`/api/generate` + md5 sign). class Free2Gpt extends BaseProvider { @override String get name => 'Free2Gpt'; @override bool get working => true; @override List get models => ['gpt-3.5-turbo', 'gpt-4o-mini', 'gemini']; @override String get defaultModel => 'gpt-3.5-turbo'; static const _hosts = [ 'https://chat.free2gpt.xyz', 'https://chatz.free2gpt.xyz', 'https://gpt.aifree.site', 'https://chat.aifree.site', 'https://gemini.free2gpt.com', ]; @override Stream createAsyncGenerator( String model, List messages, { bool stream = false, }) async* { Object? last; for (final host in _hosts) { try { yield await _generate(host, messages); return; } catch (e) { last = e; } } throw Exception('Free2Gpt failed: $last'); } Future _generate(String host, List messages) async { final lastUser = getLastUserMessage(messages); final t = DateTime.now().millisecondsSinceEpoch; final sign = md5.convert(utf8.encode('$t:$lastUser:undefined')).toString(); final payload = jsonEncode({ 'messages': toOpenAiMessages(messages), 'time': t, 'pass': null, 'sign': sign, }); final urls = [ '$host/api/generate', '$host/api/openai/v1/chat/completions', ]; Object? last; for (final url in urls) { try { final req = http.Request('POST', Uri.parse(url)); req.headers.addAll(browserHeaders( origin: host, referer: '$host/', contentType: 'application/json', )); req.body = payload; final res = await g4fHttp.send(req).timeout(const Duration(seconds: 14)); if (res.statusCode != 200) { last = 'HTTP ${res.statusCode}'; continue; } final raw = utf8.decode(await res.stream.toBytes().timeout(const Duration(seconds: 14))); final openai = extractOpenAiMessage(raw); if (openai != null && looksLikeModelOutput(openai)) return openai.trim(); final sse = extractAllSseContent(raw).join(); if (looksLikeModelOutput(sse)) return sse.trim(); if (looksLikeModelOutput(raw)) return raw.trim(); last = 'unusable'; } catch (e) { last = e; } } throw Exception('$host failed: $last'); } }