Skip to content

Instantly share code, notes, and snippets.

@MateusLeguir
Forked from riceo/deliver_sm.py
Created August 30, 2023 16:27
Show Gist options
  • Save MateusLeguir/7d46575036886a937c5dc72621213ae8 to your computer and use it in GitHub Desktop.
Save MateusLeguir/7d46575036886a937c5dc72621213ae8 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment