def refresh_whatsapp_templates(): """ Runs across all WhatsApp templates that have connected FB accounts and syncs the templates which are active. """ from .type import ( CONFIG_FB_BUSINESS_ID, CONFIG_FB_ACCESS_TOKEN, CONFIG_FB_TEMPLATE_LIST_DOMAIN, LANGUAGE_MAPPING, STATUS_MAPPING, TEMPLATE_LIST_URL, ) r = get_redis_connection() if r.get("refresh_whatsapp_templates"): # pragma: no cover return with r.lock("refresh_whatsapp_templates", 1800): # for every whatsapp channel for channel in Channel.objects.filter(is_active=True, channel_type="WA"): # move on if we have no FB credentials if (CONFIG_FB_BUSINESS_ID not in channel.config or CONFIG_FB_ACCESS_TOKEN not in channel.config): # pragma: no cover continue # fetch all our templates start = timezone.now() try: # Retrieve the template domain, fallback to the default for channels # that have been setup earlier for backwards compatibility facebook_template_domain = channel.config.get( CONFIG_FB_TEMPLATE_LIST_DOMAIN, "graph.facebook.com") facebook_business_id = channel.config.get( CONFIG_FB_BUSINESS_ID) url = TEMPLATE_LIST_URL % (facebook_template_domain, facebook_business_id) # we should never need to paginate because facebook limits accounts to 255 templates response = requests.get( url, params=dict( access_token=channel.config[CONFIG_FB_ACCESS_TOKEN], limit=255)) elapsed = (timezone.now() - start).total_seconds() * 1000 HTTPLog.create_from_response(HTTPLog.WHATSAPP_TEMPLATES_SYNCED, url, response, channel=channel, request_time=elapsed) if response.status_code != 200: # pragma: no cover continue # run through all our templates making sure they are present in our DB seen = [] for template in response.json()["data"]: # if this is a status we don't know about if template["status"] not in STATUS_MAPPING: logger.error( f"unknown whatsapp status: {template['status']}") continue status = STATUS_MAPPING[template["status"]] content_parts = [] all_supported = True for component in template["components"]: if component["type"] not in [ "HEADER", "BODY", "FOOTER" ]: logger.error( f"unknown component type: {component}") continue if component["type"] in [ "HEADER", "FOOTER" ] and _calculate_variable_count(component["text"]): logger.error( f"unsupported component type wih variables: {component}" ) all_supported = False content_parts.append(component["text"]) if not content_parts or not all_supported: continue content = "\n\n".join(content_parts) variable_count = _calculate_variable_count(content) language = LANGUAGE_MAPPING.get(template["language"]) # its a (non fatal) error if we see a language we don't know if language is None: status = TemplateTranslation.STATUS_UNSUPPORTED_LANGUAGE language = template["language"] translation = TemplateTranslation.get_or_create( channel=channel, name=template["name"], language=language, content=content, variable_count=variable_count, status=status, external_id=template["id"], ) seen.append(translation) # trim any translations we didn't see TemplateTranslation.trim(channel, seen) except RequestException as e: # pragma: no cover HTTPLog.create_from_exception( HTTPLog.WHATSAPP_TEMPLATES_SYNCED, url, e, start, channel=channel)
def refresh_whatsapp_templates(): """ Runs across all WhatsApp templates that have connected FB accounts and syncs the templates which are active. """ from .type import ( CONFIG_FB_BUSINESS_ID, CONFIG_FB_ACCESS_TOKEN, CONFIG_FB_TEMPLATE_LIST_DOMAIN, LANGUAGE_MAPPING, STATUS_MAPPING, TEMPLATE_LIST_URL, ) r = get_redis_connection() if r.get("refresh_whatsapp_templates"): # pragma: no cover return with r.lock("refresh_whatsapp_templates", 1800): # for every whatsapp channel for channel in Channel.objects.filter(is_active=True, channel_type="WA"): # move on if we have no FB credentials if (CONFIG_FB_BUSINESS_ID not in channel.config or CONFIG_FB_ACCESS_TOKEN not in channel.config): # pragma: no cover continue # fetch all our templates try: # Retrieve the template domain, fallback to the default for channels # that have been setup earlier for backwards compatibility facebook_template_domain = channel.config.get( CONFIG_FB_TEMPLATE_LIST_DOMAIN, "graph.facebook.com") facebook_business_id = channel.config.get( CONFIG_FB_BUSINESS_ID) # we should never need to paginate because facebook limits accounts to 255 templates response = requests.get( TEMPLATE_LIST_URL % (facebook_template_domain, facebook_business_id), params=dict( access_token=channel.config[CONFIG_FB_ACCESS_TOKEN], limit=255), ) if response.status_code != 200: # pragma: no cover raise Exception( f"received non 200 status: {response.status_code} {response.content}" ) # run through all our templates making sure they are present in our DB seen = [] for template in response.json()["data"]: # its a (non fatal) error if we see a language we don't know if template["language"] not in LANGUAGE_MAPPING: logger.error( f"unknown whatsapp language: {template['language']}" ) continue # or if this is a status we don't know about if template["status"] not in STATUS_MAPPING: logger.error( f"unknown whatsapp status: {template['status']}") continue # try to get the body out if template["components"][0][ "type"] != "BODY": # pragma: no cover logger.error( f"unknown component type: {template['components'][0]}" ) continue content = template["components"][0]["text"] translation = TemplateTranslation.get_or_create( channel=channel, name=template["name"], language=LANGUAGE_MAPPING[template["language"]], content=content, variable_count=_calculate_variable_count(content), status=STATUS_MAPPING[template["status"]], external_id=template["id"], ) seen.append(translation) # trim any translations we didn't see TemplateTranslation.trim(channel, seen) except Exception as e: # pragma: no cover logger.error( f"error fetching templates for whatsapp channel: {str(e)}")
def deactivate(self, channel): # deactivate all translations associated with us TemplateTranslation.trim(channel, [])
def update_local_templates(channel, templates_data): channel_namespace = channel.config.get("fb_namespace", "") # run through all our templates making sure they are present in our DB seen = [] for template in templates_data: template_status = template["status"] template_status = template_status.upper() # if this is a status we don't know about if template_status not in STATUS_MAPPING: continue status = STATUS_MAPPING[template_status] content_parts = [] all_supported = True for component in template["components"]: if component["type"] not in ["HEADER", "BODY", "FOOTER"]: continue if "text" not in component: continue if component["type"] in [ "HEADER", "FOOTER" ] and _calculate_variable_count(component["text"]): all_supported = False content_parts.append(component["text"]) if not content_parts or not all_supported: continue content = "\n\n".join(content_parts) variable_count = _calculate_variable_count(content) language, country = LANGUAGE_MAPPING.get(template["language"], (None, None)) # its a (non fatal) error if we see a language we don't know if language is None: status = TemplateTranslation.STATUS_UNSUPPORTED_LANGUAGE language = template["language"] missing_external_id = f"{template['language']}/{template['name']}" translation = TemplateTranslation.get_or_create( channel=channel, name=template["name"], language=language, country=country, content=content, variable_count=variable_count, status=status, external_id=template.get("id", missing_external_id), namespace=template.get("namespace", channel_namespace), ) seen.append(translation) # trim any translations we didn't see TemplateTranslation.trim(channel, seen)