-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
250 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import 'dart:convert'; | ||
|
||
import 'package:domine/constants.dart'; | ||
import 'package:domine/misc.dart'; | ||
import 'package:domine/models.dart'; | ||
import 'package:http/http.dart'; | ||
|
||
Future<List<CheckedDomain>> batchCheck(Iterable<String> input) async { | ||
final futures = <Future>[]; | ||
|
||
for (final domain in input) { | ||
for (final variant in expand(domain)) { | ||
final parts = variant.split('.'); | ||
final name = parts.first; | ||
final tld = parts.sublist(1).join('.'); | ||
|
||
futures.add(check(name, tlds: tld == '*' ? asteriskTLDs : [tld])); | ||
} | ||
} | ||
|
||
final checks = await Future.wait(futures); | ||
|
||
return [ | ||
for (final check in checks) ...[for (final domain in check) domain] | ||
]; | ||
} | ||
|
||
Future<List<CheckedDomain>> check(String name, | ||
{required List<String> tlds}) async { | ||
final parameters = { | ||
'tlds': tlds.join(','), | ||
'hash': hash(name, 27).toString(), | ||
}; | ||
|
||
final dnsNamesReponse = await get( | ||
Uri.https( | ||
'instantdomainsearch.com', | ||
'/services/dns-names/$name', | ||
parameters, | ||
), | ||
); | ||
|
||
final zoneNamesReponse = await get( | ||
Uri.https( | ||
'instantdomainsearch.com', | ||
'/services/zone-names/$name', | ||
parameters, | ||
), | ||
); | ||
|
||
final decoded = [ | ||
if (zoneNamesReponse.body.isNotEmpty) | ||
...zoneNamesReponse.body.trim().split('\n'), | ||
if (dnsNamesReponse.body.isNotEmpty) | ||
...dnsNamesReponse.body.trim().split('\n'), | ||
].map((e) => jsonDecode(e)); | ||
|
||
return [ | ||
for (final check in decoded) | ||
CheckedDomain( | ||
name, | ||
check['tld'], | ||
status: switch (check['isRegistered']) { | ||
true => CheckStatus.taken, | ||
false => CheckStatus.available, | ||
_ => CheckStatus.unknown, | ||
}, | ||
) | ||
]; | ||
} | ||
|
||
int hash(String name, int t) { | ||
for (int o = 0; o < name.length; o++) { | ||
t = ((t << 5) - t + name.codeUnitAt(o)).toSigned(32); | ||
} | ||
|
||
return t; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:args/command_runner.dart'; | ||
import 'package:cli_spinner/cli_spinner.dart'; | ||
import 'package:dart_openai/dart_openai.dart'; | ||
import 'package:domine/checker.dart'; | ||
import 'package:domine/misc.dart'; | ||
import 'package:domine/models.dart'; | ||
import 'package:tint/tint.dart'; | ||
|
||
class BrainstormCommand extends Command { | ||
final List<CheckedDomain> _searches = []; | ||
|
||
@override | ||
String get description => 'Choose a domain with AI'; | ||
|
||
@override | ||
String get name => 'brainstorm'; | ||
|
||
@override | ||
List<String> get aliases => ['b']; | ||
|
||
BrainstormCommand() { | ||
argParser.addOption( | ||
'openai-key', | ||
abbr: 'k', | ||
help: 'OpenAI API key', | ||
); | ||
argParser.addOption( | ||
'model', | ||
abbr: 'm', | ||
help: 'GPT model', | ||
defaultsTo: 'gpt-3.5-turbo', | ||
); | ||
} | ||
|
||
@override | ||
void run() async { | ||
final results = argResults!; | ||
OpenAI.apiKey = Platform.environment['OPENAI_KEY'] ?? results['openai-key']; | ||
|
||
await _brainstorm(results.rest.join(' '), model: results['model']); | ||
} | ||
|
||
Future<void> _brainstorm(String prompt, {required String model}) async { | ||
domainTable(_searches); | ||
|
||
final spinner = | ||
Spinner.type('Synthesizing domains with GPT...', SpinnerType.dots) | ||
..start(); | ||
|
||
final response = await OpenAI.instance.chat.create( | ||
model: model, | ||
messages: [ | ||
OpenAIChatCompletionChoiceMessageModel( | ||
role: OpenAIChatMessageRole.system, | ||
content: [ | ||
'You are a creative AI that task is to find domains for your client.', | ||
'Example queries: superbakery.com, nudes4sale.co', | ||
'Domain search prompt: $prompt' | ||
].join('\n'), | ||
), | ||
if (_searches.isNotEmpty) | ||
OpenAIChatCompletionChoiceMessageModel( | ||
functionName: 'checkDomains', | ||
role: OpenAIChatMessageRole.function, | ||
content: 'Already been checked: ${_searches.join(', ')}', | ||
) | ||
], | ||
functionCall: FunctionCall.forFunction('checkDomains'), | ||
functions: [ | ||
OpenAIFunctionModel.withParameters( | ||
name: 'checkDomains', | ||
parameters: [ | ||
OpenAIFunctionProperty.array( | ||
name: 'queries', | ||
description: 'Must be at least 10 queries', | ||
items: OpenAIFunctionProperty.string( | ||
name: 'domain', | ||
description: 'Name and a TLD (example: google.com, nesper.co)', | ||
), | ||
) | ||
], | ||
), | ||
], | ||
); | ||
final message = response.choices.first.message; | ||
final queries = | ||
List<String>.from(message.functionCall!.arguments!['queries'].where( | ||
(e) => | ||
e.contains('.') && | ||
!e.endsWith('.') && | ||
!_searches.any((v) => v.toString() == e), | ||
)); | ||
|
||
if (queries.isNotEmpty) { | ||
spinner.updateMessage('Checking ${queries.length} domains...'); | ||
final checks = await batchCheck(queries); | ||
_searches.addAll(checks); | ||
} | ||
|
||
spinner | ||
..updateMessage('Currently, ${_searches.length} domains have been checked' | ||
.dim() | ||
.underline()) | ||
..stop(); | ||
|
||
await _brainstorm(prompt, model: model); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.