feat: embed a Flutter G4F client with Cronet, cookie jar, and origin WebView
Port the Oracle off the Python g4f container onto on-device providers: Chromium HTTP via Cronet, a shared cookie session, IterList retry, hosted OpenAI-compat aggregators, and same-origin WebView fallbacks instead of CORS fetch.
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../http_client.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import '../models.dart';
|
||||
import 'base_provider.dart';
|
||||
|
||||
class DeepAi extends BaseProvider {
|
||||
@override
|
||||
String get name => 'DeepAI';
|
||||
|
||||
@override
|
||||
String get label => 'DeepAI';
|
||||
|
||||
@override
|
||||
String get url => 'https://deepai.org/chat';
|
||||
|
||||
@override
|
||||
bool get working => true;
|
||||
|
||||
@override
|
||||
bool get supportsStream => true;
|
||||
|
||||
@override
|
||||
String get defaultModel => 'standard';
|
||||
|
||||
@override
|
||||
Map<String, String> get modelAliases => const {
|
||||
G4FModels.gpt4: 'standard',
|
||||
G4FModels.gpt4o: 'standard',
|
||||
G4FModels.gpt4oMini: 'standard',
|
||||
};
|
||||
|
||||
@override
|
||||
List<String> get models => [
|
||||
'standard',
|
||||
'online',
|
||||
'gemma-4',
|
||||
'gemini-2.5-flash-lite',
|
||||
'deepseek-v3.2',
|
||||
];
|
||||
|
||||
String _reverseString(String input) {
|
||||
return input.split('').reversed.join('');
|
||||
}
|
||||
|
||||
String _myHashFunction(String inputStr) {
|
||||
var bytes = utf8.encode(inputStr);
|
||||
var digest = md5.convert(bytes);
|
||||
return _reverseString(digest.toString());
|
||||
}
|
||||
|
||||
String _generateApiKey(String userAgent) {
|
||||
final random = Random();
|
||||
final myRandomStr = (random.nextDouble() * 100000000000).round().toString();
|
||||
|
||||
final hash1 = _myHashFunction(
|
||||
userAgent + myRandomStr + "hackers_become_a_little_stinkier_every_time_they_hack");
|
||||
final hash2 = _myHashFunction(userAgent + hash1);
|
||||
final hash3 = _myHashFunction(userAgent + hash2);
|
||||
|
||||
return "tryit-$myRandomStr-$hash3";
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<String> createAsyncGenerator(
|
||||
String model,
|
||||
List<Message> messages, {
|
||||
bool stream = true,
|
||||
}) async* {
|
||||
final userAgent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36';
|
||||
final apiKey = _generateApiKey(userAgent);
|
||||
|
||||
final headers = {
|
||||
'api-key': apiKey,
|
||||
'user-agent': userAgent,
|
||||
'origin': 'https://deepai.org',
|
||||
'referer': 'https://deepai.org/chat',
|
||||
};
|
||||
|
||||
final cleanedMessages = messages.map((m) => {'role': m.role.name, 'content': m.content}).toList();
|
||||
|
||||
var request = http.MultipartRequest('POST', Uri.parse('https://api.deepai.org/hacking_is_a_serious_crime'));
|
||||
request.headers.addAll(headers);
|
||||
|
||||
request.fields['chat_style'] = 'chat';
|
||||
request.fields['chatHistory'] = jsonEncode(cleanedMessages);
|
||||
request.fields['model'] = model;
|
||||
request.fields['session_uuid'] = const Uuid().v4();
|
||||
request.fields['sensitivity_request_id'] = const Uuid().v4();
|
||||
request.fields['hacker_is_stinky'] = 'very_stinky';
|
||||
request.fields['enabled_tools'] = jsonEncode(["image_generator", "image_editor"]);
|
||||
|
||||
final response = await g4fHttp.send(request).timeout(requestTimeout);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
final errorText = await response.stream.bytesToString();
|
||||
throw Exception('DeepAI API returned status: ${response.statusCode} $errorText');
|
||||
}
|
||||
|
||||
const fs = '\x1c';
|
||||
var buffer = '';
|
||||
await for (final chunk in response.stream.transform(utf8.decoder)) {
|
||||
if (chunk.isEmpty) continue;
|
||||
if (chunk.contains(fs) || buffer.contains(fs)) {
|
||||
buffer += chunk;
|
||||
final parts = buffer.split(fs);
|
||||
if (parts[0].trim().isNotEmpty) {
|
||||
yield parts[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
yield chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user