Skip to content

chore: restart bot on network error #214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/app-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/layout/header/account-switcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
22 changes: 19 additions & 3 deletions src/external/bot-skeleton/scratch/dbot.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
153 changes: 115 additions & 38 deletions src/external/bot-skeleton/services/api/api-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -40,6 +39,8 @@ type TApiBaseApi = {
};
} & ReturnType<typeof generateDerivApiInstance>;

let reconnect_timeout: NodeJS.Timeout | null = null;

class APIBase {
api: TApiBaseApi | null = null;
token: string = '';
Expand All @@ -57,64 +58,134 @@ class APIBase {
active_symbols_promise: Promise<void> | 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() {
Expand All @@ -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() {
Expand All @@ -139,17 +210,23 @@ class APIBase {

async createNewInstance(account_id: string) {
if (this.account_id !== account_id) {
await this.init();
await this.initApi(true);
}
}

reconnectIfNotConnected = () => {
// 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);
}
};

Expand Down Expand Up @@ -208,7 +285,7 @@ class APIBase {
return subscription;
},
[],
this
this.api
);
};

Expand Down
14 changes: 7 additions & 7 deletions src/external/bot-skeleton/services/api/appId.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/external/bot-skeleton/services/api/ticks_service.js
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
5 changes: 3 additions & 2 deletions src/external/bot-skeleton/services/tradeEngine/trade/Ticks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
10 changes: 5 additions & 5 deletions src/pages/main/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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]);
Expand Down
2 changes: 2 additions & 0 deletions src/stores/app-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -186,6 +187,7 @@ export default class AppStore {
};

onUnmount = () => {
console.log('test onUnmount ---------');
DBot.terminateBot();
DBot.terminateConnection();
if (window.Blockly?.derivWorkspace) {
Expand Down
Loading