diff --git a/README.md b/README.md
index 934d1a9..14cbd42 100644
--- a/README.md
+++ b/README.md
@@ -214,6 +214,36 @@ response = activitysmith.notifications.send(
)
```
+## Rich Push Notifications with Media
+
+
+
+
+
+```ruby
+response = activitysmith.notifications.send(
+ {
+ title: "Homepage ready",
+ message: "Your agent finished the redesign.",
+ media: "https://cdn.example.com/output/homepage-v2.png",
+ redirection: "https://github.com/acme/web/pull/482"
+ }
+)
+```
+
+Send images, videos, or audio with your push notifications, press and hold to preview media directly from the notification, then tap through to open the linked content.
+
+
+
+
+
+What will work:
+
+- direct image URL: `.jpg`, `.png`, `.gif`, etc.
+- direct audio file URL: `.mp3`, `.m4a`, etc.
+- direct video file URL: `.mp4`, `.mov`, etc.
+- URL that responds with a proper media `Content-Type`, even if the path has no extension
+
## Push Notification Redirection and Actions
Push notification redirection and actions are optional and can be used to redirect the user to a specific URL when they tap the notification or to trigger a specific action when they long-press the notification.
diff --git a/lib/activitysmith.rb b/lib/activitysmith.rb
index 085acda..e13ce00 100644
--- a/lib/activitysmith.rb
+++ b/lib/activitysmith.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative "activitysmith/version"
+require_relative "activitysmith/versioned_user_agent"
require_relative "activitysmith/notifications"
require_relative "activitysmith/live_activities"
require_relative "activitysmith/client"
diff --git a/lib/activitysmith/client.rb b/lib/activitysmith/client.rb
index 24ab8cd..23e9506 100644
--- a/lib/activitysmith/client.rb
+++ b/lib/activitysmith/client.rb
@@ -11,6 +11,7 @@ def initialize(api_key:)
config = OpenapiClient::Configuration.new
config.access_token = api_key
+ config.user_agent = VersionedUserAgent.value if config.respond_to?(:user_agent=)
api_client = OpenapiClient::ApiClient.new(config)
@notifications = Notifications.new(OpenapiClient::PushNotificationsApi.new(api_client))
diff --git a/lib/activitysmith/notifications.rb b/lib/activitysmith/notifications.rb
index ec7fa69..f875d68 100644
--- a/lib/activitysmith/notifications.rb
+++ b/lib/activitysmith/notifications.rb
@@ -7,12 +7,16 @@ def initialize(api)
end
def send(request, opts = {})
- @api.send_push_notification(normalize_channels_target(request), opts)
+ normalized = normalize_channels_target(request)
+ assert_valid_media_actions!(normalized)
+ @api.send_push_notification(normalized, opts)
end
# Backward-compatible alias.
def send_push_notification(push_notification_request, opts = {})
- @api.send_push_notification(normalize_channels_target(push_notification_request), opts)
+ normalized = normalize_channels_target(push_notification_request)
+ assert_valid_media_actions!(normalized)
+ @api.send_push_notification(normalized, opts)
end
def method_missing(name, *args, &block)
@@ -49,5 +53,29 @@ def normalize_channels(channels)
[]
end
end
+
+ def assert_valid_media_actions!(request)
+ media = request_value(request, :media)
+ actions = request_value(request, :actions)
+ has_media = media.is_a?(String) ? !media.strip.empty? : !media.nil?
+ has_actions = actions.respond_to?(:empty?) ? !actions.empty? : !actions.nil?
+
+ return unless has_media && has_actions
+
+ raise ArgumentError, "ActivitySmith: media cannot be combined with actions"
+ end
+
+ def request_value(request, key)
+ if request.is_a?(Hash)
+ return request[key] if request.key?(key)
+ return request[key.to_s] if request.key?(key.to_s)
+
+ return nil
+ end
+
+ return request.public_send(key) if request.respond_to?(key)
+
+ nil
+ end
end
end
diff --git a/lib/activitysmith/versioned_user_agent.rb b/lib/activitysmith/versioned_user_agent.rb
new file mode 100644
index 0000000..ee3cdc6
--- /dev/null
+++ b/lib/activitysmith/versioned_user_agent.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module ActivitySmith
+ module VersionedUserAgent
+ def self.value
+ "activitysmith-ruby/#{ActivitySmith::VERSION}"
+ end
+ end
+end
diff --git a/test/resources_test.rb b/test/resources_test.rb
index 0418301..bcfce38 100644
--- a/test/resources_test.rb
+++ b/test/resources_test.rb
@@ -83,6 +83,44 @@ def test_notifications_map_channels_to_target
)
end
+ def test_notifications_preserve_media_and_redirection
+ api = FakePushApi.new
+ resource = ActivitySmith::Notifications.new(api)
+
+ payload = {
+ title: "Voice Over Generated",
+ media: "https://cdn.activitysmith.com/voice_over.mp3",
+ redirection: "https://studio.acme.com/voice-overs/482/review"
+ }
+
+ resource.send(payload)
+
+ assert_equal(
+ [
+ [:send_push_notification, payload, {}]
+ ],
+ api.calls
+ )
+ end
+
+ def test_notifications_reject_media_and_actions
+ api = FakePushApi.new
+ resource = ActivitySmith::Notifications.new(api)
+
+ error = assert_raises(ArgumentError) do
+ resource.send(
+ {
+ title: "Voice Over Generated",
+ media: "https://cdn.activitysmith.com/voice_over.mp3",
+ actions: [{ title: "Open", type: "open_url", url: "https://example.com" }]
+ }
+ )
+ end
+
+ assert_equal "ActivitySmith: media cannot be combined with actions", error.message
+ assert_empty api.calls
+ end
+
def test_live_activities_short_and_legacy_methods
api = FakeLiveApi.new
resource = ActivitySmith::LiveActivities.new(api)