fixes: #11834 This pull request introduces TikTok channel integration, enabling users to connect and manage TikTok business accounts similarly to other supported social channels. The changes span backend API endpoints, authentication helpers, webhook handling, configuration, and frontend components to support TikTok as a first-class channel. **Key Notes** * This integration is only compatible with TikTok Business Accounts * Special permissions are required to access the TikTok [Business Messaging API](https://business-api.tiktok.com/portal/docs?id=1832183871604753). * The Business Messaging API is region-restricted and is currently unavailable to users in the EU. * Only TEXT, IMAGE, and POST_SHARE messages are currently supported due to limitations in the TikTok Business Messaging API * A message will be successfully sent only if it contains text alone or one image attachment. Messages with multiple attachments or those combining text and attachments will fail and receive a descriptive error status. * Messages sent directly from the TikTok App will be synced into the system * Initiating a new conversation from the system is not permitted due to limitations from the TikTok Business Messaging API. **Backend: TikTok Channel Integration** * Added `Api::V1::Accounts::Tiktok::AuthorizationsController` to handle TikTok OAuth authorization initiation, returning the TikTok authorization URL. * Implemented `Tiktok::CallbacksController` to handle TikTok OAuth callback, process authorization results, create or update channel/inbox, and handle errors or denied scopes. * Added `Webhooks::TiktokController` to receive and verify TikTok webhook events, including signature verification and event dispatching. * Created `Tiktok::IntegrationHelper` module for JWT-based token generation and verification for secure TikTok OAuth state management. **Configuration and Feature Flags** * Added TikTok app credentials (`TIKTOK_APP_ID`, `TIKTOK_APP_SECRET`) to allowed configs and app config, and registered TikTok as a feature in the super admin features YAML. [[1]](diffhunk://#diff-5e46e1d248631a1147521477d84a54f8ba6846ea21c61eca5f70042d960467f4R43) [[2]](diffhunk://#diff-8bf37a019cab1dedea458c437bd93e34af1d6e22b1672b1d43ef6eaa4dcb7732R69) [[3]](diffhunk://#diff-123164bea29f3c096b0d018702b090d5ae670760c729141bd4169a36f5f5c1caR74-R79) **Frontend: TikTok Channel UI and Messaging Support** * Added `TiktokChannel` API client for frontend TikTok authorization requests. * Updated channel icon mappings and tests to include TikTok (`Channel::Tiktok`). [[1]](diffhunk://#diff-b852739ed45def61218d581d0de1ba73f213f55570aa5eec52aaa08f380d0e16R16) [[2]](diffhunk://#diff-3cd3ae32e94ef85f1f2c4435abf0775cc0614fb37ee25d97945cd51573ef199eR64-R69) * Enabled TikTok as a supported channel in contact forms, channel widgets, and feature toggles. [[1]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R47) [[2]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R69) [[3]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R26-R29) [[4]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R51-R54) [[5]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R68) * Updated message meta logic to support TikTok-specific message statuses (sent, delivered, read). [[1]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696R23) [[2]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L63-R65) [[3]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L81-R84) [[4]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L103-R107) * Added support for embedded message attachments (e.g., TikTok embeds) with a new `EmbedBubble` component and updated message rendering logic. [[1]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR31) [[2]](diffhunk://#diff-047859f9368a46d6d20177df7d6d623768488ecc38a5b1e284f958fad49add68R1-R19) [[3]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR316) [[4]](diffhunk://#diff-cbc85e7c4c8d56f2a847d0b01cd48ef36e5f87b43023bff0520fdfc707283085R52) * Adjusted reply policy and UI messaging for TikTok's 48-hour reply window. [[1]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R208-R210) [[2]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R224-R226) These changes collectively enable end-to-end TikTok channel support, from configuration and OAuth flow to webhook processing and frontend message handling. ------------ # TikTok App Setup & Configuration 1. Grant access to the Business Messaging API ([Documentation](https://business-api.tiktok.com/portal/docs?id=1832184145137922)) 2. Set the app authorization redirect URL to `https://FRONTEND_URL/tiktok/callback` 3. Update the installation config with TikTok App ID and Secret 4. Create a Business Messaging Webhook configuration and set the callback url to `https://FRONTEND_URL/webhooks/tiktok` ([Documentation](https://business-api.tiktok.com/portal/docs?id=1832190670631937)) . You can do this by calling `Tiktok::AuthClient.update_webhook_callback` from rails console once you finish Tiktok channel configuration in super admin ( will be automated in future ) 5. Enable TikTok channel feature in an account --------- Co-authored-by: Sojan Jose <sojan@pepalo.com> Co-authored-by: iamsivin <iamsivin@gmail.com>
138 lines
5.8 KiB
Ruby
138 lines
5.8 KiB
Ruby
json.id resource.id
|
|
json.avatar_url resource.try(:avatar_url)
|
|
json.channel_id resource.channel_id
|
|
json.name resource.name
|
|
json.channel_type resource.channel_type
|
|
json.greeting_enabled resource.greeting_enabled
|
|
json.greeting_message resource.greeting_message
|
|
json.working_hours_enabled resource.working_hours_enabled
|
|
json.enable_email_collect resource.enable_email_collect
|
|
json.csat_survey_enabled resource.csat_survey_enabled
|
|
json.csat_config resource.csat_config
|
|
json.enable_auto_assignment resource.enable_auto_assignment
|
|
json.auto_assignment_config resource.auto_assignment_config
|
|
json.out_of_office_message resource.out_of_office_message
|
|
json.working_hours resource.weekly_schedule
|
|
json.timezone resource.timezone
|
|
json.callback_webhook_url resource.callback_webhook_url
|
|
json.allow_messages_after_resolved resource.allow_messages_after_resolved
|
|
json.lock_to_single_conversation resource.lock_to_single_conversation
|
|
json.sender_name_type resource.sender_name_type
|
|
json.business_name resource.business_name
|
|
|
|
if resource.portal.present?
|
|
json.help_center do
|
|
json.name resource.portal.name
|
|
json.slug resource.portal.slug
|
|
end
|
|
end
|
|
|
|
## Channel specific settings
|
|
## TODO : Clean up and move the attributes into channel sub section
|
|
|
|
json.tweets_enabled resource.channel.try(:tweets_enabled) if resource.twitter?
|
|
|
|
## WebWidget Attributes
|
|
json.allowed_domains resource.channel.try(:allowed_domains)
|
|
json.widget_color resource.channel.try(:widget_color)
|
|
json.website_url resource.channel.try(:website_url)
|
|
json.hmac_mandatory resource.channel.try(:hmac_mandatory)
|
|
json.welcome_title resource.channel.try(:welcome_title)
|
|
json.welcome_tagline resource.channel.try(:welcome_tagline)
|
|
json.web_widget_script resource.channel.try(:web_widget_script)
|
|
json.website_token resource.channel.try(:website_token)
|
|
json.selected_feature_flags resource.channel.try(:selected_feature_flags)
|
|
json.reply_time resource.channel.try(:reply_time)
|
|
if resource.web_widget?
|
|
json.hmac_token resource.channel.try(:hmac_token) if Current.account_user&.administrator?
|
|
json.pre_chat_form_enabled resource.channel.try(:pre_chat_form_enabled)
|
|
json.pre_chat_form_options resource.channel.try(:pre_chat_form_options)
|
|
json.continuity_via_email resource.channel.try(:continuity_via_email)
|
|
end
|
|
|
|
## Facebook Attributes
|
|
if resource.facebook?
|
|
json.page_id resource.channel.try(:page_id)
|
|
json.reauthorization_required resource.channel.try(:reauthorization_required?)
|
|
end
|
|
|
|
## Instagram Attributes
|
|
json.reauthorization_required resource.channel.try(:reauthorization_required?) if resource.instagram?
|
|
json.instagram_id resource.channel.try(:instagram_id) if resource.instagram?
|
|
|
|
## Tiktok Attributes
|
|
json.reauthorization_required resource.channel.try(:reauthorization_required?) if resource.tiktok?
|
|
|
|
## Twilio Attributes
|
|
json.messaging_service_sid resource.channel.try(:messaging_service_sid)
|
|
json.phone_number resource.channel.try(:phone_number)
|
|
json.medium resource.channel.try(:medium) if resource.twilio?
|
|
if resource.twilio?
|
|
json.content_templates resource.channel.try(:content_templates)
|
|
if Current.account_user&.administrator?
|
|
json.auth_token resource.channel.try(:auth_token)
|
|
json.account_sid resource.channel.try(:account_sid)
|
|
end
|
|
end
|
|
|
|
if resource.email?
|
|
## Email Channel Attributes
|
|
json.email resource.channel.try(:email)
|
|
json.forwarding_enabled ENV.fetch('MAILER_INBOUND_EMAIL_DOMAIN', '').present?
|
|
json.forward_to_email resource.channel.try(:forward_to_email) if ENV.fetch('MAILER_INBOUND_EMAIL_DOMAIN', '').present?
|
|
|
|
## IMAP
|
|
if Current.account_user&.administrator?
|
|
json.imap_login resource.channel.try(:imap_login)
|
|
json.imap_password resource.channel.try(:imap_password)
|
|
json.imap_address resource.channel.try(:imap_address)
|
|
json.imap_port resource.channel.try(:imap_port)
|
|
json.imap_enabled resource.channel.try(:imap_enabled)
|
|
json.imap_enable_ssl resource.channel.try(:imap_enable_ssl)
|
|
|
|
if resource.channel.try(:microsoft?) || resource.channel.try(:google?) || resource.channel.try(:legacy_google?)
|
|
json.reauthorization_required resource.channel.try(:provider_config).empty? || resource.channel.try(:reauthorization_required?)
|
|
end
|
|
end
|
|
|
|
## SMTP
|
|
if Current.account_user&.administrator?
|
|
json.smtp_login resource.channel.try(:smtp_login)
|
|
json.smtp_password resource.channel.try(:smtp_password)
|
|
json.smtp_address resource.channel.try(:smtp_address)
|
|
json.smtp_port resource.channel.try(:smtp_port)
|
|
json.smtp_enabled resource.channel.try(:smtp_enabled)
|
|
json.smtp_domain resource.channel.try(:smtp_domain)
|
|
json.smtp_enable_ssl_tls resource.channel.try(:smtp_enable_ssl_tls)
|
|
json.smtp_enable_starttls_auto resource.channel.try(:smtp_enable_starttls_auto)
|
|
json.smtp_openssl_verify_mode resource.channel.try(:smtp_openssl_verify_mode)
|
|
json.smtp_authentication resource.channel.try(:smtp_authentication)
|
|
end
|
|
end
|
|
|
|
## API Channel Attributes
|
|
if resource.api?
|
|
json.hmac_token resource.channel.try(:hmac_token) if Current.account_user&.administrator?
|
|
json.webhook_url resource.channel.try(:webhook_url)
|
|
json.inbox_identifier resource.channel.try(:identifier)
|
|
json.additional_attributes resource.channel.try(:additional_attributes)
|
|
end
|
|
|
|
json.provider resource.channel.try(:provider)
|
|
|
|
## Telegram Attributes
|
|
json.bot_name resource.channel.try(:bot_name) if resource.telegram?
|
|
|
|
### WhatsApp Channel
|
|
if resource.whatsapp?
|
|
json.message_templates resource.channel.try(:message_templates)
|
|
json.provider_config resource.channel.try(:provider_config) if Current.account_user&.administrator?
|
|
json.reauthorization_required resource.channel.try(:reauthorization_required?)
|
|
end
|
|
|
|
## Voice Channel Attributes
|
|
if resource.channel_type == 'Channel::Voice'
|
|
json.voice_call_webhook_url resource.channel.try(:voice_call_webhook_url)
|
|
json.voice_status_webhook_url resource.channel.try(:voice_status_webhook_url)
|
|
end
|