Skip to content

Instantly share code, notes, and snippets.

@riceo
Forked from zspine/deliver_sm.py
Last active August 30, 2023 16:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save riceo/80de777ca9209c86037159a7d6e6f27a to your computer and use it in GitHub Desktop.
Save riceo/80de777ca9209c86037159a7d6e6f27a to your computer and use it in GitHub Desktop.
Fork of zspine/deliver_sm.py, updated to work with changes in Jasmin SMS since the original was created + fixes a rabbitmq bug. This script will take a submit_sm PDU, send an OK back to the originator whilst replaying it to jasmin as a deliver_sm, effectively converting a submit_sm to deliver_sm. Also will send back a DELIVRD DLR!
# V0.64
# Bump the above version when modifying this script / pushing to the CLI -
# it's a nice way to confirm that the right code is running on jasmin.
import pickle
import logging
import uuid
import pika
from datetime import datetime
from jasmin.managers.content import DeliverSmContent
from jasmin.routing.Routables import RoutableDeliverSm
from jasmin.routing.jasminApi import *
from jasmin.protocols.smpp.operations import DeliverSM
from jasmin.managers.content import DLRContentForSmpps
from smpp.pdu.pdu_types import RegisteredDeliveryReceipt, RegisteredDelivery
from smpp.pdu.pdu_types import (EsmClass, EsmClassMode, EsmClassType, EsmClassGsmFeatures,
MoreMessagesToSend, MessageState, AddrTon, AddrNpi)
RABBITMQ_URL = 'amqp://guest:guest@rabbitmq:5672/%2F'
logger = logging.getLogger('logging-example')
if len(logger.handlers) != 1:
hdlr = logging.FileHandler('/var/log/jasmin/submit2deliver.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.DEBUG)
logger.debug("Received a routable from user %s", routable.user.uid)
submit_sm = routable.pdu
# The [rough] time we received the original submit_sm. Used to send back in the
# DLR
sub_date = datetime.now()
logger.debug("Got submit_sm: %s", submit_sm)
deliver_sm = DeliverSM(
submit_sm.seqNum,
service_type=submit_sm.params['service_type'],
source_addr_ton=submit_sm.params['source_addr_ton'],
source_addr_npi=submit_sm.params['source_addr_npi'],
source_addr=submit_sm.params['source_addr'],
dest_addr_ton=submit_sm.params['dest_addr_ton'],
dest_addr_npi=submit_sm.params['dest_addr_npi'],
destination_addr=submit_sm.params['destination_addr'],
esm_class=submit_sm.params['esm_class'],
protocol_id=submit_sm.params['protocol_id'],
priority_flag=submit_sm.params['priority_flag'],
registered_delivery=submit_sm.params['registered_delivery'],
replace_if_present_flag=submit_sm.params['replace_if_present_flag'],
data_coding=submit_sm.params['data_coding'],
short_message=submit_sm.params['short_message'],
sm_default_msg_id=submit_sm.params['sm_default_msg_id'])
logger.debug("Prepared a new deliver_sm: %s", deliver_sm)
# Generate a new routable deliver_sm PDU for injection
_routable = RoutableDeliverSm(deliver_sm, Connector(routable.user.uid))
content = DeliverSmContent(_routable, routable.user.uid, pickleProtocol=pickle.HIGHEST_PROTOCOL)
routing_key = 'deliver.sm.%s' % routable.user.uid
connection = pika.BlockingConnection(pika.URLParameters(RABBITMQ_URL))
channel = connection.channel()
# Inject the deliver_sm version of the submit_sm into rabbit
logger.debug('RabbitMQ channel ready, publishing now msgid %s ...', content.properties['message-id'])
channel.basic_publish(
'messaging',
routing_key,
content.body,
pika.BasicProperties(
message_id=content.properties['message-id'],
headers=content.properties['headers'])
)
logger.debug('Published deliver_sm to %s', routing_key)
# Setting the following status' will stop the Jasmin from trying to route this routable
logger.debug("Returning smpp_status %s http_status %s", smpp_status, http_status)
smpp_status = 0
http_status = 200
extra['message_id'] = str(uuid.uuid4())
# Start DLR hacking - lifted from protocols/smpp/operations.py #232 getReceipt()
# Also see https://smpp.org/SMPP_v3_4_Issue1_2.pdf, `4.6.1 “DELIVER_SM” Syntax`.
# We are crafting a `deliver_sm` PDU here that meets the above specification for a DLR
# with a `DELIVRD` status
def get_enum(enum_type, value):
# not very pythonic, but imported here as I think there's some
# twisted async magic going on when importing this outside of the function
# resulting in the import not being available here.
from enum import Enum
if isinstance(value, Enum):
return value
_value = value.split('.')
if len(_value) == 2:
return getattr(enum_type, _value[1])
else:
return getattr(enum_type, value)
short_message = r"id:%s submit date:%s done date:%s stat:%s err:%s" % (
extra['message_id'],
sub_date.strftime("%y%m%d%H%M"),
datetime.now().strftime("%y%m%d%H%M"),
"DELIVRD",
0,
)
dlr_deliver_sm = DeliverSM(
source_addr=submit_sm.params['destination_addr'],
destination_addr=submit_sm.params['source_addr'],
esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.SMSC_DELIVERY_RECEIPT),
receipted_message_id=extra['message_id'],
short_message=short_message,
message_state=MessageState.DELIVERED,
source_addr_ton=get_enum(AddrTon, submit_sm.params['dest_addr_ton']),
source_addr_npi=get_enum(AddrNpi, submit_sm.params['dest_addr_npi']),
dest_addr_ton=get_enum(AddrTon, submit_sm.params['source_addr_ton']),
dest_addr_npi=get_enum(AddrNpi, submit_sm.params['source_addr_npi']),
)
# Prepare for deliver_sm injection
dlr_routable = RoutableDeliverSm(dlr_deliver_sm, Connector(routable.user.uid))
# Use this to build your MORouter filter on.
dlr_routable.addTag(666)
dlr_content = DeliverSmContent(dlr_routable, routable.user.uid, pickleProtocol=pickle.HIGHEST_PROTOCOL)
routing_key = 'deliver.sm.%s' % routable.user.uid
channel.basic_publish(
'messaging',
routing_key,
dlr_content.body,
pika.BasicProperties(
message_id=dlr_content.properties['message-id'],
headers=dlr_content.properties['headers']))
logger.debug('Published DLR deliver_sm to %s', routing_key)
connection.close()
@MateusLeguir
Copy link

MateusLeguir commented Aug 30, 2023

Hello, I'm testing the script that you were able to modify and run, but I don't know if I'm doing everything right in the configuration. In this case, I configure it as MTInterceptor and create a tag in MORouter. Do I need to configure anything beyond that or modify something in the script?
In this case, is the mtinterceptor type StaticMTInterceptor with tag filter with order 1, 10 , 100? And how is the order, since jasmin seems to only execute the DefaultInterceptor with order 0?

Best regards

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment