from dataclasses import dataclass from typing import Any import phonenumbers from anydi import singleton from ows_text_campaigns.adapters.ows_url_shortener import OwsUrlShortenerClient from ows_text_campaigns.artist.services import ArtistSettingsService from ows_text_campaigns.campaigns.exceptions import ( PersonalizedAttributesLimitExceededError, ) from ows_text_campaigns.campaigns.models import Campaign from ows_text_campaigns.campaigns.services import CampaignService from ows_text_campaigns.campaigns.types import ( FanRenderedMessage, ShortenedUrlPersonalizedAttributes, ) from ows_text_campaigns.config import Settings @dataclass(kw_only=True) class RenderCampaignMessagesBatchRequest: campaign_id: str personalized_attributes: list[ShortenedUrlPersonalizedAttributes] @singleton class RenderCampaignMessagesBatchHandler: def __init__( self, campaign_service: CampaignService, artist_settings_service: ArtistSettingsService, ows_url_shortener_client: OwsUrlShortenerClient, settings: Settings, ) -> None: self.campaign_service = campaign_service self.artist_settings_service = artist_settings_service self.ows_url_shortener_client = ows_url_shortener_client self.settings = settings def handle( self, request: RenderCampaignMessagesBatchRequest ) -> list[FanRenderedMessage]: campaign = self.campaign_service.get_campaign(request.campaign_id) if ( len(request.personalized_attributes) > self.settings.max_allowed_personalized_attributes_limit ): raise PersonalizedAttributesLimitExceededError( max_allowed_limit=self.settings.max_allowed_personalized_attributes_limit ) # Get artist settings artist_settings = self.artist_settings_service.get_configured_settings( campaign.global_participant_id ) shortened_urls = self._get_shortened_urls( campaign, attributes=request.personalized_attributes ) result: list[FanRenderedMessage] = [] for attributes_item in request.personalized_attributes: fan_id = attributes_item["fan_id"] channel = attributes_item["channel"] recipient = attributes_item["phone_number"] recipient_numobj = phonenumbers.parse(recipient) recipient_country_code = phonenumbers.region_code_for_number( recipient_numobj ) sender = artist_settings.get_send_phone_number(recipient_country_code) message = self.campaign_service.render_message_for_channel( content=campaign.get_content(), channel=channel, shortened_urls=self._get_fan_shortened_urls( fan_id=fan_id, shortened_urls=shortened_urls, ), ) result.append( FanRenderedMessage( fan_id=fan_id, channel=channel, recipient=recipient, sender=sender, message=message, media_url=campaign.get_media_url(), ) ) return result def _get_shortened_urls( self, campaign: Campaign, *, attributes: list[ShortenedUrlPersonalizedAttributes], ) -> dict[str, Any]: """Prepare a dictionary of shortened URLs for all users.""" result: dict[str, Any] = {} for shortened_url in campaign.shortened_urls: if shortened_url.is_standard: result[shortened_url.id] = shortened_url.preview_url else: response = self.ows_url_shortener_client.bulk_shorten( url=shortened_url.url, domain=shortened_url.domain, path_prefix=shortened_url.path, additional_attributes=[ { "campaign_id": campaign.id, "shortened_url_id": shortened_url.id, "fan_id": attrs["fan_id"], } for attrs in attributes ], ) for short_url in response.short_urls: if not short_url.additional_attributes: continue fan_id = short_url.additional_attributes.get("fan_id") if not fan_id: continue result.setdefault(shortened_url.id, {}) result[shortened_url.id][fan_id] = short_url.short_url return result @staticmethod def _get_fan_shortened_urls( fan_id: str, shortened_urls: dict[str, Any] ) -> dict[str, str]: result: dict[str, str] = {} for shortened_url_id, shortened_url_item in shortened_urls.items(): if isinstance(shortened_url_item, dict): shortened_url = shortened_url_item.get(fan_id) if isinstance(shortened_url, str): result[shortened_url_id] = shortened_url elif isinstance(shortened_url_item, str): result[shortened_url_id] = shortened_url_item return result