lex-microsoft_teams 0.6.45 → 0.6.46
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/.gitignore +1 -0
- data/CHANGELOG.md +2 -0
- data/CLAUDE.md +29 -266
- data/lex-microsoft_teams.gemspec +1 -0
- data/lib/legion/extensions/microsoft_teams/absorbers/channel.rb +29 -17
- data/lib/legion/extensions/microsoft_teams/absorbers/chat.rb +20 -14
- data/lib/legion/extensions/microsoft_teams/absorbers/meeting.rb +21 -14
- data/lib/legion/extensions/microsoft_teams/actors/absorb_channel.rb +7 -4
- data/lib/legion/extensions/microsoft_teams/actors/absorb_chat.rb +7 -4
- data/lib/legion/extensions/microsoft_teams/actors/absorb_meeting.rb +7 -4
- data/lib/legion/extensions/microsoft_teams/actors/api_ingest.rb +13 -15
- data/lib/legion/extensions/microsoft_teams/actors/cache_bulk_ingest.rb +3 -3
- data/lib/legion/extensions/microsoft_teams/actors/cache_sync.rb +2 -1
- data/lib/legion/extensions/microsoft_teams/actors/channel_poller.rb +25 -16
- data/lib/legion/extensions/microsoft_teams/actors/direct_chat_poller.rb +16 -10
- data/lib/legion/extensions/microsoft_teams/actors/incremental_sync.rb +8 -8
- data/lib/legion/extensions/microsoft_teams/actors/meeting_ingest.rb +30 -22
- data/lib/legion/extensions/microsoft_teams/actors/observed_chat_poller.rb +14 -8
- data/lib/legion/extensions/microsoft_teams/actors/presence_poller.rb +14 -8
- data/lib/legion/extensions/microsoft_teams/actors/profile_ingest.rb +13 -16
- data/lib/legion/extensions/microsoft_teams/helpers/client.rb +10 -4
- data/lib/legion/extensions/microsoft_teams/helpers/high_water_mark.rb +3 -2
- data/lib/legion/extensions/microsoft_teams/helpers/prompt_resolver.rb +4 -1
- data/lib/legion/extensions/microsoft_teams/helpers/session_manager.rb +8 -2
- data/lib/legion/extensions/microsoft_teams/helpers/subscription_registry.rb +5 -3
- data/lib/legion/extensions/microsoft_teams/helpers/trace_retriever.rb +6 -1
- data/lib/legion/extensions/microsoft_teams/local_cache/extractor.rb +1 -1
- data/lib/legion/extensions/microsoft_teams/local_cache/sstable_reader.rb +2 -1
- data/lib/legion/extensions/microsoft_teams/runners/activities.rb +42 -0
- data/lib/legion/extensions/microsoft_teams/runners/ai_insights.rb +61 -0
- data/lib/legion/extensions/microsoft_teams/runners/api_ingest.rb +19 -14
- data/lib/legion/extensions/microsoft_teams/runners/app_installations.rb +85 -0
- data/lib/legion/extensions/microsoft_teams/runners/auth.rb +3 -107
- data/lib/legion/extensions/microsoft_teams/runners/bot.rb +20 -12
- data/lib/legion/extensions/microsoft_teams/runners/cache_ingest.rb +7 -5
- data/lib/legion/extensions/microsoft_teams/runners/call_events.rb +72 -0
- data/lib/legion/extensions/microsoft_teams/runners/channel_messages.rb +85 -0
- data/lib/legion/extensions/microsoft_teams/runners/channels.rb +68 -0
- data/lib/legion/extensions/microsoft_teams/runners/chats.rb +56 -0
- data/lib/legion/extensions/microsoft_teams/runners/files.rb +77 -0
- data/lib/legion/extensions/microsoft_teams/runners/local_cache.rb +4 -0
- data/lib/legion/extensions/microsoft_teams/runners/loop.rb +5 -0
- data/lib/legion/extensions/microsoft_teams/runners/meeting_artifacts.rb +54 -0
- data/lib/legion/extensions/microsoft_teams/runners/meetings.rb +92 -0
- data/lib/legion/extensions/microsoft_teams/runners/messages.rb +61 -0
- data/lib/legion/extensions/microsoft_teams/runners/ownership.rb +11 -0
- data/lib/legion/extensions/microsoft_teams/runners/people.rb +24 -0
- data/lib/legion/extensions/microsoft_teams/runners/presence.rb +14 -0
- data/lib/legion/extensions/microsoft_teams/runners/profile_ingest.rb +17 -4
- data/lib/legion/extensions/microsoft_teams/runners/subscriptions.rb +86 -0
- data/lib/legion/extensions/microsoft_teams/runners/teams.rb +30 -0
- data/lib/legion/extensions/microsoft_teams/runners/transcripts.rb +35 -0
- data/lib/legion/extensions/microsoft_teams/version.rb +1 -1
- data/lib/legion/extensions/microsoft_teams.rb +10 -3
- metadata +20 -8
- data/lib/legion/extensions/microsoft_teams/actors/auth_validator.rb +0 -123
- data/lib/legion/extensions/microsoft_teams/actors/token_refresher.rb +0 -122
- data/lib/legion/extensions/microsoft_teams/cli/auth.rb +0 -94
- data/lib/legion/extensions/microsoft_teams/helpers/browser_auth.rb +0 -270
- data/lib/legion/extensions/microsoft_teams/helpers/callback_server.rb +0 -90
- data/lib/legion/extensions/microsoft_teams/helpers/token_cache.rb +0 -412
- data/lib/legion/extensions/microsoft_teams/hooks/auth.rb +0 -17
|
@@ -9,18 +9,42 @@ module Legion
|
|
|
9
9
|
module People
|
|
10
10
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
11
|
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[profile people person colleague colleagues contact contacts]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :get_profile,
|
|
17
|
+
desc: 'Get the Microsoft Graph profile for a user',
|
|
18
|
+
mcp_prefix: 'teams.get_profile',
|
|
19
|
+
mcp_category: 'teams_people',
|
|
20
|
+
mcp_tier: :low,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
trigger_words: %w[profile self]
|
|
23
|
+
|
|
12
24
|
def get_profile(user_id: 'me', **)
|
|
25
|
+
log.debug("People#get_profile user_id=#{user_id}")
|
|
13
26
|
response = graph_connection(**).get(user_path(user_id).to_s)
|
|
14
27
|
{ result: response.body }
|
|
15
28
|
rescue StandardError => e
|
|
29
|
+
handle_exception(e, level: :error, operation: 'People#get_profile', user_id: user_id)
|
|
16
30
|
{ error: e.message }
|
|
17
31
|
end
|
|
18
32
|
|
|
33
|
+
definition :list_people,
|
|
34
|
+
desc: 'List people relevant to the current user (colleagues, contacts)',
|
|
35
|
+
mcp_prefix: 'teams.list_people',
|
|
36
|
+
mcp_category: 'teams_people',
|
|
37
|
+
mcp_tier: :standard,
|
|
38
|
+
idempotent: true,
|
|
39
|
+
trigger_words: %w[people colleagues contacts]
|
|
40
|
+
|
|
19
41
|
def list_people(user_id: 'me', top: 25, **)
|
|
42
|
+
log.debug("People#list_people user_id=#{user_id} top=#{top}")
|
|
20
43
|
params = { '$top' => top }
|
|
21
44
|
response = graph_connection(**).get("#{user_path(user_id)}/people", params)
|
|
22
45
|
{ result: response.body }
|
|
23
46
|
rescue StandardError => e
|
|
47
|
+
handle_exception(e, level: :error, operation: 'People#list_people', user_id: user_id)
|
|
24
48
|
{ error: e.message }
|
|
25
49
|
end
|
|
26
50
|
|
|
@@ -9,7 +9,20 @@ module Legion
|
|
|
9
9
|
module Presence
|
|
10
10
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
11
|
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[presence availability available status online busy away]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :get_presence,
|
|
17
|
+
desc: 'Get the presence/availability status for a user',
|
|
18
|
+
mcp_prefix: 'teams.get_presence',
|
|
19
|
+
mcp_category: 'teams_presence',
|
|
20
|
+
mcp_tier: :low,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
trigger_words: %w[presence availability status online]
|
|
23
|
+
|
|
12
24
|
def get_presence(user_id: 'me', **)
|
|
25
|
+
log.debug("Presence#get_presence user_id=#{user_id}")
|
|
13
26
|
conn = graph_connection(**)
|
|
14
27
|
response = conn.get("#{user_path(user_id)}/presence")
|
|
15
28
|
body = response.body || {}
|
|
@@ -19,6 +32,7 @@ module Legion
|
|
|
19
32
|
fetched_at: Time.now.utc
|
|
20
33
|
}
|
|
21
34
|
rescue StandardError => e
|
|
35
|
+
handle_exception(e, level: :warn, operation: 'Presence#get_presence', user_id: user_id)
|
|
22
36
|
{ availability: 'Offline', activity: 'OffWork', error: e.message, fetched_at: Time.now.utc }
|
|
23
37
|
end
|
|
24
38
|
|
|
@@ -16,7 +16,15 @@ module Legion
|
|
|
16
16
|
include Helpers::HighWaterMark
|
|
17
17
|
extend self
|
|
18
18
|
|
|
19
|
+
definition :full_ingest, mcp_exposed: false
|
|
20
|
+
definition :ingest_self, mcp_exposed: false
|
|
21
|
+
definition :ingest_people, mcp_exposed: false
|
|
22
|
+
definition :ingest_conversations, mcp_exposed: false
|
|
23
|
+
definition :ingest_teams_and_meetings, mcp_exposed: false
|
|
24
|
+
definition :incremental_sync, mcp_exposed: false
|
|
25
|
+
|
|
19
26
|
def full_ingest(token:, top_people: 10, message_depth: 50, **)
|
|
27
|
+
log.debug("ProfileIngest#full_ingest top_people=#{top_people} message_depth=#{message_depth}")
|
|
20
28
|
self_result = ingest_self(token: token)
|
|
21
29
|
people_result = ingest_people(token: token, top: 25)
|
|
22
30
|
people = people_result[:skipped] ? [] : (people_result[:people] || [])
|
|
@@ -42,7 +50,7 @@ module Legion
|
|
|
42
50
|
presence = begin
|
|
43
51
|
conn.get('me/presence').body
|
|
44
52
|
rescue StandardError => e
|
|
45
|
-
|
|
53
|
+
handle_exception(e, level: :debug, operation: 'ProfileIngest#ingest_self presence') if defined?(handle_exception)
|
|
46
54
|
{}
|
|
47
55
|
end
|
|
48
56
|
unless presence.empty?
|
|
@@ -57,6 +65,7 @@ module Legion
|
|
|
57
65
|
|
|
58
66
|
{ profile: profile, presence: presence }
|
|
59
67
|
rescue StandardError => e
|
|
68
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_self')
|
|
60
69
|
{ error: e.message }
|
|
61
70
|
end
|
|
62
71
|
|
|
@@ -88,6 +97,7 @@ module Legion
|
|
|
88
97
|
|
|
89
98
|
{ people: people, count: people.length }
|
|
90
99
|
rescue StandardError => e
|
|
100
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_people')
|
|
91
101
|
{ error: e.message, skipped: false }
|
|
92
102
|
end
|
|
93
103
|
|
|
@@ -132,6 +142,7 @@ module Legion
|
|
|
132
142
|
|
|
133
143
|
{ ingested: ingested }
|
|
134
144
|
rescue StandardError => e
|
|
145
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_conversations')
|
|
135
146
|
{ error: e.message, ingested: ingested || 0 }
|
|
136
147
|
end
|
|
137
148
|
|
|
@@ -177,6 +188,7 @@ module Legion
|
|
|
177
188
|
|
|
178
189
|
{ teams: teams_count, meetings: meetings_count }
|
|
179
190
|
rescue StandardError => e
|
|
191
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_teams_and_meetings')
|
|
180
192
|
{ error: e.message }
|
|
181
193
|
end
|
|
182
194
|
|
|
@@ -200,7 +212,7 @@ module Legion
|
|
|
200
212
|
members.any? { |m| m['email']&.downcase == email.downcase }
|
|
201
213
|
end
|
|
202
214
|
rescue StandardError => e
|
|
203
|
-
|
|
215
|
+
handle_exception(e, level: :debug, operation: 'ProfileIngest#find_chat_for_person')
|
|
204
216
|
nil
|
|
205
217
|
end
|
|
206
218
|
|
|
@@ -212,7 +224,8 @@ module Legion
|
|
|
212
224
|
resp = conn.get("chats/#{chat_id}/messages", params)
|
|
213
225
|
(resp.body || {}).fetch('value', [])
|
|
214
226
|
rescue StandardError => e
|
|
215
|
-
|
|
227
|
+
handle_exception(e, level: :warn, operation: 'ProfileIngest#fetch_new_messages',
|
|
228
|
+
chat_id: chat_id)
|
|
216
229
|
[]
|
|
217
230
|
end
|
|
218
231
|
|
|
@@ -233,7 +246,7 @@ module Legion
|
|
|
233
246
|
llm_ask(message: "#{definition[:prompt]}\n\nConversation with #{peer_name}:\n#{text}")
|
|
234
247
|
end
|
|
235
248
|
rescue StandardError => e
|
|
236
|
-
|
|
249
|
+
handle_exception(e, level: :debug, operation: 'ProfileIngest#extract_conversation')
|
|
237
250
|
nil
|
|
238
251
|
end
|
|
239
252
|
|
|
@@ -9,16 +9,55 @@ module Legion
|
|
|
9
9
|
module Subscriptions
|
|
10
10
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
11
|
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[subscription subscriptions webhook webhooks subscribe watch]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :list_subscriptions,
|
|
17
|
+
desc: 'List active Graph API subscriptions',
|
|
18
|
+
mcp_prefix: 'teams.list_subscriptions',
|
|
19
|
+
mcp_category: 'teams_subscriptions',
|
|
20
|
+
mcp_tier: :standard,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
trigger_words: %w[subscriptions webhooks]
|
|
23
|
+
|
|
12
24
|
def list_subscriptions(**)
|
|
13
25
|
response = graph_connection(**).get('subscriptions')
|
|
14
26
|
{ result: response.body }
|
|
15
27
|
end
|
|
16
28
|
|
|
29
|
+
definition :get_subscription,
|
|
30
|
+
desc: 'Get a specific Graph API subscription',
|
|
31
|
+
mcp_prefix: 'teams.get_subscription',
|
|
32
|
+
mcp_category: 'teams_subscriptions',
|
|
33
|
+
mcp_tier: :standard,
|
|
34
|
+
idempotent: true,
|
|
35
|
+
inputs: { properties: { subscription_id: { type: 'string' } },
|
|
36
|
+
required: ['subscription_id'] },
|
|
37
|
+
trigger_words: %w[subscription webhook]
|
|
38
|
+
|
|
17
39
|
def get_subscription(subscription_id:, **)
|
|
18
40
|
response = graph_connection(**).get("subscriptions/#{subscription_id}")
|
|
19
41
|
{ result: response.body }
|
|
20
42
|
end
|
|
21
43
|
|
|
44
|
+
definition :create_subscription,
|
|
45
|
+
desc: 'Create a new Graph API change notification subscription',
|
|
46
|
+
mcp_prefix: 'teams.create_subscription',
|
|
47
|
+
mcp_category: 'teams_subscriptions',
|
|
48
|
+
mcp_tier: :elevated,
|
|
49
|
+
idempotent: false,
|
|
50
|
+
inputs: { properties: { resource: { type: 'string',
|
|
51
|
+
description: 'Graph resource path to subscribe to' },
|
|
52
|
+
change_type: { type: 'string',
|
|
53
|
+
description: 'created, updated, deleted' },
|
|
54
|
+
notification_url: { type: 'string',
|
|
55
|
+
description: 'HTTPS webhook URL' },
|
|
56
|
+
expiration: { type: 'string',
|
|
57
|
+
description: 'ISO 8601 expiration datetime' } },
|
|
58
|
+
required: %w[resource change_type notification_url expiration] },
|
|
59
|
+
trigger_words: %w[subscribe webhook create]
|
|
60
|
+
|
|
22
61
|
def create_subscription(resource:, change_type:, notification_url:, expiration:,
|
|
23
62
|
client_state: nil, include_resource_data: false, **)
|
|
24
63
|
payload = {
|
|
@@ -33,17 +72,51 @@ module Legion
|
|
|
33
72
|
{ result: response.body }
|
|
34
73
|
end
|
|
35
74
|
|
|
75
|
+
definition :renew_subscription,
|
|
76
|
+
desc: 'Renew an expiring Graph API subscription',
|
|
77
|
+
mcp_prefix: 'teams.renew_subscription',
|
|
78
|
+
mcp_category: 'teams_subscriptions',
|
|
79
|
+
mcp_tier: :elevated,
|
|
80
|
+
idempotent: false,
|
|
81
|
+
inputs: { properties: { subscription_id: { type: 'string' },
|
|
82
|
+
expiration: { type: 'string',
|
|
83
|
+
description: 'New ISO 8601 expiration datetime' } },
|
|
84
|
+
required: %w[subscription_id expiration] },
|
|
85
|
+
trigger_words: %w[renew extend]
|
|
86
|
+
|
|
36
87
|
def renew_subscription(subscription_id:, expiration:, **)
|
|
37
88
|
payload = { expirationDateTime: expiration }
|
|
38
89
|
response = graph_connection(**).patch("subscriptions/#{subscription_id}", payload)
|
|
39
90
|
{ result: response.body }
|
|
40
91
|
end
|
|
41
92
|
|
|
93
|
+
definition :delete_subscription,
|
|
94
|
+
desc: 'Delete a Graph API subscription',
|
|
95
|
+
mcp_prefix: 'teams.delete_subscription',
|
|
96
|
+
mcp_category: 'teams_subscriptions',
|
|
97
|
+
mcp_tier: :elevated,
|
|
98
|
+
idempotent: false,
|
|
99
|
+
inputs: { properties: { subscription_id: { type: 'string' } },
|
|
100
|
+
required: ['subscription_id'] },
|
|
101
|
+
trigger_words: %w[unsubscribe delete]
|
|
102
|
+
|
|
42
103
|
def delete_subscription(subscription_id:, **)
|
|
43
104
|
response = graph_connection(**).delete("subscriptions/#{subscription_id}")
|
|
44
105
|
{ result: response.body }
|
|
45
106
|
end
|
|
46
107
|
|
|
108
|
+
definition :subscribe_to_chat_messages,
|
|
109
|
+
desc: 'Subscribe to new and updated messages in a Teams chat',
|
|
110
|
+
mcp_prefix: 'teams.subscribe_to_chat_messages',
|
|
111
|
+
mcp_category: 'teams_subscriptions',
|
|
112
|
+
mcp_tier: :elevated,
|
|
113
|
+
idempotent: false,
|
|
114
|
+
inputs: { properties: { chat_id: { type: 'string' },
|
|
115
|
+
notification_url: { type: 'string' },
|
|
116
|
+
expiration: { type: 'string' } },
|
|
117
|
+
required: %w[chat_id notification_url expiration] },
|
|
118
|
+
trigger_words: %w[subscribe watch chat]
|
|
119
|
+
|
|
47
120
|
def subscribe_to_chat_messages(chat_id:, notification_url:, expiration:, client_state: nil, **)
|
|
48
121
|
create_subscription(
|
|
49
122
|
resource: "/chats/#{chat_id}/messages",
|
|
@@ -55,6 +128,19 @@ module Legion
|
|
|
55
128
|
)
|
|
56
129
|
end
|
|
57
130
|
|
|
131
|
+
definition :subscribe_to_channel_messages,
|
|
132
|
+
desc: 'Subscribe to new and updated messages in a Teams channel',
|
|
133
|
+
mcp_prefix: 'teams.subscribe_to_channel_messages',
|
|
134
|
+
mcp_category: 'teams_subscriptions',
|
|
135
|
+
mcp_tier: :elevated,
|
|
136
|
+
idempotent: false,
|
|
137
|
+
inputs: { properties: { team_id: { type: 'string' },
|
|
138
|
+
channel_id: { type: 'string' },
|
|
139
|
+
notification_url: { type: 'string' },
|
|
140
|
+
expiration: { type: 'string' } },
|
|
141
|
+
required: %w[team_id channel_id notification_url expiration] },
|
|
142
|
+
trigger_words: %w[subscribe watch channel]
|
|
143
|
+
|
|
58
144
|
def subscribe_to_channel_messages(team_id:, channel_id:, notification_url:, expiration:,
|
|
59
145
|
client_state: nil, **)
|
|
60
146
|
create_subscription(
|
|
@@ -9,16 +9,46 @@ module Legion
|
|
|
9
9
|
module Teams
|
|
10
10
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
11
|
|
|
12
|
+
def self.trigger_words
|
|
13
|
+
%w[team teams workspace group membership]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
definition :list_joined_teams,
|
|
17
|
+
desc: 'List Teams the current user has joined',
|
|
18
|
+
mcp_prefix: 'teams.list_joined_teams',
|
|
19
|
+
mcp_category: 'teams_teams',
|
|
20
|
+
mcp_tier: :low,
|
|
21
|
+
idempotent: true,
|
|
22
|
+
trigger_words: %w[teams joined membership]
|
|
23
|
+
|
|
12
24
|
def list_joined_teams(user_id: 'me', **)
|
|
13
25
|
response = graph_connection(**).get("#{user_path(user_id)}/joinedTeams")
|
|
14
26
|
{ result: response.body }
|
|
15
27
|
end
|
|
16
28
|
|
|
29
|
+
definition :get_team,
|
|
30
|
+
desc: 'Get details for a specific Team by ID',
|
|
31
|
+
mcp_prefix: 'teams.get_team',
|
|
32
|
+
mcp_category: 'teams_teams',
|
|
33
|
+
mcp_tier: :low,
|
|
34
|
+
idempotent: true,
|
|
35
|
+
inputs: { properties: { team_id: { type: 'string' } }, required: ['team_id'] },
|
|
36
|
+
trigger_words: %w[team details workspace]
|
|
37
|
+
|
|
17
38
|
def get_team(team_id:, **)
|
|
18
39
|
response = graph_connection(**).get("teams/#{team_id}")
|
|
19
40
|
{ result: response.body }
|
|
20
41
|
end
|
|
21
42
|
|
|
43
|
+
definition :list_team_members,
|
|
44
|
+
desc: 'List members of a Team',
|
|
45
|
+
mcp_prefix: 'teams.list_team_members',
|
|
46
|
+
mcp_category: 'teams_teams',
|
|
47
|
+
mcp_tier: :standard,
|
|
48
|
+
idempotent: true,
|
|
49
|
+
inputs: { properties: { team_id: { type: 'string' } }, required: ['team_id'] },
|
|
50
|
+
trigger_words: %w[members roster]
|
|
51
|
+
|
|
22
52
|
def list_team_members(team_id:, **)
|
|
23
53
|
response = graph_connection(**).get("teams/#{team_id}/members")
|
|
24
54
|
{ result: response.body }
|
|
@@ -14,11 +14,35 @@ module Legion
|
|
|
14
14
|
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
15
15
|
}.freeze
|
|
16
16
|
|
|
17
|
+
def self.trigger_words
|
|
18
|
+
%w[transcript transcripts vtt spoken]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
definition :list_transcripts,
|
|
22
|
+
desc: 'List transcripts for an online meeting',
|
|
23
|
+
mcp_prefix: 'teams.list_transcripts',
|
|
24
|
+
mcp_category: 'teams_meetings',
|
|
25
|
+
mcp_tier: :standard,
|
|
26
|
+
idempotent: true,
|
|
27
|
+
inputs: { properties: { meeting_id: { type: 'string' } }, required: ['meeting_id'] },
|
|
28
|
+
trigger_words: ['transcripts']
|
|
29
|
+
|
|
17
30
|
def list_transcripts(meeting_id:, user_id: 'me', **)
|
|
18
31
|
response = graph_connection(**).get("#{user_path(user_id)}/onlineMeetings/#{meeting_id}/transcripts")
|
|
19
32
|
{ result: response.body }
|
|
20
33
|
end
|
|
21
34
|
|
|
35
|
+
definition :get_transcript,
|
|
36
|
+
desc: 'Get metadata for a specific meeting transcript',
|
|
37
|
+
mcp_prefix: 'teams.get_transcript',
|
|
38
|
+
mcp_category: 'teams_meetings',
|
|
39
|
+
mcp_tier: :standard,
|
|
40
|
+
idempotent: true,
|
|
41
|
+
inputs: { properties: { meeting_id: { type: 'string' },
|
|
42
|
+
transcript_id: { type: 'string' } },
|
|
43
|
+
required: %w[meeting_id transcript_id] },
|
|
44
|
+
trigger_words: %w[transcript details]
|
|
45
|
+
|
|
22
46
|
def get_transcript(meeting_id:, transcript_id:, user_id: 'me', **)
|
|
23
47
|
response = graph_connection(**).get(
|
|
24
48
|
"#{user_path(user_id)}/onlineMeetings/#{meeting_id}/transcripts/#{transcript_id}"
|
|
@@ -26,6 +50,17 @@ module Legion
|
|
|
26
50
|
{ result: response.body }
|
|
27
51
|
end
|
|
28
52
|
|
|
53
|
+
definition :get_transcript_content,
|
|
54
|
+
desc: 'Download the content of a meeting transcript (VTT or DOCX)',
|
|
55
|
+
mcp_prefix: 'teams.get_transcript_content',
|
|
56
|
+
mcp_category: 'teams_meetings',
|
|
57
|
+
mcp_tier: :standard,
|
|
58
|
+
idempotent: true,
|
|
59
|
+
inputs: { properties: { meeting_id: { type: 'string' },
|
|
60
|
+
transcript_id: { type: 'string' } },
|
|
61
|
+
required: %w[meeting_id transcript_id] },
|
|
62
|
+
trigger_words: %w[content vtt text read]
|
|
63
|
+
|
|
29
64
|
def get_transcript_content(meeting_id:, transcript_id:, user_id: 'me', format: :vtt, **)
|
|
30
65
|
accept = CONTENT_TYPES.fetch(format)
|
|
31
66
|
response = graph_connection(**).get(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'legion/extensions/identity/entra/helpers/token_manager'
|
|
3
4
|
require 'legion/extensions/microsoft_teams/version'
|
|
4
5
|
require 'legion/extensions/microsoft_teams/helpers/client'
|
|
5
6
|
require 'legion/extensions/microsoft_teams/runners/auth'
|
|
@@ -21,6 +22,11 @@ require 'legion/extensions/microsoft_teams/runners/people'
|
|
|
21
22
|
require 'legion/extensions/microsoft_teams/runners/profile_ingest'
|
|
22
23
|
require 'legion/extensions/microsoft_teams/runners/ownership'
|
|
23
24
|
require 'legion/extensions/microsoft_teams/runners/api_ingest'
|
|
25
|
+
require 'legion/extensions/microsoft_teams/runners/activities'
|
|
26
|
+
require 'legion/extensions/microsoft_teams/runners/meeting_artifacts'
|
|
27
|
+
require 'legion/extensions/microsoft_teams/runners/call_events'
|
|
28
|
+
require 'legion/extensions/microsoft_teams/runners/app_installations'
|
|
29
|
+
require 'legion/extensions/microsoft_teams/runners/files'
|
|
24
30
|
|
|
25
31
|
# Helpers (bot)
|
|
26
32
|
require 'legion/extensions/microsoft_teams/helpers/high_water_mark'
|
|
@@ -28,9 +34,6 @@ require 'legion/extensions/microsoft_teams/helpers/prompt_resolver'
|
|
|
28
34
|
require 'legion/extensions/microsoft_teams/helpers/trace_retriever'
|
|
29
35
|
require 'legion/extensions/microsoft_teams/helpers/session_manager'
|
|
30
36
|
require 'legion/extensions/microsoft_teams/helpers/subscription_registry'
|
|
31
|
-
require 'legion/extensions/microsoft_teams/helpers/token_cache'
|
|
32
|
-
require 'legion/extensions/microsoft_teams/helpers/callback_server'
|
|
33
|
-
require 'legion/extensions/microsoft_teams/helpers/browser_auth'
|
|
34
37
|
require 'legion/extensions/microsoft_teams/helpers/permission_guard'
|
|
35
38
|
require 'legion/extensions/microsoft_teams/helpers/transform_definitions'
|
|
36
39
|
require 'legion/extensions/microsoft_teams/helpers/graph_client'
|
|
@@ -57,6 +60,10 @@ module Legion
|
|
|
57
60
|
module MicrosoftTeams
|
|
58
61
|
extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core, false
|
|
59
62
|
|
|
63
|
+
def self.trigger_words
|
|
64
|
+
%w[teams microsoft_teams microsoftteams microsoft-teams msteams ms-teams]
|
|
65
|
+
end
|
|
66
|
+
|
|
60
67
|
def self.remote_invocable?
|
|
61
68
|
false
|
|
62
69
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-microsoft_teams
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.46
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -135,6 +135,20 @@ dependencies:
|
|
|
135
135
|
- - ">="
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
137
|
version: 1.3.9
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: lex-identity-entra
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: 0.3.0
|
|
145
|
+
type: :runtime
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: 0.3.0
|
|
138
152
|
- !ruby/object:Gem::Dependency
|
|
139
153
|
name: snappy
|
|
140
154
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -182,7 +196,6 @@ files:
|
|
|
182
196
|
- lib/legion/extensions/microsoft_teams/actors/absorb_chat.rb
|
|
183
197
|
- lib/legion/extensions/microsoft_teams/actors/absorb_meeting.rb
|
|
184
198
|
- lib/legion/extensions/microsoft_teams/actors/api_ingest.rb
|
|
185
|
-
- lib/legion/extensions/microsoft_teams/actors/auth_validator.rb
|
|
186
199
|
- lib/legion/extensions/microsoft_teams/actors/cache_bulk_ingest.rb
|
|
187
200
|
- lib/legion/extensions/microsoft_teams/actors/cache_sync.rb
|
|
188
201
|
- lib/legion/extensions/microsoft_teams/actors/channel_poller.rb
|
|
@@ -193,11 +206,7 @@ files:
|
|
|
193
206
|
- lib/legion/extensions/microsoft_teams/actors/observed_chat_poller.rb
|
|
194
207
|
- lib/legion/extensions/microsoft_teams/actors/presence_poller.rb
|
|
195
208
|
- lib/legion/extensions/microsoft_teams/actors/profile_ingest.rb
|
|
196
|
-
- lib/legion/extensions/microsoft_teams/actors/token_refresher.rb
|
|
197
|
-
- lib/legion/extensions/microsoft_teams/cli/auth.rb
|
|
198
209
|
- lib/legion/extensions/microsoft_teams/client.rb
|
|
199
|
-
- lib/legion/extensions/microsoft_teams/helpers/browser_auth.rb
|
|
200
|
-
- lib/legion/extensions/microsoft_teams/helpers/callback_server.rb
|
|
201
210
|
- lib/legion/extensions/microsoft_teams/helpers/client.rb
|
|
202
211
|
- lib/legion/extensions/microsoft_teams/helpers/graph_client.rb
|
|
203
212
|
- lib/legion/extensions/microsoft_teams/helpers/high_water_mark.rb
|
|
@@ -205,24 +214,27 @@ files:
|
|
|
205
214
|
- lib/legion/extensions/microsoft_teams/helpers/prompt_resolver.rb
|
|
206
215
|
- lib/legion/extensions/microsoft_teams/helpers/session_manager.rb
|
|
207
216
|
- lib/legion/extensions/microsoft_teams/helpers/subscription_registry.rb
|
|
208
|
-
- lib/legion/extensions/microsoft_teams/helpers/token_cache.rb
|
|
209
217
|
- lib/legion/extensions/microsoft_teams/helpers/trace_retriever.rb
|
|
210
218
|
- lib/legion/extensions/microsoft_teams/helpers/transform_definitions.rb
|
|
211
|
-
- lib/legion/extensions/microsoft_teams/hooks/auth.rb
|
|
212
219
|
- lib/legion/extensions/microsoft_teams/local_cache/extractor.rb
|
|
213
220
|
- lib/legion/extensions/microsoft_teams/local_cache/record_parser.rb
|
|
214
221
|
- lib/legion/extensions/microsoft_teams/local_cache/sstable_reader.rb
|
|
222
|
+
- lib/legion/extensions/microsoft_teams/runners/activities.rb
|
|
215
223
|
- lib/legion/extensions/microsoft_teams/runners/adaptive_cards.rb
|
|
216
224
|
- lib/legion/extensions/microsoft_teams/runners/ai_insights.rb
|
|
217
225
|
- lib/legion/extensions/microsoft_teams/runners/api_ingest.rb
|
|
226
|
+
- lib/legion/extensions/microsoft_teams/runners/app_installations.rb
|
|
218
227
|
- lib/legion/extensions/microsoft_teams/runners/auth.rb
|
|
219
228
|
- lib/legion/extensions/microsoft_teams/runners/bot.rb
|
|
220
229
|
- lib/legion/extensions/microsoft_teams/runners/cache_ingest.rb
|
|
230
|
+
- lib/legion/extensions/microsoft_teams/runners/call_events.rb
|
|
221
231
|
- lib/legion/extensions/microsoft_teams/runners/channel_messages.rb
|
|
222
232
|
- lib/legion/extensions/microsoft_teams/runners/channels.rb
|
|
223
233
|
- lib/legion/extensions/microsoft_teams/runners/chats.rb
|
|
234
|
+
- lib/legion/extensions/microsoft_teams/runners/files.rb
|
|
224
235
|
- lib/legion/extensions/microsoft_teams/runners/local_cache.rb
|
|
225
236
|
- lib/legion/extensions/microsoft_teams/runners/loop.rb
|
|
237
|
+
- lib/legion/extensions/microsoft_teams/runners/meeting_artifacts.rb
|
|
226
238
|
- lib/legion/extensions/microsoft_teams/runners/meetings.rb
|
|
227
239
|
- lib/legion/extensions/microsoft_teams/runners/messages.rb
|
|
228
240
|
- lib/legion/extensions/microsoft_teams/runners/ownership.rb
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Legion
|
|
4
|
-
module Extensions
|
|
5
|
-
module MicrosoftTeams
|
|
6
|
-
module Actor
|
|
7
|
-
class AuthValidator < Legion::Extensions::Actors::Once # rubocop:disable Legion/Extension/SelfContainedActorRunnerClass
|
|
8
|
-
def use_runner? = false
|
|
9
|
-
def check_subtask? = false
|
|
10
|
-
def generate_task? = false
|
|
11
|
-
|
|
12
|
-
def delay
|
|
13
|
-
90.0
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def enabled?
|
|
17
|
-
defined?(Legion::Extensions::MicrosoftTeams::Helpers::TokenCache)
|
|
18
|
-
rescue StandardError => e
|
|
19
|
-
log.debug("AuthValidator#enabled?: #{e.message}")
|
|
20
|
-
false
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def token_cache
|
|
24
|
-
Legion::Extensions::MicrosoftTeams::Helpers::TokenCache.instance
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def manual
|
|
28
|
-
log.info('AuthValidator starting')
|
|
29
|
-
cache = token_cache
|
|
30
|
-
log.debug("Token loaded: authenticated?=#{cache.authenticated?}")
|
|
31
|
-
|
|
32
|
-
if cache.authenticated?
|
|
33
|
-
token = cache.cached_delegated_token
|
|
34
|
-
if token
|
|
35
|
-
log.info('Teams delegated auth restored (token valid)')
|
|
36
|
-
elsif cache.previously_authenticated? || auto_authenticate?
|
|
37
|
-
log.info('Token loaded but expired, attempting browser re-auth')
|
|
38
|
-
attempt_browser_reauth(cache)
|
|
39
|
-
else
|
|
40
|
-
log.debug('Token loaded but expired, no re-auth configured')
|
|
41
|
-
end
|
|
42
|
-
elsif cache.previously_authenticated?
|
|
43
|
-
log.warn('Token file found but could not load, attempting re-authentication')
|
|
44
|
-
attempt_browser_reauth(cache)
|
|
45
|
-
elsif auto_authenticate?
|
|
46
|
-
log.info('auto_authenticate enabled, opening browser for initial authentication...')
|
|
47
|
-
attempt_browser_reauth(cache)
|
|
48
|
-
else
|
|
49
|
-
log.debug('No Teams delegated auth configured, skipping')
|
|
50
|
-
end
|
|
51
|
-
log.info('AuthValidator complete')
|
|
52
|
-
rescue StandardError => e
|
|
53
|
-
log.error("AuthValidator: #{e.message}")
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
private
|
|
57
|
-
|
|
58
|
-
def attempt_browser_reauth(cache)
|
|
59
|
-
settings = teams_auth_settings
|
|
60
|
-
unless settings[:tenant_id] && settings[:client_id]
|
|
61
|
-
log.warn("Cannot re-auth: tenant_id=#{settings[:tenant_id] ? 'present' : 'nil'}, client_id=#{settings[:client_id] ? 'present' : 'nil'}")
|
|
62
|
-
return false
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
log.warn('Delegated token expired, opening browser for re-authentication...')
|
|
66
|
-
|
|
67
|
-
scopes = settings.dig(:delegated, :scopes) ||
|
|
68
|
-
Legion::Extensions::MicrosoftTeams::Helpers::BrowserAuth::DEFAULT_SCOPES
|
|
69
|
-
log.debug("Using scopes: #{scopes}")
|
|
70
|
-
browser_auth = Legion::Extensions::MicrosoftTeams::Helpers::BrowserAuth.new(
|
|
71
|
-
tenant_id: settings[:tenant_id],
|
|
72
|
-
client_id: settings[:client_id],
|
|
73
|
-
scopes: scopes
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
result = browser_auth.authenticate
|
|
77
|
-
if result[:error]
|
|
78
|
-
log.error("Browser auth returned error: #{result[:error]} - #{result[:description]}")
|
|
79
|
-
return false
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
body = result[:result]
|
|
83
|
-
log.info("Browser auth succeeded, storing token (expires_in=#{body['expires_in']})")
|
|
84
|
-
cache.store_delegated_token(
|
|
85
|
-
access_token: body['access_token'],
|
|
86
|
-
refresh_token: body['refresh_token'],
|
|
87
|
-
expires_in: body['expires_in'],
|
|
88
|
-
scopes: scopes
|
|
89
|
-
)
|
|
90
|
-
cache.save_to_vault
|
|
91
|
-
log.info('Teams delegated auth restored via browser')
|
|
92
|
-
true
|
|
93
|
-
rescue StandardError => e
|
|
94
|
-
log.error("Browser re-auth failed: #{e.message}")
|
|
95
|
-
false
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def auto_authenticate?
|
|
99
|
-
settings = teams_auth_settings
|
|
100
|
-
result = settings.dig(:delegated, :auto_authenticate) == true
|
|
101
|
-
log.debug("auto_authenticate? => #{result}")
|
|
102
|
-
result
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def teams_auth_settings
|
|
106
|
-
settings = if defined?(Legion::Settings)
|
|
107
|
-
ms = Legion::Settings[:microsoft_teams]
|
|
108
|
-
auth = ms && ms[:auth].is_a?(Hash) ? ms[:auth].dup : {}
|
|
109
|
-
auth[:tenant_id] ||= ms[:tenant_id] if ms
|
|
110
|
-
auth[:client_id] ||= ms[:client_id] if ms
|
|
111
|
-
auth
|
|
112
|
-
else
|
|
113
|
-
{}
|
|
114
|
-
end
|
|
115
|
-
settings[:tenant_id] ||= ENV.fetch('AZURE_TENANT_ID', nil)
|
|
116
|
-
settings[:client_id] ||= ENV.fetch('AZURE_CLIENT_ID', nil)
|
|
117
|
-
settings
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|