diff --git a/src/app/app-root.tsx b/src/app/app-root.tsx index 7ea13967..d415f351 100644 --- a/src/app/app-root.tsx +++ b/src/app/app-root.tsx @@ -42,7 +42,7 @@ const AppRoot = () => { useEffect(() => { const initializeApi = async () => { if (!api_base_initialized.current) { - await api_base.init(); + await api_base.initApi(); api_base_initialized.current = true; setIsApiInitialized(true); } diff --git a/src/components/layout/header/account-switcher.tsx b/src/components/layout/header/account-switcher.tsx index 0f36c9a1..09f2212a 100644 --- a/src/components/layout/header/account-switcher.tsx +++ b/src/components/layout/header/account-switcher.tsx @@ -138,7 +138,7 @@ const AccountSwitcher = observer(({ activeAccount }: TAccountSwitcher) => { if (!token) return; localStorage.setItem('authToken', token); localStorage.setItem('active_loginid', loginId.toString()); - await api_base?.init(true); + await api_base?.initApi(true); }; return ( diff --git a/src/external/bot-skeleton/scratch/dbot.js b/src/external/bot-skeleton/scratch/dbot.js index 58b9a353..c40d66d6 100644 --- a/src/external/bot-skeleton/scratch/dbot.js +++ b/src/external/bot-skeleton/scratch/dbot.js @@ -25,6 +25,7 @@ class DBot { * Initialises the workspace and mounts it to a container element (app_contents). */ async initWorkspace(public_path, store, api_helpers_store, is_mobile, is_dark_mode) { + console.log('test initWorkspace ---------', this.interpreter); await loadBlockly(is_dark_mode); const recent_files = await getSavedWorkspaces(); this.interpreter = Interpreter(); @@ -246,10 +247,25 @@ class DBot { } async initializeInterpreter() { - if (this.interpreter) { - await this.interpreter.terminateSession(); + console.log('test initializeInterpreter ---------', this.interpreter, this.is_bot_running); + if (this.is_bot_running) { + this.interpreter?.unsubscribeFromTicksService?.()?.then(async () => { + await this.interpreter?.bot?.tradeEngine?.watchTicks(this.interpreter?.bot?.tradeEngine?.symbol, true); + console.log('test making proposal open contract request ---------', { + proposal_open_contract: 1, + contract_id: this.interpreter.bot.tradeEngine.contractId, + }); + api_base.api.send({ + proposal_open_contract: 1, + contract_id: this.interpreter.bot.tradeEngine.contractId, + }); + }); + } else { + if (this.interpreter) { + await this.interpreter.terminateSession(); + } + this.interpreter = Interpreter(); } - this.interpreter = Interpreter(); } /** * Runs the bot. Does a sanity check before attempting to generate the diff --git a/src/external/bot-skeleton/services/api/api-base.ts b/src/external/bot-skeleton/services/api/api-base.ts index 75ac4081..8bd7ceaf 100644 --- a/src/external/bot-skeleton/services/api/api-base.ts +++ b/src/external/bot-skeleton/services/api/api-base.ts @@ -12,7 +12,6 @@ import { } from './observables/connection-status-stream'; import ApiHelpers from './api-helpers'; import { generateDerivApiInstance, V2GetActiveClientId, V2GetActiveToken } from './appId'; -import chart_api from './chart-api'; type CurrentSubscription = { id: string; @@ -40,6 +39,8 @@ type TApiBaseApi = { }; } & ReturnType; +let reconnect_timeout: NodeJS.Timeout | null = null; + class APIBase { api: TApiBaseApi | null = null; token: string = ''; @@ -57,64 +58,134 @@ class APIBase { active_symbols_promise: Promise | null = null; common_store: CommonStore | undefined; landing_company: string | null = null; + //TODO : Need to remove this api call because we have it in client store + async getLandingCompany() { + if (!this.api || !this.account_info?.country) { + return null; + } + try { + const landing_company = await this.api.send({ landing_company: this.account_info.country }); + this.landing_company = landing_company; + } catch (error) { + console.error('Error fetching landing company:', error); + this.landing_company = null; + } + return this.landing_company; + } unsubscribeAllSubscriptions = () => { - this.current_auth_subscriptions?.forEach(subscription_promise => { - subscription_promise.then(({ subscription }) => { - if (subscription?.id) { - this.api?.send({ - forget: subscription.id, - }); - } - }); - }); + // this.current_auth_subscriptions?.forEach(subscription_promise => { + // subscription_promise.then(({ subscription }) => { + // if (subscription?.id) { + // this.api?.send({ + // forget: subscription.id, + // }); + // } + // }); + // }); this.current_auth_subscriptions = []; }; onsocketopen() { setConnectionStatus(CONNECTION_STATUS.OPENED); + console.log('test on open ---------'); } onsocketclose() { setConnectionStatus(CONNECTION_STATUS.CLOSED); this.reconnectIfNotConnected(); + const error = { + error: { + code: 'DisconnectError', + message: 'Connection lost', + }, + }; + globalObserver?.emit('Error', error); } - async init(force_create_connection = false) { + async initApi(force_create_connection = false) { this.toggleRunButton(true); if (this.api) { this.unsubscribeAllSubscriptions(); } - if (!this.api || this.api?.connection.readyState !== 1 || force_create_connection) { - if (this.api?.connection) { + if (!this.api || this.api?.connection.readyState > 1 || force_create_connection) { + if (this.api?.connection && !force_create_connection) { ApiHelpers.disposeInstance(); setConnectionStatus(CONNECTION_STATUS.CLOSED); - this.api.disconnect(); - this.api.connection.removeEventListener('open', this.onsocketopen.bind(this)); - this.api.connection.removeEventListener('close', this.onsocketclose.bind(this)); - } - this.api = generateDerivApiInstance(); - this.api?.connection.addEventListener('open', this.onsocketopen.bind(this)); - this.api?.connection.addEventListener('close', this.onsocketclose.bind(this)); - } + console.log('test ------------------- reconnect ---------'); + await this.api.reconnect(); + // this.api.connection.removeEventListener('open', this.onsocketopen.bind(this)); + // this.api.connection.removeEventListener('close', this.onsocketclose.bind(this)); + } else { + this.api?.disconnect(); + this.api = generateDerivApiInstance(); + console.log('test ------------------- new ---------', this.api.onOpen); + // Example: WebSocket API that returns Observable for 'open' events + api_base.api.onOpen().subscribe({ + next: openEvent => { + // This block is executed when WebSocket opens + console.log('WebSocket connection opened:', openEvent); + if (!this.has_active_symbols) { + this.active_symbols_promise = this.getActiveSymbols(); + } + + this.initEventListeners(); + + if (this.time_interval) clearInterval(this.time_interval); + this.time_interval = null; + + if (V2GetActiveToken()) { + setIsAuthorizing(true); + this.authorizeAndSubscribe(); + } + this.onsocketopen(); + }, + error: err => { + // Handle any errors (unlikely for WebSocket "open", but error handling is always good) + console.error('Error occurred while opening WebSocket:', err); + }, + complete: () => { + // Complete is called when the Observable completes or the connection closes + console.log('WebSocket onOpen event stream completed'); + }, + }); - if (!this.has_active_symbols) { - this.active_symbols_promise = this.getActiveSymbols(); - } + this.api.onClose().subscribe({ + next: closeEvent => { + console.log('WebSocket connection closed:', closeEvent); + this.onsocketclose(); + // throw new Error('DisconnectError'); + }, + error: err => { + console.error('Error occurred while closing WebSocket:', err); + }, + complete: () => { + console.log('WebSocket onClose event stream completed'); + }, + }); - this.initEventListeners(); + // Subscribing to the 'open' event + // const openSubscription: Subscription = openObservable; + // this.api?.connection.addEventListener('open', this.onsocketopen.bind(this)); + // this.api?.connection.addEventListener('close', this.onsocketclose.bind(this)); + // this.api.onClose(() => { + // console.log('test on close ---------'); + // }); + } + } - if (this.time_interval) clearInterval(this.time_interval); - this.time_interval = null; + // chart_api.init(force_create_connection); - if (V2GetActiveToken()) { - setIsAuthorizing(true); - await this.authorizeAndSubscribe(); + if (!reconnect_timeout) { + setInterval(() => { + console.log('-------- Disconnecting the socket ---------'); + // this.api?.disconnect(); + }, 30000); + } else { + reconnect_timeout = null; } - - chart_api.init(force_create_connection); } getConnectionStatus() { @@ -127,7 +198,7 @@ class APIBase { terminate() { // eslint-disable-next-line no-console - if (this.api) this.api.disconnect(); + // if (this.api) this.api.disconnect(); } initEventListeners() { @@ -139,7 +210,7 @@ class APIBase { async createNewInstance(account_id: string) { if (this.account_id !== account_id) { - await this.init(); + await this.initApi(true); } } @@ -147,9 +218,15 @@ class APIBase { // eslint-disable-next-line no-console console.log('connection state: ', this.api?.connection?.readyState); if (this.api?.connection?.readyState && this.api?.connection?.readyState > 1) { - // eslint-disable-next-line no-console - console.log('Info: Connection to the server was closed, trying to reconnect.'); - this.init(true); + // Debounce reconnection attempts to avoid multiple rapid reconnects + if (reconnect_timeout) { + clearTimeout(reconnect_timeout as NodeJS.Timeout); + } + reconnect_timeout = setTimeout(() => { + // eslint-disable-next-line no-console + console.log('Info: Connection to the server was closed, trying to reconnect.'); + this.initApi(); + }, 3000); } }; @@ -208,7 +285,7 @@ class APIBase { return subscription; }, [], - this + this.api ); }; diff --git a/src/external/bot-skeleton/services/api/appId.js b/src/external/bot-skeleton/services/api/appId.js index 318451e4..cc3243f1 100644 --- a/src/external/bot-skeleton/services/api/appId.js +++ b/src/external/bot-skeleton/services/api/appId.js @@ -1,16 +1,16 @@ -import { getAppId, getSocketURL } from '@/components/shared'; -import { website_name } from '@/utils/site-config'; +import { getAppId } from '@/components/shared'; import DerivAPIBasic from '@deriv/deriv-api/dist/DerivAPIBasic'; -import { getInitialLanguage } from '@deriv-com/translations'; import APIMiddleware from './api-middleware'; export const generateDerivApiInstance = () => { - const cleanedServer = getSocketURL().replace(/[^a-zA-Z0-9.]/g, ''); + // const cleanedServer = getSocketURL().replace(/[^a-zA-Z0-9.]/g, ''); const cleanedAppId = getAppId()?.replace?.(/[^a-zA-Z0-9]/g, '') ?? getAppId(); - const socket_url = `wss://${cleanedServer}/websockets/v3?app_id=${cleanedAppId}&l=${getInitialLanguage()}&brand=${website_name.toLowerCase()}`; - const deriv_socket = new WebSocket(socket_url); + // const socket_url = `wss://${cleanedServer}/websockets/v3?app_id=${cleanedAppId}&l=${getInitialLanguage()}&brand=${website_name.toLowerCase()}`; + // const deriv_socket = new WebSocket(socket_url); const deriv_api = new DerivAPIBasic({ - connection: deriv_socket, + // connection: deriv_socket, + app_id: cleanedAppId, + endpoint: 'ws.derivws.com', middleware: new APIMiddleware({}), }); return deriv_api; diff --git a/src/external/bot-skeleton/services/api/ticks_service.js b/src/external/bot-skeleton/services/api/ticks_service.js index 22b36e50..459dedcd 100644 --- a/src/external/bot-skeleton/services/api/ticks_service.js +++ b/src/external/bot-skeleton/services/api/ticks_service.js @@ -265,6 +265,7 @@ export default class TicksService { }; return new Promise((resolve, reject) => { if (!api_base.api) resolve([]); + console.log('test ------------------- requestTicks ---------', request_object); doUntilDone(() => api_base.api.send(request_object), [], api_base) .then(r => { if (style === 'ticks') { diff --git a/src/external/bot-skeleton/services/tradeEngine/trade/OpenContract.js b/src/external/bot-skeleton/services/tradeEngine/trade/OpenContract.js index 2634b14a..718ecf5e 100644 --- a/src/external/bot-skeleton/services/tradeEngine/trade/OpenContract.js +++ b/src/external/bot-skeleton/services/tradeEngine/trade/OpenContract.js @@ -9,6 +9,7 @@ export default Engine => if (!api_base.api) return; const subscription = api_base.api.onMessage().subscribe(({ data }) => { if (data.msg_type === 'proposal_open_contract') { + console.log('test ------------------- observeOpenContract ---------', data?.msg_type); const contract = data.proposal_open_contract; if (!contract || !this.expectedContractId(contract?.contract_id)) { diff --git a/src/external/bot-skeleton/services/tradeEngine/trade/Ticks.js b/src/external/bot-skeleton/services/tradeEngine/trade/Ticks.js index eea64dca..7cae4707 100644 --- a/src/external/bot-skeleton/services/tradeEngine/trade/Ticks.js +++ b/src/external/bot-skeleton/services/tradeEngine/trade/Ticks.js @@ -12,8 +12,9 @@ let tickListenerKey; export default Engine => class Ticks extends Engine { - async watchTicks(symbol) { - if (symbol && this.symbol !== symbol) { + async watchTicks(symbol, restartTrade = false) { + console.log('test ------------------- watchTicks ---------', symbol); + if (symbol && (this.symbol !== symbol || restartTrade)) { this.symbol = symbol; const { ticksService } = this.$scope; diff --git a/src/pages/main/main.tsx b/src/pages/main/main.tsx index 58877e91..3cbd0600 100644 --- a/src/pages/main/main.tsx +++ b/src/pages/main/main.tsx @@ -9,7 +9,7 @@ import MobileWrapper from '@/components/shared_ui/mobile-wrapper'; import Tabs from '@/components/shared_ui/tabs/tabs'; import TradingViewModal from '@/components/trading-view-chart/trading-view-modal'; import { DBOT_TABS, TAB_IDS } from '@/constants/bot-contents'; -import { api_base, updateWorkspaceName } from '@/external/bot-skeleton'; +import { updateWorkspaceName } from '@/external/bot-skeleton'; import { CONNECTION_STATUS } from '@/external/bot-skeleton/services/api/observables/connection-status-stream'; import { isDbotRTL } from '@/external/bot-skeleton/utils/workspace'; import { useApiBase } from '@/hooks/useApiBase'; @@ -75,10 +75,10 @@ const AppWrapper = observer(() => { if (connectionStatus !== CONNECTION_STATUS.OPENED) { const is_bot_running = document.getElementById('db-animation__stop-button') !== null; if (is_bot_running) { - clear(); - stopBot(); - api_base.setIsRunning(false); - setWebSocketState(false); + // clear(); + // stopBot(); + // api_base.setIsRunning(false); + // setWebSocketState(false); } } }, [clear, connectionStatus, setWebSocketState, stopBot]); diff --git a/src/stores/app-store.ts b/src/stores/app-store.ts index cc320ec8..f18128cb 100644 --- a/src/stores/app-store.ts +++ b/src/stores/app-store.ts @@ -139,6 +139,7 @@ export default class AppStore { }; onMount = async () => { + console.log('test onMount ---------'); const { blockly_store, run_panel } = this.root_store; const { client, ui } = this.core; this.showDigitalOptionsMaltainvestError(); @@ -186,6 +187,7 @@ export default class AppStore { }; onUnmount = () => { + console.log('test onUnmount ---------'); DBot.terminateBot(); DBot.terminateConnection(); if (window.Blockly?.derivWorkspace) {