Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

naz

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

naz

Naz is an async SMPP client.

  • 0.8.1
  • PyPI
  • Socket score

Maintainers
1

naz

|Codacy Badge| |ci| |codecov| |Code style: black|

| naz is an async SMPP client. | It’s name is derived from Kenyan hip hop artiste, Nazizi.

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 <https://en.wikipedia.org/wiki/Short_Message_Peer-to-Peer>__

| naz currently only supports SMPP version 3.4. | naz has no third-party dependencies and it requires python version 3.7+

| naz is in active development and it’s API may change in backward incompatible ways. | https://pypi.python.org/pypi/naz

Comprehensive documetion is available -> Documentation <https://komuw.github.io/naz>__

| Contents: | Installation <#installation>__ | Usage <#usage>__ | + As a library <#1-as-a-library>__ | + As cli app <#2-as-a-cli-app>__

| Features <#features>__ | + async everywhere <#1-async-everywhere>__ | + monitoring-and-observability <#2-monitoring-and-observability>__ | + logging <#21-logging>__ | + hooks <#22-hooks>__ + integration with bug trackers(eg Sentry ) <#23-integration-with-bug-trackers>__ + Rate limiting <#3-rate-limiting>__ | + Throttle handling <#4-throttle-handling>__ | + Broker <#5-broker>__

Benchmarks <./benchmarks/README.md>__

Installation

.. code:: shell

pip install naz

Usage

  1. As a library ^^^^^^^^^^^^^^^

.. code:: python

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()

| NB: | (a) For more information about all the parameters that naz.Client can take, consult the documentation here <https://komuw.github.io/naz/client.html>__ | (b) More examples can be found here <https://github.com/komuw/naz/tree/master/examples>__ | (c) if you need a SMSC server/gateway to test with, you can use the docker-compose file in this repo <https://github.com/komuw/naz/blob/master/docker-compose.yml>__ to bring up an SMSC simulator. | That docker-compose file also has a redis and rabbitMQ container if you would like to use those as your broker.

  1. As a cli app ^^^^^^^^^^^^^^^

| naz also ships with a commandline interface app called naz-cli. | create a python config file, eg; | /tmp/my_config.py

.. code:: python

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:

.. code:: python

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()

| then run: | naz-cli --client tmp.my_config.client

.. code:: shell

    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'}

| NB: | (a) The naz config file(ie, the dotted path we pass in to naz-cli --client) is any python file that has a naz.Client instance <https://komuw.github.io/naz/client.html>\ _ declared in it. | (b) More examples can be found here <https://github.com/komuw/naz/tree/master/examples>__. As an example, start the SMSC simulator(docker-compose up) then in another terminal run, naz-cli --client examples.example_config.client

To see help:

naz-cli --help

.. code:: shell

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 ^^^^^^^^^^^^^^^^^^^

| SMPP is an async protocol; the client can send a request and only get a response from SMSC/server 20mins later out of band. | It thus makes sense to write your SMPP client in an async manner. We leverage python3’s async/await to do so.

.. code:: python

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, )

  1. monitoring and observability ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

it’s a loaded term, I know.

2.1 logging '''''''''''

| In naz you have the ability to annotate all the log events that naz will generate with anything you want. | So, for example if you wanted to annotate all log-events with a release version and your app’s running environment.

.. code:: python

import naz

logger = naz.log.SimpleLogger( "naz.client", log_metadata={ "environment": "production", "release": "v5.6.8"} ) cli = naz.Client( ... logger=logger, )

| and then these will show up in all log events. | by default, naz annotates all log events with smsc_host, system_id and client_id

2.2 hooks '''''''''

| a hook is a class with two methods to_smsc and from_smsc, ie it implements naz\ ’s BaseHook interface as defined here <https://github.com/komuw/naz/blob/master/naz/hooks.py>. | naz will call the to_smsc method just before sending data to SMSC and also call the from_smsc method just after getting data from SMSC. | the default hook that naz uses is naz.hooks.SimpleHook which does nothing but logs. | If you wanted, for example to keep metrics of all requests and responses to SMSC in your prometheus <https://prometheus.io/> setup;

.. code:: python

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;

.. code:: python

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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

| If you want to integrate naz with your bug/issue tracker of choice, all you have to do is use their logging integrator. | As an example, to integrate naz with sentry <https://sentry.io/>__, all you have to do is import and init the sentry sdk. A good place to do that would be in the naz config file, ie; | /tmp/my_config.py

.. code:: python

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() )

| then run the naz-cli as usual: | naz-cli --client tmp.my_config.my_naz_client | And just like that you are good to go. This is what errors from naz will look like on sentry(sans the emojis, ofcourse):

.. figure:: https://raw.githubusercontent.com/komuw/naz/master/documentation/sphinx-docs/naz-sentry.png :alt: naz integration with sentry

naz integration with sentry

  1. Rate limiting ^^^^^^^^^^^^^^^^

| Sometimes you want to control the rate at which the client sends requests to an SMSC/server. naz lets you do this, by allowing you to specify a custom rate limiter. By default, naz uses a simple token bucket rate limiting algorithm implemented here <https://github.com/komuw/naz/blob/master/naz/ratelimiter.py>. | You can customize naz\ ’s ratelimiter or even write your own ratelimiter (if you decide to write your own, you just have to satisfy the BaseRateLimiter interface found here <https://github.com/komuw/naz/blob/master/naz/ratelimiter.py> ) | To customize the default ratelimiter, for example to send at a rate of 35 requests per second.

.. code:: python

import naz

myLimiter = naz.ratelimiter.SimpleRateLimiter(send_rate=35) cli = naz.Client( ... rate_limiter=myLimiter, )

  1. Throttle handling ^^^^^^^^^^^^^^^^^^^^

| Sometimes, when a client sends requests to an SMSC/server, the SMSC may reply with an ESME_RTHROTTLED status. | This can happen, say if the client has surpassed the rate at which it is supposed to send requests at, or the SMSC is under load or for whatever reason ¯(ツ)/¯ | The way naz handles throtlling is via Throttle handlers. | A throttle handler is a class that implements the BaseThrottleHandler interface as defined here <https://github.com/komuw/naz/blob/master/naz/throttle.py>__ | naz calls that class’s throttled method everytime it gets a throttled(ESME_RTHROTTLED) response from the SMSC and it also calls that class’s not_throttled method everytime it gets a response from the SMSC and the response is NOT a throttled response. | naz will also call that class’s allow_request method just before sending a request to SMSC. the allow_request method should return True if requests should be allowed to SMSC else it should return False if requests should not be sent. | By default naz uses ```naz.throttle.SimpleThrottleHandler<https://github.com/komuw/naz/blob/master/naz/throttle.py>`__ to handle throttling. | The waySimpleThrottleHandler`` works is, it calculates the percentage of responses that are throttle responses and then denies outgoing requests(towards SMSC) if percentage of responses that are throttles goes above a certain metric. | As an example if you want to deny outgoing requests if the percentage of throttles is above 1.2% over a period of 180 seconds and the total number of responses from SMSC is greater than 45, then;

.. code:: python

import naz

throttler = naz.throttle.SimpleThrottleHandler(sampling_period=180, sample_size=45, deny_request_at=1.2) cli = naz.Client( ... throttle_handler=throttler, )

  1. Broker ^^^^^^^^^

| How does your application and naz talk with each other? | It’s via a broker interface. Your application queues messages to a broker, naz consumes from that broker and then naz sends those messages to SMSC/server. | You can implement the broker mechanism any way you like, so long as it satisfies the BaseBroker interface as defined here <https://github.com/komuw/naz/blob/master/naz/broker.py>__ | Your application should call that class’s enqueue method to -you guessed it- enqueue messages to the queue while naz will call the class’s dequeue method to consume from the broker.

| naz ships with a simple broker implementation called ```naz.broker.SimpleBroker`` https://github.com/komuw/naz/blob/master/naz/broker.py`__. | An example of using that;

.. code:: python

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;

.. code:: python

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;

.. code:: python

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;

.. code:: python

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) )

  1. Well written(if I have to say so myself): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  • Good test coverage <https://codecov.io/gh/komuw/naz>__
  • Passing continous integration <https://github.com/komuw/naz/actions>__
  • statically analyzed code <https://www.codacy.com/app/komuw/naz/dashboard>__

Development setup

  • see documentation on contributing <https://github.com/komuw/naz/blob/master/.github/CONTRIBUTING.md>__
  • NB: I make no commitment of accepting your pull requests.

TODO


.. |Codacy Badge| image:: https://api.codacy.com/project/badge/Grade/616e5c6664dd4c1abb26f34f0bf566ae :target: https://www.codacy.com/app/komuw/naz .. |ci| image:: https://github.com/komuw/naz/workflows/naz%20ci/badge.svg :target: https://github.com/komuw/naz/actions .. |codecov| image:: https://codecov.io/gh/komuw/naz/branch/master/graph/badge.svg :target: https://codecov.io/gh/komuw/naz .. |Code style: black| image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/komuw/naz

Keywords

FAQs


Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc