feat: Random.org dice and daily word matrix, interpreter voices, curated lexicon
Wire cleromancy rolls and the once-per-UTC-day 000-999 word shuffle to RANDOM.ORG JSON-RPC, with a local CSPRNG fallback. Add settings for interpreter personas and a cleaner default oracle prompt. Replace proper names and bureaucratic filler in the 1000-word lexicon with sacred symbols.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../models/interpreter_persona.dart';
|
||||
import '../secrets.dart';
|
||||
|
||||
class SettingsRepository extends ChangeNotifier {
|
||||
static final SettingsRepository _instance = SettingsRepository._internal();
|
||||
factory SettingsRepository() => _instance;
|
||||
SettingsRepository._internal();
|
||||
|
||||
static const _kPersonaKey = 'oracle_interpreter_persona_v1';
|
||||
static const _kRandomOrgKey = 'oracle_random_org_api_key_v1';
|
||||
|
||||
String _personaId = InterpreterPersona.defaultId;
|
||||
String _randomOrgApiKey = '';
|
||||
bool _ready = false;
|
||||
|
||||
bool get isReady => _ready;
|
||||
String get personaId => _personaId;
|
||||
InterpreterPersona get persona => InterpreterPersona.byId(_personaId);
|
||||
String get randomOrgApiKey => _randomOrgApiKey;
|
||||
bool get hasRandomOrgApiKey => _randomOrgApiKey.trim().isNotEmpty;
|
||||
|
||||
String get randomOrgApiKeyMasked {
|
||||
final key = _randomOrgApiKey.trim();
|
||||
if (key.isEmpty) return '';
|
||||
if (key.length <= 8) return '••••';
|
||||
return '${key.substring(0, 4)}••••${key.substring(key.length - 4)}';
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
_personaId = prefs.getString(_kPersonaKey) ?? InterpreterPersona.defaultId;
|
||||
_randomOrgApiKey = prefs.getString(_kRandomOrgKey) ?? '';
|
||||
if (_randomOrgApiKey.trim().isEmpty) {
|
||||
const fromEnv = String.fromEnvironment(
|
||||
'RANDOM_ORG_API_KEY',
|
||||
defaultValue: '',
|
||||
);
|
||||
final seeded = fromEnv.isNotEmpty ? fromEnv : kLocalRandomOrgApiKey;
|
||||
if (seeded.trim().isNotEmpty) {
|
||||
_randomOrgApiKey = seeded.trim();
|
||||
await prefs.setString(_kRandomOrgKey, _randomOrgApiKey);
|
||||
}
|
||||
}
|
||||
} catch (_) {
|
||||
_personaId = InterpreterPersona.defaultId;
|
||||
if (_randomOrgApiKey.isEmpty) {
|
||||
_randomOrgApiKey = kLocalRandomOrgApiKey.trim();
|
||||
}
|
||||
}
|
||||
_ready = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> setPersonaId(String id) async {
|
||||
final next = InterpreterPersona.byId(id).id;
|
||||
if (next == _personaId && _ready) return;
|
||||
_personaId = next;
|
||||
notifyListeners();
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_kPersonaKey, next);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> setRandomOrgApiKey(String key) async {
|
||||
final next = key.trim();
|
||||
if (next == _randomOrgApiKey && _ready) return;
|
||||
_randomOrgApiKey = next;
|
||||
notifyListeners();
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
if (next.isEmpty) {
|
||||
await prefs.remove(_kRandomOrgKey);
|
||||
} else {
|
||||
await prefs.setString(_kRandomOrgKey, next);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user