Class: Teams::ActivityContext
- Inherits:
-
Object
- Object
- Teams::ActivityContext
- Defined in:
- lib/teams/activity_context.rb
Instance Attribute Summary collapse
-
#activity ⇒ Object
readonly
Returns the value of attribute activity.
-
#app ⇒ Object
readonly
Returns the value of attribute app.
-
#conversation_reference ⇒ Object
readonly
Returns the value of attribute conversation_reference.
-
#extra ⇒ Object
readonly
Returns the value of attribute extra.
-
#stream ⇒ Object
readonly
Returns the value of attribute stream.
Instance Method Summary collapse
- #api ⇒ Object
-
#app_graph ⇒ Object
Microsoft Graph client with the app's own identity (app-only tokens).
-
#initialize(app:, activity:, conversation_reference:, extra: {}, stream: nil) ⇒ ActivityContext
constructor
A new instance of ActivityContext.
- #log ⇒ Object
-
#post(activity_or_text) ⇒ Object
The TypeScript, Python, and .NET SDKs call this operation
send. -
#quote(message_id, activity_or_text) ⇒ Object
Quoted replies are plain sends carrying the quote placeholder and entity, matching the TypeScript and Python SDKs; no replyToId is set.
- #ref ⇒ Object
- #reference ⇒ Object
- #reply(activity_or_text) ⇒ Object
-
#sign_in(connection_name: nil, oauth_card_text: "Please Sign In...", sign_in_button_text: "Sign In") ⇒ Object
Starts the user sign-in flow: returns the token if the user is already signed in, otherwise sends an OAuth card and returns nil.
-
#sign_out(connection_name: nil) ⇒ Object
Clears the user's token for the connection.
- #storage ⇒ Object
- #typing(text = nil) ⇒ Object
-
#update(activity_id, activity_or_text) ⇒ Object
Sugar over post: the SDKs update by sending an activity that already carries an id, and post does exactly that.
-
#user_graph(connection_name: nil) ⇒ Object
Microsoft Graph client with the signed-in user's token.
Constructor Details
#initialize(app:, activity:, conversation_reference:, extra: {}, stream: nil) ⇒ ActivityContext
Returns a new instance of ActivityContext.
10 11 12 13 14 15 16 |
# File 'lib/teams/activity_context.rb', line 10 def initialize(app:, activity:, conversation_reference:, extra: {}, stream: nil) @app = app @activity = activity @conversation_reference = conversation_reference @extra = extra @stream = stream end |
Instance Attribute Details
#activity ⇒ Object (readonly)
Returns the value of attribute activity.
8 9 10 |
# File 'lib/teams/activity_context.rb', line 8 def activity @activity end |
#app ⇒ Object (readonly)
Returns the value of attribute app.
8 9 10 |
# File 'lib/teams/activity_context.rb', line 8 def app @app end |
#conversation_reference ⇒ Object (readonly)
Returns the value of attribute conversation_reference.
8 9 10 |
# File 'lib/teams/activity_context.rb', line 8 def conversation_reference @conversation_reference end |
#extra ⇒ Object (readonly)
Returns the value of attribute extra.
8 9 10 |
# File 'lib/teams/activity_context.rb', line 8 def extra @extra end |
#stream ⇒ Object (readonly)
Returns the value of attribute stream.
8 9 10 |
# File 'lib/teams/activity_context.rb', line 8 def stream @stream end |
Instance Method Details
#api ⇒ Object
34 35 36 |
# File 'lib/teams/activity_context.rb', line 34 def api app.api end |
#app_graph ⇒ Object
Microsoft Graph client with the app's own identity (app-only tokens).
71 72 73 |
# File 'lib/teams/activity_context.rb', line 71 def app_graph app.graph end |
#log ⇒ Object
26 27 28 |
# File 'lib/teams/activity_context.rb', line 26 def log app.logger end |
#post(activity_or_text) ⇒ Object
The TypeScript, Python, and .NET SDKs call this operation send.
Ruby already defines Object#send for dynamic dispatch, so the public
Ruby API uses post to avoid shadowing a core language method.
41 42 43 |
# File 'lib/teams/activity_context.rb', line 41 def post(activity_or_text) app.send_activity(conversation_reference, apply_targeted_defaults(activity_or_text)) end |
#quote(message_id, activity_or_text) ⇒ Object
Quoted replies are plain sends carrying the quote placeholder and entity, matching the TypeScript and Python SDKs; no replyToId is set.
56 57 58 |
# File 'lib/teams/activity_context.rb', line 56 def quote(, activity_or_text) post(quoted_activity(, activity_or_text)) end |
#ref ⇒ Object
18 19 20 |
# File 'lib/teams/activity_context.rb', line 18 def ref conversation_reference end |
#reference ⇒ Object
22 23 24 |
# File 'lib/teams/activity_context.rb', line 22 def reference conversation_reference end |
#reply(activity_or_text) ⇒ Object
45 46 47 48 49 50 51 52 |
# File 'lib/teams/activity_context.rb', line 45 def reply(activity_or_text) return post(activity_or_text) unless activity.id # Replies to targeted inbound messages are targeted sends: no quoted # reply and no replyToId, matching the TypeScript and Python SDKs. return post(activity_or_text) if targeted_reply?(activity_or_text) quote(activity.id, activity_or_text) end |
#sign_in(connection_name: nil, oauth_card_text: "Please Sign In...", sign_in_button_text: "Sign In") ⇒ Object
Starts the user sign-in flow: returns the token if the user is already signed in, otherwise sends an OAuth card and returns nil. In group conversations the card goes to a 1:1 conversation with the user (group OAuth is not supported by Teams), like the TypeScript/Python SDKs.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/teams/activity_context.rb', line 98 def sign_in(connection_name: nil, oauth_card_text: "Please Sign In...", sign_in_button_text: "Sign In") connection_name ||= app.default_connection_name begin response = api.users.get_token( user_id: activity.from.id, connection_name:, channel_id: activity.channel_id ) return response.token if response.token && !response.token.to_s.empty? rescue HttpError # No token yet; continue with the OAuth card flow. end conversation_id = conversation_reference.conversation_id if activity.conversation.is_group one_on_one = api.conversations.create( members: [activity.from.to_h], tenant_id: activity.conversation.tenant_id ) conversation_id = one_on_one.id # Deliberately posts the plain text notice into the group (matching # TypeScript and Python): group chats don't support SSO, so the card # itself goes to the 1:1 below while the group sees only the notice. post(oauth_card_text) end state = Base64.strict_encode64(JSON.generate( "connectionName" => connection_name, "conversation" => conversation_reference.to_h, "msAppId" => app.client_id )) resource = api.bots.sign_in.get_resource(state:) card = { "text" => oauth_card_text, "connectionName" => connection_name, "tokenExchangeResource" => resource.token_exchange_resource&.to_h, "tokenPostResource" => resource.token_post_resource&.to_h, "buttons" => [ { "type" => "signin", "title" => , "value" => resource.sign_in_link } ] }.compact payload = { "type" => "message", "recipient" => activity.from.to_h, "attachments" => [ { "contentType" => "application/vnd.microsoft.card.oauth", "content" => card } ] } app.send_activity(sign_in_reference(conversation_id), payload) nil end |
#sign_out(connection_name: nil) ⇒ Object
Clears the user's token for the connection. Failures are logged, not raised, matching the TypeScript/Python SDKs.
156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/teams/activity_context.rb', line 156 def sign_out(connection_name: nil) api.users.sign_out( user_id: activity.from.id, connection_name: connection_name || app.default_connection_name, channel_id: activity.channel_id ) nil rescue HttpError => error log&.error("Failed to sign out user: #{error.}") nil end |
#storage ⇒ Object
30 31 32 |
# File 'lib/teams/activity_context.rb', line 30 def storage app.storage end |
#typing(text = nil) ⇒ Object
66 67 68 |
# File 'lib/teams/activity_context.rb', line 66 def typing(text = nil) post(Api::TypingActivity.new(text)) end |
#update(activity_id, activity_or_text) ⇒ Object
Sugar over post: the SDKs update by sending an activity that already carries an id, and post does exactly that. See AGENTS.md.
62 63 64 |
# File 'lib/teams/activity_context.rb', line 62 def update(activity_id, activity_or_text) post(activity_with_id(activity_id, activity_or_text)) end |
#user_graph(connection_name: nil) ⇒ Object
Microsoft Graph client with the signed-in user's token. Raises when the user is not signed in, like the Python SDK's user_graph.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/teams/activity_context.rb', line 77 def user_graph(connection_name: nil) connection = connection_name || app.default_connection_name @user_graph ||= {} @user_graph[connection] ||= begin response = api.users.get_token( user_id: activity.from.id, connection_name: connection, channel_id: activity.channel_id ) raise Error, "User must be signed in to access the Graph client" if response.token.to_s.empty? Graph::Client.new(token: response.token, base_url_root: app.graph.base_url_root) rescue HttpError raise Error, "User must be signed in to access the Graph client" end end |