-
-
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!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# V0.75 | |
# 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 | |
import time | |
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) | |
import multiprocessing | |
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 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(content.properties['message-id']) | |
# 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 sub:%03d dlvrd:%03d submit date:%s done date:%s stat:%s err:%s text: " % ( | |
extra['message_id'], | |
1, # submitted message parts | |
1, # delivered message parts | |
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) | |
logger.debug("Prepared a new DLR deliver_sm: %s", dlr_deliver_sm) | |
### Async this part | |
def raise_dlr(content): | |
import time | |
import pika | |
time.sleep(20) | |
RABBITMQ_URL = 'amqp://guest:guest@rabbitmq:5672/%2F' | |
connection = pika.BlockingConnection(pika.URLParameters(RABBITMQ_URL)) | |
channel = connection.channel() | |
routing_key = 'deliver.sm.%s' % content[1].user.uid | |
channel.basic_publish( | |
'messaging', | |
routing_key, | |
content[0].body, | |
pika.BasicProperties( | |
message_id=content[0].properties['message-id'], | |
headers=content[0].properties['headers'])) | |
print("published") | |
connection.close() | |
p = multiprocessing.Process(target=raise_dlr, args=([dlr_content, routable],), daemon=True) | |
p.start() | |
connection.close() |
Hello @riceo ,
It is working great. The only drawback i can see is that they DLR´s conten is in bytes type and it is not undestand by the platform i am testing with. I have tried to decode but no luck. I can see that is the default behavior for jasmin´s DLR. Any suggestion to change to string.
Hi @mrsanvicente,
I’m really glad to hear the code is working well for you! I always want to help, but detailed deployment questions can take quite a bit of time to work through.
If you’d like one-on-one support for your setup, you can book a 30-minute consulting session here: https://cal.com/riceo/30min.
Thanks for understanding, and I look forward to helping however I can!
Hello Aaron,Ok , ok sure we can go that way.It will help me alot.But first, I think this is direct point. Can the delivery_sm shortMessage can go clear text? Because it is code base 64 ( I undestand rabbitmq needs toBe that way) , but just on smpp on the delivery send a clear text. Because all platforms notUnderstanding the formart.SaludosMario San VicenteOn 29 Apr 2025, at 1:04 p.m., Aaron Rice ***@***.***> wrote:Re: ***@***.*** commented on this gist.Hi @mrsanvicente,I’m really glad to hear the code is working well for you! I always want to help, but detailed deployment questions can take quite a bit of time to work through.If you’d like one-on-one support for your setup, you can book a 30-minute consulting session here: https://cal.com/riceo/30min.Thanks for understanding, and I look forward to helping however I can!—Reply to this email directly, view it on GitHub or unsubscribe.You are receiving this email because you were mentioned.Triage notifications on the go with GitHub Mobile for iOS or Android.
Please confirm if it is doable. So I scheduled the meeting. Can the delivery_sm shortMessage can go clear text? Because it is code base 64 ( I undestand rabbitmq needs toBe that way) , but just on smpp on the delivery send a clear text. Because all platforms notUnderstanding the formart.SaludosMario San VicenteSaludosMario San VicenteOn 29 Apr 2025, at 4:41 p.m., mrsanvicente ***@***.***> wrote:Hello Aaron,Ok , ok sure we can go that way.It will help me alot.But first, I think this is direct point. Can the delivery_sm shortMessage can go clear text? Because it is code base 64 ( I undestand rabbitmq needs toBe that way) , but just on smpp on the delivery send a clear text. Because all platforms notUnderstanding the formart.SaludosMario San VicenteOn 29 Apr 2025, at 1:04 p.m., Aaron Rice ***@***.***> wrote:Re: ***@***.*** commented on this gist.Hi @mrsanvicente,I’m really glad to hear the code is working well for you! I always want to help, but detailed deployment questions can take quite a bit of time to work through.If you’d like one-on-one support for your setup, you can book a 30-minute consulting session here: https://cal.com/riceo/30min.Thanks for understanding, and I look forward to helping however I can!—Reply to this email directly, view it on GitHub or unsubscribe.You are receiving this email because you were mentioned.Triage notifications on the go with GitHub Mobile for iOS or Android.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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