Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support updating existing channels for Facebook and Instagram, remove… #5692

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add check credentials view for Facebook and Instagram channels
  • Loading branch information
norkans7 committed Jan 9, 2025
commit 1f00ef6fe19f7b378c0784e322e895357d9d258f
31 changes: 27 additions & 4 deletions temba/channels/types/facebookapp/type.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import requests

from django.conf import settings
from django.urls import re_path
from django.utils.translation import gettext_lazy as _

from temba.contacts.models import URN
from temba.triggers.models import Trigger

from ...models import Channel, ChannelType
from .views import ClaimView
from .views import CheckCredentials, ClaimView


class FacebookAppType(ChannelType):
@@ -32,9 +33,17 @@ class FacebookAppType(ChannelType):
) % {"link": '<a target="_blank" href="http://facebook.com">Facebook</a>'}
claim_view = ClaimView

menu_items = [
dict(label=_("Reconnect Facebook Page"), view_name="channels.types.facebookapp.claim", obj_view=False)
]
menu_items = [dict(label=_("Check Credentials"), view_name="channels.types.facebookapp.check_credentials")]

def get_urls(self):
return [
self.get_claim_url(),
re_path(
r"^(?P<uuid>[a-z0-9\-]+)/check_credentials/$",
CheckCredentials.as_view(channel_type=self),
name="check_credentials",
),
]

def deactivate(self, channel):
config = channel.config
@@ -81,3 +90,17 @@ def get_redact_values(self, channel) -> tuple: # pragma: needs cover

def get_error_ref_url(self, channel, code: str) -> str:
return "https://developers.facebook.com/docs/messenger-platform/error-codes"

def check_credentials(self, config: dict) -> bool:
app_id = settings.FACEBOOK_APPLICATION_ID
app_secret = settings.FACEBOOK_APPLICATION_SECRET
url = "https://graph.facebook.com/v18.0/debug_token"
params = {
"access_token": f"{app_id}|{app_secret}",
"input_token": config[Channel.CONFIG_AUTH_TOKEN],
}
resp = requests.get(url, params=params)

if resp.status_code == 200:
return resp.json().get("data", dict()).get("is_valid", False)
return False
22 changes: 21 additions & 1 deletion temba/channels/types/facebookapp/views.py
Original file line number Diff line number Diff line change
@@ -6,10 +6,11 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from temba.orgs.views.base import BaseReadView
from temba.utils.text import truncate

from ...models import Channel
from ...views import ClaimViewMixin
from ...views import ChannelTypeMixin, ClaimViewMixin


class ClaimView(ClaimViewMixin, SmartFormView):
@@ -141,3 +142,22 @@ def form_valid(self, form):
)

return super().form_valid(form)


class CheckCredentials(ChannelTypeMixin, BaseReadView):
slug_url_kwarg = "uuid"
permission = "channels.channel_claim"
fields = ()
template_name = "channels/types/facebookapp/check_credentials.html"

def derive_menu_path(self):
return f"/settings/channels/{self.get_object().uuid}"

def get_queryset(self):
return self.request.org.channels.filter(is_active=True, channel_type=self.channel_type.code)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["update_token_url"] = f"{reverse("channels.types.facebookapp.claim")}?update=1"
context["valid_token"] = self.object.type.check_credentials(self.object.config)
return context
31 changes: 27 additions & 4 deletions temba/channels/types/instagram/type.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import requests

from django.conf import settings
from django.urls import re_path
from django.utils.translation import gettext_lazy as _

from temba.contacts.models import URN

from ...models import Channel, ChannelType
from .views import ClaimView
from .views import CheckCredentials, ClaimView


class InstagramType(ChannelType):
@@ -29,9 +30,17 @@ class InstagramType(ChannelType):
}
claim_view = ClaimView

menu_items = [
dict(label=_("Reconnect Business Account"), view_name="channels.types.instagram.claim", obj_view=False)
]
menu_items = [dict(label=_("Reconnect Business Account"), view_name="channels.types.instagram.check_credentials")]

def get_urls(self):
return [
self.get_claim_url(),
re_path(
r"^(?P<uuid>[a-z0-9\-]+)/check_credentials/$",
CheckCredentials.as_view(channel_type=self),
name="check_credentials",
),
]

def deactivate(self, channel):
config = channel.config
@@ -48,3 +57,17 @@ def get_redact_values(self, channel) -> tuple: # pragma: needs cover

def get_error_ref_url(self, channel, code: str) -> str:
return "https://developers.facebook.com/docs/instagram-api/reference/error-codes"

def check_credentials(self, config: dict) -> bool:
app_id = settings.FACEBOOK_APPLICATION_ID
app_secret = settings.FACEBOOK_APPLICATION_SECRET
url = "https://graph.facebook.com/v18.0/debug_token"
params = {
"access_token": f"{app_id}|{app_secret}",
"input_token": config[Channel.CONFIG_AUTH_TOKEN],
}
resp = requests.get(url, params=params)

if resp.status_code == 200:
return resp.json().get("data", dict()).get("is_valid", False)
return False
24 changes: 22 additions & 2 deletions temba/channels/types/instagram/views.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import logging

import requests
from smartmin.views import SmartFormView
from smartmin.views import SmartFormView, SmartReadView

from django import forms
from django.conf import settings
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from temba.orgs.views.mixins import OrgObjPermsMixin
from temba.utils.text import truncate

from ...models import Channel
from ...views import ClaimViewMixin
from ...views import ChannelTypeMixin, ClaimViewMixin

logger = logging.getLogger(__name__)

@@ -172,3 +173,22 @@ def form_valid(self, form):
)

return super().form_valid(form)


class CheckCredentials(ChannelTypeMixin, OrgObjPermsMixin, SmartReadView):
slug_url_kwarg = "uuid"
permission = "channels.channel_claim"
fields = ()
template_name = "channels/types/instagram/check_credentials.html"

def derive_menu_path(self):
return f"/settings/channels/{self.get_object().uuid}"

def get_queryset(self):
return self.request.org.channels.filter(is_active=True, channel_type=self.channel_type.code)

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["update_token_url"] = f"{reverse("channels.types.instagram.claim")}?update=1"
context["valid_token"] = self.object.type.check_credentials(self.object.config)
return context
4 changes: 1 addition & 3 deletions temba/channels/types/whatsapp/type.py
Original file line number Diff line number Diff line change
@@ -35,9 +35,7 @@ class WhatsAppType(ChannelType):
claim_blurb = _("If you have an enterprise WhatsApp account, you can connect it to communicate with your contacts")
claim_view = ClaimView

menu_items = [
dict(label=_("Verify Number"), view_name="channels.types.whatsapp.request_code", obj_view=True),
]
menu_items = [dict(label=_("Verify Number"), view_name="channels.types.whatsapp.request_code")]

def get_urls(self):
return [
5 changes: 1 addition & 4 deletions temba/channels/views.py
Original file line number Diff line number Diff line change
@@ -489,10 +489,7 @@ def build_context_menu(self, menu):
obj = self.get_object()

for item in obj.type.menu_items:
menu.add_link(
item["label"],
reverse(item["view_name"], args=[obj.uuid]) if item["obj_view"] else reverse(item["view_name"]),
)
menu.add_link(item["label"], reverse(item["view_name"], args=[obj.uuid]))

if obj.type.config_ui:
menu.add_link(_("Configuration"), reverse("channels.channel_configuration", args=[obj.uuid]))
18 changes: 18 additions & 0 deletions templates/channels/types/facebookapp/check_credentials.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends "smartmin/read.html" %}
{% load smartmin temba humanize channels i18n tz %}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Monosnap Facebook 2024-11-27 00-56-19 Monosnap Facebook 2024-11-27 00-54-39


{% block content %}
<div class="mt-4 card">
<p id="fb-status">
{% if not valid_token %}
{% trans "Error with token, you need to reconnect the Facebook page by clicking the button below" %}
{% else %}
{% trans "Everything looks good. No need to reconnect" %}
{% endif %}
</p>
<div class="mt-4">
<div onclick="javascript:history.go(-1)" class="button-light">{% trans "Go Back" %}</div>
<a class="button-primary mt-4 ml-2" href="{{ update_token_url }}">{% trans "Reconnect Facebook page" %}</a>
</div>
</div>
{% endblock content %}
18 changes: 18 additions & 0 deletions templates/channels/types/instagram/check_credentials.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends "smartmin/read.html" %}
{% load smartmin temba humanize channels i18n tz %}

{% block content %}
<div class="mt-4 card">
<p id="fb-status">
{% if not valid_token %}
{% trans "Error with token, you need to reconnect the Instagram Business Account by clicking the button below" %}
{% else %}
{% trans "Everything looks good. No need to reconnect" %}
{% endif %}
</p>
<div class="mt-4">
<div onclick="javascript:history.go(-1)" class="button-light">{% trans "Go Back" %}</div>
<a class="button-primary mt-4 ml-2" href="{{ update_token_url }}">{% trans "Reconnect Instagram Business Account" %}</a>
</div>
</div>
{% endblock content %}