lex-microsoft_teams 0.6.8 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/legion/extensions/microsoft_teams/actors/auth_validator.rb +18 -34
- data/lib/legion/extensions/microsoft_teams/actors/cache_bulk_ingest.rb +3 -3
- data/lib/legion/extensions/microsoft_teams/actors/cache_sync.rb +2 -2
- data/lib/legion/extensions/microsoft_teams/actors/channel_poller.rb +11 -23
- data/lib/legion/extensions/microsoft_teams/actors/direct_chat_poller.rb +6 -18
- data/lib/legion/extensions/microsoft_teams/actors/incremental_sync.rb +1 -1
- data/lib/legion/extensions/microsoft_teams/actors/meeting_ingest.rb +14 -30
- data/lib/legion/extensions/microsoft_teams/actors/observed_chat_poller.rb +2 -2
- data/lib/legion/extensions/microsoft_teams/actors/presence_poller.rb +4 -18
- data/lib/legion/extensions/microsoft_teams/actors/profile_ingest.rb +4 -4
- data/lib/legion/extensions/microsoft_teams/actors/token_refresher.rb +14 -30
- data/lib/legion/extensions/microsoft_teams/cli/auth.rb +12 -41
- data/lib/legion/extensions/microsoft_teams/helpers/browser_auth.rb +37 -66
- data/lib/legion/extensions/microsoft_teams/helpers/permission_guard.rb +4 -9
- data/lib/legion/extensions/microsoft_teams/helpers/session_manager.rb +3 -5
- data/lib/legion/extensions/microsoft_teams/helpers/subscription_registry.rb +5 -6
- data/lib/legion/extensions/microsoft_teams/helpers/token_cache.rb +47 -60
- data/lib/legion/extensions/microsoft_teams/runners/bot.rb +5 -13
- data/lib/legion/extensions/microsoft_teams/runners/cache_ingest.rb +2 -2
- data/lib/legion/extensions/microsoft_teams/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: facefcf933d33cf6fafd3a08ab9a723b366bb23f876c16f470f0e701d136ef15
|
|
4
|
+
data.tar.gz: 6fdd195db4cd7b46855fb2817731fca9a7acc53cc670d407af40933a41db643e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d7b123f842ca82a63c4ad0749735f2872a55566d1ade3f4e4156495ce189e3054a9963b2659460f46ee2468193000594e7136fdaf4011a7c055f8b1af41b69e9
|
|
7
|
+
data.tar.gz: 0c69a4338443a3ab4be698edf8c3a48094e6f5a04cb1c807428be7febe42e17bfc9015c957bb2c030713d6f4a9cc56c84a452424a5bb9068d67cff6b574848d6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.9] - 2026-03-22
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- Replace direct `Legion::Logging` calls with injected `log` helper from `Helpers::Lex` across all actors, runners, helpers, and CLI
|
|
7
|
+
- Remove private `log_debug`, `log_info`, `log_warn`, `log_error` wrapper methods (net -161 lines)
|
|
8
|
+
- Add `Helpers::Lex` stub in spec_helper for test environment compatibility
|
|
9
|
+
|
|
3
10
|
## [0.6.8] - 2026-03-22
|
|
4
11
|
|
|
5
12
|
### Fixed
|
|
@@ -24,32 +24,32 @@ module Legion
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def manual
|
|
27
|
-
|
|
27
|
+
log.info('AuthValidator starting')
|
|
28
28
|
cache = token_cache
|
|
29
|
-
|
|
29
|
+
log.debug("Token loaded: authenticated?=#{cache.authenticated?}")
|
|
30
30
|
|
|
31
31
|
if cache.authenticated?
|
|
32
32
|
token = cache.cached_delegated_token
|
|
33
33
|
if token
|
|
34
|
-
|
|
34
|
+
log.info('Teams delegated auth restored (token valid)')
|
|
35
35
|
elsif cache.previously_authenticated? || auto_authenticate?
|
|
36
|
-
|
|
36
|
+
log.info('Token loaded but expired, attempting browser re-auth')
|
|
37
37
|
attempt_browser_reauth(cache)
|
|
38
38
|
else
|
|
39
|
-
|
|
39
|
+
log.debug('Token loaded but expired, no re-auth configured')
|
|
40
40
|
end
|
|
41
41
|
elsif cache.previously_authenticated?
|
|
42
|
-
|
|
42
|
+
log.warn('Token file found but could not load, attempting re-authentication')
|
|
43
43
|
attempt_browser_reauth(cache)
|
|
44
44
|
elsif auto_authenticate?
|
|
45
|
-
|
|
45
|
+
log.info('auto_authenticate enabled, opening browser for initial authentication...')
|
|
46
46
|
attempt_browser_reauth(cache)
|
|
47
47
|
else
|
|
48
|
-
|
|
48
|
+
log.debug('No Teams delegated auth configured, skipping')
|
|
49
49
|
end
|
|
50
|
-
|
|
50
|
+
log.info('AuthValidator complete')
|
|
51
51
|
rescue StandardError => e
|
|
52
|
-
|
|
52
|
+
log.error("AuthValidator: #{e.message}")
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
private
|
|
@@ -57,15 +57,15 @@ module Legion
|
|
|
57
57
|
def attempt_browser_reauth(cache)
|
|
58
58
|
settings = teams_auth_settings
|
|
59
59
|
unless settings[:tenant_id] && settings[:client_id]
|
|
60
|
-
|
|
60
|
+
log.warn("Cannot re-auth: tenant_id=#{settings[:tenant_id] ? 'present' : 'nil'}, client_id=#{settings[:client_id] ? 'present' : 'nil'}")
|
|
61
61
|
return false
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
log.warn('Delegated token expired, opening browser for re-authentication...')
|
|
65
65
|
|
|
66
66
|
scopes = settings.dig(:delegated, :scopes) ||
|
|
67
67
|
Legion::Extensions::MicrosoftTeams::Helpers::BrowserAuth::DEFAULT_SCOPES
|
|
68
|
-
|
|
68
|
+
log.debug("Using scopes: #{scopes}")
|
|
69
69
|
browser_auth = Legion::Extensions::MicrosoftTeams::Helpers::BrowserAuth.new(
|
|
70
70
|
tenant_id: settings[:tenant_id],
|
|
71
71
|
client_id: settings[:client_id],
|
|
@@ -74,12 +74,12 @@ module Legion
|
|
|
74
74
|
|
|
75
75
|
result = browser_auth.authenticate
|
|
76
76
|
if result[:error]
|
|
77
|
-
|
|
77
|
+
log.error("Browser auth returned error: #{result[:error]} - #{result[:description]}")
|
|
78
78
|
return false
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
body = result[:result]
|
|
82
|
-
|
|
82
|
+
log.info("Browser auth succeeded, storing token (expires_in=#{body['expires_in']})")
|
|
83
83
|
cache.store_delegated_token(
|
|
84
84
|
access_token: body['access_token'],
|
|
85
85
|
refresh_token: body['refresh_token'],
|
|
@@ -87,17 +87,17 @@ module Legion
|
|
|
87
87
|
scopes: scopes
|
|
88
88
|
)
|
|
89
89
|
cache.save_to_vault
|
|
90
|
-
|
|
90
|
+
log.info('Teams delegated auth restored via browser')
|
|
91
91
|
true
|
|
92
92
|
rescue StandardError => e
|
|
93
|
-
|
|
93
|
+
log.error("Browser re-auth failed: #{e.message}")
|
|
94
94
|
false
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def auto_authenticate?
|
|
98
98
|
settings = teams_auth_settings
|
|
99
99
|
result = settings.dig(:delegated, :auto_authenticate) == true
|
|
100
|
-
|
|
100
|
+
log.debug("auto_authenticate? => #{result}")
|
|
101
101
|
result
|
|
102
102
|
end
|
|
103
103
|
|
|
@@ -111,22 +111,6 @@ module Legion
|
|
|
111
111
|
settings[:client_id] ||= ENV.fetch('AZURE_CLIENT_ID', nil)
|
|
112
112
|
settings
|
|
113
113
|
end
|
|
114
|
-
|
|
115
|
-
def log_info(msg)
|
|
116
|
-
Legion::Logging.info("[Teams::AuthValidator] #{msg}") if defined?(Legion::Logging)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def log_warn(msg)
|
|
120
|
-
Legion::Logging.warn("[Teams::AuthValidator] #{msg}") if defined?(Legion::Logging)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def log_debug(msg)
|
|
124
|
-
Legion::Logging.debug("[Teams::AuthValidator] #{msg}") if defined?(Legion::Logging)
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
def log_error(msg)
|
|
128
|
-
Legion::Logging.error("[Teams::AuthValidator] #{msg}") if defined?(Legion::Logging)
|
|
129
|
-
end
|
|
130
114
|
end
|
|
131
115
|
end
|
|
132
116
|
end
|
|
@@ -22,12 +22,12 @@ module Legion
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def manual
|
|
25
|
-
|
|
25
|
+
log.info('CacheBulkIngest firing')
|
|
26
26
|
result = runner_class.ingest_cache(**args)
|
|
27
|
-
|
|
27
|
+
log.info("Complete: #{result.inspect[0, 200]}")
|
|
28
28
|
result
|
|
29
29
|
rescue StandardError => e
|
|
30
|
-
|
|
30
|
+
log.error("CacheBulkIngest error: #{e.message}")
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def args
|
|
@@ -38,10 +38,10 @@ module Legion
|
|
|
38
38
|
latest = result[:result][:latest_time]
|
|
39
39
|
@last_sync_time = latest if latest
|
|
40
40
|
stored = result[:result][:stored] || 0
|
|
41
|
-
|
|
41
|
+
log.info("CacheSync: ingested #{stored} new Teams messages") if stored.positive?
|
|
42
42
|
end
|
|
43
43
|
rescue StandardError => e
|
|
44
|
-
|
|
44
|
+
log.error("CacheSync: #{e.message}")
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -47,23 +47,23 @@ module Legion
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def manual
|
|
50
|
-
|
|
50
|
+
log.info('ChannelPoller polling team channels')
|
|
51
51
|
token = token_cache.cached_graph_token
|
|
52
52
|
unless token
|
|
53
|
-
|
|
53
|
+
log.debug('No token available, skipping poll')
|
|
54
54
|
return
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
teams = fetch_joined_teams(token: token)
|
|
58
|
-
|
|
58
|
+
log.debug("Found #{teams.length} joined team(s)")
|
|
59
59
|
|
|
60
60
|
teams.first(max_teams).each do |team|
|
|
61
61
|
poll_team(team: team, token: token)
|
|
62
62
|
rescue StandardError => e
|
|
63
|
-
|
|
63
|
+
log.error("Error polling team #{team['displayName']}: #{e.message}")
|
|
64
64
|
end
|
|
65
65
|
rescue StandardError => e
|
|
66
|
-
|
|
66
|
+
log.error("ChannelPoller: #{e.message}")
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
private
|
|
@@ -73,7 +73,7 @@ module Legion
|
|
|
73
73
|
response = conn.get('me/joinedTeams')
|
|
74
74
|
response.body&.dig('value') || []
|
|
75
75
|
rescue StandardError => e
|
|
76
|
-
|
|
76
|
+
log.error("Failed to fetch joined teams: #{e.message}")
|
|
77
77
|
[]
|
|
78
78
|
end
|
|
79
79
|
|
|
@@ -87,7 +87,7 @@ module Legion
|
|
|
87
87
|
selected.first(max_channels_per_team).each do |channel|
|
|
88
88
|
poll_channel(team_id: team_id, team_name: team_name, channel: channel, token: token)
|
|
89
89
|
rescue StandardError => e
|
|
90
|
-
|
|
90
|
+
log.error("Error polling channel #{channel['displayName']} in #{team_name}: #{e.message}")
|
|
91
91
|
end
|
|
92
92
|
end
|
|
93
93
|
|
|
@@ -96,7 +96,7 @@ module Legion
|
|
|
96
96
|
response = conn.get("teams/#{team_id}/channels")
|
|
97
97
|
response.body&.dig('value') || []
|
|
98
98
|
rescue StandardError => e
|
|
99
|
-
|
|
99
|
+
log.error("Failed to fetch channels for team #{team_id}: #{e.message}")
|
|
100
100
|
[]
|
|
101
101
|
end
|
|
102
102
|
|
|
@@ -121,7 +121,7 @@ module Legion
|
|
|
121
121
|
new_msgs = filter_new_messages(channel_id: channel_id, messages: messages)
|
|
122
122
|
return if new_msgs.empty?
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
log.info("#{team_name} / #{channel_name}: #{new_msgs.length} new message(s)")
|
|
125
125
|
new_msgs.each do |msg|
|
|
126
126
|
log_message(team_name: team_name, channel_name: channel_name, msg: msg)
|
|
127
127
|
store_channel_message_trace(team_name: team_name, channel_name: channel_name, msg: msg) if memory_available?
|
|
@@ -142,7 +142,7 @@ module Legion
|
|
|
142
142
|
sender = msg.dig('from', 'user', 'displayName') || 'Unknown'
|
|
143
143
|
content = (msg.dig('body', 'content') || '').gsub(/<[^>]+>/, '').strip
|
|
144
144
|
snippet = content.length > 100 ? "#{content[0, 100]}..." : content
|
|
145
|
-
|
|
145
|
+
log.info(" [#{team_name}] ##{channel_name} | #{sender}: #{snippet}")
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def max_teams
|
|
@@ -172,19 +172,7 @@ module Legion
|
|
|
172
172
|
confidence: 0.7
|
|
173
173
|
)
|
|
174
174
|
rescue StandardError => e
|
|
175
|
-
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def log_debug(msg)
|
|
179
|
-
Legion::Logging.debug("[Teams::ChannelPoller] #{msg}") if defined?(Legion::Logging)
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
def log_info(msg)
|
|
183
|
-
Legion::Logging.info("[Teams::ChannelPoller] #{msg}") if defined?(Legion::Logging)
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
def log_error(msg)
|
|
187
|
-
Legion::Logging.error("[Teams::ChannelPoller] #{msg}") if defined?(Legion::Logging)
|
|
175
|
+
log.error("Failed to store channel message trace: #{e.message}")
|
|
188
176
|
end
|
|
189
177
|
end
|
|
190
178
|
end
|
|
@@ -39,16 +39,16 @@ module Legion
|
|
|
39
39
|
def manual
|
|
40
40
|
token = token_cache.cached_graph_token
|
|
41
41
|
unless token
|
|
42
|
-
|
|
42
|
+
log.debug('No token available, skipping poll')
|
|
43
43
|
return
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
log.info('Polling bot DM chats')
|
|
47
47
|
chats = fetch_bot_chats(token: token)
|
|
48
|
-
|
|
48
|
+
log.info("Found #{chats.length} bot chats")
|
|
49
49
|
chats.each { |chat| poll_chat(chat_id: chat[:id], token: token) }
|
|
50
50
|
rescue StandardError => e
|
|
51
|
-
|
|
51
|
+
log.error("DirectChatPoller: #{e.message}")
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
private
|
|
@@ -69,7 +69,7 @@ module Legion
|
|
|
69
69
|
new_msgs.reject! { |m| m[:from_id] == @bot_id }
|
|
70
70
|
return if new_msgs.empty?
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
log.info("Chat #{chat_id}: #{new_msgs.length} new message(s)")
|
|
73
73
|
new_msgs.each { |msg| publish_message(msg.merge(chat_id: chat_id, mode: :direct)) }
|
|
74
74
|
update_hwm_from_messages(chat_id: chat_id, messages: new_msgs)
|
|
75
75
|
end
|
|
@@ -77,7 +77,7 @@ module Legion
|
|
|
77
77
|
def publish_message(payload)
|
|
78
78
|
Legion::Extensions::MicrosoftTeams::Transport::Messages::TeamsMessage.new.publish(payload)
|
|
79
79
|
rescue StandardError => e
|
|
80
|
-
|
|
80
|
+
log.error("DirectChatPoller publish failed: #{e.message}")
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def normalize_messages(messages)
|
|
@@ -104,18 +104,6 @@ module Legion
|
|
|
104
104
|
|
|
105
105
|
Legion::Settings.dig(:microsoft_teams, :bot, key) || default
|
|
106
106
|
end
|
|
107
|
-
|
|
108
|
-
def log_debug(msg)
|
|
109
|
-
Legion::Logging.debug("[Teams::DirectChatPoller] #{msg}") if defined?(Legion::Logging)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def log_info(msg)
|
|
113
|
-
Legion::Logging.info("[Teams::DirectChatPoller] #{msg}") if defined?(Legion::Logging)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def log_error(msg)
|
|
117
|
-
Legion::Logging.error("[Teams::DirectChatPoller] #{msg}") if defined?(Legion::Logging)
|
|
118
|
-
end
|
|
119
107
|
end
|
|
120
108
|
end
|
|
121
109
|
end
|
|
@@ -49,14 +49,14 @@ module Legion
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def manual
|
|
52
|
-
|
|
52
|
+
log.info('MeetingIngest polling for meetings')
|
|
53
53
|
token = token_cache.cached_graph_token
|
|
54
54
|
return if token.nil?
|
|
55
55
|
|
|
56
56
|
conn = graph_connection(token: token)
|
|
57
57
|
response = conn.get("#{user_path('me')}/onlineMeetings")
|
|
58
58
|
meetings = response.body&.dig('value') || []
|
|
59
|
-
|
|
59
|
+
log.info("Found #{meetings.length} online meeting(s)")
|
|
60
60
|
|
|
61
61
|
meetings.each do |meeting|
|
|
62
62
|
meeting_id = meeting['id']
|
|
@@ -66,11 +66,11 @@ module Legion
|
|
|
66
66
|
process_meeting(meeting_id: meeting_id, subject: meeting['subject'], token: token)
|
|
67
67
|
@processed_meetings.add(meeting_id)
|
|
68
68
|
rescue StandardError => e
|
|
69
|
-
|
|
69
|
+
log.error("Failed to process meeting #{meeting_id}: #{e.message}")
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
rescue StandardError => e
|
|
73
|
-
|
|
73
|
+
log.error("MeetingIngest: #{e.message}")
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
private
|
|
@@ -79,7 +79,7 @@ module Legion
|
|
|
79
79
|
conn = graph_connection(token: token)
|
|
80
80
|
|
|
81
81
|
transcripts = fetch_transcripts(conn: conn, meeting_id: meeting_id)
|
|
82
|
-
|
|
82
|
+
log.info("Meeting '#{subject}' (#{meeting_id}): #{transcripts.length} transcript(s)")
|
|
83
83
|
|
|
84
84
|
transcripts.each do |transcript|
|
|
85
85
|
fetch_and_log_transcript_content(
|
|
@@ -98,7 +98,7 @@ module Legion
|
|
|
98
98
|
response = conn.get("#{user_path('me')}/onlineMeetings/#{meeting_id}/transcripts")
|
|
99
99
|
response.body&.dig('value') || []
|
|
100
100
|
rescue StandardError => e
|
|
101
|
-
|
|
101
|
+
log.warn("Could not fetch transcripts for meeting #{meeting_id}: #{e.message}")
|
|
102
102
|
[]
|
|
103
103
|
end
|
|
104
104
|
|
|
@@ -112,30 +112,30 @@ module Legion
|
|
|
112
112
|
)
|
|
113
113
|
content = content_response.body.to_s
|
|
114
114
|
preview = content[0, 200]
|
|
115
|
-
|
|
115
|
+
log.debug("Meeting '#{subject}' transcript #{tid}: #{preview}")
|
|
116
116
|
store_transcript_trace(meeting_id: meeting_id, subject: subject, transcript_id: tid, content: content) if memory_available?
|
|
117
117
|
rescue StandardError => e
|
|
118
|
-
|
|
118
|
+
log.warn("Could not fetch transcript content #{tid} for meeting #{meeting_id}: #{e.message}")
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def fetch_and_log_ai_insights(conn:, meeting_id:, subject:)
|
|
122
122
|
response = conn.get("#{user_path('me')}/onlineMeetings/#{meeting_id}/aiInsights")
|
|
123
123
|
insights = response.body&.dig('value') || []
|
|
124
|
-
|
|
124
|
+
log.info("Meeting '#{subject}' (#{meeting_id}): #{insights.length} AI insight(s)")
|
|
125
125
|
|
|
126
126
|
insights.each do |insight|
|
|
127
127
|
action_items = insight['actionItems'] || []
|
|
128
128
|
next if action_items.empty?
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
log.info("Meeting '#{subject}' AI insight action items (#{action_items.length}):")
|
|
131
131
|
action_items.each do |item|
|
|
132
|
-
|
|
132
|
+
log.info(" - #{item['text'] || item.inspect}")
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
store_insight_trace(meeting_id: meeting_id, subject: subject, insight: insight) if memory_available?
|
|
136
136
|
end
|
|
137
137
|
rescue StandardError => e
|
|
138
|
-
|
|
138
|
+
log.warn("Could not fetch AI insights for meeting #{meeting_id}: #{e.message}")
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
def store_transcript_trace(meeting_id:, subject:, transcript_id:, content:) # rubocop:disable Lint/UnusedMethodArgument
|
|
@@ -147,7 +147,7 @@ module Legion
|
|
|
147
147
|
confidence: 0.9
|
|
148
148
|
)
|
|
149
149
|
rescue StandardError => e
|
|
150
|
-
|
|
150
|
+
log.warn("Could not store transcript trace for meeting #{meeting_id}: #{e.message}")
|
|
151
151
|
end
|
|
152
152
|
|
|
153
153
|
def store_insight_trace(meeting_id:, subject:, insight:) # rubocop:disable Lint/UnusedMethodArgument
|
|
@@ -159,23 +159,7 @@ module Legion
|
|
|
159
159
|
confidence: 0.8
|
|
160
160
|
)
|
|
161
161
|
rescue StandardError => e
|
|
162
|
-
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
def log_debug(msg)
|
|
166
|
-
Legion::Logging.debug("[Teams::MeetingIngest] #{msg}") if defined?(Legion::Logging)
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def log_info(msg)
|
|
170
|
-
Legion::Logging.info("[Teams::MeetingIngest] #{msg}") if defined?(Legion::Logging)
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def log_warn(msg)
|
|
174
|
-
Legion::Logging.warn("[Teams::MeetingIngest] #{msg}") if defined?(Legion::Logging)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def log_error(msg)
|
|
178
|
-
Legion::Logging.error("[Teams::MeetingIngest] #{msg}") if defined?(Legion::Logging)
|
|
162
|
+
log.warn("Could not store insight trace for meeting #{meeting_id}: #{e.message}")
|
|
179
163
|
end
|
|
180
164
|
end
|
|
181
165
|
end
|
|
@@ -54,7 +54,7 @@ module Legion
|
|
|
54
54
|
)
|
|
55
55
|
end
|
|
56
56
|
rescue StandardError => e
|
|
57
|
-
|
|
57
|
+
log.error("ObservedChatPoller: #{e.message}")
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
private
|
|
@@ -83,7 +83,7 @@ module Legion
|
|
|
83
83
|
def publish_message(payload)
|
|
84
84
|
Legion::Extensions::MicrosoftTeams::Transport::Messages::TeamsMessage.new.publish(payload)
|
|
85
85
|
rescue StandardError => e
|
|
86
|
-
|
|
86
|
+
log.error("ObservedChatPoller publish failed: #{e.message}")
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def normalize_messages(messages)
|
|
@@ -35,7 +35,7 @@ module Legion
|
|
|
35
35
|
def manual
|
|
36
36
|
token = token_cache.cached_graph_token
|
|
37
37
|
unless token
|
|
38
|
-
|
|
38
|
+
log.debug('No token available, skipping presence poll')
|
|
39
39
|
return
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -49,27 +49,13 @@ module Legion
|
|
|
49
49
|
current = { availability: availability, activity: activity }
|
|
50
50
|
|
|
51
51
|
if current == @last_presence
|
|
52
|
-
|
|
52
|
+
log.debug("Presence unchanged: availability=#{availability}, activity=#{activity}")
|
|
53
53
|
else
|
|
54
|
-
|
|
54
|
+
log.info("Presence changed: availability=#{availability}, activity=#{activity}")
|
|
55
55
|
@last_presence = current
|
|
56
56
|
end
|
|
57
57
|
rescue StandardError => e
|
|
58
|
-
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
private
|
|
62
|
-
|
|
63
|
-
def log_debug(msg)
|
|
64
|
-
Legion::Logging.debug("[Teams::PresencePoller] #{msg}") if defined?(Legion::Logging)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def log_info(msg)
|
|
68
|
-
Legion::Logging.info("[Teams::PresencePoller] #{msg}") if defined?(Legion::Logging)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def log_error(msg)
|
|
72
|
-
Legion::Logging.error("[Teams::PresencePoller] #{msg}") if defined?(Legion::Logging)
|
|
58
|
+
log.error("PresencePoller: #{e.message}")
|
|
73
59
|
end
|
|
74
60
|
end
|
|
75
61
|
end
|
|
@@ -23,13 +23,13 @@ module Legion
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def manual
|
|
26
|
-
|
|
26
|
+
log.info('ProfileIngest firing')
|
|
27
27
|
token = resolve_token
|
|
28
28
|
unless token
|
|
29
|
-
|
|
29
|
+
log.warn('No token available, skipping')
|
|
30
30
|
return
|
|
31
31
|
end
|
|
32
|
-
|
|
32
|
+
log.info('Token acquired, starting ingest')
|
|
33
33
|
|
|
34
34
|
settings = begin
|
|
35
35
|
Legion::Settings[:microsoft_teams] || {}
|
|
@@ -43,7 +43,7 @@ module Legion
|
|
|
43
43
|
message_depth: ingest.fetch(:message_depth, 50)
|
|
44
44
|
)
|
|
45
45
|
rescue StandardError => e
|
|
46
|
-
|
|
46
|
+
log.error("ProfileIngest: #{e.message}")
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
private
|
|
@@ -33,25 +33,25 @@ module Legion
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def manual
|
|
36
|
-
|
|
36
|
+
log.debug('TokenRefresher tick')
|
|
37
37
|
unless token_cache.authenticated?
|
|
38
|
-
|
|
38
|
+
log.debug('No active delegated token, skipping refresh')
|
|
39
39
|
return
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
log.info('Checking delegated token freshness')
|
|
43
43
|
token = token_cache.cached_delegated_token
|
|
44
44
|
if token
|
|
45
|
-
|
|
45
|
+
log.info('Delegated token still valid, persisting')
|
|
46
46
|
token_cache.save_to_vault
|
|
47
47
|
elsif token_cache.previously_authenticated?
|
|
48
|
-
|
|
48
|
+
log.warn('Delegated token expired, attempting browser re-auth')
|
|
49
49
|
attempt_browser_reauth(token_cache)
|
|
50
50
|
else
|
|
51
|
-
|
|
51
|
+
log.warn('Delegated token expired, no previous auth to restore')
|
|
52
52
|
end
|
|
53
53
|
rescue StandardError => e
|
|
54
|
-
|
|
54
|
+
log.error("TokenRefresher: #{e.message}")
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
private
|
|
@@ -59,15 +59,15 @@ module Legion
|
|
|
59
59
|
def attempt_browser_reauth(cache)
|
|
60
60
|
settings = teams_auth_settings
|
|
61
61
|
unless settings[:tenant_id] && settings[:client_id]
|
|
62
|
-
|
|
62
|
+
log.warn("Cannot re-auth: tenant_id=#{settings[:tenant_id] ? 'present' : 'nil'}, client_id=#{settings[:client_id] ? 'present' : 'nil'}")
|
|
63
63
|
return false
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
log.warn('Delegated token expired, opening browser for re-authentication...')
|
|
67
67
|
|
|
68
68
|
scopes = settings.dig(:delegated, :scopes) ||
|
|
69
69
|
Legion::Extensions::MicrosoftTeams::Helpers::BrowserAuth::DEFAULT_SCOPES
|
|
70
|
-
|
|
70
|
+
log.debug("Using scopes: #{scopes}")
|
|
71
71
|
browser_auth = Legion::Extensions::MicrosoftTeams::Helpers::BrowserAuth.new(
|
|
72
72
|
tenant_id: settings[:tenant_id],
|
|
73
73
|
client_id: settings[:client_id],
|
|
@@ -76,12 +76,12 @@ module Legion
|
|
|
76
76
|
|
|
77
77
|
result = browser_auth.authenticate
|
|
78
78
|
if result[:error]
|
|
79
|
-
|
|
79
|
+
log.error("Browser auth returned error: #{result[:error]} - #{result[:description]}")
|
|
80
80
|
return false
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
body = result[:result]
|
|
84
|
-
|
|
84
|
+
log.info("Browser auth succeeded, storing token (expires_in=#{body['expires_in']})")
|
|
85
85
|
cache.store_delegated_token(
|
|
86
86
|
access_token: body['access_token'],
|
|
87
87
|
refresh_token: body['refresh_token'],
|
|
@@ -89,10 +89,10 @@ module Legion
|
|
|
89
89
|
scopes: scopes
|
|
90
90
|
)
|
|
91
91
|
cache.save_to_vault
|
|
92
|
-
|
|
92
|
+
log.info('Teams delegated auth restored via browser')
|
|
93
93
|
true
|
|
94
94
|
rescue StandardError => e
|
|
95
|
-
|
|
95
|
+
log.error("Browser re-auth failed: #{e.message}")
|
|
96
96
|
false
|
|
97
97
|
end
|
|
98
98
|
|
|
@@ -106,22 +106,6 @@ module Legion
|
|
|
106
106
|
settings[:client_id] ||= ENV.fetch('AZURE_CLIENT_ID', nil)
|
|
107
107
|
settings
|
|
108
108
|
end
|
|
109
|
-
|
|
110
|
-
def log_debug(msg)
|
|
111
|
-
Legion::Logging.debug("[Teams::TokenRefresher] #{msg}") if defined?(Legion::Logging)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def log_info(msg)
|
|
115
|
-
Legion::Logging.info("[Teams::TokenRefresher] #{msg}") if defined?(Legion::Logging)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def log_warn(msg)
|
|
119
|
-
Legion::Logging.warn("[Teams::TokenRefresher] #{msg}") if defined?(Legion::Logging)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def log_error(msg)
|
|
123
|
-
Legion::Logging.error("[Teams::TokenRefresher] #{msg}") if defined?(Legion::Logging)
|
|
124
|
-
end
|
|
125
109
|
end
|
|
126
110
|
end
|
|
127
111
|
end
|