1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2// See LICENSE.txt for license information.
3
4package telemetry
5
6import (
7	"context"
8	"os"
9	"path/filepath"
10	"runtime"
11	"strings"
12	"time"
13
14	rudder "github.com/rudderlabs/analytics-go"
15
16	"github.com/mattermost/mattermost-server/v6/model"
17	"github.com/mattermost/mattermost-server/v6/plugin"
18	"github.com/mattermost/mattermost-server/v6/services/httpservice"
19	"github.com/mattermost/mattermost-server/v6/services/marketplace"
20	"github.com/mattermost/mattermost-server/v6/services/searchengine"
21	"github.com/mattermost/mattermost-server/v6/shared/mlog"
22	"github.com/mattermost/mattermost-server/v6/store"
23	"github.com/mattermost/mattermost-server/v6/utils"
24)
25
26const (
27	DayMilliseconds   = 24 * 60 * 60 * 1000
28	MonthMilliseconds = 31 * DayMilliseconds
29
30	RudderKey          = "placeholder_rudder_key"
31	RudderDataplaneURL = "placeholder_rudder_dataplane_url"
32
33	EnvVarInstallType = "MM_INSTALL_TYPE"
34
35	TrackConfigService           = "config_service"
36	TrackConfigTeam              = "config_team"
37	TrackConfigClientReq         = "config_client_requirements"
38	TrackConfigSQL               = "config_sql"
39	TrackConfigLog               = "config_log"
40	TrackConfigAudit             = "config_audit"
41	TrackConfigNotificationLog   = "config_notifications_log"
42	TrackConfigFile              = "config_file"
43	TrackConfigRate              = "config_rate"
44	TrackConfigEmail             = "config_email"
45	TrackConfigPrivacy           = "config_privacy"
46	TrackConfigTheme             = "config_theme"
47	TrackConfigOAuth             = "config_oauth"
48	TrackConfigLDAP              = "config_ldap"
49	TrackConfigCompliance        = "config_compliance"
50	TrackConfigLocalization      = "config_localization"
51	TrackConfigSAML              = "config_saml"
52	TrackConfigPassword          = "config_password"
53	TrackConfigCluster           = "config_cluster"
54	TrackConfigMetrics           = "config_metrics"
55	TrackConfigSupport           = "config_support"
56	TrackConfigNativeApp         = "config_nativeapp"
57	TrackConfigExperimental      = "config_experimental"
58	TrackConfigAnalytics         = "config_analytics"
59	TrackConfigAnnouncement      = "config_announcement"
60	TrackConfigElasticsearch     = "config_elasticsearch"
61	TrackConfigPlugin            = "config_plugin"
62	TrackConfigDataRetention     = "config_data_retention"
63	TrackConfigMessageExport     = "config_message_export"
64	TrackConfigDisplay           = "config_display"
65	TrackConfigGuestAccounts     = "config_guest_accounts"
66	TrackConfigImageProxy        = "config_image_proxy"
67	TrackConfigBleve             = "config_bleve"
68	TrackConfigExport            = "config_export"
69	TrackFeatureFlags            = "config_feature_flags"
70	TrackPermissionsGeneral      = "permissions_general"
71	TrackPermissionsSystemScheme = "permissions_system_scheme"
72	TrackPermissionsTeamSchemes  = "permissions_team_schemes"
73	TrackPermissionsSystemRoles  = "permissions_system_roles"
74	TrackElasticsearch           = "elasticsearch"
75	TrackGroups                  = "groups"
76	TrackChannelModeration       = "channel_moderation"
77	TrackWarnMetrics             = "warn_metrics"
78
79	TrackActivity = "activity"
80	TrackLicense  = "license"
81	TrackServer   = "server"
82	TrackPlugins  = "plugins"
83)
84
85type ServerIface interface {
86	Config() *model.Config
87	IsLeader() bool
88	HTTPService() httpservice.HTTPService
89	GetPluginsEnvironment() *plugin.Environment
90	License() *model.License
91	GetRoleByName(context.Context, string) (*model.Role, *model.AppError)
92	GetSchemes(string, int, int) ([]*model.Scheme, *model.AppError)
93}
94
95type TelemetryService struct {
96	srv                        ServerIface
97	dbStore                    store.Store
98	searchEngine               *searchengine.Broker
99	log                        *mlog.Logger
100	rudderClient               rudder.Client
101	TelemetryID                string
102	timestampLastTelemetrySent time.Time
103}
104
105type RudderConfig struct {
106	RudderKey    string
107	DataplaneURL string
108}
109
110func New(srv ServerIface, dbStore store.Store, searchEngine *searchengine.Broker, log *mlog.Logger) *TelemetryService {
111	service := &TelemetryService{
112		srv:          srv,
113		dbStore:      dbStore,
114		searchEngine: searchEngine,
115		log:          log,
116	}
117	service.ensureTelemetryID()
118	return service
119}
120
121func (ts *TelemetryService) ensureTelemetryID() {
122	if ts.TelemetryID != "" {
123		return
124	}
125
126	id := model.NewId()
127	systemID := &model.System{Name: model.SystemTelemetryId, Value: id}
128	systemID, err := ts.dbStore.System().InsertIfExists(systemID)
129	if err != nil {
130		mlog.Error("unable to get the telemetry ID", mlog.Err(err))
131		return
132	}
133
134	ts.TelemetryID = systemID.Value
135}
136
137func (ts *TelemetryService) getRudderConfig() RudderConfig {
138	if !strings.Contains(RudderKey, "placeholder") && !strings.Contains(RudderDataplaneURL, "placeholder") {
139		return RudderConfig{RudderKey, RudderDataplaneURL}
140	} else if os.Getenv("RudderKey") != "" && os.Getenv("RudderDataplaneURL") != "" {
141		return RudderConfig{os.Getenv("RudderKey"), os.Getenv("RudderDataplaneURL")}
142	} else {
143		return RudderConfig{}
144	}
145}
146
147func (ts *TelemetryService) telemetryEnabled() bool {
148	return *ts.srv.Config().LogSettings.EnableDiagnostics && ts.srv.IsLeader()
149}
150
151func (ts *TelemetryService) sendDailyTelemetry(override bool) {
152	config := ts.getRudderConfig()
153	if ts.telemetryEnabled() && ((config.DataplaneURL != "" && config.RudderKey != "") || override) {
154		ts.initRudder(config.DataplaneURL, config.RudderKey)
155		ts.trackActivity()
156		ts.trackConfig()
157		ts.trackLicense()
158		ts.trackPlugins()
159		ts.trackServer()
160		ts.trackPermissions()
161		ts.trackElasticsearch()
162		ts.trackGroups()
163		ts.trackChannelModeration()
164		ts.trackWarnMetrics()
165	}
166}
167
168func (ts *TelemetryService) sendTelemetry(event string, properties map[string]interface{}) {
169	if ts.rudderClient != nil {
170		var context *rudder.Context
171		// if we are part of a cloud installation, add it's ID to the tracked event's context
172		if installationId := os.Getenv("MM_CLOUD_INSTALLATION_ID"); installationId != "" {
173			context = &rudder.Context{Traits: map[string]interface{}{"installationId": installationId}}
174		}
175		ts.rudderClient.Enqueue(rudder.Track{
176			Event:      event,
177			UserId:     ts.TelemetryID,
178			Properties: properties,
179			Context:    context,
180		})
181	}
182}
183
184func isDefaultArray(setting, defaultValue []string) bool {
185	if len(setting) != len(defaultValue) {
186		return false
187	}
188	for i := 0; i < len(setting); i++ {
189		if setting[i] != defaultValue[i] {
190			return false
191		}
192	}
193	return true
194}
195
196func isDefault(setting interface{}, defaultValue interface{}) bool {
197	return setting == defaultValue
198}
199
200func pluginSetting(pluginSettings *model.PluginSettings, plugin, key string, defaultValue interface{}) interface{} {
201	settings, ok := pluginSettings.Plugins[plugin]
202	if !ok {
203		return defaultValue
204	}
205	if value, ok := settings[key]; ok {
206		return value
207	}
208	return defaultValue
209}
210
211func pluginActivated(pluginStates map[string]*model.PluginState, pluginId string) bool {
212	state, ok := pluginStates[pluginId]
213	if !ok {
214		return false
215	}
216	return state.Enable
217}
218
219func pluginVersion(pluginsAvailable []*model.BundleInfo, pluginId string) string {
220	for _, plugin := range pluginsAvailable {
221		if plugin.Manifest != nil && plugin.Manifest.Id == pluginId {
222			return plugin.Manifest.Version
223		}
224	}
225	return ""
226}
227
228func (ts *TelemetryService) trackActivity() {
229	var userCount int64
230	var guestAccountsCount int64
231	var botAccountsCount int64
232	var inactiveUserCount int64
233	var publicChannelCount int64
234	var privateChannelCount int64
235	var directChannelCount int64
236	var deletedPublicChannelCount int64
237	var deletedPrivateChannelCount int64
238	var postsCount int64
239	var postsCountPreviousDay int64
240	var botPostsCountPreviousDay int64
241	var slashCommandsCount int64
242	var incomingWebhooksCount int64
243	var outgoingWebhooksCount int64
244
245	activeUsersDailyCountChan := make(chan store.StoreResult, 1)
246	go func() {
247		count, err := ts.dbStore.User().AnalyticsActiveCount(DayMilliseconds, model.UserCountOptions{IncludeBotAccounts: false, IncludeDeleted: false})
248		activeUsersDailyCountChan <- store.StoreResult{Data: count, NErr: err}
249		close(activeUsersDailyCountChan)
250	}()
251
252	activeUsersMonthlyCountChan := make(chan store.StoreResult, 1)
253	go func() {
254		count, err := ts.dbStore.User().AnalyticsActiveCount(MonthMilliseconds, model.UserCountOptions{IncludeBotAccounts: false, IncludeDeleted: false})
255		activeUsersMonthlyCountChan <- store.StoreResult{Data: count, NErr: err}
256		close(activeUsersMonthlyCountChan)
257	}()
258
259	if count, err := ts.dbStore.User().Count(model.UserCountOptions{IncludeDeleted: true}); err == nil {
260		userCount = count
261	}
262
263	if count, err := ts.dbStore.User().AnalyticsGetGuestCount(); err == nil {
264		guestAccountsCount = count
265	}
266
267	if count, err := ts.dbStore.User().Count(model.UserCountOptions{IncludeBotAccounts: true, ExcludeRegularUsers: true}); err == nil {
268		botAccountsCount = count
269	}
270
271	if iucr, err := ts.dbStore.User().AnalyticsGetInactiveUsersCount(); err == nil {
272		inactiveUserCount = iucr
273	}
274
275	teamCount, err := ts.dbStore.Team().AnalyticsTeamCount(nil)
276	if err != nil {
277		mlog.Info("Could not get team count", mlog.Err(err))
278	}
279
280	if ucc, err := ts.dbStore.Channel().AnalyticsTypeCount("", "O"); err == nil {
281		publicChannelCount = ucc
282	}
283
284	if pcc, err := ts.dbStore.Channel().AnalyticsTypeCount("", "P"); err == nil {
285		privateChannelCount = pcc
286	}
287
288	if dcc, err := ts.dbStore.Channel().AnalyticsTypeCount("", "D"); err == nil {
289		directChannelCount = dcc
290	}
291
292	if duccr, err := ts.dbStore.Channel().AnalyticsDeletedTypeCount("", "O"); err == nil {
293		deletedPublicChannelCount = duccr
294	}
295
296	if dpccr, err := ts.dbStore.Channel().AnalyticsDeletedTypeCount("", "P"); err == nil {
297		deletedPrivateChannelCount = dpccr
298	}
299
300	postsCount, _ = ts.dbStore.Post().AnalyticsPostCount("", false, false)
301
302	postCountsOptions := &model.AnalyticsPostCountsOptions{TeamId: "", BotsOnly: false, YesterdayOnly: true}
303	postCountsYesterday, _ := ts.dbStore.Post().AnalyticsPostCountsByDay(postCountsOptions)
304	postsCountPreviousDay = 0
305	if len(postCountsYesterday) > 0 {
306		postsCountPreviousDay = int64(postCountsYesterday[0].Value)
307	}
308
309	postCountsOptions = &model.AnalyticsPostCountsOptions{TeamId: "", BotsOnly: true, YesterdayOnly: true}
310	botPostCountsYesterday, _ := ts.dbStore.Post().AnalyticsPostCountsByDay(postCountsOptions)
311	botPostsCountPreviousDay = 0
312	if len(botPostCountsYesterday) > 0 {
313		botPostsCountPreviousDay = int64(botPostCountsYesterday[0].Value)
314	}
315
316	slashCommandsCount, _ = ts.dbStore.Command().AnalyticsCommandCount("")
317
318	if c, err := ts.dbStore.Webhook().AnalyticsIncomingCount(""); err == nil {
319		incomingWebhooksCount = c
320	}
321
322	outgoingWebhooksCount, _ = ts.dbStore.Webhook().AnalyticsOutgoingCount("")
323
324	var activeUsersDailyCount int64
325	if r := <-activeUsersDailyCountChan; r.NErr == nil {
326		activeUsersDailyCount = r.Data.(int64)
327	}
328
329	var activeUsersMonthlyCount int64
330	if r := <-activeUsersMonthlyCountChan; r.NErr == nil {
331		activeUsersMonthlyCount = r.Data.(int64)
332	}
333
334	ts.sendTelemetry(TrackActivity, map[string]interface{}{
335		"registered_users":             userCount,
336		"bot_accounts":                 botAccountsCount,
337		"guest_accounts":               guestAccountsCount,
338		"active_users_daily":           activeUsersDailyCount,
339		"active_users_monthly":         activeUsersMonthlyCount,
340		"registered_deactivated_users": inactiveUserCount,
341		"teams":                        teamCount,
342		"public_channels":              publicChannelCount,
343		"private_channels":             privateChannelCount,
344		"direct_message_channels":      directChannelCount,
345		"public_channels_deleted":      deletedPublicChannelCount,
346		"private_channels_deleted":     deletedPrivateChannelCount,
347		"posts_previous_day":           postsCountPreviousDay,
348		"bot_posts_previous_day":       botPostsCountPreviousDay,
349		"posts":                        postsCount,
350		"slash_commands":               slashCommandsCount,
351		"incoming_webhooks":            incomingWebhooksCount,
352		"outgoing_webhooks":            outgoingWebhooksCount,
353	})
354}
355
356func (ts *TelemetryService) trackConfig() {
357	cfg := ts.srv.Config()
358	ts.sendTelemetry(TrackConfigService, map[string]interface{}{
359		"web_server_mode":                                         *cfg.ServiceSettings.WebserverMode,
360		"enable_security_fix_alert":                               *cfg.ServiceSettings.EnableSecurityFixAlert,
361		"enable_insecure_outgoing_connections":                    *cfg.ServiceSettings.EnableInsecureOutgoingConnections,
362		"enable_incoming_webhooks":                                cfg.ServiceSettings.EnableIncomingWebhooks,
363		"enable_outgoing_webhooks":                                cfg.ServiceSettings.EnableOutgoingWebhooks,
364		"enable_commands":                                         *cfg.ServiceSettings.EnableCommands,
365		"enable_post_username_override":                           cfg.ServiceSettings.EnablePostUsernameOverride,
366		"enable_post_icon_override":                               cfg.ServiceSettings.EnablePostIconOverride,
367		"enable_user_access_tokens":                               *cfg.ServiceSettings.EnableUserAccessTokens,
368		"enable_custom_emoji":                                     *cfg.ServiceSettings.EnableCustomEmoji,
369		"enable_emoji_picker":                                     *cfg.ServiceSettings.EnableEmojiPicker,
370		"enable_gif_picker":                                       *cfg.ServiceSettings.EnableGifPicker,
371		"gfycat_api_key":                                          isDefault(*cfg.ServiceSettings.GfycatAPIKey, model.ServiceSettingsDefaultGfycatAPIKey),
372		"gfycat_api_secret":                                       isDefault(*cfg.ServiceSettings.GfycatAPISecret, model.ServiceSettingsDefaultGfycatAPISecret),
373		"experimental_enable_authentication_transfer":             *cfg.ServiceSettings.ExperimentalEnableAuthenticationTransfer,
374		"enable_testing":                                          cfg.ServiceSettings.EnableTesting,
375		"enable_developer":                                        *cfg.ServiceSettings.EnableDeveloper,
376		"enable_multifactor_authentication":                       *cfg.ServiceSettings.EnableMultifactorAuthentication,
377		"enforce_multifactor_authentication":                      *cfg.ServiceSettings.EnforceMultifactorAuthentication,
378		"enable_oauth_service_provider":                           cfg.ServiceSettings.EnableOAuthServiceProvider,
379		"connection_security":                                     *cfg.ServiceSettings.ConnectionSecurity,
380		"tls_strict_transport":                                    *cfg.ServiceSettings.TLSStrictTransport,
381		"uses_letsencrypt":                                        *cfg.ServiceSettings.UseLetsEncrypt,
382		"forward_80_to_443":                                       *cfg.ServiceSettings.Forward80To443,
383		"maximum_login_attempts":                                  *cfg.ServiceSettings.MaximumLoginAttempts,
384		"extend_session_length_with_activity":                     *cfg.ServiceSettings.ExtendSessionLengthWithActivity,
385		"session_length_web_in_days":                              *cfg.ServiceSettings.SessionLengthWebInDays,
386		"session_length_mobile_in_days":                           *cfg.ServiceSettings.SessionLengthMobileInDays,
387		"session_length_sso_in_days":                              *cfg.ServiceSettings.SessionLengthSSOInDays,
388		"session_cache_in_minutes":                                *cfg.ServiceSettings.SessionCacheInMinutes,
389		"session_idle_timeout_in_minutes":                         *cfg.ServiceSettings.SessionIdleTimeoutInMinutes,
390		"isdefault_site_url":                                      isDefault(*cfg.ServiceSettings.SiteURL, model.ServiceSettingsDefaultSiteURL),
391		"isdefault_tls_cert_file":                                 isDefault(*cfg.ServiceSettings.TLSCertFile, model.ServiceSettingsDefaultTLSCertFile),
392		"isdefault_tls_key_file":                                  isDefault(*cfg.ServiceSettings.TLSKeyFile, model.ServiceSettingsDefaultTLSKeyFile),
393		"isdefault_read_timeout":                                  isDefault(*cfg.ServiceSettings.ReadTimeout, model.ServiceSettingsDefaultReadTimeout),
394		"isdefault_write_timeout":                                 isDefault(*cfg.ServiceSettings.WriteTimeout, model.ServiceSettingsDefaultWriteTimeout),
395		"isdefault_idle_timeout":                                  isDefault(*cfg.ServiceSettings.IdleTimeout, model.ServiceSettingsDefaultIdleTimeout),
396		"isdefault_google_developer_key":                          isDefault(cfg.ServiceSettings.GoogleDeveloperKey, ""),
397		"isdefault_allow_cors_from":                               isDefault(*cfg.ServiceSettings.AllowCorsFrom, model.ServiceSettingsDefaultAllowCorsFrom),
398		"isdefault_cors_exposed_headers":                          isDefault(cfg.ServiceSettings.CorsExposedHeaders, ""),
399		"cors_allow_credentials":                                  *cfg.ServiceSettings.CorsAllowCredentials,
400		"cors_debug":                                              *cfg.ServiceSettings.CorsDebug,
401		"isdefault_allowed_untrusted_internal_connections":        isDefault(*cfg.ServiceSettings.AllowedUntrustedInternalConnections, ""),
402		"post_edit_time_limit":                                    *cfg.ServiceSettings.PostEditTimeLimit,
403		"enable_user_typing_messages":                             *cfg.ServiceSettings.EnableUserTypingMessages,
404		"enable_channel_viewed_messages":                          *cfg.ServiceSettings.EnableChannelViewedMessages,
405		"time_between_user_typing_updates_milliseconds":           *cfg.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds,
406		"cluster_log_timeout_milliseconds":                        *cfg.ServiceSettings.ClusterLogTimeoutMilliseconds,
407		"enable_post_search":                                      *cfg.ServiceSettings.EnablePostSearch,
408		"minimum_hashtag_length":                                  *cfg.ServiceSettings.MinimumHashtagLength,
409		"enable_user_statuses":                                    *cfg.ServiceSettings.EnableUserStatuses,
410		"enable_preview_features":                                 *cfg.ServiceSettings.EnablePreviewFeatures,
411		"enable_tutorial":                                         *cfg.ServiceSettings.EnableTutorial,
412		"enable_onboarding_flow":                                  *cfg.ServiceSettings.EnableOnboardingFlow,
413		"experimental_enable_default_channel_leave_join_messages": *cfg.ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages,
414		"experimental_group_unread_channels":                      *cfg.ServiceSettings.ExperimentalGroupUnreadChannels,
415		"collapsed_threads":                                       *cfg.ServiceSettings.CollapsedThreads,
416		"websocket_url":                                           isDefault(*cfg.ServiceSettings.WebsocketURL, ""),
417		"allow_cookies_for_subdomains":                            *cfg.ServiceSettings.AllowCookiesForSubdomains,
418		"enable_api_team_deletion":                                *cfg.ServiceSettings.EnableAPITeamDeletion,
419		"enable_api_user_deletion":                                *cfg.ServiceSettings.EnableAPIUserDeletion,
420		"enable_api_channel_deletion":                             *cfg.ServiceSettings.EnableAPIChannelDeletion,
421		"experimental_enable_hardened_mode":                       *cfg.ServiceSettings.ExperimentalEnableHardenedMode,
422		"experimental_strict_csrf_enforcement":                    *cfg.ServiceSettings.ExperimentalStrictCSRFEnforcement,
423		"enable_email_invitations":                                *cfg.ServiceSettings.EnableEmailInvitations,
424		"disable_bots_when_owner_is_deactivated":                  *cfg.ServiceSettings.DisableBotsWhenOwnerIsDeactivated,
425		"enable_bot_account_creation":                             *cfg.ServiceSettings.EnableBotAccountCreation,
426		"enable_svgs":                                             *cfg.ServiceSettings.EnableSVGs,
427		"enable_latex":                                            *cfg.ServiceSettings.EnableLatex,
428		"enable_opentracing":                                      *cfg.ServiceSettings.EnableOpenTracing,
429		"enable_local_mode":                                       *cfg.ServiceSettings.EnableLocalMode,
430		"managed_resource_paths":                                  isDefault(*cfg.ServiceSettings.ManagedResourcePaths, ""),
431		"thread_auto_follow":                                      *cfg.ServiceSettings.ThreadAutoFollow,
432		"enable_link_previews":                                    *cfg.ServiceSettings.EnableLinkPreviews,
433		"enable_permalink_previews":                               *cfg.ServiceSettings.EnablePermalinkPreviews,
434		"enable_file_search":                                      *cfg.ServiceSettings.EnableFileSearch,
435		"restrict_link_previews":                                  isDefault(*cfg.ServiceSettings.RestrictLinkPreviews, ""),
436	})
437
438	ts.sendTelemetry(TrackConfigTeam, map[string]interface{}{
439		"enable_user_creation":                    cfg.TeamSettings.EnableUserCreation,
440		"enable_open_server":                      *cfg.TeamSettings.EnableOpenServer,
441		"enable_user_deactivation":                *cfg.TeamSettings.EnableUserDeactivation,
442		"enable_custom_user_statuses":             *cfg.TeamSettings.EnableCustomUserStatuses,
443		"enable_custom_brand":                     *cfg.TeamSettings.EnableCustomBrand,
444		"restrict_direct_message":                 *cfg.TeamSettings.RestrictDirectMessage,
445		"max_notifications_per_channel":           *cfg.TeamSettings.MaxNotificationsPerChannel,
446		"enable_confirm_notifications_to_channel": *cfg.TeamSettings.EnableConfirmNotificationsToChannel,
447		"max_users_per_team":                      *cfg.TeamSettings.MaxUsersPerTeam,
448		"max_channels_per_team":                   *cfg.TeamSettings.MaxChannelsPerTeam,
449		"teammate_name_display":                   *cfg.TeamSettings.TeammateNameDisplay,
450		"experimental_view_archived_channels":     *cfg.TeamSettings.ExperimentalViewArchivedChannels,
451		"lock_teammate_name_display":              *cfg.TeamSettings.LockTeammateNameDisplay,
452		"isdefault_site_name":                     isDefault(cfg.TeamSettings.SiteName, "Mattermost"),
453		"isdefault_custom_brand_text":             isDefault(*cfg.TeamSettings.CustomBrandText, model.TeamSettingsDefaultCustomBrandText),
454		"isdefault_custom_description_text":       isDefault(*cfg.TeamSettings.CustomDescriptionText, model.TeamSettingsDefaultCustomDescriptionText),
455		"isdefault_user_status_away_timeout":      isDefault(*cfg.TeamSettings.UserStatusAwayTimeout, model.TeamSettingsDefaultUserStatusAwayTimeout),
456		"experimental_enable_automatic_replies":   *cfg.TeamSettings.ExperimentalEnableAutomaticReplies,
457		"experimental_primary_team":               isDefault(*cfg.TeamSettings.ExperimentalPrimaryTeam, ""),
458		"experimental_default_channels":           len(cfg.TeamSettings.ExperimentalDefaultChannels),
459	})
460
461	ts.sendTelemetry(TrackConfigClientReq, map[string]interface{}{
462		"android_latest_version": cfg.ClientRequirements.AndroidLatestVersion,
463		"android_min_version":    cfg.ClientRequirements.AndroidMinVersion,
464		"desktop_latest_version": cfg.ClientRequirements.DesktopLatestVersion,
465		"desktop_min_version":    cfg.ClientRequirements.DesktopMinVersion,
466		"ios_latest_version":     cfg.ClientRequirements.IosLatestVersion,
467		"ios_min_version":        cfg.ClientRequirements.IosMinVersion,
468	})
469
470	ts.sendTelemetry(TrackConfigSQL, map[string]interface{}{
471		"driver_name":                    *cfg.SqlSettings.DriverName,
472		"trace":                          cfg.SqlSettings.Trace,
473		"max_idle_conns":                 *cfg.SqlSettings.MaxIdleConns,
474		"conn_max_lifetime_milliseconds": *cfg.SqlSettings.ConnMaxLifetimeMilliseconds,
475		"conn_max_idletime_milliseconds": *cfg.SqlSettings.ConnMaxIdleTimeMilliseconds,
476		"max_open_conns":                 *cfg.SqlSettings.MaxOpenConns,
477		"data_source_replicas":           len(cfg.SqlSettings.DataSourceReplicas),
478		"data_source_search_replicas":    len(cfg.SqlSettings.DataSourceSearchReplicas),
479		"query_timeout":                  *cfg.SqlSettings.QueryTimeout,
480		"disable_database_search":        *cfg.SqlSettings.DisableDatabaseSearch,
481	})
482
483	ts.sendTelemetry(TrackConfigLog, map[string]interface{}{
484		"enable_console":           cfg.LogSettings.EnableConsole,
485		"console_level":            cfg.LogSettings.ConsoleLevel,
486		"console_json":             *cfg.LogSettings.ConsoleJson,
487		"enable_file":              cfg.LogSettings.EnableFile,
488		"file_level":               cfg.LogSettings.FileLevel,
489		"file_json":                cfg.LogSettings.FileJson,
490		"enable_webhook_debugging": cfg.LogSettings.EnableWebhookDebugging,
491		"isdefault_file_location":  isDefault(cfg.LogSettings.FileLocation, ""),
492		"advanced_logging_config":  *cfg.LogSettings.AdvancedLoggingConfig != "",
493	})
494
495	ts.sendTelemetry(TrackConfigAudit, map[string]interface{}{
496		"file_enabled":            *cfg.ExperimentalAuditSettings.FileEnabled,
497		"file_max_size_mb":        *cfg.ExperimentalAuditSettings.FileMaxSizeMB,
498		"file_max_age_days":       *cfg.ExperimentalAuditSettings.FileMaxAgeDays,
499		"file_max_backups":        *cfg.ExperimentalAuditSettings.FileMaxBackups,
500		"file_compress":           *cfg.ExperimentalAuditSettings.FileCompress,
501		"file_max_queue_size":     *cfg.ExperimentalAuditSettings.FileMaxQueueSize,
502		"advanced_logging_config": *cfg.ExperimentalAuditSettings.AdvancedLoggingConfig != "",
503	})
504
505	ts.sendTelemetry(TrackConfigNotificationLog, map[string]interface{}{
506		"enable_console":          *cfg.NotificationLogSettings.EnableConsole,
507		"console_level":           *cfg.NotificationLogSettings.ConsoleLevel,
508		"console_json":            *cfg.NotificationLogSettings.ConsoleJson,
509		"enable_file":             *cfg.NotificationLogSettings.EnableFile,
510		"file_level":              *cfg.NotificationLogSettings.FileLevel,
511		"file_json":               *cfg.NotificationLogSettings.FileJson,
512		"isdefault_file_location": isDefault(*cfg.NotificationLogSettings.FileLocation, ""),
513		"advanced_logging_config": *cfg.NotificationLogSettings.AdvancedLoggingConfig != "",
514	})
515
516	ts.sendTelemetry(TrackConfigPassword, map[string]interface{}{
517		"minimum_length": *cfg.PasswordSettings.MinimumLength,
518		"lowercase":      *cfg.PasswordSettings.Lowercase,
519		"number":         *cfg.PasswordSettings.Number,
520		"uppercase":      *cfg.PasswordSettings.Uppercase,
521		"symbol":         *cfg.PasswordSettings.Symbol,
522	})
523
524	ts.sendTelemetry(TrackConfigFile, map[string]interface{}{
525		"enable_public_links":     cfg.FileSettings.EnablePublicLink,
526		"driver_name":             *cfg.FileSettings.DriverName,
527		"isdefault_directory":     isDefault(*cfg.FileSettings.Directory, model.FileSettingsDefaultDirectory),
528		"isabsolute_directory":    filepath.IsAbs(*cfg.FileSettings.Directory),
529		"extract_content":         *cfg.FileSettings.ExtractContent,
530		"archive_recursion":       *cfg.FileSettings.ArchiveRecursion,
531		"amazon_s3_ssl":           *cfg.FileSettings.AmazonS3SSL,
532		"amazon_s3_sse":           *cfg.FileSettings.AmazonS3SSE,
533		"amazon_s3_signv2":        *cfg.FileSettings.AmazonS3SignV2,
534		"amazon_s3_trace":         *cfg.FileSettings.AmazonS3Trace,
535		"max_file_size":           *cfg.FileSettings.MaxFileSize,
536		"max_image_resolution":    *cfg.FileSettings.MaxImageResolution,
537		"enable_file_attachments": *cfg.FileSettings.EnableFileAttachments,
538		"enable_mobile_upload":    *cfg.FileSettings.EnableMobileUpload,
539		"enable_mobile_download":  *cfg.FileSettings.EnableMobileDownload,
540	})
541
542	ts.sendTelemetry(TrackConfigEmail, map[string]interface{}{
543		"enable_sign_up_with_email":            cfg.EmailSettings.EnableSignUpWithEmail,
544		"enable_sign_in_with_email":            *cfg.EmailSettings.EnableSignInWithEmail,
545		"enable_sign_in_with_username":         *cfg.EmailSettings.EnableSignInWithUsername,
546		"require_email_verification":           cfg.EmailSettings.RequireEmailVerification,
547		"send_email_notifications":             cfg.EmailSettings.SendEmailNotifications,
548		"use_channel_in_email_notifications":   *cfg.EmailSettings.UseChannelInEmailNotifications,
549		"email_notification_contents_type":     *cfg.EmailSettings.EmailNotificationContentsType,
550		"enable_smtp_auth":                     *cfg.EmailSettings.EnableSMTPAuth,
551		"connection_security":                  cfg.EmailSettings.ConnectionSecurity,
552		"send_push_notifications":              *cfg.EmailSettings.SendPushNotifications,
553		"push_notification_contents":           *cfg.EmailSettings.PushNotificationContents,
554		"enable_email_batching":                *cfg.EmailSettings.EnableEmailBatching,
555		"email_batching_buffer_size":           *cfg.EmailSettings.EmailBatchingBufferSize,
556		"email_batching_interval":              *cfg.EmailSettings.EmailBatchingInterval,
557		"enable_preview_mode_banner":           *cfg.EmailSettings.EnablePreviewModeBanner,
558		"isdefault_feedback_name":              isDefault(cfg.EmailSettings.FeedbackName, ""),
559		"isdefault_feedback_email":             isDefault(cfg.EmailSettings.FeedbackEmail, ""),
560		"isdefault_reply_to_address":           isDefault(cfg.EmailSettings.ReplyToAddress, ""),
561		"isdefault_feedback_organization":      isDefault(*cfg.EmailSettings.FeedbackOrganization, model.EmailSettingsDefaultFeedbackOrganization),
562		"skip_server_certificate_verification": *cfg.EmailSettings.SkipServerCertificateVerification,
563		"isdefault_login_button_color":         isDefault(*cfg.EmailSettings.LoginButtonColor, ""),
564		"isdefault_login_button_border_color":  isDefault(*cfg.EmailSettings.LoginButtonBorderColor, ""),
565		"isdefault_login_button_text_color":    isDefault(*cfg.EmailSettings.LoginButtonTextColor, ""),
566		"smtp_server_timeout":                  *cfg.EmailSettings.SMTPServerTimeout,
567	})
568
569	ts.sendTelemetry(TrackConfigRate, map[string]interface{}{
570		"enable_rate_limiter":      *cfg.RateLimitSettings.Enable,
571		"vary_by_remote_address":   *cfg.RateLimitSettings.VaryByRemoteAddr,
572		"vary_by_user":             *cfg.RateLimitSettings.VaryByUser,
573		"per_sec":                  *cfg.RateLimitSettings.PerSec,
574		"max_burst":                *cfg.RateLimitSettings.MaxBurst,
575		"memory_store_size":        *cfg.RateLimitSettings.MemoryStoreSize,
576		"isdefault_vary_by_header": isDefault(cfg.RateLimitSettings.VaryByHeader, ""),
577	})
578
579	ts.sendTelemetry(TrackConfigPrivacy, map[string]interface{}{
580		"show_email_address": cfg.PrivacySettings.ShowEmailAddress,
581		"show_full_name":     cfg.PrivacySettings.ShowFullName,
582	})
583
584	ts.sendTelemetry(TrackConfigTheme, map[string]interface{}{
585		"enable_theme_selection":  *cfg.ThemeSettings.EnableThemeSelection,
586		"isdefault_default_theme": isDefault(*cfg.ThemeSettings.DefaultTheme, model.TeamSettingsDefaultTeamText),
587		"allow_custom_themes":     *cfg.ThemeSettings.AllowCustomThemes,
588		"allowed_themes":          len(cfg.ThemeSettings.AllowedThemes),
589	})
590
591	ts.sendTelemetry(TrackConfigOAuth, map[string]interface{}{
592		"enable_gitlab":    cfg.GitLabSettings.Enable,
593		"openid_gitlab":    *cfg.GitLabSettings.Enable && strings.Contains(*cfg.GitLabSettings.Scope, model.ServiceOpenid),
594		"enable_google":    cfg.GoogleSettings.Enable,
595		"openid_google":    *cfg.GoogleSettings.Enable && strings.Contains(*cfg.GoogleSettings.Scope, model.ServiceOpenid),
596		"enable_office365": cfg.Office365Settings.Enable,
597		"openid_office365": *cfg.Office365Settings.Enable && strings.Contains(*cfg.Office365Settings.Scope, model.ServiceOpenid),
598		"enable_openid":    cfg.OpenIdSettings.Enable,
599	})
600
601	ts.sendTelemetry(TrackConfigSupport, map[string]interface{}{
602		"isdefault_terms_of_service_link":              isDefault(*cfg.SupportSettings.TermsOfServiceLink, model.SupportSettingsDefaultTermsOfServiceLink),
603		"isdefault_privacy_policy_link":                isDefault(*cfg.SupportSettings.PrivacyPolicyLink, model.SupportSettingsDefaultPrivacyPolicyLink),
604		"isdefault_about_link":                         isDefault(*cfg.SupportSettings.AboutLink, model.SupportSettingsDefaultAboutLink),
605		"isdefault_help_link":                          isDefault(*cfg.SupportSettings.HelpLink, model.SupportSettingsDefaultHelpLink),
606		"isdefault_report_a_problem_link":              isDefault(*cfg.SupportSettings.ReportAProblemLink, model.SupportSettingsDefaultReportAProblemLink),
607		"isdefault_support_email":                      isDefault(*cfg.SupportSettings.SupportEmail, model.SupportSettingsDefaultSupportEmail),
608		"custom_terms_of_service_enabled":              *cfg.SupportSettings.CustomTermsOfServiceEnabled,
609		"custom_terms_of_service_re_acceptance_period": *cfg.SupportSettings.CustomTermsOfServiceReAcceptancePeriod,
610		"enable_ask_community_link":                    *cfg.SupportSettings.EnableAskCommunityLink,
611	})
612
613	ts.sendTelemetry(TrackConfigLDAP, map[string]interface{}{
614		"enable":                                 *cfg.LdapSettings.Enable,
615		"enable_sync":                            *cfg.LdapSettings.EnableSync,
616		"enable_admin_filter":                    *cfg.LdapSettings.EnableAdminFilter,
617		"connection_security":                    *cfg.LdapSettings.ConnectionSecurity,
618		"skip_certificate_verification":          *cfg.LdapSettings.SkipCertificateVerification,
619		"sync_interval_minutes":                  *cfg.LdapSettings.SyncIntervalMinutes,
620		"query_timeout":                          *cfg.LdapSettings.QueryTimeout,
621		"max_page_size":                          *cfg.LdapSettings.MaxPageSize,
622		"isdefault_first_name_attribute":         isDefault(*cfg.LdapSettings.FirstNameAttribute, model.LdapSettingsDefaultFirstNameAttribute),
623		"isdefault_last_name_attribute":          isDefault(*cfg.LdapSettings.LastNameAttribute, model.LdapSettingsDefaultLastNameAttribute),
624		"isdefault_email_attribute":              isDefault(*cfg.LdapSettings.EmailAttribute, model.LdapSettingsDefaultEmailAttribute),
625		"isdefault_username_attribute":           isDefault(*cfg.LdapSettings.UsernameAttribute, model.LdapSettingsDefaultUsernameAttribute),
626		"isdefault_nickname_attribute":           isDefault(*cfg.LdapSettings.NicknameAttribute, model.LdapSettingsDefaultNicknameAttribute),
627		"isdefault_id_attribute":                 isDefault(*cfg.LdapSettings.IdAttribute, model.LdapSettingsDefaultIdAttribute),
628		"isdefault_position_attribute":           isDefault(*cfg.LdapSettings.PositionAttribute, model.LdapSettingsDefaultPositionAttribute),
629		"isdefault_login_id_attribute":           isDefault(*cfg.LdapSettings.LoginIdAttribute, ""),
630		"isdefault_login_field_name":             isDefault(*cfg.LdapSettings.LoginFieldName, model.LdapSettingsDefaultLoginFieldName),
631		"isdefault_login_button_color":           isDefault(*cfg.LdapSettings.LoginButtonColor, ""),
632		"isdefault_login_button_border_color":    isDefault(*cfg.LdapSettings.LoginButtonBorderColor, ""),
633		"isdefault_login_button_text_color":      isDefault(*cfg.LdapSettings.LoginButtonTextColor, ""),
634		"isempty_group_filter":                   isDefault(*cfg.LdapSettings.GroupFilter, ""),
635		"isdefault_group_display_name_attribute": isDefault(*cfg.LdapSettings.GroupDisplayNameAttribute, model.LdapSettingsDefaultGroupDisplayNameAttribute),
636		"isdefault_group_id_attribute":           isDefault(*cfg.LdapSettings.GroupIdAttribute, model.LdapSettingsDefaultGroupIdAttribute),
637		"isempty_guest_filter":                   isDefault(*cfg.LdapSettings.GuestFilter, ""),
638		"isempty_admin_filter":                   isDefault(*cfg.LdapSettings.AdminFilter, ""),
639		"isnotempty_picture_attribute":           !isDefault(*cfg.LdapSettings.PictureAttribute, ""),
640		"isnotempty_public_certificate":          !isDefault(*cfg.LdapSettings.PublicCertificateFile, ""),
641		"isnotempty_private_key":                 !isDefault(*cfg.LdapSettings.PrivateKeyFile, ""),
642	})
643
644	ts.sendTelemetry(TrackConfigCompliance, map[string]interface{}{
645		"enable":       *cfg.ComplianceSettings.Enable,
646		"enable_daily": *cfg.ComplianceSettings.EnableDaily,
647	})
648
649	ts.sendTelemetry(TrackConfigLocalization, map[string]interface{}{
650		"default_server_locale": *cfg.LocalizationSettings.DefaultServerLocale,
651		"default_client_locale": *cfg.LocalizationSettings.DefaultClientLocale,
652		"available_locales":     *cfg.LocalizationSettings.AvailableLocales,
653	})
654
655	ts.sendTelemetry(TrackConfigSAML, map[string]interface{}{
656		"enable":                              *cfg.SamlSettings.Enable,
657		"enable_sync_with_ldap":               *cfg.SamlSettings.EnableSyncWithLdap,
658		"enable_sync_with_ldap_include_auth":  *cfg.SamlSettings.EnableSyncWithLdapIncludeAuth,
659		"ignore_guests_ldap_sync":             *cfg.SamlSettings.IgnoreGuestsLdapSync,
660		"enable_admin_attribute":              *cfg.SamlSettings.EnableAdminAttribute,
661		"verify":                              *cfg.SamlSettings.Verify,
662		"encrypt":                             *cfg.SamlSettings.Encrypt,
663		"sign_request":                        *cfg.SamlSettings.SignRequest,
664		"isdefault_signature_algorithm":       isDefault(*cfg.SamlSettings.SignatureAlgorithm, ""),
665		"isdefault_canonical_algorithm":       isDefault(*cfg.SamlSettings.CanonicalAlgorithm, ""),
666		"isdefault_scoping_idp_provider_id":   isDefault(*cfg.SamlSettings.ScopingIDPProviderId, ""),
667		"isdefault_scoping_idp_name":          isDefault(*cfg.SamlSettings.ScopingIDPName, ""),
668		"isdefault_id_attribute":              isDefault(*cfg.SamlSettings.IdAttribute, model.SamlSettingsDefaultIdAttribute),
669		"isdefault_guest_attribute":           isDefault(*cfg.SamlSettings.GuestAttribute, model.SamlSettingsDefaultGuestAttribute),
670		"isdefault_admin_attribute":           isDefault(*cfg.SamlSettings.AdminAttribute, model.SamlSettingsDefaultAdminAttribute),
671		"isdefault_first_name_attribute":      isDefault(*cfg.SamlSettings.FirstNameAttribute, model.SamlSettingsDefaultFirstNameAttribute),
672		"isdefault_last_name_attribute":       isDefault(*cfg.SamlSettings.LastNameAttribute, model.SamlSettingsDefaultLastNameAttribute),
673		"isdefault_email_attribute":           isDefault(*cfg.SamlSettings.EmailAttribute, model.SamlSettingsDefaultEmailAttribute),
674		"isdefault_username_attribute":        isDefault(*cfg.SamlSettings.UsernameAttribute, model.SamlSettingsDefaultUsernameAttribute),
675		"isdefault_nickname_attribute":        isDefault(*cfg.SamlSettings.NicknameAttribute, model.SamlSettingsDefaultNicknameAttribute),
676		"isdefault_locale_attribute":          isDefault(*cfg.SamlSettings.LocaleAttribute, model.SamlSettingsDefaultLocaleAttribute),
677		"isdefault_position_attribute":        isDefault(*cfg.SamlSettings.PositionAttribute, model.SamlSettingsDefaultPositionAttribute),
678		"isdefault_login_button_text":         isDefault(*cfg.SamlSettings.LoginButtonText, model.UserAuthServiceSamlText),
679		"isdefault_login_button_color":        isDefault(*cfg.SamlSettings.LoginButtonColor, ""),
680		"isdefault_login_button_border_color": isDefault(*cfg.SamlSettings.LoginButtonBorderColor, ""),
681		"isdefault_login_button_text_color":   isDefault(*cfg.SamlSettings.LoginButtonTextColor, ""),
682	})
683
684	ts.sendTelemetry(TrackConfigCluster, map[string]interface{}{
685		"enable":                                *cfg.ClusterSettings.Enable,
686		"network_interface":                     isDefault(*cfg.ClusterSettings.NetworkInterface, ""),
687		"bind_address":                          isDefault(*cfg.ClusterSettings.BindAddress, ""),
688		"advertise_address":                     isDefault(*cfg.ClusterSettings.AdvertiseAddress, ""),
689		"use_ip_address":                        *cfg.ClusterSettings.UseIPAddress,
690		"enable_experimental_gossip_encryption": *cfg.ClusterSettings.EnableExperimentalGossipEncryption,
691		"enable_gossip_compression":             *cfg.ClusterSettings.EnableGossipCompression,
692		"read_only_config":                      *cfg.ClusterSettings.ReadOnlyConfig,
693	})
694
695	ts.sendTelemetry(TrackConfigMetrics, map[string]interface{}{
696		"enable":             *cfg.MetricsSettings.Enable,
697		"block_profile_rate": *cfg.MetricsSettings.BlockProfileRate,
698	})
699
700	ts.sendTelemetry(TrackConfigNativeApp, map[string]interface{}{
701		"isdefault_app_custom_url_schemes":    isDefaultArray(cfg.NativeAppSettings.AppCustomURLSchemes, model.GetDefaultAppCustomURLSchemes()),
702		"isdefault_app_download_link":         isDefault(*cfg.NativeAppSettings.AppDownloadLink, model.NativeappSettingsDefaultAppDownloadLink),
703		"isdefault_android_app_download_link": isDefault(*cfg.NativeAppSettings.AndroidAppDownloadLink, model.NativeappSettingsDefaultAndroidAppDownloadLink),
704		"isdefault_iosapp_download_link":      isDefault(*cfg.NativeAppSettings.IosAppDownloadLink, model.NativeappSettingsDefaultIosAppDownloadLink),
705	})
706
707	ts.sendTelemetry(TrackConfigExperimental, map[string]interface{}{
708		"client_side_cert_enable":            *cfg.ExperimentalSettings.ClientSideCertEnable,
709		"isdefault_client_side_cert_check":   isDefault(*cfg.ExperimentalSettings.ClientSideCertCheck, model.ClientSideCertCheckPrimaryAuth),
710		"link_metadata_timeout_milliseconds": *cfg.ExperimentalSettings.LinkMetadataTimeoutMilliseconds,
711		"enable_click_to_reply":              *cfg.ExperimentalSettings.EnableClickToReply,
712		"restrict_system_admin":              *cfg.ExperimentalSettings.RestrictSystemAdmin,
713		"use_new_saml_library":               *cfg.ExperimentalSettings.UseNewSAMLLibrary,
714		"cloud_billing":                      *cfg.ExperimentalSettings.CloudBilling,
715		"cloud_user_limit":                   *cfg.ExperimentalSettings.CloudUserLimit,
716		"enable_shared_channels":             *cfg.ExperimentalSettings.EnableSharedChannels,
717		"enable_remote_cluster_service":      *cfg.ExperimentalSettings.EnableRemoteClusterService && cfg.FeatureFlags.EnableRemoteClusterService,
718	})
719
720	ts.sendTelemetry(TrackConfigAnalytics, map[string]interface{}{
721		"isdefault_max_users_for_statistics": isDefault(*cfg.AnalyticsSettings.MaxUsersForStatistics, model.AnalyticsSettingsDefaultMaxUsersForStatistics),
722	})
723
724	ts.sendTelemetry(TrackConfigAnnouncement, map[string]interface{}{
725		"enable_banner":               *cfg.AnnouncementSettings.EnableBanner,
726		"isdefault_banner_color":      isDefault(*cfg.AnnouncementSettings.BannerColor, model.AnnouncementSettingsDefaultBannerColor),
727		"isdefault_banner_text_color": isDefault(*cfg.AnnouncementSettings.BannerTextColor, model.AnnouncementSettingsDefaultBannerTextColor),
728		"allow_banner_dismissal":      *cfg.AnnouncementSettings.AllowBannerDismissal,
729		"admin_notices_enabled":       *cfg.AnnouncementSettings.AdminNoticesEnabled,
730		"user_notices_enabled":        *cfg.AnnouncementSettings.UserNoticesEnabled,
731	})
732
733	ts.sendTelemetry(TrackConfigElasticsearch, map[string]interface{}{
734		"isdefault_connection_url":          isDefault(*cfg.ElasticsearchSettings.ConnectionURL, model.ElasticsearchSettingsDefaultConnectionURL),
735		"isdefault_username":                isDefault(*cfg.ElasticsearchSettings.Username, model.ElasticsearchSettingsDefaultUsername),
736		"isdefault_password":                isDefault(*cfg.ElasticsearchSettings.Password, model.ElasticsearchSettingsDefaultPassword),
737		"enable_indexing":                   *cfg.ElasticsearchSettings.EnableIndexing,
738		"enable_searching":                  *cfg.ElasticsearchSettings.EnableSearching,
739		"enable_autocomplete":               *cfg.ElasticsearchSettings.EnableAutocomplete,
740		"sniff":                             *cfg.ElasticsearchSettings.Sniff,
741		"post_index_replicas":               *cfg.ElasticsearchSettings.PostIndexReplicas,
742		"post_index_shards":                 *cfg.ElasticsearchSettings.PostIndexShards,
743		"channel_index_replicas":            *cfg.ElasticsearchSettings.ChannelIndexReplicas,
744		"channel_index_shards":              *cfg.ElasticsearchSettings.ChannelIndexShards,
745		"user_index_replicas":               *cfg.ElasticsearchSettings.UserIndexReplicas,
746		"user_index_shards":                 *cfg.ElasticsearchSettings.UserIndexShards,
747		"isdefault_index_prefix":            isDefault(*cfg.ElasticsearchSettings.IndexPrefix, model.ElasticsearchSettingsDefaultIndexPrefix),
748		"live_indexing_batch_size":          *cfg.ElasticsearchSettings.LiveIndexingBatchSize,
749		"bulk_indexing_time_window_seconds": *cfg.ElasticsearchSettings.BulkIndexingTimeWindowSeconds,
750		"request_timeout_seconds":           *cfg.ElasticsearchSettings.RequestTimeoutSeconds,
751		"skip_tls_verification":             *cfg.ElasticsearchSettings.SkipTLSVerification,
752		"trace":                             *cfg.ElasticsearchSettings.Trace,
753	})
754
755	ts.trackPluginConfig(cfg, model.PluginSettingsDefaultMarketplaceURL)
756
757	ts.sendTelemetry(TrackConfigDataRetention, map[string]interface{}{
758		"enable_message_deletion": *cfg.DataRetentionSettings.EnableMessageDeletion,
759		"enable_file_deletion":    *cfg.DataRetentionSettings.EnableFileDeletion,
760		"message_retention_days":  *cfg.DataRetentionSettings.MessageRetentionDays,
761		"file_retention_days":     *cfg.DataRetentionSettings.FileRetentionDays,
762		"deletion_job_start_time": *cfg.DataRetentionSettings.DeletionJobStartTime,
763		"batch_size":              *cfg.DataRetentionSettings.BatchSize,
764	})
765
766	ts.sendTelemetry(TrackConfigMessageExport, map[string]interface{}{
767		"enable_message_export":                 *cfg.MessageExportSettings.EnableExport,
768		"export_format":                         *cfg.MessageExportSettings.ExportFormat,
769		"daily_run_time":                        *cfg.MessageExportSettings.DailyRunTime,
770		"default_export_from_timestamp":         *cfg.MessageExportSettings.ExportFromTimestamp,
771		"batch_size":                            *cfg.MessageExportSettings.BatchSize,
772		"global_relay_customer_type":            *cfg.MessageExportSettings.GlobalRelaySettings.CustomerType,
773		"is_default_global_relay_smtp_username": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SMTPUsername, ""),
774		"is_default_global_relay_smtp_password": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SMTPPassword, ""),
775		"is_default_global_relay_email_address": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.EmailAddress, ""),
776		"global_relay_smtp_server_timeout":      *cfg.MessageExportSettings.GlobalRelaySettings.SMTPServerTimeout,
777		"download_export_results":               *cfg.MessageExportSettings.DownloadExportResults,
778	})
779
780	ts.sendTelemetry(TrackConfigDisplay, map[string]interface{}{
781		"experimental_timezone":        *cfg.DisplaySettings.ExperimentalTimezone,
782		"isdefault_custom_url_schemes": len(cfg.DisplaySettings.CustomURLSchemes) != 0,
783	})
784
785	ts.sendTelemetry(TrackConfigGuestAccounts, map[string]interface{}{
786		"enable":                                 *cfg.GuestAccountsSettings.Enable,
787		"allow_email_accounts":                   *cfg.GuestAccountsSettings.AllowEmailAccounts,
788		"enforce_multifactor_authentication":     *cfg.GuestAccountsSettings.EnforceMultifactorAuthentication,
789		"isdefault_restrict_creation_to_domains": isDefault(*cfg.GuestAccountsSettings.RestrictCreationToDomains, ""),
790	})
791
792	ts.sendTelemetry(TrackConfigImageProxy, map[string]interface{}{
793		"enable":                               *cfg.ImageProxySettings.Enable,
794		"image_proxy_type":                     *cfg.ImageProxySettings.ImageProxyType,
795		"isdefault_remote_image_proxy_url":     isDefault(*cfg.ImageProxySettings.RemoteImageProxyURL, ""),
796		"isdefault_remote_image_proxy_options": isDefault(*cfg.ImageProxySettings.RemoteImageProxyOptions, ""),
797	})
798
799	ts.sendTelemetry(TrackConfigBleve, map[string]interface{}{
800		"enable_indexing":                   *cfg.BleveSettings.EnableIndexing,
801		"enable_searching":                  *cfg.BleveSettings.EnableSearching,
802		"enable_autocomplete":               *cfg.BleveSettings.EnableAutocomplete,
803		"bulk_indexing_time_window_seconds": *cfg.BleveSettings.BulkIndexingTimeWindowSeconds,
804	})
805
806	ts.sendTelemetry(TrackConfigExport, map[string]interface{}{
807		"retention_days": *cfg.ExportSettings.RetentionDays,
808	})
809
810	// Convert feature flags to map[string]interface{} for sending
811	flags := cfg.FeatureFlags.ToMap()
812	interfaceFlags := make(map[string]interface{})
813	for k, v := range flags {
814		interfaceFlags[k] = v
815	}
816	ts.sendTelemetry(TrackFeatureFlags, interfaceFlags)
817}
818
819func (ts *TelemetryService) trackLicense() {
820	if license := ts.srv.License(); license != nil {
821		data := map[string]interface{}{
822			"customer_id": license.Customer.Id,
823			"license_id":  license.Id,
824			"issued":      license.IssuedAt,
825			"start":       license.StartsAt,
826			"expire":      license.ExpiresAt,
827			"users":       *license.Features.Users,
828			"edition":     license.SkuShortName,
829		}
830
831		features := license.Features.ToMap()
832		for featureName, featureValue := range features {
833			data["feature_"+featureName] = featureValue
834		}
835
836		ts.sendTelemetry(TrackLicense, data)
837	}
838}
839
840func (ts *TelemetryService) trackPlugins() {
841	pluginsEnvironment := ts.srv.GetPluginsEnvironment()
842	if pluginsEnvironment == nil {
843		return
844	}
845
846	totalEnabledCount := 0
847	webappEnabledCount := 0
848	backendEnabledCount := 0
849	totalDisabledCount := 0
850	webappDisabledCount := 0
851	backendDisabledCount := 0
852	brokenManifestCount := 0
853	settingsCount := 0
854
855	pluginStates := ts.srv.Config().PluginSettings.PluginStates
856	plugins, _ := pluginsEnvironment.Available()
857
858	if pluginStates != nil && plugins != nil {
859		for _, plugin := range plugins {
860			if plugin.Manifest == nil {
861				brokenManifestCount += 1
862				continue
863			}
864
865			if state, ok := pluginStates[plugin.Manifest.Id]; ok && state.Enable {
866				totalEnabledCount += 1
867				if plugin.Manifest.HasServer() {
868					backendEnabledCount += 1
869				}
870				if plugin.Manifest.HasWebapp() {
871					webappEnabledCount += 1
872				}
873			} else {
874				totalDisabledCount += 1
875				if plugin.Manifest.HasServer() {
876					backendDisabledCount += 1
877				}
878				if plugin.Manifest.HasWebapp() {
879					webappDisabledCount += 1
880				}
881			}
882			if plugin.Manifest.SettingsSchema != nil {
883				settingsCount += 1
884			}
885		}
886	} else {
887		totalEnabledCount = -1  // -1 to indicate disabled or error
888		totalDisabledCount = -1 // -1 to indicate disabled or error
889	}
890
891	ts.sendTelemetry(TrackPlugins, map[string]interface{}{
892		"enabled_plugins":               totalEnabledCount,
893		"enabled_webapp_plugins":        webappEnabledCount,
894		"enabled_backend_plugins":       backendEnabledCount,
895		"disabled_plugins":              totalDisabledCount,
896		"disabled_webapp_plugins":       webappDisabledCount,
897		"disabled_backend_plugins":      backendDisabledCount,
898		"plugins_with_settings":         settingsCount,
899		"plugins_with_broken_manifests": brokenManifestCount,
900	})
901}
902
903func (ts *TelemetryService) trackServer() {
904	data := map[string]interface{}{
905		"edition":           model.BuildEnterpriseReady,
906		"version":           model.CurrentVersion,
907		"database_type":     *ts.srv.Config().SqlSettings.DriverName,
908		"operating_system":  runtime.GOOS,
909		"installation_type": os.Getenv(EnvVarInstallType),
910	}
911
912	if scr, err := ts.dbStore.User().AnalyticsGetSystemAdminCount(); err == nil {
913		data["system_admins"] = scr
914	}
915
916	if scr, err := ts.dbStore.GetDbVersion(false); err == nil {
917		data["database_version"] = scr
918	}
919
920	ts.sendTelemetry(TrackServer, data)
921}
922
923func (ts *TelemetryService) trackPermissions() {
924	phase1Complete := false
925	if _, err := ts.dbStore.System().GetByName(model.AdvancedPermissionsMigrationKey); err == nil {
926		phase1Complete = true
927	}
928
929	phase2Complete := false
930	if _, err := ts.dbStore.System().GetByName(model.MigrationKeyAdvancedPermissionsPhase2); err == nil {
931		phase2Complete = true
932	}
933
934	ts.sendTelemetry(TrackPermissionsGeneral, map[string]interface{}{
935		"phase_1_migration_complete": phase1Complete,
936		"phase_2_migration_complete": phase2Complete,
937	})
938
939	systemAdminPermissions := ""
940	if role, err := ts.srv.GetRoleByName(context.Background(), model.SystemAdminRoleId); err == nil {
941		systemAdminPermissions = strings.Join(role.Permissions, " ")
942	}
943
944	systemUserPermissions := ""
945	if role, err := ts.srv.GetRoleByName(context.Background(), model.SystemUserRoleId); err == nil {
946		systemUserPermissions = strings.Join(role.Permissions, " ")
947	}
948
949	teamAdminPermissions := ""
950	if role, err := ts.srv.GetRoleByName(context.Background(), model.TeamAdminRoleId); err == nil {
951		teamAdminPermissions = strings.Join(role.Permissions, " ")
952	}
953
954	teamUserPermissions := ""
955	if role, err := ts.srv.GetRoleByName(context.Background(), model.TeamUserRoleId); err == nil {
956		teamUserPermissions = strings.Join(role.Permissions, " ")
957	}
958
959	teamGuestPermissions := ""
960	if role, err := ts.srv.GetRoleByName(context.Background(), model.TeamGuestRoleId); err == nil {
961		teamGuestPermissions = strings.Join(role.Permissions, " ")
962	}
963
964	channelAdminPermissions := ""
965	if role, err := ts.srv.GetRoleByName(context.Background(), model.ChannelAdminRoleId); err == nil {
966		channelAdminPermissions = strings.Join(role.Permissions, " ")
967	}
968
969	channelUserPermissions := ""
970	if role, err := ts.srv.GetRoleByName(context.Background(), model.ChannelUserRoleId); err == nil {
971		channelUserPermissions = strings.Join(role.Permissions, " ")
972	}
973
974	channelGuestPermissions := ""
975	if role, err := ts.srv.GetRoleByName(context.Background(), model.ChannelGuestRoleId); err == nil {
976		channelGuestPermissions = strings.Join(role.Permissions, " ")
977	}
978
979	systemManagerPermissions := ""
980	systemManagerPermissionsModified := false
981	if role, err := ts.srv.GetRoleByName(context.Background(), model.SystemManagerRoleId); err == nil {
982		systemManagerPermissionsModified = len(model.PermissionsChangedByPatch(role, &model.RolePatch{Permissions: &model.SystemManagerDefaultPermissions})) > 0
983		systemManagerPermissions = strings.Join(role.Permissions, " ")
984	}
985	systemManagerCount, countErr := ts.dbStore.User().Count(model.UserCountOptions{Roles: []string{model.SystemManagerRoleId}})
986	if countErr != nil {
987		systemManagerCount = 0
988	}
989
990	systemUserManagerPermissions := ""
991	systemUserManagerPermissionsModified := false
992	if role, err := ts.srv.GetRoleByName(context.Background(), model.SystemUserManagerRoleId); err == nil {
993		systemUserManagerPermissionsModified = len(model.PermissionsChangedByPatch(role, &model.RolePatch{Permissions: &model.SystemUserManagerDefaultPermissions})) > 0
994		systemUserManagerPermissions = strings.Join(role.Permissions, " ")
995	}
996	systemUserManagerCount, countErr := ts.dbStore.User().Count(model.UserCountOptions{Roles: []string{model.SystemUserManagerRoleId}})
997	if countErr != nil {
998		systemManagerCount = 0
999	}
1000
1001	systemReadOnlyAdminPermissions := ""
1002	systemReadOnlyAdminPermissionsModified := false
1003	if role, err := ts.srv.GetRoleByName(context.Background(), model.SystemReadOnlyAdminRoleId); err == nil {
1004		systemReadOnlyAdminPermissionsModified = len(model.PermissionsChangedByPatch(role, &model.RolePatch{Permissions: &model.SystemReadOnlyAdminDefaultPermissions})) > 0
1005		systemReadOnlyAdminPermissions = strings.Join(role.Permissions, " ")
1006	}
1007	systemReadOnlyAdminCount, countErr := ts.dbStore.User().Count(model.UserCountOptions{Roles: []string{model.SystemReadOnlyAdminRoleId}})
1008	if countErr != nil {
1009		systemReadOnlyAdminCount = 0
1010	}
1011
1012	ts.sendTelemetry(TrackPermissionsSystemScheme, map[string]interface{}{
1013		"system_admin_permissions":                    systemAdminPermissions,
1014		"system_user_permissions":                     systemUserPermissions,
1015		"system_manager_permissions":                  systemManagerPermissions,
1016		"system_user_manager_permissions":             systemUserManagerPermissions,
1017		"system_read_only_admin_permissions":          systemReadOnlyAdminPermissions,
1018		"team_admin_permissions":                      teamAdminPermissions,
1019		"team_user_permissions":                       teamUserPermissions,
1020		"team_guest_permissions":                      teamGuestPermissions,
1021		"channel_admin_permissions":                   channelAdminPermissions,
1022		"channel_user_permissions":                    channelUserPermissions,
1023		"channel_guest_permissions":                   channelGuestPermissions,
1024		"system_manager_permissions_modified":         systemManagerPermissionsModified,
1025		"system_manager_count":                        systemManagerCount,
1026		"system_user_manager_permissions_modified":    systemUserManagerPermissionsModified,
1027		"system_user_manager_count":                   systemUserManagerCount,
1028		"system_read_only_admin_permissions_modified": systemReadOnlyAdminPermissionsModified,
1029		"system_read_only_admin_count":                systemReadOnlyAdminCount,
1030	})
1031
1032	if schemes, err := ts.srv.GetSchemes(model.SchemeScopeTeam, 0, 100); err == nil {
1033		for _, scheme := range schemes {
1034			teamAdminPermissions := ""
1035			if role, err := ts.srv.GetRoleByName(context.Background(), scheme.DefaultTeamAdminRole); err == nil {
1036				teamAdminPermissions = strings.Join(role.Permissions, " ")
1037			}
1038
1039			teamUserPermissions := ""
1040			if role, err := ts.srv.GetRoleByName(context.Background(), scheme.DefaultTeamUserRole); err == nil {
1041				teamUserPermissions = strings.Join(role.Permissions, " ")
1042			}
1043
1044			teamGuestPermissions := ""
1045			if role, err := ts.srv.GetRoleByName(context.Background(), scheme.DefaultTeamGuestRole); err == nil {
1046				teamGuestPermissions = strings.Join(role.Permissions, " ")
1047			}
1048
1049			channelAdminPermissions := ""
1050			if role, err := ts.srv.GetRoleByName(context.Background(), scheme.DefaultChannelAdminRole); err == nil {
1051				channelAdminPermissions = strings.Join(role.Permissions, " ")
1052			}
1053
1054			channelUserPermissions := ""
1055			if role, err := ts.srv.GetRoleByName(context.Background(), scheme.DefaultChannelUserRole); err == nil {
1056				channelUserPermissions = strings.Join(role.Permissions, " ")
1057			}
1058
1059			channelGuestPermissions := ""
1060			if role, err := ts.srv.GetRoleByName(context.Background(), scheme.DefaultChannelGuestRole); err == nil {
1061				channelGuestPermissions = strings.Join(role.Permissions, " ")
1062			}
1063
1064			count, _ := ts.dbStore.Team().AnalyticsGetTeamCountForScheme(scheme.Id)
1065
1066			ts.sendTelemetry(TrackPermissionsTeamSchemes, map[string]interface{}{
1067				"scheme_id":                 scheme.Id,
1068				"team_admin_permissions":    teamAdminPermissions,
1069				"team_user_permissions":     teamUserPermissions,
1070				"team_guest_permissions":    teamGuestPermissions,
1071				"channel_admin_permissions": channelAdminPermissions,
1072				"channel_user_permissions":  channelUserPermissions,
1073				"channel_guest_permissions": channelGuestPermissions,
1074				"team_count":                count,
1075			})
1076		}
1077	}
1078}
1079
1080func (ts *TelemetryService) trackElasticsearch() {
1081	data := map[string]interface{}{}
1082
1083	for _, engine := range ts.searchEngine.GetActiveEngines() {
1084		if engine.GetVersion() != 0 && engine.GetName() == "elasticsearch" {
1085			data["elasticsearch_server_version"] = engine.GetVersion()
1086		}
1087	}
1088
1089	ts.sendTelemetry(TrackElasticsearch, data)
1090}
1091
1092func (ts *TelemetryService) trackGroups() {
1093	groupCount, err := ts.dbStore.Group().GroupCount()
1094	if err != nil {
1095		mlog.Debug("Could not get group_count", mlog.Err(err))
1096	}
1097
1098	groupTeamCount, err := ts.dbStore.Group().GroupTeamCount()
1099	if err != nil {
1100		mlog.Debug("Could not get group_team_count", mlog.Err(err))
1101	}
1102
1103	groupChannelCount, err := ts.dbStore.Group().GroupChannelCount()
1104	if err != nil {
1105		mlog.Debug("Could not get group_channel_count", mlog.Err(err))
1106	}
1107
1108	groupSyncedTeamCount, nErr := ts.dbStore.Team().GroupSyncedTeamCount()
1109	if nErr != nil {
1110		mlog.Debug("Could not get group_synced_team_count", mlog.Err(nErr))
1111	}
1112
1113	groupSyncedChannelCount, nErr := ts.dbStore.Channel().GroupSyncedChannelCount()
1114	if nErr != nil {
1115		mlog.Debug("Could not get group_synced_channel_count", mlog.Err(nErr))
1116	}
1117
1118	groupMemberCount, err := ts.dbStore.Group().GroupMemberCount()
1119	if err != nil {
1120		mlog.Debug("Could not get group_member_count", mlog.Err(err))
1121	}
1122
1123	distinctGroupMemberCount, err := ts.dbStore.Group().DistinctGroupMemberCount()
1124	if err != nil {
1125		mlog.Debug("Could not get distinct_group_member_count", mlog.Err(err))
1126	}
1127
1128	groupCountWithAllowReference, err := ts.dbStore.Group().GroupCountWithAllowReference()
1129	if err != nil {
1130		mlog.Debug("Could not get group_count_with_allow_reference", mlog.Err(err))
1131	}
1132
1133	ts.sendTelemetry(TrackGroups, map[string]interface{}{
1134		"group_count":                      groupCount,
1135		"group_team_count":                 groupTeamCount,
1136		"group_channel_count":              groupChannelCount,
1137		"group_synced_team_count":          groupSyncedTeamCount,
1138		"group_synced_channel_count":       groupSyncedChannelCount,
1139		"group_member_count":               groupMemberCount,
1140		"distinct_group_member_count":      distinctGroupMemberCount,
1141		"group_count_with_allow_reference": groupCountWithAllowReference,
1142	})
1143}
1144
1145func (ts *TelemetryService) trackChannelModeration() {
1146	channelSchemeCount, err := ts.dbStore.Scheme().CountByScope(model.SchemeScopeChannel)
1147	if err != nil {
1148		mlog.Debug("Could not get channel_scheme_count", mlog.Err(err))
1149	}
1150
1151	createPostUser, err := ts.dbStore.Scheme().CountWithoutPermission(model.SchemeScopeChannel, model.PermissionCreatePost.Id, model.RoleScopeChannel, model.RoleTypeUser)
1152	if err != nil {
1153		mlog.Debug("Could not get create_post_user_disabled_count", mlog.Err(err))
1154	}
1155
1156	createPostGuest, err := ts.dbStore.Scheme().CountWithoutPermission(model.SchemeScopeChannel, model.PermissionCreatePost.Id, model.RoleScopeChannel, model.RoleTypeGuest)
1157	if err != nil {
1158		mlog.Debug("Could not get create_post_guest_disabled_count", mlog.Err(err))
1159	}
1160
1161	// only need to track one of 'add_reaction' or 'remove_reaction` because they're both toggled together by the channel moderation feature
1162	postReactionsUser, err := ts.dbStore.Scheme().CountWithoutPermission(model.SchemeScopeChannel, model.PermissionAddReaction.Id, model.RoleScopeChannel, model.RoleTypeUser)
1163	if err != nil {
1164		mlog.Debug("Could not get post_reactions_user_disabled_count", mlog.Err(err))
1165	}
1166
1167	postReactionsGuest, err := ts.dbStore.Scheme().CountWithoutPermission(model.SchemeScopeChannel, model.PermissionAddReaction.Id, model.RoleScopeChannel, model.RoleTypeGuest)
1168	if err != nil {
1169		mlog.Debug("Could not get post_reactions_guest_disabled_count", mlog.Err(err))
1170	}
1171
1172	// only need to track one of 'manage_public_channel_members' or 'manage_private_channel_members` because they're both toggled together by the channel moderation feature
1173	manageMembersUser, err := ts.dbStore.Scheme().CountWithoutPermission(model.SchemeScopeChannel, model.PermissionManagePublicChannelMembers.Id, model.RoleScopeChannel, model.RoleTypeUser)
1174	if err != nil {
1175		mlog.Debug("Could not get manage_members_user_disabled_count", mlog.Err(err))
1176	}
1177
1178	useChannelMentionsUser, err := ts.dbStore.Scheme().CountWithoutPermission(model.SchemeScopeChannel, model.PermissionUseChannelMentions.Id, model.RoleScopeChannel, model.RoleTypeUser)
1179	if err != nil {
1180		mlog.Debug("Could not get use_channel_mentions_user_disabled_count", mlog.Err(err))
1181	}
1182
1183	useChannelMentionsGuest, err := ts.dbStore.Scheme().CountWithoutPermission(model.SchemeScopeChannel, model.PermissionUseChannelMentions.Id, model.RoleScopeChannel, model.RoleTypeGuest)
1184	if err != nil {
1185		mlog.Debug("Could not get use_channel_mentions_guest_disabled_count", mlog.Err(err))
1186	}
1187
1188	ts.sendTelemetry(TrackChannelModeration, map[string]interface{}{
1189		"channel_scheme_count": channelSchemeCount,
1190
1191		"create_post_user_disabled_count":  createPostUser,
1192		"create_post_guest_disabled_count": createPostGuest,
1193
1194		"post_reactions_user_disabled_count":  postReactionsUser,
1195		"post_reactions_guest_disabled_count": postReactionsGuest,
1196
1197		"manage_members_user_disabled_count": manageMembersUser, // the UI does not allow this to be removed for guests
1198
1199		"use_channel_mentions_user_disabled_count":  useChannelMentionsUser,
1200		"use_channel_mentions_guest_disabled_count": useChannelMentionsGuest,
1201	})
1202}
1203
1204func (ts *TelemetryService) initRudder(endpoint string, rudderKey string) {
1205	if ts.rudderClient == nil {
1206		config := rudder.Config{}
1207		config.Logger = rudder.StdLogger(ts.log.With(mlog.String("source", "rudder")).StdLogger(mlog.LvlDebug))
1208		config.Endpoint = endpoint
1209		// For testing
1210		if endpoint != RudderDataplaneURL {
1211			config.Verbose = true
1212			config.BatchSize = 1
1213		}
1214		client, err := rudder.NewWithConfig(rudderKey, endpoint, config)
1215		if err != nil {
1216			mlog.Error("Failed to create Rudder instance", mlog.Err(err))
1217			return
1218		}
1219		client.Enqueue(rudder.Identify{
1220			UserId: ts.TelemetryID,
1221		})
1222
1223		ts.rudderClient = client
1224	}
1225}
1226
1227func (ts *TelemetryService) doTelemetryIfNeeded(firstRun time.Time) {
1228	hoursSinceFirstServerRun := time.Since(firstRun).Hours()
1229	// Send once every 10 minutes for the first hour
1230	// Send once every hour thereafter for the first 12 hours
1231	// Send at the 24 hour mark and every 24 hours after
1232	if hoursSinceFirstServerRun < 1 {
1233		ts.doTelemetry()
1234	} else if hoursSinceFirstServerRun <= 12 && time.Since(ts.timestampLastTelemetrySent) >= time.Hour {
1235		ts.doTelemetry()
1236	} else if hoursSinceFirstServerRun > 12 && time.Since(ts.timestampLastTelemetrySent) >= 24*time.Hour {
1237		ts.doTelemetry()
1238	}
1239}
1240
1241func (ts *TelemetryService) RunTelemetryJob(firstRun int64) {
1242	// Send on boot
1243	ts.doTelemetry()
1244	model.CreateRecurringTask("Telemetry", func() {
1245		ts.doTelemetryIfNeeded(utils.TimeFromMillis(firstRun))
1246	}, time.Minute*10)
1247}
1248
1249func (ts *TelemetryService) doTelemetry() {
1250	if *ts.srv.Config().LogSettings.EnableDiagnostics {
1251		ts.timestampLastTelemetrySent = time.Now()
1252		ts.sendDailyTelemetry(false)
1253	}
1254}
1255
1256// Shutdown closes the telemetry client.
1257func (ts *TelemetryService) Shutdown() error {
1258	if ts.rudderClient != nil {
1259		return ts.rudderClient.Close()
1260	}
1261	return nil
1262}
1263
1264func (ts *TelemetryService) trackWarnMetrics() {
1265	systemDataList, nErr := ts.dbStore.System().Get()
1266	if nErr != nil {
1267		return
1268	}
1269	for key, value := range systemDataList {
1270		if strings.HasPrefix(key, model.WarnMetricStatusStorePrefix) {
1271			if _, ok := model.WarnMetricsTable[key]; ok {
1272				ts.sendTelemetry(TrackWarnMetrics, map[string]interface{}{
1273					key: value != "false",
1274				})
1275			}
1276		}
1277	}
1278}
1279
1280func (ts *TelemetryService) trackPluginConfig(cfg *model.Config, marketplaceURL string) {
1281	pluginConfigData := map[string]interface{}{
1282		"enable_nps_survey":             pluginSetting(&cfg.PluginSettings, "com.mattermost.nps", "enablesurvey", true),
1283		"enable":                        *cfg.PluginSettings.Enable,
1284		"enable_uploads":                *cfg.PluginSettings.EnableUploads,
1285		"allow_insecure_download_url":   *cfg.PluginSettings.AllowInsecureDownloadURL,
1286		"enable_health_check":           *cfg.PluginSettings.EnableHealthCheck,
1287		"enable_marketplace":            *cfg.PluginSettings.EnableMarketplace,
1288		"require_pluginSignature":       *cfg.PluginSettings.RequirePluginSignature,
1289		"enable_remote_marketplace":     *cfg.PluginSettings.EnableRemoteMarketplace,
1290		"automatic_prepackaged_plugins": *cfg.PluginSettings.AutomaticPrepackagedPlugins,
1291		"is_default_marketplace_url":    isDefault(*cfg.PluginSettings.MarketplaceURL, model.PluginSettingsDefaultMarketplaceURL),
1292		"signature_public_key_files":    len(cfg.PluginSettings.SignaturePublicKeyFiles),
1293		"chimera_oauth_proxy_url":       *cfg.PluginSettings.ChimeraOAuthProxyURL,
1294	}
1295
1296	// knownPluginIDs lists all known plugin IDs in the Marketplace
1297	knownPluginIDs := []string{
1298		"antivirus",
1299		"com.github.manland.mattermost-plugin-gitlab",
1300		"com.github.moussetc.mattermost.plugin.giphy",
1301		"com.github.phillipahereza.mattermost-plugin-digitalocean",
1302		"com.mattermost.aws-sns",
1303		"com.mattermost.confluence",
1304		"com.mattermost.custom-attributes",
1305		"com.mattermost.mscalendar",
1306		"com.mattermost.nps",
1307		"com.mattermost.plugin-channel-export",
1308		"com.mattermost.plugin-incident-management",
1309		"playbooks",
1310		"com.mattermost.plugin-todo",
1311		"com.mattermost.webex",
1312		"com.mattermost.welcomebot",
1313		"github",
1314		"jenkins",
1315		"jira",
1316		"jitsi",
1317		"mattermost-autolink",
1318		"memes",
1319		"skype4business",
1320		"zoom",
1321		"focalboard",
1322	}
1323
1324	marketplacePlugins, err := ts.getAllMarketplaceplugins(marketplaceURL)
1325	if err != nil {
1326		mlog.Info("Failed to fetch marketplace plugins for telemetry. Using predefined list.", mlog.Err(err))
1327
1328		for _, id := range knownPluginIDs {
1329			pluginConfigData["enable_"+id] = pluginActivated(cfg.PluginSettings.PluginStates, id)
1330		}
1331	} else {
1332		for _, p := range marketplacePlugins {
1333			id := p.Manifest.Id
1334
1335			pluginConfigData["enable_"+id] = pluginActivated(cfg.PluginSettings.PluginStates, id)
1336		}
1337	}
1338
1339	pluginsEnvironment := ts.srv.GetPluginsEnvironment()
1340	if pluginsEnvironment != nil {
1341		if plugins, appErr := pluginsEnvironment.Available(); appErr != nil {
1342			mlog.Warn("Unable to add plugin versions to telemetry", mlog.Err(appErr))
1343		} else {
1344			// If marketplace request failed, use predefined list
1345			if marketplacePlugins == nil {
1346				for _, id := range knownPluginIDs {
1347					pluginConfigData["version_"+id] = pluginVersion(plugins, id)
1348				}
1349			} else {
1350				for _, p := range marketplacePlugins {
1351					id := p.Manifest.Id
1352
1353					pluginConfigData["version_"+id] = pluginVersion(plugins, id)
1354				}
1355			}
1356		}
1357	}
1358
1359	ts.sendTelemetry(TrackConfigPlugin, pluginConfigData)
1360}
1361
1362func (ts *TelemetryService) getAllMarketplaceplugins(marketplaceURL string) ([]*model.BaseMarketplacePlugin, error) {
1363	marketplaceClient, err := marketplace.NewClient(
1364		marketplaceURL,
1365		ts.srv.HTTPService(),
1366	)
1367	if err != nil {
1368		return nil, err
1369	}
1370
1371	// Fetch all plugins from marketplace.
1372	filter := &model.MarketplacePluginFilter{
1373		PerPage:       -1,
1374		ServerVersion: model.CurrentVersion,
1375	}
1376
1377	license := ts.srv.License()
1378	if license != nil && *license.Features.EnterprisePlugins {
1379		filter.EnterprisePlugins = true
1380	}
1381
1382	if model.BuildEnterpriseReady == "true" {
1383		filter.BuildEnterpriseReady = true
1384	}
1385
1386	return marketplaceClient.GetPlugins(filter)
1387}
1388