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

Commit cc1a26d

Browse files
authored
Merge pull request #1296 from Drakkar-Software/dev
Dev merge
2 parents e2cc4b7 + 34c877c commit cc1a26d

File tree

7 files changed

+165
-13
lines changed

7 files changed

+165
-13
lines changed

Services/Interfaces/web_interface/advanced_templates/advanced_layout.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
{% import 'components/community/user_details.html' as m_user_details %}
2+
13
<!doctype html>
24
<html lang="en" data-mdb-theme="{{get_color_mode()}}">
35
{% set active_page = active_page|default('advanced.home') -%}
@@ -42,6 +44,14 @@
4244
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css', u=LAST_UPDATED_STATIC_FILES) }}">
4345
<link rel="stylesheet" href="{{ url_for('static', filename='css/layout.css', u=LAST_UPDATED_STATIC_FILES) }}">
4446

47+
{% if IS_DEMO or IS_CLOUD or IS_ALLOWING_TRACKING%}
48+
<script>
49+
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
50+
posthog.init('{{PH_TRACKING_ID}}',{api_host:'https://eu.i.posthog.com',
51+
})
52+
</script>
53+
{% endif %}
54+
4555
{% block additional_style %}
4656
{% endblock additional_style %}
4757
</head>
@@ -67,6 +77,7 @@
6777
<script src="{{ url_for('static', filename='js/common/bot_connection.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
6878
<script src="{{ url_for('static', filename='js/common/dom_updater.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
6979
<script src="{{ url_for('static', filename='js/common/required.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
80+
<script src="{{ url_for('static', filename='js/common/tracking.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
7081

7182
<nav class="navbar navbar-expand-md py-0 py-md-2" id="main-nav-bar">
7283
<div class="navbar-collapse collapse w-100 order-1 order-md-0 dual-collapse2">
@@ -125,5 +136,15 @@
125136

126137
{% block additional_scripts %}
127138
{% endblock additional_scripts %}
139+
{{ m_user_details.user_details(
140+
IS_ALLOWING_TRACKING,
141+
USER_EMAIL,
142+
USER_SELECTED_BOT_ID,
143+
has_open_source_package,
144+
PROFILE_NAME,
145+
TRADING_MODE_NAME,
146+
EXCHANGE_NAMES,
147+
IS_REAL_TRADING
148+
) }}
128149
</body>
129150
</html>

Services/Interfaces/web_interface/flask_util/context_processor.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@
1515
# License along with this library.
1616
import octobot_commons.symbols.symbol_util as symbol_util
1717
import octobot_commons.constants as commons_constants
18+
import octobot_commons.authentication as authentication
1819
import octobot.constants as constants
1920
import octobot.enums as enums
2021
import octobot.community.identifiers_provider as identifiers_provider
22+
import octobot.community.supabase_backend.enums as community_enums
2123
import tentacles.Services.Interfaces.web_interface.models as models
2224
import tentacles.Services.Interfaces.web_interface.models.configuration as configuration_model
2325
import tentacles.Services.Interfaces.web_interface.enums as web_enums
@@ -122,6 +124,18 @@ def get_enabled_tentacles(tentacles_info_by_name):
122124
if info[web_constants.ACTIVATION_KEY]:
123125
return name
124126

127+
def get_logged_in_email():
128+
try:
129+
return authentication.Authenticator.instance().get_logged_in_email()
130+
except (authentication.AuthenticationRequired, authentication.UnavailableError):
131+
return ""
132+
133+
current_profile = models.get_current_profile()
134+
trading_mode = models.get_config_activated_trading_mode()
135+
selected_bot = models.get_selected_user_bot()
136+
selected_bot_id = (selected_bot.get(community_enums.BotKeys.ID.value) or "") if selected_bot else ""
137+
138+
125139
return dict(
126140
LAST_UPDATED_STATIC_FILES=web_interface.LAST_UPDATED_STATIC_FILES,
127141
OCTOBOT_WEBSITE_URL=constants.OCTOBOT_WEBSITE_URL,
@@ -140,6 +154,13 @@ def get_enabled_tentacles(tentacles_info_by_name):
140154
CAN_INSTALL_TENTACLES=constants.CAN_INSTALL_TENTACLES,
141155
IS_ALLOWING_TRACKING=models.get_metrics_enabled(),
142156
TRACKING_ID=constants.TRACKING_ID,
157+
PH_TRACKING_ID=constants.PH_TRACKING_ID,
158+
USER_EMAIL=get_logged_in_email(),
159+
USER_SELECTED_BOT_ID=selected_bot_id,
160+
PROFILE_NAME=current_profile.name,
161+
TRADING_MODE_NAME=trading_mode.get_name() if trading_mode else "",
162+
EXCHANGE_NAMES=",".join(get_profile_exchanges(current_profile)),
163+
IS_REAL_TRADING=models.is_real_trading(current_profile),
143164
TAB_START=web_enums.TabsLocation.START,
144165
TAB_END=web_enums.TabsLocation.END,
145166
get_color_mode=get_color_mode,
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
2+
$(document).ready(function() {
3+
4+
const getUserEmail = () => {
5+
return getUserDetails().email
6+
}
7+
8+
const getUserDetails = () => {
9+
return _USER_DETAILS
10+
}
11+
12+
const updateUserDetails = () => {
13+
posthog.capture(
14+
getUserEmail(),
15+
event='update_user_details',
16+
properties={
17+
'$set': getUserDetails(),
18+
}
19+
)
20+
}
21+
22+
const shouldUpdateUserDetails = () => {
23+
const currentProperties = posthog.get_property('$stored_person_properties');
24+
return (
25+
getUserEmail() !== ""
26+
&& isDefined(currentProperties)
27+
&& JSON.stringify(currentProperties) !== JSON.stringify(getUserDetails())
28+
)
29+
}
30+
31+
const shouldReset = (newEmail) => {
32+
const previousId = posthog.get_distinct_id();
33+
return (
34+
newEmail !== previousId
35+
// if @ is the user id, it's an email which is different from the current one: this is a new user
36+
&& previousId.indexOf("@") !== -1
37+
);
38+
}
39+
40+
const identify = (email) => {
41+
posthog.identify(
42+
email,
43+
getUserDetails() // optional: set additional person properties
44+
);
45+
}
46+
47+
const updateUserIfNecessary = () => {
48+
if (!_IS_ALLOWING_TRACKING){
49+
// tracking disabled
50+
return
51+
}
52+
const email = getUserEmail();
53+
if (email !== "" && posthog.get_distinct_id() !== email){
54+
if (shouldReset(email)){
55+
// If you also want to reset the device_id so that the device will be considered a new device in
56+
// future events, you can pass true as an argument
57+
// => past events will be bound to the current user as soon as he connects but avoid binding later events
58+
// in case the user changes
59+
console.log("PH: Resetting user")
60+
const resetDeviceId = true
61+
posthog.reset(resetDeviceId);
62+
}
63+
// new authenticated email: identify
64+
console.log("PH: Identifying user")
65+
identify(email);
66+
}else{
67+
if (shouldUpdateUserDetails()){
68+
console.log("PH: updating user details")
69+
updateUserDetails();
70+
}
71+
}
72+
}
73+
74+
updateUserIfNecessary();
75+
});

Services/Interfaces/web_interface/templates/about.html

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,9 @@ <h2>Help us to improve OctoBot</h2>
7171
{% if not IS_CLOUD %}
7272
<div class="custom-control custom-switch">
7373
<input type="checkbox" class="custom-control-input" id="metricsCheckbox" update-url="{{ url_for('metrics_settings') }}" {{ 'checked' if metrics_enabled else ''}}>
74-
<label class="custom-control-label" for="metricsCheckbox">Share</label> anonymous data to help the OctoBot Community
75-
<p>This will grant you access to the <a href="{{ url_for('community_metrics') }}"> OctoBot Community metrics</a> and greatly help the OctoBot team to figure out the best ways to improve OctoBot.
76-
<i class="fa-solid fa-question" data-toggle="tooltip" data-placement="top"
77-
title="Shared data are:
78-
Bot version and running time, trader type, the name of used exchanges, tentacles, traded pairs, profile name and notification systems.
79-
Overall portfolio value in reference market, traded volumes and profitability are shared to evaluate strategies performances.
80-
Finally the current environment details OctoBot is running on and the way you support the project (if you are) are also shared as
81-
well as if you are emitting or receiving signals and your web interface usage stats.
82-
Nothing else is ever shared: no personal data, no identification enabling data.">
83-
</i>
84-
It won't slow your OctoBot down.
74+
<label class="custom-control-label" for="metricsCheckbox">Share</label> metrics to help the OctoBot Community
75+
<p>
76+
This will greatly help the OctoBot team to figure out the best ways to improve OctoBot and won't slow your OctoBot down.
8577
</p>
8678
</div>
8779
{% endif %}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{% macro user_details(
2+
IS_ALLOWING_TRACKING,
3+
USER_EMAIL,
4+
USER_SELECTED_BOT_ID,
5+
has_open_source_package,
6+
PROFILE_NAME,
7+
TRADING_MODE_NAME,
8+
EXCHANGE_NAMES,
9+
IS_REAL_TRADING)
10+
-%}
11+
<script>
12+
const _IS_ALLOWING_TRACKING = {{ 'true' if IS_ALLOWING_TRACKING else 'false' }}
13+
const _USER_DETAILS = {
14+
email: "{{ USER_EMAIL }}",
15+
botId: "{{ USER_SELECTED_BOT_ID }}",
16+
hasPremiumExtension: {{ 'true' if has_open_source_package() else 'false'}},
17+
profileName: "{{ PROFILE_NAME }}",
18+
tradingMode: "{{ TRADING_MODE_NAME }}",
19+
exchanges: "{{ EXCHANGE_NAMES }}".split(","),
20+
isRealTrading: {{ 'true' if IS_REAL_TRADING else 'false' }},
21+
}
22+
</script>
23+
{%- endmacro %}

Services/Interfaces/web_interface/templates/layout.html

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{% import 'macros/trading_state.html' as m_trading_state %}
22
{% import 'components/modals/trading_state_modal.html' as m_trading_state_modal %}
33
{% import 'components/modals/generic_modal.html' as m_generic_modal %}
4+
{% import 'components/community/user_details.html' as m_user_details %}
45
<!doctype html>
56
<html lang="en" data-mdb-theme="{{get_color_mode()}}">
67
{% set active_page = active_page|default('home') -%}
@@ -57,6 +58,14 @@
5758
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css', u=LAST_UPDATED_STATIC_FILES) }}">
5859
<link rel="stylesheet" href="{{ url_for('static', filename='css/layout.css', u=LAST_UPDATED_STATIC_FILES) }}">
5960

61+
{% if IS_DEMO or IS_CLOUD or IS_ALLOWING_TRACKING%}
62+
<script>
63+
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys getNextSurveyStep onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
64+
posthog.init('{{PH_TRACKING_ID}}',{api_host:'https://eu.i.posthog.com',
65+
})
66+
</script>
67+
{% endif %}
68+
6069
{% block additional_style %}
6170
{% endblock additional_style %}
6271
</head>
@@ -88,7 +97,8 @@
8897
<script src="{{ url_for('static', filename='js/common/required.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
8998
<script src="{{ url_for('static', filename='js/common/tutorial.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
9099
<script src="{{ url_for('static', filename='js/common/feedback.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
91-
{% if IS_DEMO or IS_CLOUD or IS_ALLOWING_TRACKING%}
100+
<script src="{{ url_for('static', filename='js/common/tracking.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
101+
{% if IS_DEMO or IS_CLOUD or IS_ALLOWING_TRACKING%}
92102
<script type="text/javascript">
93103
(function(c,l,a,r,i,t,y){
94104
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
@@ -269,5 +279,15 @@ <h5 class="mb-0">
269279
{% block additional_scripts %}
270280
{% endblock additional_scripts %}
271281
<script src="{{ url_for('static', filename='js/common/on_load.js', u=LAST_UPDATED_STATIC_FILES) }}"></script>
282+
{{ m_user_details.user_details(
283+
IS_ALLOWING_TRACKING,
284+
USER_EMAIL,
285+
USER_SELECTED_BOT_ID,
286+
has_open_source_package,
287+
PROFILE_NAME,
288+
TRADING_MODE_NAME,
289+
EXCHANGE_NAMES,
290+
IS_REAL_TRADING
291+
) }}
272292
</body>
273293
</html>

Services/Interfaces/web_interface/templates/profile.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ <h4 class="alert-heading">Strategies</h4>
324324
<div class="container-fluid row col-12">
325325
<div class="col-md-6">
326326
<label for="AddCurrencySelect">Add a cryptocurrency to trade:</label>
327-
<select id="AddCurrencySelect" data-live-search="true"
327+
<select id="AddCurrencySelect" data-live-search="true" data-window-padding="25"
328328
reference_market="{{ config_reference_market }}" data-fetch-url="{{ url_for('api.currency_list') }}">
329329

330330
</select>

0 commit comments

Comments
 (0)