Naz is an async SMPP client.
Project description
naz
SMPP is a protocol designed for the transfer of short message data between External Short Messaging Entities(ESMEs), Routing Entities(REs) and Short Message Service Center(SMSC). - Wikipedia
Comprehensive documetion is available -> Documentation
Installation
pip install naz
Usage
1. As a library
import asyncio
import naz
loop = asyncio.get_event_loop()
broker = naz.broker.SimpleBroker(maxsize=1000)
cli = naz.Client(
smsc_host="127.0.0.1",
smsc_port=2775,
system_id="smppclient1",
password="password",
broker=broker,
)
# queue messages to send
for i in range(0, 4):
print("submit_sm round:", i)
msg = naz.protocol.SubmitSM(
short_message="Hello World-{0}".format(str(i)),
log_id="myid12345",
source_addr="254722111111",
destination_addr="254722999999",
)
loop.run_until_complete(
cli.send_message(msg)
)
try:
# 1. connect to the SMSC host
# 2. bind to the SMSC host
# 3. send any queued messages to SMSC
# 4. read any data from SMSC
# 5. continually check the state of the SMSC
tasks = asyncio.gather(
cli.connect(),
cli.tranceiver_bind(),
cli.dequeue_messages(),
cli.receive_data(),
cli.enquire_link(),
)
loop.run_until_complete(tasks)
except Exception as e:
print("exception occured. error={0}".format(str(e)))
finally:
loop.run_until_complete(cli.unbind())
loop.stop()
2. As a cli app
import naz
from myfile import ExampleBroker
client = naz.Client(
smsc_host="127.0.0.1",
smsc_port=2775,
system_id="smppclient1",
password="password",
broker=ExampleBroker()
)
and a python file, myfile.py (in the current working directory) with the contents:
import asyncio
import naz
class ExampleBroker(naz.broker.BaseBroker):
def __init__(self):
loop = asyncio.get_event_loop()
self.queue = asyncio.Queue(maxsize=1000, loop=loop)
async def enqueue(self, message):
self.queue.put_nowait(message)
async def dequeue(self):
return await self.queue.get()
Naz: the SMPP client.
{'event': 'naz.Client.connect', 'stage': 'start', 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
{'event': 'naz.Client.connect', 'stage': 'end', 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
{'event': 'naz.Client.tranceiver_bind', 'stage': 'start', 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
{'event': 'naz.Client.send_data', 'stage': 'start', 'smpp_command': 'bind_transceiver', 'log_id': None, 'msg': 'hello', 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
{'event': 'naz.SimpleHook.to_smsc', 'stage': 'start', 'smpp_command': 'bind_transceiver', 'log_id': None, 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
{'event': 'naz.Client.send_data', 'stage': 'end', 'smpp_command': 'bind_transceiver', 'log_id': None, 'msg': 'hello', 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
{'event': 'naz.Client.tranceiver_bind', 'stage': 'end', 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
{'event': 'naz.Client.dequeue_messages', 'stage': 'start', 'environment': 'production', 'release': 'canary', 'smsc_host': '127.0.0.1', 'system_id': 'smppclient1', 'client_id': '2VU55VT86KHWXTW7X'}
To see help:
naz-cli --help
naz is an async SMPP client.
example usage: naz-cli --client path.to.my_config.client
optional arguments:
-h, --help show this help message and exit
--version The currently installed naz version.
--client CLIENT The config file to use. eg: --client path.to.my_config.client
Features
1. async everywhere
import naz
import asyncio
loop = asyncio.get_event_loop()
broker = naz.broker.SimpleBroker(maxsize=1000)
cli = naz.Client(
smsc_host="127.0.0.1",
smsc_port=2775,
system_id="smppclient1",
password="password",
broker=broker,
)
2. monitoring and observability
it’s a loaded term, I know.
2.1 logging
import naz
logger = naz.log.SimpleLogger(
"naz.client",
log_metadata={ "environment": "production", "release": "v5.6.8"}
)
cli = naz.Client(
...
logger=logger,
)
2.2 hooks
import naz
from prometheus_client import Counter
class MyPrometheusHook(naz.hooks.BaseHook):
async def to_smsc(self, smpp_command, log_id, hook_metadata, pdu):
c = Counter('my_requests', 'Description of counter')
c.inc() # Increment by 1
async def from_smsc(self,
smpp_command,
log_id,
hook_metadata,
status,
pdu):
c = Counter('my_responses', 'Description of counter')
c.inc() # Increment by 1
myHook = MyPrometheusHook()
cli = naz.Client(
...
hook=myHook,
)
another example is if you want to update a database record whenever you get a delivery notification event;
import sqlite3
import naz
class SetMessageStateHook(naz.hooks.BaseHook):
async def to_smsc(self, smpp_command, log_id, hook_metadata, pdu):
pass
async def from_smsc(self,
smpp_command,
log_id,
hook_metadata,
status,
pdu):
if smpp_command == naz.SmppCommand.DELIVER_SM:
conn = sqlite3.connect('mySmsDB.db')
c = conn.cursor()
t = (log_id,)
# watch out for SQL injections!!
c.execute("UPDATE SmsTable SET State='delivered' WHERE CorrelatinID=?", t)
conn.commit()
conn.close()
stateHook = SetMessageStateHook()
cli = naz.Client(
...
hook=stateHook,
)
2.3 integration with bug trackers
import naz
from myfile import ExampleBroker
import sentry_sdk # import sentry SDK
sentry_sdk.init("https://<YOUR_SENTRY_PUBLIC_KEY>@sentry.io/<YOUR_SENTRY_PROJECT_ID>")
my_naz_client = naz.Client(
smsc_host="127.0.0.1",
smsc_port=2775,
system_id="smppclient1",
password="password",
broker=ExampleBroker()
)
3. Rate limiting
import naz
myLimiter = naz.ratelimiter.SimpleRateLimiter(send_rate=35)
cli = naz.Client(
...
rate_limiter=myLimiter,
)
4. Throttle handling
import naz
throttler = naz.throttle.SimpleThrottleHandler(sampling_period=180,
sample_size=45,
deny_request_at=1.2)
cli = naz.Client(
...
throttle_handler=throttler,
)
5. Broker
import asyncio
import naz
loop = asyncio.get_event_loop()
my_broker = naz.broker.SimpleBroker(maxsize=1000,) # can hold upto 1000 items
cli = naz.Client(
...
broker=my_broker,
)
try:
# 1. connect to the SMSC host
# 2. bind to the SMSC host
# 3. send any queued messages to SMSC
# 4. read any data from SMSC
# 5. continually check the state of the SMSC
tasks = asyncio.gather(
cli.connect(),
cli.tranceiver_bind(),
cli.dequeue_messages(),
cli.receive_data(),
cli.enquire_link(),
)
loop.run_until_complete(tasks)
except Exception as e:
print("exception occured. error={0}".format(str(e)))
finally:
loop.run_until_complete(cli.unbind())
loop.stop()
then in your application, queue items to the queue;
# queue messages to send
for i in range(0, 4):
msg = naz.protocol.SubmitSM(
short_message="Hello World-{0}".format(str(i)),
log_id="myid12345",
source_addr="254722111111",
destination_addr="254722999999",
)
loop.run_until_complete(
cli.send_message(msg)
)
Here is another example, but where we now use redis for our broker;
import json
import asyncio
import naz
import aioredis
class RedisExampleBroker(naz.broker.BaseBroker):
"""
use redis as our broker.
This implements a basic FIFO queue using redis.
Basically we use the redis command LPUSH to push messages onto the queue and BRPOP to pull them off.
https://redis.io/commands/lpush
https://redis.io/commands/brpop
You should use a non-blocking redis client eg https://github.com/aio-libs/aioredis
"""
def __init__(self):
self.queue_name = "myqueue"
async def enqueue(self, item):
_redis = await aioredis.create_redis_pool(address=("localhost", 6379))
await _redis.lpush(self.queue_name, json.dumps(item))
async def dequeue(self):
_redis = await aioredis.create_redis_pool(address=("localhost", 6379))
x = await _redis.brpop(self.queue_name)
dequed_item = json.loads(x[1].decode())
return dequed_item
loop = asyncio.get_event_loop()
broker = RedisExampleBroker()
cli = naz.Client(
smsc_host="127.0.0.1",
smsc_port=2775,
system_id="smppclient1",
password="password",
broker=broker,
)
try:
# 1. connect to the SMSC host
# 2. bind to the SMSC host
# 3. send any queued messages to SMSC
# 4. read any data from SMSC
# 5. continually check the state of the SMSC
tasks = asyncio.gather(
cli.connect(),
cli.tranceiver_bind(),
cli.dequeue_messages(),
cli.receive_data(),
cli.enquire_link(),
)
tasks = asyncio.gather(cli.dequeue_messages(), cli.receive_data(), cli.enquire_link())
loop.run_until_complete(tasks)
except Exception as e:
print("error={0}".format(str(e)))
finally:
loop.run_until_complete(cli.unbind())
loop.stop()
then queue on your application side;
# queue messages to send
for i in range(0, 5):
print("submit_sm round:", i)
msg = naz.protocol.SubmitSM(
short_message="Hello World-{0}".format(str(i)),
log_id="myid12345",
source_addr="254722111111",
destination_addr="254722999999",
)
loop.run_until_complete(
cli.send_message(msg)
)
6. Well written(if I have to say so myself):
Development setup
NB: I make no commitment of accepting your pull requests.
## TODO
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file naz-0.8.1.tar.gz
.
File metadata
- Download URL: naz-0.8.1.tar.gz
- Upload date:
- Size: 60.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.49.0 CPython/3.8.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 1d149da5f447654754684310e7d4d494944bb9230b8d8f05748ea1d4432ca628 |
|
MD5 | c72cd2170268ba075087424f188d5c5c |
|
BLAKE2b-256 | 9eb432d34d57cc496ce1e1fba518c64a3ba30f2fceb66e1d37bcd3598089c534 |
File details
Details for the file naz-0.8.1-py3-none-any.whl
.
File metadata
- Download URL: naz-0.8.1-py3-none-any.whl
- Upload date:
- Size: 58.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.2.0 pkginfo/1.5.0.1 requests/2.24.0 setuptools/41.2.0 requests-toolbelt/0.9.1 tqdm/4.49.0 CPython/3.8.2
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 9e0d93bfcb128d791482aceae67f42b8246f9db33bbb5d61223085d466e7afdf |
|
MD5 | b5e24fda72da187ad36859782c118ad9 |
|
BLAKE2b-256 | 0dc4d6a34c7e8f0bb5ef4619558d0076cc8b2353dbbffc805c96db113e63a4b5 |