import 'dart:convert'; class HotgameBridgeActions { static const String recharge = 'recharge'; static const String quit = 'quit'; static const String loadComplete = 'loadComplete'; static const String debugLog = 'debugLog'; static String normalize(String action) { final trimmed = action.trim(); final lower = trimmed.toLowerCase(); switch (lower) { case 'recharge': case 'pay': case 'gamepay': case 'gamerecharge': case 'insufficient': case 'insufficientbalance': return recharge; case 'quit': case 'exit': case 'close': case 'closegame': case 'destroy': return quit; case 'loadcomplete': case 'gameloaded': case 'loaded': case 'ready': return loadComplete; case 'debuglog': return debugLog; default: return trimmed; } } } class HotgameBridgeMessage { const HotgameBridgeMessage({ required this.action, this.payload = const {}, }); final String action; final Map payload; static HotgameBridgeMessage fromAction(String action, String rawMessage) { return HotgameBridgeMessage( action: HotgameBridgeActions.normalize(action), payload: _parsePayload(rawMessage), ); } static HotgameBridgeMessage parse(String rawMessage) { try { final dynamic decoded = jsonDecode(rawMessage); if (decoded is Map) { return HotgameBridgeMessage( action: HotgameBridgeActions.normalize( (decoded['action'] ?? decoded['cmd'] ?? decoded['event'] ?? decoded['method'] ?? decoded['type'] ?? '') .toString(), ), payload: _parsePayload( decoded['payload'] ?? decoded['data'] ?? decoded['params'], ), ); } } catch (_) {} return HotgameBridgeMessage( action: HotgameBridgeActions.normalize(rawMessage), ); } static Map _parsePayload(dynamic rawPayload) { if (rawPayload is Map) { return rawPayload; } if (rawPayload is String) { final trimmed = rawPayload.trim(); if (trimmed.isEmpty) { return const {}; } try { final dynamic decoded = jsonDecode(trimmed); if (decoded is Map) { return decoded; } } catch (_) {} return {'raw': trimmed}; } return const {}; } } class HotgameJsBridge { static const String channelName = 'HotgameBridgeChannel'; static String bootstrapScript({Map? launchPayload}) { final safeLaunchPayload = jsonEncode( launchPayload ?? const {}, ); return ''' (function() { const launchPayload = $safeLaunchPayload; function toPayload(input) { if (typeof input === 'string') { try { return JSON.parse(input); } catch (_) { return input.trim() ? { raw: input.trim() } : {}; } } if (input && typeof input === 'object') { return input; } return {}; } function stringifyForLog(value) { if (value == null) { return ''; } if (typeof value === 'string') { return value.length > 320 ? value.slice(0, 320) + '...' : value; } try { const text = JSON.stringify(value); return text.length > 320 ? text.slice(0, 320) + '...' : text; } catch (_) { return String(value); } } function postMessage(action, input) { const payload = toPayload(input); $channelName.postMessage(JSON.stringify({ action: action, payload: payload })); return payload; } function postDebug(tag, payload) { postMessage('${HotgameBridgeActions.debugLog}', { tag: tag, message: stringifyForLog(payload) }); } function dispatchLaunchPayload(payload) { const normalizedPayload = payload && typeof payload === 'object' ? payload : {}; const launchConfig = normalizedPayload.launchConfig && typeof normalizedPayload.launchConfig === 'object' ? normalizedPayload.launchConfig : {}; const entry = normalizedPayload.entry && typeof normalizedPayload.entry === 'object' ? normalizedPayload.entry : {}; window.__hotgameLaunchPayload = normalizedPayload; window.hotgameLaunchPayload = normalizedPayload; window.__hotgameLaunchConfig = launchConfig; window.hotgameLaunchConfig = launchConfig; window.__hotgameLaunchEntry = entry; window.hotgameLaunchEntry = entry; if (typeof window.onHotgameLaunchConfig === 'function') { window.onHotgameLaunchConfig(launchConfig, normalizedPayload); } if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') { window.dispatchEvent( new CustomEvent('hotgameLaunchConfig', { detail: launchConfig }) ); } } function installPostMessageObject(name, action) { const existing = window[name]; if (!existing || typeof existing.postMessage !== 'function') { window[name] = { postMessage: function(body) { return postMessage(action, body); } }; } } function installWebkitHandler(name, action) { window.webkit = window.webkit || {}; window.webkit.messageHandlers = window.webkit.messageHandlers || {}; if (!window.webkit.messageHandlers[name] || typeof window.webkit.messageHandlers[name].postMessage !== 'function') { window.webkit.messageHandlers[name] = { postMessage: function(body) { return postMessage(action, body); } }; } } function installJsBridgeFunction(name, action) { window.JsBridge = window.JsBridge || {}; window.JsBridge[name] = function(body) { return postMessage(action, body || {}); }; } if (!window.__hotgameBridgeReady) { window.__hotgameBridgeReady = true; installJsBridgeFunction('recharge', '${HotgameBridgeActions.recharge}'); installJsBridgeFunction('quit', '${HotgameBridgeActions.quit}'); installJsBridgeFunction('exit', '${HotgameBridgeActions.quit}'); installJsBridgeFunction('closeGame', '${HotgameBridgeActions.quit}'); installPostMessageObject('recharge', '${HotgameBridgeActions.recharge}'); installPostMessageObject('quit', '${HotgameBridgeActions.quit}'); installPostMessageObject('loadComplete', '${HotgameBridgeActions.loadComplete}'); installWebkitHandler('recharge', '${HotgameBridgeActions.recharge}'); installWebkitHandler('quit', '${HotgameBridgeActions.quit}'); installWebkitHandler('loadComplete', '${HotgameBridgeActions.loadComplete}'); window.NativeBridge = window.NativeBridge || {}; window.NativeBridge.recharge = function(body) { return postMessage('${HotgameBridgeActions.recharge}', body || {}); }; window.NativeBridge.quit = function(body) { return postMessage('${HotgameBridgeActions.quit}', body || {}); }; window.NativeBridge.closeGame = function(body) { return postMessage('${HotgameBridgeActions.quit}', body || {}); }; window.NativeBridge.rechargeSuccess = function(body) { return notifyRechargeSuccess(body || {}); }; window.__hotgameBridgeDebug = postDebug; postDebug('bridge.ready', { href: window.location && window.location.href, hasJsBridge: !!window.JsBridge, hasRechargeSuccess: typeof window.rechargeSuccess === 'function' }); } function notifyRechargeSuccess(body) { const payload = body || {}; let notified = false; if (typeof window.rechargeSuccess === 'function') { window.rechargeSuccess(); notified = true; } if (typeof window.onRechargeSuccess === 'function') { window.onRechargeSuccess(payload); notified = true; } if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') { window.dispatchEvent( new CustomEvent('rechargeSuccess', { detail: payload }) ); } return notified; } window.__hotgameNotifyRechargeSuccess = notifyRechargeSuccess; dispatchLaunchPayload(launchPayload); })(); '''; } static String buildRechargeSuccessScript({Map? payload}) { final safePayload = jsonEncode( payload ?? {'timestamp': DateTime.now().millisecondsSinceEpoch}, ); return ''' (function() { const payload = $safePayload; if (typeof window.__hotgameBridgeDebug === 'function') { window.__hotgameBridgeDebug('recharge.success', payload); } if (typeof window.__hotgameNotifyRechargeSuccess === 'function') { window.__hotgameNotifyRechargeSuccess(payload); return; } if (typeof window.rechargeSuccess === 'function') { window.rechargeSuccess(); } if (typeof window.onRechargeSuccess === 'function') { window.onRechargeSuccess(payload); } if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') { window.dispatchEvent( new CustomEvent('rechargeSuccess', { detail: payload }) ); } })(); '''; } }