Skip to content

Commit

Permalink
Shortlinks in SMS now require a header as per new TRAI SMS rules from…
Browse files Browse the repository at this point in the history
… Oct 1
  • Loading branch information
jace committed Oct 4, 2024
1 parent 59e334f commit a8739b2
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 35 deletions.
24 changes: 23 additions & 1 deletion funnel/views/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,20 +718,42 @@ def cleanurl_filter(url: str | furl) -> str:


@app.template_filter('shortlink')
def shortlink(url: str, actor: Account | None = None, shorter: bool = True) -> str:
def shortlink(
url: str,
actor: Account | None = None,
shorter: bool = True,
header: str | None = None,
) -> str:
"""
Return a short link suitable for sharing, in a template filter.
Caller must perform a database commit.
:param url: URL to shorten
:param actor: Optional actor to save against the shortlink
:param shorter: Use a shorter shortlink, ideal for SMS or a small database
:param header: Insert a brand header into the shortlink, for SMS DLT
"""
sl = Shortlink.new(url, reuse=True, shorter=shorter, actor=actor)
db.session.add(sl)
g.require_db_commit = True
if header is not None:
return app_url_for(
shortlinkapp, 'link', name=sl.name, header=header, _external=True
)
return app_url_for(shortlinkapp, 'link', name=sl.name, _external=True)


def sms_shortlink(url: str) -> str:
"""SMS version of :func:`shortlink`.
:param url: URL to shorten
"""
return shortlink(
url, shorter=True, header=app.config.get('SMS_DLT_SHORTURL_HEADER')
)


# MARK: Request/response handlers ------------------------------------------------------


Expand Down
4 changes: 2 additions & 2 deletions funnel/views/notifications/comment_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
Proposal,
)
from ...transports.sms import SmsPriority, SmsTemplate
from ..helpers import shortlink
from ..helpers import sms_shortlink
from ..notification import RenderNotification
from .mixins import TemplateVarMixin

Expand Down Expand Up @@ -252,7 +252,7 @@ def email_content(self) -> str:
)

def sms(self) -> SmsTemplate:
url = shortlink(
url = sms_shortlink(
self.comment.url_for(_external=True, **self.tracking_tags('sms'))
)
if self.document_type == 'comment':
Expand Down
17 changes: 7 additions & 10 deletions funnel/views/notifications/project_starting_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Session,
)
from ...transports.sms import SmsPriority, SmsTemplate
from ..helpers import shortlink
from ..helpers import sms_shortlink
from ..notification import RenderNotification
from .mixins import TemplateVarMixin

Expand Down Expand Up @@ -117,9 +117,8 @@ def email_content(self) -> str:
def sms(self) -> SmsTemplate:
return ProjectStartingTemplate(
project=self.project,
url=shortlink(
self.project.url_for(_external=True, **self.tracking_tags('sms')),
shorter=True,
url=sms_shortlink(
self.project.url_for(_external=True, **self.tracking_tags('sms'))
),
)

Expand Down Expand Up @@ -156,16 +155,14 @@ def sms(self) -> SmsTemplate:
return ProjectStartingTomorrowVenueTemplate(
account=self.project.account,
venue=venue,
url=shortlink(
self.project.url_for(_external=True, **self.tracking_tags('sms')),
shorter=True,
url=sms_shortlink(
self.project.url_for(_external=True, **self.tracking_tags('sms'))
),
)
return ProjectStartingTomorrowLocationTemplate(
account=self.project.account,
location=self.project.location,
url=shortlink(
self.project.url_for(_external=True, **self.tracking_tags('sms')),
shorter=True,
url=sms_shortlink(
self.project.url_for(_external=True, **self.tracking_tags('sms'))
),
)
12 changes: 5 additions & 7 deletions funnel/views/notifications/proposal_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
sa_orm,
)
from ...transports.sms import SmsPriority, SmsTemplate
from ..helpers import shortlink
from ..helpers import sms_shortlink
from ..notification import RenderNotification
from .mixins import TemplateVarMixin

Expand Down Expand Up @@ -110,9 +110,8 @@ def sms(self) -> ProposalReceivedTemplate:
return ProposalReceivedTemplate(
project=self.project,
actor=self.proposal.first_user,
url=shortlink(
self.proposal.url_for(_external=True, **self.tracking_tags('sms')),
shorter=True,
url=sms_shortlink(
self.proposal.url_for(_external=True, **self.tracking_tags('sms'))
),
)

Expand Down Expand Up @@ -152,8 +151,7 @@ def email_content(self) -> str:
def sms(self) -> ProposalSubmittedTemplate:
return ProposalSubmittedTemplate(
project=self.proposal.project,
url=shortlink(
self.proposal.url_for(_external=True, **self.tracking_tags('sms')),
shorter=True,
url=sms_shortlink(
self.proposal.url_for(_external=True, **self.tracking_tags('sms'))
),
)
6 changes: 3 additions & 3 deletions funnel/views/notifications/rsvp_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
)
from ...transports import email
from ...transports.sms import SmsPriority, SmsTemplate
from ..helpers import shortlink
from ..helpers import sms_shortlink
from ..notification import RenderNotification
from ..schedule import schedule_ical
from .mixins import TemplateVarMixin
Expand Down Expand Up @@ -128,8 +128,8 @@ def sms(
) -> RegistrationConfirmationTemplate | RegistrationConfirmationWithNextTemplate:
project = self.rsvp.project
next_at = project.next_starting_at()
url = shortlink(
project.url_for(_external=True, **self.tracking_tags('sms')), shorter=True
url = sms_shortlink(
project.url_for(_external=True, **self.tracking_tags('sms'))
)
if next_at:
return RegistrationConfirmationWithNextTemplate(
Expand Down
12 changes: 5 additions & 7 deletions funnel/views/notifications/update_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from ...models import Account, Project, ProjectUpdateNotification, Update
from ...transports.sms import SmsPriority, SmsTemplate
from ..helpers import shortlink
from ..helpers import sms_shortlink
from ..notification import RenderNotification
from .mixins import TemplateVarMixin

Expand Down Expand Up @@ -90,16 +90,14 @@ def sms(self) -> UpdateMergedTitleTemplate | UpdateSplitTitleTemplate:
if len(self.update.project.title_parts) == 1:
return UpdateMergedTitleTemplate(
project=self.update.project,
url=shortlink(
self.update.url_for(_external=True, **self.tracking_tags('sms')),
shorter=True,
url=sms_shortlink(
self.update.url_for(_external=True, **self.tracking_tags('sms'))
),
)
return UpdateSplitTitleTemplate(
project_title=self.update.project,
account_title=self.update.project.account,
url=shortlink(
self.update.url_for(_external=True, **self.tracking_tags('sms')),
shorter=True,
url=sms_shortlink(
self.update.url_for(_external=True, **self.tracking_tags('sms'))
),
)
13 changes: 8 additions & 5 deletions funnel/views/shortlink.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

from __future__ import annotations

from datetime import datetime, timedelta
from datetime import timedelta

from flask import abort, redirect

from coaster.utils import utcnow

from .. import app, shortlinkapp, unsubscribeapp
from ..models import Shortlink
from ..typing import Response
Expand All @@ -18,8 +20,9 @@ def shortlink_index() -> Response:
return redirect(app_url_for(app, 'index'), 301)


@shortlinkapp.route('/<name>')
def link(name: str) -> Response:
@shortlinkapp.route('/<header>/<name>') # 'header' is required for SMS DLT URLs
@shortlinkapp.route('/<name>', defaults={'header': None})
def link(name: str, header: str | None = None) -> Response: # noqa: ARG001
"""Redirect from a shortlink to the full link."""
sl = Shortlink.get(name, True)
if sl is None:
Expand All @@ -29,10 +32,10 @@ def link(name: str) -> Response:
response = redirect(str(sl.url), 301)
response.cache_control.private = True
response.cache_control.max_age = 90
response.expires = datetime.utcnow() + timedelta(seconds=90)
response.expires = utcnow() + timedelta(seconds=90)

# These two borrowed from Bitly and TinyURL's response headers. They tell the
# browser to reproduce the HTTP Referer header that was sent to this endpoint, to
# browser to reproduce the HTTP `Referer` header that was sent to this endpoint, to
# send it again to the destination URL

# Needs Werkzeug >= 2.0.2
Expand Down
1 change: 1 addition & 0 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ FLASK_SMS_TWILIO_FROM=null

#: DLT registered entity id and template ids (required for SMS to Indian numbers)
FLASK_SMS_DLT_ENTITY_ID=null
FLASK_SMS_DLT_SHORTURL_HEADER=null
FLASK_SMS_DLT_TEMPLATE_IDS__web_otp_template=null
FLASK_SMS_DLT_TEMPLATE_IDS__project_starting_template=null
FLASK_SMS_DLT_TEMPLATE_IDS__project_starting_tomorrow_venue_template=null
Expand Down

0 comments on commit a8739b2

Please sign in to comment.