Skip to content
This repository was archived by the owner on Feb 21, 2026. It is now read-only.

Commit 75dcf7c

Browse files
authored
Merge pull request #742 from Drakkar-Software/dev
Master merge
2 parents 3bbe302 + 1bf9d13 commit 75dcf7c

File tree

23 files changed

+322
-61
lines changed

23 files changed

+322
-61
lines changed

Services/Interfaces/telegram_bot_interface/telegram_bot.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,12 @@ def command_market_status(update, _):
265265
"evaluations, please retry in a few seconds.`")
266266

267267
@staticmethod
268-
def command_error(update, _, error=None):
268+
def command_error(update, context, error=None):
269+
ctx_error = context.error if hasattr(context, 'error') else None
270+
if update is None and error is None and ctx_error is not None:
271+
TelegramBotInterface.get_logger().error(f"Telegram bot error: {ctx_error}")
272+
return
273+
error = error or ctx_error
269274
TelegramBotInterface.get_logger().warning("Command receiver error. Please check logs for more details.") \
270275
if error is None else TelegramBotInterface.get_logger().exception(error, False)
271276
if update is not None and TelegramBotInterface._is_valid_user(update):

Services/Interfaces/web_interface/api/bots.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@
2020
import tentacles.Services.Interfaces.web_interface.api as api
2121
import tentacles.Services.Interfaces.web_interface.login as login
2222
import tentacles.Services.Interfaces.web_interface.models as models
23+
import tentacles.Services.Interfaces.web_interface.util as util
2324

2425

2526
@api.api.route("/select_bot", methods=['POST'])
2627
@login.login_required_when_activated
2728
def select_bot():
29+
if not models.can_select_bot():
30+
return util.get_rest_reply(flask.jsonify("Can't select bot on this setup"), 500)
2831
models.select_bot(flask.request.get_json())
2932
bot = models.get_selected_user_bot()
3033
flask.flash(f"Selected {bot['name']} bot", "success")
@@ -34,6 +37,8 @@ def select_bot():
3437
@api.api.route("/create_bot", methods=['POST'])
3538
@login.login_required_when_activated
3639
def create_bot():
40+
if not models.can_select_bot():
41+
return util.get_rest_reply(flask.jsonify("Can't create bot on this setup"), 500)
3742
new_bot = models.create_new_bot()
3843
models.select_bot(community.CommunityUserAccount.get_bot_id(new_bot))
3944
bot = models.get_selected_user_bot()

Services/Interfaces/web_interface/controllers/community.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ def community():
5050
all_user_bots=models.get_all_user_bots(),
5151
selected_user_bot=models.get_selected_user_bot(),
5252
default_tentacles_package_image=default_image,
53-
can_logout=not authentication.Authenticator.instance().must_be_authenticated_through_authenticator()
53+
can_logout=models.can_logout(),
54+
can_select_bot=models.can_select_bot(),
5455
)
5556

5657

Services/Interfaces/web_interface/controllers/community_authentication.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ def community_login():
6060
@web_interface.server_instance.route("/community_logout")
6161
@login.login_required_when_activated
6262
def community_logout():
63-
if authentication.Authenticator.instance().must_be_authenticated_through_authenticator():
64-
# can't logout when authentication is required
63+
if models.can_logout():
6564
return flask.redirect(flask.url_for('community'))
6665
authentication.Authenticator.instance().logout()
6766
interfaces_util.run_in_bot_main_loop(

Services/Interfaces/web_interface/controllers/configuration.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ def profile():
4747
missing_tentacles = set()
4848
profiles = models.get_profiles()
4949
config_exchanges = display_config[commons_constants.CONFIG_EXCHANGES]
50-
50+
enabled_exchanges = [
51+
exchange
52+
for exchange, exchange_config in config_exchanges.items()
53+
if exchange_config.get(commons_constants.CONFIG_ENABLED_OPTION, True)
54+
]
5155
return flask.render_template('profile.html',
5256
current_profile=current_profile,
5357
profiles=profiles,
@@ -63,9 +67,7 @@ def profile():
6367

6468
real_trader_activated=interfaces_util.has_real_and_or_simulated_traders()[0],
6569

66-
symbol_list=sorted(models.get_symbol_list([exchange
67-
for exchange in display_config[
68-
commons_constants.CONFIG_EXCHANGES]])),
70+
symbol_list=sorted(models.get_symbol_list(enabled_exchanges)),
6971
full_symbol_list=models.get_all_symbols_dict(),
7072
evaluator_config=models.get_evaluator_detailed_config(media_url, missing_tentacles),
7173
strategy_config=models.get_strategy_config(media_url, missing_tentacles),

Services/Interfaces/web_interface/controllers/trading.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,11 @@ def trading():
8484
watched_symbols=models.get_watched_symbols(),
8585
pairs_with_status=interfaces_util.get_currencies_with_status(),
8686
has_real_trader=has_real_trader,
87-
exchanges_load=exchanges_load)
87+
exchanges_load=exchanges_load,
88+
is_community_feed_connected=models.is_community_feed_connected(),
89+
last_signal_time=models.get_last_signal_time(),
90+
followed_strategy_url=models.get_followed_strategy_url(),
91+
)
8892

8993

9094
@web_interface.server_instance.route("/trades")

Services/Interfaces/web_interface/models/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@
6565
get_selected_user_bot,
6666
select_bot,
6767
create_new_bot,
68+
can_select_bot,
69+
can_logout,
70+
get_followed_strategy_url,
71+
is_community_feed_connected,
72+
get_last_signal_time,
6873
)
6974
from tentacles.Services.Interfaces.web_interface.models.configuration import (
7075
get_evaluators_tentacles_startup_activation,
@@ -225,6 +230,11 @@
225230
"get_selected_user_bot",
226231
"select_bot",
227232
"create_new_bot",
233+
"can_select_bot",
234+
"can_logout",
235+
"get_followed_strategy_url",
236+
"is_community_feed_connected",
237+
"get_last_signal_time",
228238
"get_evaluators_tentacles_startup_activation",
229239
"get_trading_tentacles_startup_activation",
230240
"get_tentacle_documentation",

Services/Interfaces/web_interface/models/community.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
# License along with this library.
1616
import octobot_services.interfaces.util as interfaces_util
1717
import octobot.community as octobot_community
18+
import octobot.constants as octobot_constants
1819
import octobot_commons.authentication as authentication
20+
import octobot_trading.api as trading_api
1921

2022

2123
def get_community_metrics_to_display():
@@ -83,3 +85,31 @@ def select_bot(bot_id):
8385

8486
def create_new_bot():
8587
return interfaces_util.run_in_bot_main_loop(authentication.Authenticator.instance().create_new_bot())
88+
89+
90+
def can_select_bot():
91+
return not octobot_constants.COMMUNITY_BOT_ID
92+
93+
94+
def can_logout():
95+
return not authentication.Authenticator.instance().must_be_authenticated_through_authenticator()
96+
97+
98+
def get_followed_strategy_url():
99+
trading_mode = interfaces_util.get_bot_api().get_trading_mode()
100+
if trading_mode is None:
101+
return None
102+
identifier = trading_api.get_trading_mode_followed_strategy_signals_identifier(trading_mode)
103+
if identifier is None:
104+
return None
105+
return authentication.Authenticator.instance().get_signal_community_url(
106+
identifier
107+
)
108+
109+
110+
def is_community_feed_connected():
111+
return authentication.Authenticator.instance().is_feed_connected()
112+
113+
114+
def get_last_signal_time():
115+
return authentication.Authenticator.instance().get_feed_last_message_time()

Services/Interfaces/web_interface/models/configuration.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@
8585

8686
DEFAULT_EXCHANGE = "binance"
8787

88+
89+
def _get_currency_dict(symbol, identifier):
90+
return {
91+
SYMBOL_KEY: symbol.upper(),
92+
ID_KEY: identifier
93+
}
94+
95+
96+
# forced cryptocurrencies to be displayed in currency selector
97+
FORCED_CURRENCIES_DICT = {
98+
"HollaEx": _get_currency_dict("XHT", "hollaex-token")
99+
}
100+
88101
# buffers to faster config page loading
89102
markets_by_exchanges = {}
90103
all_symbols_dict = {}
@@ -701,10 +714,11 @@ def get_all_symbols_dict():
701714
break
702715
for currency_data in request_response.json():
703716
if _is_legit_currency(currency_data[NAME_KEY]):
704-
all_symbols_dict[currency_data[NAME_KEY]] = {
705-
SYMBOL_KEY: currency_data[SYMBOL_KEY].upper(),
706-
ID_KEY: currency_data[ID_KEY]
707-
}
717+
all_symbols_dict[currency_data[NAME_KEY]] = _get_currency_dict(
718+
currency_data[SYMBOL_KEY],
719+
currency_data[ID_KEY]
720+
)
721+
_add_forced_currencies(all_symbols_dict)
708722
except Exception as e:
709723
details = f"code: {request_response.status_code}, body: {request_response.text}" \
710724
if request_response else {request_response}
@@ -714,6 +728,10 @@ def get_all_symbols_dict():
714728
return all_symbols_dict
715729

716730

731+
def _add_forced_currencies(symbols_dict):
732+
symbols_dict.update(FORCED_CURRENCIES_DICT)
733+
734+
717735
def get_exchange_logo(exchange_name):
718736
try:
719737
return exchange_logos[exchange_name]

Services/Interfaces/web_interface/static/js/components/configuration.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ function _save_config(element, restart_after_save) {
400400
const new_value = parse_new_value(configElement);
401401
const config_key = get_config_key(configElement);
402402

403-
if (get_config_value_changed(configElement, new_value, config_key)) {
403+
if (get_config_value_changed(configElement, new_value, config_key)
404+
&& !config_key.endsWith("_Empty")) {
404405
updated_config[config_type][config_key] = new_value;
405406
}
406407
}

0 commit comments

Comments
 (0)