lex-microsoft_teams 0.6.45 → 0.6.47
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 +43 -0
- data/lib/legion/extensions/microsoft_teams/runners/ai_insights.rb +62 -0
- data/lib/legion/extensions/microsoft_teams/runners/api_ingest.rb +20 -14
- data/lib/legion/extensions/microsoft_teams/runners/app_installations.rb +86 -0
- data/lib/legion/extensions/microsoft_teams/runners/auth.rb +4 -107
- data/lib/legion/extensions/microsoft_teams/runners/bot.rb +20 -12
- data/lib/legion/extensions/microsoft_teams/runners/cache_ingest.rb +9 -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 +69 -0
- data/lib/legion/extensions/microsoft_teams/runners/chats.rb +57 -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 +6 -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 +62 -0
- data/lib/legion/extensions/microsoft_teams/runners/ownership.rb +11 -0
- data/lib/legion/extensions/microsoft_teams/runners/people.rb +25 -0
- data/lib/legion/extensions/microsoft_teams/runners/presence.rb +14 -0
- data/lib/legion/extensions/microsoft_teams/runners/profile_ingest.rb +18 -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
|
@@ -7,19 +7,58 @@ module Legion
|
|
|
7
7
|
module MicrosoftTeams
|
|
8
8
|
module Runners
|
|
9
9
|
module Messages
|
|
10
|
+
extend Legion::Extensions::Definitions
|
|
10
11
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
12
|
|
|
13
|
+
def self.trigger_words
|
|
14
|
+
%w[message messages reply replies thread send]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
definition :list_chat_messages,
|
|
18
|
+
desc: 'List messages in a Teams chat thread',
|
|
19
|
+
mcp_prefix: 'teams.list_chat_messages',
|
|
20
|
+
mcp_category: 'teams_messages',
|
|
21
|
+
mcp_tier: :standard,
|
|
22
|
+
idempotent: true,
|
|
23
|
+
inputs: { properties: { chat_id: { type: 'string',
|
|
24
|
+
description: 'Teams chat ID' } },
|
|
25
|
+
required: ['chat_id'] },
|
|
26
|
+
trigger_words: %w[messages history read]
|
|
27
|
+
|
|
12
28
|
def list_chat_messages(chat_id:, top: 50, **)
|
|
13
29
|
params = { '$top' => top }
|
|
14
30
|
response = graph_connection(**).get("chats/#{chat_id}/messages", params)
|
|
15
31
|
{ result: response.body }
|
|
16
32
|
end
|
|
17
33
|
|
|
34
|
+
definition :get_chat_message,
|
|
35
|
+
desc: 'Get a specific message from a Teams chat',
|
|
36
|
+
mcp_prefix: 'teams.get_chat_message',
|
|
37
|
+
mcp_category: 'teams_messages',
|
|
38
|
+
mcp_tier: :standard,
|
|
39
|
+
idempotent: true,
|
|
40
|
+
inputs: { properties: { chat_id: { type: 'string' },
|
|
41
|
+
message_id: { type: 'string' } },
|
|
42
|
+
required: %w[chat_id message_id] },
|
|
43
|
+
trigger_words: %w[message fetch]
|
|
44
|
+
|
|
18
45
|
def get_chat_message(chat_id:, message_id:, **)
|
|
19
46
|
response = graph_connection(**).get("chats/#{chat_id}/messages/#{message_id}")
|
|
20
47
|
{ result: response.body }
|
|
21
48
|
end
|
|
22
49
|
|
|
50
|
+
definition :send_chat_message,
|
|
51
|
+
desc: 'Send a message to a Teams chat',
|
|
52
|
+
mcp_prefix: 'teams.send_chat_message',
|
|
53
|
+
mcp_category: 'teams_messages',
|
|
54
|
+
mcp_tier: :elevated,
|
|
55
|
+
idempotent: false,
|
|
56
|
+
inputs: { properties: { chat_id: { type: 'string' },
|
|
57
|
+
content: { type: 'string',
|
|
58
|
+
description: 'Message text or HTML' } },
|
|
59
|
+
required: %w[chat_id content] },
|
|
60
|
+
trigger_words: %w[send post write]
|
|
61
|
+
|
|
23
62
|
def send_chat_message(chat_id:, content:, content_type: 'text', attachments: [], **)
|
|
24
63
|
payload = { body: { contentType: content_type, content: content } }
|
|
25
64
|
payload[:attachments] = attachments unless attachments.empty?
|
|
@@ -27,12 +66,35 @@ module Legion
|
|
|
27
66
|
{ result: response.body }
|
|
28
67
|
end
|
|
29
68
|
|
|
69
|
+
definition :reply_to_chat_message,
|
|
70
|
+
desc: 'Reply to a message in a Teams chat',
|
|
71
|
+
mcp_prefix: 'teams.reply_to_chat_message',
|
|
72
|
+
mcp_category: 'teams_messages',
|
|
73
|
+
mcp_tier: :elevated,
|
|
74
|
+
idempotent: false,
|
|
75
|
+
inputs: { properties: { chat_id: { type: 'string' },
|
|
76
|
+
message_id: { type: 'string' },
|
|
77
|
+
content: { type: 'string' } },
|
|
78
|
+
required: %w[chat_id message_id content] },
|
|
79
|
+
trigger_words: %w[reply respond]
|
|
80
|
+
|
|
30
81
|
def reply_to_chat_message(chat_id:, message_id:, content:, content_type: 'text', **)
|
|
31
82
|
payload = { body: { contentType: content_type, content: content } }
|
|
32
83
|
response = graph_connection(**).post("chats/#{chat_id}/messages/#{message_id}/replies", payload)
|
|
33
84
|
{ result: response.body }
|
|
34
85
|
end
|
|
35
86
|
|
|
87
|
+
definition :list_message_replies,
|
|
88
|
+
desc: 'List replies to a message in a Teams chat',
|
|
89
|
+
mcp_prefix: 'teams.list_message_replies',
|
|
90
|
+
mcp_category: 'teams_messages',
|
|
91
|
+
mcp_tier: :standard,
|
|
92
|
+
idempotent: true,
|
|
93
|
+
inputs: { properties: { chat_id: { type: 'string' },
|
|
94
|
+
message_id: { type: 'string' } },
|
|
95
|
+
required: %w[chat_id message_id] },
|
|
96
|
+
trigger_words: %w[replies thread]
|
|
97
|
+
|
|
36
98
|
def list_message_replies(chat_id:, message_id:, top: 50, **)
|
|
37
99
|
params = { '$top' => top }
|
|
38
100
|
response = graph_connection(**).get("chats/#{chat_id}/messages/#{message_id}/replies", params)
|
|
@@ -13,7 +13,12 @@ module Legion
|
|
|
13
13
|
TEAMS_SELECT = 'id,displayName,mail'
|
|
14
14
|
OWNERS_SELECT = 'id,displayName,mail'
|
|
15
15
|
|
|
16
|
+
definition :sync_owners, mcp_exposed: false
|
|
17
|
+
definition :detect_orphans, mcp_exposed: false
|
|
18
|
+
definition :get_team_owners, mcp_exposed: false
|
|
19
|
+
|
|
16
20
|
def sync_owners(team_id: nil, **)
|
|
21
|
+
log.debug("Ownership#sync_owners team_id=#{team_id.inspect}")
|
|
17
22
|
conn = graph_connection(**)
|
|
18
23
|
if team_id
|
|
19
24
|
owners = fetch_owners(conn: conn, team_id: team_id)
|
|
@@ -26,10 +31,12 @@ module Legion
|
|
|
26
31
|
{ owners: all_owners, team_count: teams.length, synced_at: Time.now.utc.iso8601 }
|
|
27
32
|
end
|
|
28
33
|
rescue StandardError => e
|
|
34
|
+
handle_exception(e, level: :error, operation: 'Ownership#sync_owners', team_id: team_id)
|
|
29
35
|
{ error: e.message }
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
def detect_orphans(**)
|
|
39
|
+
log.debug('Ownership#detect_orphans')
|
|
33
40
|
conn = graph_connection(**)
|
|
34
41
|
teams = fetch_all_teams(conn: conn)
|
|
35
42
|
orphaned = []
|
|
@@ -39,16 +46,20 @@ module Legion
|
|
|
39
46
|
orphaned << { id: team['id'], display_name: team['displayName'], mail: team['mail'] } if owners.empty?
|
|
40
47
|
end
|
|
41
48
|
|
|
49
|
+
log.info("Ownership#detect_orphans found #{orphaned.length}/#{teams.length} orphaned teams")
|
|
42
50
|
{ orphaned_teams: orphaned, total_scanned: teams.length, orphan_count: orphaned.length }
|
|
43
51
|
rescue StandardError => e
|
|
52
|
+
handle_exception(e, level: :error, operation: 'Ownership#detect_orphans')
|
|
44
53
|
{ error: e.message }
|
|
45
54
|
end
|
|
46
55
|
|
|
47
56
|
def get_team_owners(team_id:, **)
|
|
57
|
+
log.debug("Ownership#get_team_owners team_id=#{team_id}")
|
|
48
58
|
conn = graph_connection(**)
|
|
49
59
|
owners = fetch_owners(conn: conn, team_id: team_id)
|
|
50
60
|
{ team_id: team_id, owners: owners }
|
|
51
61
|
rescue StandardError => e
|
|
62
|
+
handle_exception(e, level: :error, operation: 'Ownership#get_team_owners', team_id: team_id)
|
|
52
63
|
{ error: e.message }
|
|
53
64
|
end
|
|
54
65
|
|
|
@@ -7,20 +7,45 @@ module Legion
|
|
|
7
7
|
module MicrosoftTeams
|
|
8
8
|
module Runners
|
|
9
9
|
module People
|
|
10
|
+
extend Legion::Extensions::Definitions
|
|
10
11
|
include Legion::Extensions::MicrosoftTeams::Helpers::Client
|
|
11
12
|
|
|
13
|
+
def self.trigger_words
|
|
14
|
+
%w[profile people person colleague colleagues contact contacts]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
definition :get_profile,
|
|
18
|
+
desc: 'Get the Microsoft Graph profile for a user',
|
|
19
|
+
mcp_prefix: 'teams.get_profile',
|
|
20
|
+
mcp_category: 'teams_people',
|
|
21
|
+
mcp_tier: :low,
|
|
22
|
+
idempotent: true,
|
|
23
|
+
trigger_words: %w[profile self]
|
|
24
|
+
|
|
12
25
|
def get_profile(user_id: 'me', **)
|
|
26
|
+
log.debug("People#get_profile user_id=#{user_id}")
|
|
13
27
|
response = graph_connection(**).get(user_path(user_id).to_s)
|
|
14
28
|
{ result: response.body }
|
|
15
29
|
rescue StandardError => e
|
|
30
|
+
handle_exception(e, level: :error, operation: 'People#get_profile', user_id: user_id)
|
|
16
31
|
{ error: e.message }
|
|
17
32
|
end
|
|
18
33
|
|
|
34
|
+
definition :list_people,
|
|
35
|
+
desc: 'List people relevant to the current user (colleagues, contacts)',
|
|
36
|
+
mcp_prefix: 'teams.list_people',
|
|
37
|
+
mcp_category: 'teams_people',
|
|
38
|
+
mcp_tier: :standard,
|
|
39
|
+
idempotent: true,
|
|
40
|
+
trigger_words: %w[people colleagues contacts]
|
|
41
|
+
|
|
19
42
|
def list_people(user_id: 'me', top: 25, **)
|
|
43
|
+
log.debug("People#list_people user_id=#{user_id} top=#{top}")
|
|
20
44
|
params = { '$top' => top }
|
|
21
45
|
response = graph_connection(**).get("#{user_path(user_id)}/people", params)
|
|
22
46
|
{ result: response.body }
|
|
23
47
|
rescue StandardError => e
|
|
48
|
+
handle_exception(e, level: :error, operation: 'People#list_people', user_id: user_id)
|
|
24
49
|
{ error: e.message }
|
|
25
50
|
end
|
|
26
51
|
|
|
@@ -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
|
|
|
@@ -11,12 +11,21 @@ module Legion
|
|
|
11
11
|
module MicrosoftTeams
|
|
12
12
|
module Runners
|
|
13
13
|
module ProfileIngest
|
|
14
|
+
extend Legion::Extensions::Definitions
|
|
14
15
|
include Helpers::Client
|
|
15
16
|
include Helpers::PermissionGuard
|
|
16
17
|
include Helpers::HighWaterMark
|
|
17
18
|
extend self
|
|
18
19
|
|
|
20
|
+
definition :full_ingest, mcp_exposed: false
|
|
21
|
+
definition :ingest_self, mcp_exposed: false
|
|
22
|
+
definition :ingest_people, mcp_exposed: false
|
|
23
|
+
definition :ingest_conversations, mcp_exposed: false
|
|
24
|
+
definition :ingest_teams_and_meetings, mcp_exposed: false
|
|
25
|
+
definition :incremental_sync, mcp_exposed: false
|
|
26
|
+
|
|
19
27
|
def full_ingest(token:, top_people: 10, message_depth: 50, **)
|
|
28
|
+
log.debug("ProfileIngest#full_ingest top_people=#{top_people} message_depth=#{message_depth}")
|
|
20
29
|
self_result = ingest_self(token: token)
|
|
21
30
|
people_result = ingest_people(token: token, top: 25)
|
|
22
31
|
people = people_result[:skipped] ? [] : (people_result[:people] || [])
|
|
@@ -42,7 +51,7 @@ module Legion
|
|
|
42
51
|
presence = begin
|
|
43
52
|
conn.get('me/presence').body
|
|
44
53
|
rescue StandardError => e
|
|
45
|
-
|
|
54
|
+
handle_exception(e, level: :debug, operation: 'ProfileIngest#ingest_self presence') if defined?(handle_exception)
|
|
46
55
|
{}
|
|
47
56
|
end
|
|
48
57
|
unless presence.empty?
|
|
@@ -57,6 +66,7 @@ module Legion
|
|
|
57
66
|
|
|
58
67
|
{ profile: profile, presence: presence }
|
|
59
68
|
rescue StandardError => e
|
|
69
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_self')
|
|
60
70
|
{ error: e.message }
|
|
61
71
|
end
|
|
62
72
|
|
|
@@ -88,6 +98,7 @@ module Legion
|
|
|
88
98
|
|
|
89
99
|
{ people: people, count: people.length }
|
|
90
100
|
rescue StandardError => e
|
|
101
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_people')
|
|
91
102
|
{ error: e.message, skipped: false }
|
|
92
103
|
end
|
|
93
104
|
|
|
@@ -132,6 +143,7 @@ module Legion
|
|
|
132
143
|
|
|
133
144
|
{ ingested: ingested }
|
|
134
145
|
rescue StandardError => e
|
|
146
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_conversations')
|
|
135
147
|
{ error: e.message, ingested: ingested || 0 }
|
|
136
148
|
end
|
|
137
149
|
|
|
@@ -177,6 +189,7 @@ module Legion
|
|
|
177
189
|
|
|
178
190
|
{ teams: teams_count, meetings: meetings_count }
|
|
179
191
|
rescue StandardError => e
|
|
192
|
+
handle_exception(e, level: :error, operation: 'ProfileIngest#ingest_teams_and_meetings')
|
|
180
193
|
{ error: e.message }
|
|
181
194
|
end
|
|
182
195
|
|
|
@@ -200,7 +213,7 @@ module Legion
|
|
|
200
213
|
members.any? { |m| m['email']&.downcase == email.downcase }
|
|
201
214
|
end
|
|
202
215
|
rescue StandardError => e
|
|
203
|
-
|
|
216
|
+
handle_exception(e, level: :debug, operation: 'ProfileIngest#find_chat_for_person')
|
|
204
217
|
nil
|
|
205
218
|
end
|
|
206
219
|
|
|
@@ -212,7 +225,8 @@ module Legion
|
|
|
212
225
|
resp = conn.get("chats/#{chat_id}/messages", params)
|
|
213
226
|
(resp.body || {}).fetch('value', [])
|
|
214
227
|
rescue StandardError => e
|
|
215
|
-
|
|
228
|
+
handle_exception(e, level: :warn, operation: 'ProfileIngest#fetch_new_messages',
|
|
229
|
+
chat_id: chat_id)
|
|
216
230
|
[]
|
|
217
231
|
end
|
|
218
232
|
|
|
@@ -233,7 +247,7 @@ module Legion
|
|
|
233
247
|
llm_ask(message: "#{definition[:prompt]}\n\nConversation with #{peer_name}:\n#{text}")
|
|
234
248
|
end
|
|
235
249
|
rescue StandardError => e
|
|
236
|
-
|
|
250
|
+
handle_exception(e, level: :debug, operation: 'ProfileIngest#extract_conversation')
|
|
237
251
|
nil
|
|
238
252
|
end
|
|
239
253
|
|
|
@@ -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
|