# djmessenger package

## Subpackages

- djmessenger.management package
  - Subpackages
    - djmessenger.management.commands package
      - Submodules
      - djmessenger.management.commands.configure_get_started_button module
      - djmessenger.management.commands.configure_greetings module
      - djmessenger.management.commands.configure_persistent_menu module
      - djmessenger.management.commands.get_random_endpoint module
      - Module contents
  - Module contents
- djmessenger.utils package
  - Submodules
  - djmessenger.utils.decorators module
  - djmessenger.utils.default_routing_policy module
  - djmessenger.utils.filelock module
  - djmessenger.utils.geocoding module
  - djmessenger.utils.serializable module
  - djmessenger.utils.utils module
  - Module contents

## Submodules

## djmessenger.admin module

## djmessenger.apps module

class `djmessenger.apps.``DjmessengerConfig`(app_name, app_module)

  Bases: `django.apps.config.AppConfig`

  `name` = 'djmessenger'

## djmessenger.exceptions module

exception `djmessenger.exceptions.``DJMException`

  Bases: `Exception`

  Base Exception for DJM

exception `djmessenger.exceptions.``DJMInvalidConfigException`

  Bases: `djmessenger.exceptions.DJMException`

  This exception will be raised if user tried to do something that breaks the assumption made by Facebbok. For example, threading setting greeting text can have only one, if the user tried to add more, raise this exception

exception `djmessenger.exceptions.``DJMInvalidValueException`

  Bases: `djmessenger.exceptions.DJMException`

## djmessenger.facebook_locales module

An enum to represent locales that Facebook supports

class `djmessenger.facebook_locales.``FacebookLocale`

  Bases: `enum.Enum`

## djmessenger.filtering module

### Filtering Module

Filtering module defines a `BaseFilter` as the most basic abstract class and all its subclasses provide some specific logic to filter a given `djmessenger.receiving.Messaging` object

class `djmessenger.filtering.``AuthenticationFilter`

  Bases: `djmessenger.filtering.BaseFilter`

  `should_pass`(messaging)

class `djmessenger.filtering.``BaseFilter`

  Bases: `djmessenger.utils.serializable.Serializable`

  BaseFilter is an ABC for filters, it defines an abstract method which takes a Messaging object and returns a bool to indicate whether this Messaging should be further processed

  `should_pass`(messaging)

    Takes a `djmessenger.receiving.Messaging` object and determines whether this object should pass this filter and continue

    Parameters: | messaging (djmessenger.receiving.Messaging) – A Messaging instance to filter against
    Returns: | True if the given messaging passed this filter and should continue with the next operation; False otherwise
    Return type: | bool

class `djmessenger.filtering.``EmailFilter`

  Bases: `djmessenger.filtering.BaseFilter`

  Filter that the text sent by user is a valid email address

  `should_pass`(messaging)

class `djmessenger.filtering.``LocationFilter`

  Bases: `djmessenger.filtering.BaseFilter`

  Pass if messaging is a Location

  `should_pass`(messaging)

class `djmessenger.filtering.``MultimediaFilter`

  Bases: `djmessenger.filtering.BaseFilter`

  Pass if messaging is multimedia

  `should_pass`(messaging)

class `djmessenger.filtering.``PostbackFilter`

  Bases: `djmessenger.filtering.BaseFilter`

  Pass if messaging is a POSTBACK

  `should_pass`(messaging)

class `djmessenger.filtering.``SimpleTextRegexFilter`(regex=None)

  Bases: `djmessenger.filtering.BaseFilter`

  Pass if messaging has a text that matches the regex

  An example in `djmessenger.utils.default_routing_policy`

      {
          "name": "djmessenger.filtering.SimpleTextRegexFilter",
          "args": {
              "regex": "^你好$"
          }
      }

  `should_pass`(messaging)

class `djmessenger.filtering.``StickerFilter`

  Bases: `djmessenger.filtering.BaseFilter`

  Pass if messaging is a STICKER and the id is within STICKER_IDS

  `STICKER_IDS` = ()
  `should_pass`(messaging)

class `djmessenger.filtering.``ThumbUpFilter`

  Bases: `djmessenger.filtering.StickerFilter`

  `STICKER_IDS` = ('810', '814', '822')

class `djmessenger.filtering.``TimeFilter`(start_time, end_time)

  Bases: `djmessenger.filtering.BaseFilter`

  Filter that passes if the messaging timestamp is within the predefined time period

  An example in policy

      {
          "name": "djmessenger.filtering.TimeFilter",
          "args": {
              "start_time": "2016-1-1T00:00:00Z-0800",
              "end_time": "2017-1-1T01:00:00Z-0800"
          }
      }

  `FORMAT` = '%Y-%m-%dT%H:%M:%SZ%z'
  `should_pass`(messaging)

## djmessenger.handling module

### Handling Module

Handling module provides abstract class and some implementation for handling a `djmessenger.receiving.Messaging` object. Handling means to do internal operations against the `Messaging` object, eg. save something into database, and etc

#### BaseHandler

Defines handle(messaging) which need to be overridden by subclasses

In handle(), you just perform operations on the data provided by `messaging`

class `djmessenger.handling.``BaseHandler`

  Bases: `abc.ABC`

  BaseHandler is an abstract base class to

  Actually handle it by adding something to database, or other internal work

  `handle`(messaging)

    Actually handles the messaging

    Parameters: | messaging (djmessenger.receiving.Messaging) – A Messaging object to handle
    Returns: | nothing

class `djmessenger.handling.``BasePayloadHandler`

  Bases: `djmessenger.handling.BaseHandler`

  Postback and QuickReply contains payload, and in order to support request chaining, we need to provide a default base class for Postback and QuickReply.

  Since the payload in Postback and QuickReply is merely a simple string limited to 1000 chars, we can utilize this space to send some bookkeeping info to achieve request chaining.

  We are going to make the payload (which is a plain text) looks like a valid json object so that we can deserialize it back to a dict and then we can figure out which handler was the sender, then do corresponding actions

  `get_payload`(messaging)

    Payload class must be a subclass of djmessenger.utils.payload.Payload, and the class defines an attribute payload_class, we can simply load the class from this fully qualified class name

    Returns: | an instance of the subclass of Payload
    Return type: | an instance of the subclass of Payload

  `get_payload_string_from_messaging`(messaging)

    Returns payload contained in `djmessenger.receiving.Messaging`

    Returns: | the corresponding payload string from Messaging
    Return type: | str

class `djmessenger.handling.``BasePostbackHandler`

  Bases: `djmessenger.handling.BasePayloadHandler`

  A base class to be extended by subclass to handle custom Postback Payload

  `get_payload_string_from_messaging`(messaging)

    Returns: | The payload portion from Messaging
    Return type: | str

  `handle`(messaging)

class `djmessenger.handling.``BaseQuickReplyHandler`

  Bases: `djmessenger.handling.BasePayloadHandler`

  A base class to be extended by subclasses to handle custom QuickReply payload

  `get_payload_string_from_messaging`(messaging)
  `get_text`(messaging)

    quick reply will come with text which is the quick reply title

    Returns: |

  `handle`(messaging)

class `djmessenger.handling.``LocationHandler`

  Bases: `djmessenger.handling.BaseHandler`

  If the user sends a location to the BOT (by click on the map pin icon next to thumb up), this handler saves this coordinates to the database

  `handle`(messaging)

class `djmessenger.handling.``SaveMessagingHandler`

  Bases: `djmessenger.handling.BaseHandler`

  This handler saves the messaging into the database for further reference

  `handle`(messaging)

class `djmessenger.handling.``ThumbUpHandler`

  Bases: `djmessenger.handling.BaseHandler`

  Handles when the user sends a thumb up

  `handle`(messaging)

class `djmessenger.handling.``UserProfileHandler`

  Bases: `djmessenger.handling.BaseHandler`

  Every messaging has sender.id, based on settings.DJM_SAVE_USER_PROFILE, this handler save user profile into database using models.FBUserProfile.

  If DJM_SAVE_USER_PROFILE was True, we fetch user profile using graph API; otherwise we only save user psid to the database

  `handle`(messaging)

## djmessenger.models module

### Django model definition

class `djmessenger.models.``FBUserProfile`(*args, **kwargs)

  Bases: `django.db.models.base.Model`

  This FBUserProfile class is to represent a user that sent a message to us via Facebook Messenger, from the message relayed by Facebook, we can look up user details which will be stored here

  exception `DoesNotExist`

    Bases: `django.core.exceptions.ObjectDoesNotExist`

  exception `FBUserProfile.``MultipleObjectsReturned`

    Bases: `django.core.exceptions.MultipleObjectsReturned`

  `FBUserProfile.``date_joined`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``first_name`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``gender`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``get_next_by_date_joined`(*moreargs, **morekwargs)
  `FBUserProfile.``get_previous_by_date_joined`(*moreargs, **morekwargs)
  `FBUserProfile.``is_active`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``last_name`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``locale`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``objects` = <django.db.models.manager.Manager object>
  `FBUserProfile.``profile_pic`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``psid`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``reply_set`

    Accessor to the related objects manager on the reverse side of a many-to-one relation.

    In the example:

        class Child(Model):
            parent = ForeignKey(Parent, related_name='children')

    `parent.children` is a `ReverseManyToOneDescriptor` instance.

    Most of the implementation is delegated to a dynamically defined manager class built by `create_forward_many_to_many_manager()` defined below.

  `FBUserProfile.``thumbups`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``timezone`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `FBUserProfile.``userlocation_set`

    Accessor to the related objects manager on the reverse side of a many-to-one relation.

    In the example:

        class Child(Model):
            parent = ForeignKey(Parent, related_name='children')

    `parent.children` is a `ReverseManyToOneDescriptor` instance.

    Most of the implementation is delegated to a dynamically defined manager class built by `create_forward_many_to_many_manager()` defined below.

class `djmessenger.models.``Messaging`(*args, **kwargs)

  Bases: `django.db.models.base.Model`

  A table to store `djmessenger.receiving.Messaging`

  exception `DoesNotExist`

    Bases: `django.core.exceptions.ObjectDoesNotExist`

  exception `Messaging.``MultipleObjectsReturned`

    Bases: `django.core.exceptions.MultipleObjectsReturned`

  `Messaging.``body`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Messaging.``id`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Messaging.``objects` = <django.db.models.manager.Manager object>
  `Messaging.``type`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

class `djmessenger.models.``Reply`(*args, **kwargs)

  Bases: `django.db.models.base.Model`

  A table to store all replies

  exception `DoesNotExist`

    Bases: `django.core.exceptions.ObjectDoesNotExist`

  exception `Reply.``MultipleObjectsReturned`

    Bases: `django.core.exceptions.MultipleObjectsReturned`

  `Reply.``data`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Reply.``date_sent`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Reply.``get_next_by_date_sent`(*moreargs, **morekwargs)
  `Reply.``get_previous_by_date_sent`(*moreargs, **morekwargs)
  `Reply.``id`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Reply.``objects` = <django.db.models.manager.Manager object>
  `Reply.``recipient`

    Accessor to the related object on the forward side of a many-to-one or one-to-one relation.

    In the example:

        class Child(Model):
            parent = ForeignKey(Parent, related_name='children')

    `child.parent` is a `ForwardManyToOneDescriptor` instance.

  `Reply.``recipient_id`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Reply.``response`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Reply.``status_code`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `Reply.``type`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

class `djmessenger.models.``UserLocation`(*args, **kwargs)

  Bases: `django.db.models.base.Model`

  From the message user sent via Facebook to us, addition to saving the raw request body into RawBody, we try to see if the sent message is actually a location that contains lat and long, if yes, then we save it here

  exception `DoesNotExist`

    Bases: `django.core.exceptions.ObjectDoesNotExist`

  exception `UserLocation.``MultipleObjectsReturned`

    Bases: `django.core.exceptions.MultipleObjectsReturned`

  `UserLocation.``date_created`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``get_next_by_date_created`(*moreargs, **morekwargs)
  `UserLocation.``get_previous_by_date_created`(*moreargs, **morekwargs)
  `UserLocation.``id`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``latitude`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``longitude`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``mid`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``objects` = <django.db.models.manager.Manager object>
  `UserLocation.``seq`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``timestamp`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``url`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

  `UserLocation.``user`

    Accessor to the related object on the forward side of a many-to-one or one-to-one relation.

    In the example:

        class Child(Model):
            parent = ForeignKey(Parent, related_name='children')

    `child.parent` is a `ForwardManyToOneDescriptor` instance.

  `UserLocation.``user_id`

    A wrapper for a deferred-loading field. When the value is read from this object the first time, the query is executed.

## djmessenger.payload module

### Payload

`Payload` object is being used in both handlers and repliers, it starts with you, as the BOT, replies the user buttons or quick replies, each of them may contain a payload.

`Payload` is represented in Facebook callback as a simple string, however, in order for BOT to better handle it, we are going to treat it as a valid json string.

A payload contains key-value pairs that are dependant to your business logic, as a result, you will need to write your own payload if you’d like to send buttons and replies.

`Payload` class provides a get_instance() classmethod that simply take a json string and try to deserialize it back to an instance of the actual subclass.

Here is a quick example, let’s say you want to display your products when the user sends you a simple text “products”, what you need to do is

1. Define routing policy

   > In your routing policy, specify `djmessenger.filtering.SimpleTextRegexFilter` and provide regex as “^products$” as a filter for SIMPLE_TEXT

2. Custom Replier

   > Write a custom replier which extends either `djmessenger.replying.BaseButtonReplier` or `djmessenger.replying.BaseQuickReplyReplier` and provide payload that contains your product info, something like

   > > Make sure you call super().__init__()

   >     class MyProductPayload(Payload):
   >         def __init__(self, name, price):
   >             super().__init__()
   >             self.name = name
   >             self.price = price

   > when you’re replying, this payload will be serialized into a single string and sent along with the template.

   > Please see `djmessenger.replying.BaseButtonReplier` and `djmessenger.replying.BaseQuickReplyReplier` for more details

   > The main difference between buttons and quick replies are:

   > 1. Buttons can only have 3 at a time
   > 2. Quick Replies can have 10 at a time
   > 3. When user clicks on Buttons, it generates a `djmessenger.receiving.Postback`
   > 4. When user clicks on quick replies, it generates a `djmessenger.receiving.QuickReply`

3. Custom handler

   > If you want to do something when user clicks on the buttons or quick replies (which you most likely would, otherwise why sent buttons, right), you need to implement a custom handler which extends either `djmessenger.handling.BaseQuickReplyHandler` or `djmessenger.handling.BasePostbackHandler`.

   > Both of them defines a method get_payload() which will return you an instance of MyProductPayload, at this point you know which user (PSID) clicks on which button (payload) and you can do something like saving to database

4. Replier

   > Then you can use built-in sender or custom sender again to send something back to the user

class `djmessenger.payload.``GetStartedPayload`

  Bases: `djmessenger.payload.Payload`

class `djmessenger.payload.``Payload`

  Bases: `djmessenger.utils.serializable.Serializable`, `abc.ABC`

  Your customized Payload subclass must extend this class and MAKE SURE that you call super().__init__ in your __init__()

  `PAYLOAD_CLASS_ATTRIBUTE` = 'payload_class'
  `get_class`()

    Returns: | the Payload subclass
    Return type: | class

  classmethod `get_instance`(json_data)

    This class method takes either a string in json format or a dict that represents a subclass of Payload. Since all payload subclass must subclass Payload and each of them has payload_class defined, we can easily determine what is the exact payload class and load the data and return an instance of that subclass

    Parameters: | json_data (str or dict) – string or dict that represents the subclass of Payload
    Returns: | an instance of the subclass of Payload
    Type: | subclass of Payload

    @raise ValueError: 1. json_data is empty
      2. json_data is neither str nor dict
      3. json_data load failed
      4. json_data load successfully but does not load as a dict
      5. loaded json_data but it doesn’t contain payload_class attribute
      6. unable to deserialize it back to the Payload subclass

    @raise ImportError: load_class failed @raise AttributeError: load_class failed

  `is_valid`()

    Facebook restricts that payload string can not exceed 1000 chars

    Returns: | True if the serialized string is less than or equal to 1000; False otherwise
    Return type: | bool

class `djmessenger.payload.``PersistentMenuFivePayload`

  Bases: `djmessenger.payload.Payload`

class `djmessenger.payload.``PersistentMenuFourPayload`

  Bases: `djmessenger.payload.Payload`

class `djmessenger.payload.``PersistentMenuOnePayload`

  Bases: `djmessenger.payload.Payload`

class `djmessenger.payload.``PersistentMenuThreePayload`

  Bases: `djmessenger.payload.Payload`

class `djmessenger.payload.``PersistentMenuTwoPayload`

  Bases: `djmessenger.payload.Payload`

## djmessenger.receiving module

# Receiving module

This module contains all classes that represent a Facebook callback. Basically this is just models to represent the json templates that Facebook would sent over as described here:

Facebook Webhook Reference

class `djmessenger.receiving.``AccountLinking`(status, authorization_code='')

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``AccountLinkingChecker`(messaging)

  Bases: `djmessenger.receiving.MessagingChecker`

  `check`()

class `djmessenger.receiving.``Attachment`(ttype, payload, url)

  Bases: `djmessenger.utils.serializable.Serializable`

  There are 2 types of Attachment payloads, LocationAttachmentPayload and MultimediaAttachmentPayload, so we are not providing _obj_map but need to override deserialize to look at self.type and determine which type of payload to deserialize to

  classmethod `deserialize`(json_data)

class `djmessenger.receiving.``AttachmentType`(name, description='')

  Bases: `djmessenger.utils.serializable.SerializableEnum`

  `AUDIO` = (AttachmentType) audio
  `FILE` = (AttachmentType) file
  `IMAGE` = (AttachmentType) image
  `LOCATION` = (AttachmentType) location
  `VIDEO` = (AttachmentType) video
  `members` = {'audio': (AttachmentType) audio, 'location': (AttachmentType) location, 'image': (AttachmentType) image, 'file': (AttachmentType) file, 'video': (AttachmentType) video}

class `djmessenger.receiving.``AuthenticationChecker`(messaging)

  Bases: `djmessenger.receiving.MessagingChecker`

  `check`()

class `djmessenger.receiving.``Callback`(object='page', entry=[])

  Bases: `djmessenger.utils.serializable.Serializable`

  https://developers.facebook.com/docs/messenger-platform/webhook-reference

  `custom_obj_map` = {'entry': [<class 'djmessenger.receiving.Entry'>, <class 'list'>]}
  classmethod `get_instance`(request)

    Parameters: | request (django.core.handlers.wsgi.WSGIRequest) – HTTP Request object
    Returns: | instance of Callback
    Return type: | Callback

  `get_sender_id`()

    Each callback must have some Messaging, we should expect that we can always get a sender id

    Returns: |

class `djmessenger.receiving.``Coordinates`(lat, llong)

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``Delivery`(watermark, seq, mids=[])

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``Entry`(iid, time, messaging=[])

  Bases: `djmessenger.utils.serializable.Serializable`

  https://developers.facebook.com/docs/messenger-platform/webhook-reference

  `custom_obj_map` = {'messaging': [<class 'djmessenger.receiving.Messaging'>, <class 'list'>]}

class `djmessenger.receiving.``LocationAttachmentPayload`(coordinates)

  Bases: `djmessenger.utils.serializable.Serializable`

  `custom_obj_map` = {'coordinates': [<class 'djmessenger.receiving.Coordinates'>, <class 'object'>]}

class `djmessenger.receiving.``Message`(text, mid, seq, sticker_id=None, quick_reply=None, attachments=[], is_echo=False, app_id='', metadata='')

  Bases: `djmessenger.utils.serializable.Serializable`

  Each Messaging could have 1 Message object

  `custom_obj_map` = {'attachments': [<class 'djmessenger.receiving.Attachment'>, <class 'list'>], 'quick_reply': [<class 'djmessenger.receiving.QuickReply'>, <class 'object'>]}

class `djmessenger.receiving.``MessageDeliveredChecker`(messaging)

  Bases: `djmessenger.receiving.MessagingChecker`

  `check`()

class `djmessenger.receiving.``MessageReadChecker`(messaging)

  Bases: `djmessenger.receiving.MessagingChecker`

  `check`()

class `djmessenger.receiving.``MessageReceivedChecker`(messaging)

  Bases: `djmessenger.receiving.MessagingChecker`

  This check handles all types in MessagedReceived and MessageEcho

  `check`()

class `djmessenger.receiving.``Messaging`(sender, recipient, timestamp=1478999839, message=None, postback=None, optin=None, account_linking=None, delivery=None, read=None)

  Bases: `djmessenger.utils.serializable.Serializable`

  Each Entry contains multiple Messaging object, Messaging has different CallbackType, we are going to define this class so that it includes all possible objects and then when we got a json data, we just load them into the objects. After we deserialized json data into Messaging, we then determine what type of a Callback this is by looking at some fields and values using CallbackTypeChecker

  `custom_obj_map` = {'message': [<class 'djmessenger.receiving.Message'>, <class 'object'>], 'delivery': [<class 'djmessenger.receiving.Delivery'>, <class 'object'>], 'optin': [<class 'djmessenger.receiving.Optin'>, <class 'object'>], 'sender': [<class 'djmessenger.receiving.Sender'>, <class 'object'>], 'read': [<class 'djmessenger.receiving.Read'>, <class 'object'>], 'postback': [<class 'djmessenger.receiving.Postback'>, <class 'object'>], 'recipient': [<class 'djmessenger.receiving.Recipient'>, <class 'object'>], 'account_linking': [<class 'djmessenger.receiving.AccountLinking'>, <class 'object'>]}
  `get_coordinates`()

    Returns: |
    Return type: | Coordinates

  `get_postback_payload`()

    Returns: |
    Return type: | str

  `get_psid`()
  `get_quick_reply_payload`()
  `get_receiving_type`()

    After loading the json data into this object, try to determine what type of callback this messaging is

    Returns: |
    Return type: | ReceivingType

  `get_sticker_id`()
  `get_text`()

    Returns: |
    Return type: | str

  `get_url`()

    Returns: |
    Return type: | str

  `get_watermark`()

class `djmessenger.receiving.``MessagingChecker`(messaging)

  Bases: `abc.ABC`

  MessagingChecker is to check a messaging that ReceivingType it should be

  `check`()

    Returns: | Corresponding CallbackType if it passed any check; None if nothing matches
    Return type: | ReceivingType

class `djmessenger.receiving.``MultimediaAttachmentPayload`(url)

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``Optin`(ref)

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``Postback`(payload)

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``PostbackReceivedChecker`(messaging)

  Bases: `djmessenger.receiving.MessagingChecker`

  `check`()

class `djmessenger.receiving.``QuickReply`(payload)

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``Read`(watermark, seq)

  Bases: `djmessenger.utils.serializable.Serializable`

class `djmessenger.receiving.``ReceivingType`(name, description='')

  Bases: `djmessenger.utils.serializable.SerializableEnum`

  ReceivingType defines all types that will be sent by Facebook

  `ACCOUNT_LINKING` = (ReceivingType) ACCOUNT_LINKING
  `AUDIO` = (ReceivingType) AUDIO
  `AUTHENTICATION` = (ReceivingType) AUTHENTICATION
  `DEFAULT` = (ReceivingType) DEFAULT
  `DELIVERED` = (ReceivingType) DELIVERED
  `ECHO` = (ReceivingType) ECHO
  `FILE` = (ReceivingType) FILE
  `GET_STARTED_BUTTON` = (ReceivingType) GET_STARTED_BUTTON
  `IMAGE` = (ReceivingType) IMAGE
  `LOCATION` = (ReceivingType) LOCATION
  `PERSISTENT_MENU_FIVE` = (ReceivingType) PERSISTENT_MENU_FIVE
  `PERSISTENT_MENU_FOUR` = (ReceivingType) PERSISTENT_MENU_FOUR
  `PERSISTENT_MENU_ONE` = (ReceivingType) PERSISTENT_MENU_ONE
  `PERSISTENT_MENU_THREE` = (ReceivingType) PERSISTENT_MENU_THREE
  `PERSISTENT_MENU_TWO` = (ReceivingType) PERSISTENT_MENU_TWO
  `POSTBACK` = (ReceivingType) POSTBACK
  `QUICK_REPLY` = (ReceivingType) QUICK_REPLY
  `READ` = (ReceivingType) READ
  `SIMPLE_TEXT` = (ReceivingType) SIMPLE_TEXT
  `STICKER` = (ReceivingType) STICKER
  `VIDEO` = (ReceivingType) VIDEO
  `members` = {'PERSISTENT_MENU_ONE': (ReceivingType) PERSISTENT_MENU_ONE, 'AUDIO': (ReceivingType) AUDIO, 'QUICK_REPLY': (ReceivingType) QUICK_REPLY, 'PERSISTENT_MENU_FIVE': (ReceivingType) PERSISTENT_MENU_FIVE, 'ECHO': (ReceivingType) ECHO, 'DEFAULT': (ReceivingType) DEFAULT, 'SIMPLE_TEXT': (ReceivingType) SIMPLE_TEXT, 'READ': (ReceivingType) READ, 'ACCOUNT_LINKING': (ReceivingType) ACCOUNT_LINKING, 'VIDEO': (ReceivingType) VIDEO, 'FILE': (ReceivingType) FILE, 'PERSISTENT_MENU_FOUR': (ReceivingType) PERSISTENT_MENU_FOUR, 'LOCATION': (ReceivingType) LOCATION, 'GET_STARTED_BUTTON': (ReceivingType) GET_STARTED_BUTTON, 'AUTHENTICATION': (ReceivingType) AUTHENTICATION, 'STICKER': (ReceivingType) STICKER, 'POSTBACK': (ReceivingType) POSTBACK, 'IMAGE': (ReceivingType) IMAGE, 'PERSISTENT_MENU_THREE': (ReceivingType) PERSISTENT_MENU_THREE, 'PERSISTENT_MENU_TWO': (ReceivingType) PERSISTENT_MENU_TWO, 'DELIVERED': (ReceivingType) DELIVERED}

class `djmessenger.receiving.``Recipient`(iid)

  Bases: `djmessenger.utils.serializable.Serializable`

  Your page

class `djmessenger.receiving.``Sender`(iid)

  Bases: `djmessenger.utils.serializable.Serializable`

  In Receiving messages, Sender represents the Facebook user who sent this message to your page

## djmessenger.replying module

### Replying Module

Similarly, this replying module is to represent the json templates that should be used when sending something back to the user based on Facebook reference:

https://developers.facebook.com/docs/messenger-platform/send-api-reference

class `djmessenger.replying.``AudioReplier`(url)

  Bases: `djmessenger.replying.MultimediaReplier`

  Reply an Audio from url

  `sending_type` = (ReplyingType) audio

class `djmessenger.replying.``BaseButtonReplier`(localized_string)

  Bases: `djmessenger.replying.SimpleMessageReplier`

  Facebook restricts to have 3 buttons at a time

  To reply a list of Button, you need to

  1. Define a subclass that extends `djmessenger.payload.Payload`, so that when user clicks on a button and another request calls back, we have a way to determine the origin by checking which subclass of Payload it has
  2. Define a subclass that extends `BaseButtonReplier`
  3. Define BUTTONS class variable like this

      class MyButtonReplier(BaseButtonReplier):
          BUTTONS = {
             'button 1': 'https://www.google.com',
             'button 2': MyButtonPayload('button payload 2')
          }

  The first button will open a new window that goes to the url; while the second button will trigger a postback request back which contains the payload

  `BUTTONS` = {}
  class `Button`(title, url='', payload=None)

    Bases: `djmessenger.utils.serializable.Serializable`

  `BaseButtonReplier.``LIMIT` = 3
  `BaseButtonReplier.``add_button`(title, url='', payload=None)
  classmethod `BaseButtonReplier.``deserialize`(json_data)
  `BaseButtonReplier.``get_message`(messaging)
  `BaseButtonReplier.``get_reply_type`()

class `djmessenger.replying.``BaseQuickReplyReplier`(localized_string)

  Bases: `djmessenger.replying.SimpleMessageReplier`

  Facebook restricts to have 10 quick replies at a time.

  To reply a list of Quick Replies, you need to

  1. Define a subclass that extends `djmessenger.payload.Payload`, so that when user clicks on a QR and another request calls back, we have a way to determine the origin by checking which subclass of Payload it has
  2. Define a subclass that extends `BaseQuickReplyReplier`
  3. Define QUICK_REPLIES class variable like this

      class MyQuickReplyReplier(BaseQuickReplyReplier):
          QUICK_REPLIES = {
             'qr 1': MyQuickReplyPayload('qr payload 1'),
             'qr 2': MyQuickReplyPayload('qr payload 2'),
             'qr 3': MyQuickReplyPayload('qr payload 3'),
          }

  `LIMIT` = 10
  `QUICK_REPLIES` = {}
  class `QuickReply`(title='', payload=None)

    Bases: `djmessenger.utils.serializable.Serializable`

  `BaseQuickReplyReplier.``add_quick_reply`(title, payload)

    An entry of quick reply button is represented like this as Facebook defined

        {
            "content_type":"text",
            "title":"Red",
            "payload":"DEVELOPER_DEFINED_PAYLOAD_FOR_PICKING_RED"
        },

    content_type must be “text”, so the arguments here are title and payload , payload must be a subclass of Payload

  classmethod `BaseQuickReplyReplier.``deserialize`(json_data)
  `BaseQuickReplyReplier.``get_message`(messaging)
  `BaseQuickReplyReplier.``get_reply_type`()

class `djmessenger.replying.``ButtonType`(name, description='')

  Bases: `djmessenger.utils.serializable.SerializableEnum`

  `POSTBACK` = (ButtonType) postback
  `WEB_URL` = (ButtonType) web_url
  `members` = {'web_url': (ButtonType) web_url, 'postback': (ButtonType) postback}

class `djmessenger.replying.``CommonReplier`

  Bases: `djmessenger.utils.serializable.Serializable`

  Base class for all repliers, 2 abstract methods need to be overridden

  `CommonReplier.get_message()` `CommonReplier.get_reply_type()`

  `get_message`(messaging)

    Based on the attributes the class constructor constructs, this method returns the message body, the returned must be a dict and valid json object.

    When construct the request json payload, if there is any text that
      could be i18n’ed, please make sure to use

    `CommonReplier.preprocess_outgoing_string()` to make sure the string is using correct locale

    Returns: |
    Return type: | dict

  `get_reply_type`()

    Return the SendingType that this Sender sends

    Returns: |
    Return type: | ReplyingType

  `notification_type` = (NotificationType) REGULAR
  static `preprocess_outgoing_string`(psid, localized_text, add_prefix=True)

    This method take cares of any preprocessing of the outgoing text. More specifically, this method converts the text into the user’s locale, if available.

    Parameters: | psid – localized_text (LocalizedString) – add_prefix (bool) – whether prepend DJM_BOT_PREFIX to the start of text, you might want to use it when processing button text
    Returns: | localized string
    Return type: | str

  `reply`(messaging)

    Ask the replier to reply to the psid from the given messaging

  `sender_action` = None

class `djmessenger.replying.``DefaultMessageReplier`

  Bases: `djmessenger.replying.SimpleMessageReplier`

  Sends back simple text DJM_DEFAULT_SENDER_TEXT

class `djmessenger.replying.``FileReplier`(url)

  Bases: `djmessenger.replying.MultimediaReplier`

  Reply a File from url

  `sending_type` = (ReplyingType) file

class `djmessenger.replying.``ImageReplier`(url)

  Bases: `djmessenger.replying.MultimediaReplier`

  Reply an Image from url

  `sending_type` = (ReplyingType) image

class `djmessenger.replying.``MultimediaReplier`(url)

  Bases: `djmessenger.replying.CommonReplier`

  Send back multimedia from a url

  ReplyingType.IMAGE = ReplyingType(‘IMAGE’) ReplyingType.AUDIO = ReplyingType(‘AUDIO’) ReplyingType.VIDEO = ReplyingType(‘VIDEO’) ReplyingType.FILE = ReplyingType(‘FILE’)

  `get_message`(messaging)
  `get_reply_type`()
  `sending_type` = None

class `djmessenger.replying.``NotificationType`(name, description='')

  Bases: `djmessenger.utils.serializable.SerializableEnum`

  `NO_PUSH` = (NotificationType) NO_PUSH
  `REGULAR` = (NotificationType) REGULAR
  `SILEN_PUSH` = (NotificationType) SILEN_PUSH
  `members` = {'NO_PUSH': (NotificationType) NO_PUSH, 'SILEN_PUSH': (NotificationType) SILEN_PUSH, 'REGULAR': (NotificationType) REGULAR}

class `djmessenger.replying.``ReplyAction`(name, description='')

  Bases: `djmessenger.utils.serializable.SerializableEnum`

  `MARK_SEEN` = (ReplyAction) mark_seen
  `TYPING_OFF` = (ReplyAction) typing_off
  `TYPING_ON` = (ReplyAction) typing_on
  `members` = {'typing_off': (ReplyAction) typing_off, 'typing_on': (ReplyAction) typing_on, 'mark_seen': (ReplyAction) mark_seen}

class `djmessenger.replying.``ReplyActionReplier`(action)

  Bases: `djmessenger.replying.CommonReplier`

  Send a ReplyAction

  `get_message`(messaging)
  `get_reply_type`()

class `djmessenger.replying.``ReplyingType`(name, description='')

  Bases: `djmessenger.utils.serializable.SerializableEnum`

  `AUDIO` = (ReplyingType) audio
  `BUTTON` = (ReplyingType) button
  `FILE` = (ReplyingType) file
  `IMAGE` = (ReplyingType) image
  `QUICK_REPLY` = (ReplyingType) quick_reply
  `SENDER_ACTION` = (ReplyingType) sender_action
  `SIMPLE_TEXT` = (ReplyingType) simple_text
  `VIDEO` = (ReplyingType) video
  `members` = {'image': (ReplyingType) image, 'simple_text': (ReplyingType) simple_text, 'quick_reply': (ReplyingType) quick_reply, 'sender_action': (ReplyingType) sender_action, 'audio': (ReplyingType) audio, 'video': (ReplyingType) video, 'file': (ReplyingType) file, 'button': (ReplyingType) button}

class `djmessenger.replying.``SimpleMessageReplier`(localized_string)

  Bases: `djmessenger.replying.CommonReplier`

  Send a simple text message

  `custom_obj_map` = {'localized_string': (<class 'djmessenger.utils.utils.LocalizedString'>, <class 'object'>)}
  `get_message`(messaging)
  `get_reply_type`()

class `djmessenger.replying.``VideoReplier`(url)

  Bases: `djmessenger.replying.MultimediaReplier`

  Reply a Video from url

  `sending_type` = (ReplyingType) video

## djmessenger.routing module

In a nutshell, the overall flow works like this

1. receiving module converts incoming request body to a Callback instance
2. Each Callback instance contains multiple Entry and each Entry contains multiple Messaging
3. For each Messaging, we are going to apply this routing policy

Each routing policy contains

1. A list of Rule

2. A rule contains a list of filters, a list of handlers and a list of repliers

   > 2.1 filters are responsible for determining whether the given Messaging

   >   is applicable for further handling, a Messaging object must pass all listed filters in order to reach handlers

   > 2.2 handlers perform internal operations against the Messaging, such like

   >   saving something into database and etc

   > 2.3 repliers reply something back to the user

See `djmessenger.utils.default_routing_policy` for sample routing policy and more details

class `djmessenger.routing.``Policy`(rules=[])

  Bases: `djmessenger.utils.serializable.Serializable`

  A `Policy` is the most important component in djmessenger. A Policy defines multiple `Rule` and when we got message from customer, we simply `Policy.apply()` the message

  `apply`(messaging)

    Apply this Policy to the given messaging instance

    Parameters: | messaging (djmessenger.receiving.Messaging) – An instance of Messaging class

  `custom_obj_map` = {'rules': [<class 'djmessenger.routing.Rule'>, <class 'list'>]}
  `get_default_rule`()

    There can only be 1 Rule that has type DEFAULT, if there were multiple, return the first one; return empty list if not found.

    The filters, handlers and repliers defined in DEFAULT Rule will be applied on all Rules

  classmethod `get_instance`(json_data)

    Given a json string, deserialize it and return a `Policy`

    Returns: | Policy
    Return type: | Policy

  `get_rules`(rtype)

class `djmessenger.routing.``Rule`(ttype, name='', filters=[], handlers=[], repliers=[])

  Bases: `djmessenger.utils.serializable.Serializable`

  A `Rule` is a class that will be applied to a `Messaging` instance. Each `Rule` contains a list of filters, a list of handlers and a list of repliers.

  Filters take a `Messaging` and determine whether this `Messaging` should proceed further, if any of the Filters returns False, this `Rule` will be skipped.

  See `djmessenger.utils.default_routing_policy` for sample routing policy and more details

  `custom_obj_map` = {'filters': [<class 'djmessenger.routing.TargetFilterClass'>, <class 'list'>], 'repliers': [<class 'djmessenger.routing.TargetReplierClass'>, <class 'list'>], 'handlers': [<class 'djmessenger.routing.TargetHandlerClass'>, <class 'list'>]}
  `get_filter_instances`()
  `get_filter_wrappers`()

    Returns: | A list of TargetFilterClass instances which is a wrapper for BaseFilter

  `get_handler_instances`()
  `get_handler_wrappers`()

    Returns: | A list of TargetHandlerClass instances which is a wrapper for BaseHandler

  `get_receiving_type`()

    Each `Rule` defines a `djmessenger.receiving.ReceivingType`, this method returns it

    Return type: | ReceivingType

  `get_replier_instances`()
  `get_replier_wrappers`()

    Returns: | A list of TargetReplierClass instances which is a wrapper for CommonReplier

class `djmessenger.routing.``TargetClass`(name, args={})

  Bases: `djmessenger.utils.serializable.Serializable`

  This class is a wrapper for filter, handler and replier which were defined in the policy file. When we are reading the policy file, we will first deserialize each dict using this class and this class is responsible for determining the exact target class and return an instance of it

  `BASE_CLASS` = None
  `get_args`()
  `get_class`()

    Loads and return the exact class from `self.name`

    Returns: | A class
    Return type: | class

  `get_instance`()

    Return an instance of the target class, which will be an instance of subclass of either `djmessenger.filtering.BaseFilter`, `djmessenger.handling.BaseHandler` or `djmessenger.replying.CommonReplier`

    Return type: | object

class `djmessenger.routing.``TargetFilterClass`(name, args={})

  Bases: `djmessenger.routing.TargetClass`

  `BASE_CLASS`

    alias of `BaseFilter`

class `djmessenger.routing.``TargetHandlerClass`(name, args={})

  Bases: `djmessenger.routing.TargetClass`

  `BASE_CLASS`

    alias of `BaseHandler`

class `djmessenger.routing.``TargetReplierClass`(name, args={})

  Bases: `djmessenger.routing.TargetClass`

  `BASE_CLASS`

    alias of `CommonReplier`

## djmessenger.settings module

`djmessenger.settings.``DJM_BOT_PREFIX` = <django.utils.functional.lazy.<locals>.__proxy__ object>

  Prefix this string to all text messages sent to users

`djmessenger.settings.``DJM_BUSINESS_ADDRESS` = ''

  This address is where your business is located at

`djmessenger.settings.``DJM_DEFAULT_SENDER_TEXT` = 'Thanks for your message'

  A sender named djmessenger.sending.DefaultSender which is a SimpleTextSender you can define your message here and directly use it in DJM_ROUTING_POLICY

`djmessenger.settings.``DJM_ENDPOINT` = 'aef40e8e2b258e647fe84655eab910a7a70bc0031e382f01ba'

  The endpoint that Facebook will relay the callback message to also being used to setup webhook

`djmessenger.settings.``DJM_ENDPOINT_VERIFY_TOKEN` = 'djmessenger_verify_token'

  Verify Token to be used when verifying endpoint in developer.facebook.com

`djmessenger.settings.``DJM_PAGE_ACCESS_TOKEN` = 'EAABtMkLqTLoBANwSkJxgqhkDtZBD9QLlzT4YW8JvxvzauC7nkaTtNRkqetF32mcTuwfQjtpm1cmZBx8HttEYLJ3MFDjeg8SLf6ZA6o84V0540McEEYHuoi7MZBHmAxvT08Kq5Iizj3uD0KqZCfUEGqwId4v5gAvXvifd7mw5TRQZDZD'

  Facebook Page Access Token, get it from developers.facebook.com

`djmessenger.settings.``DJM_ROUTING_POLICY` = {'rules': [{'type': 'DEFAULT', 'filters': [{'args': {'start_time': '2016-1-1T00:00:00Z-0800', 'end_time': '2017-1-1T01:00:00Z-0800'}, 'name': 'djmessenger.filtering.TimeFilter'}], 'name': 'default rule', 'handlers': [{'name': 'djmessenger.handling.UserProfileHandler'}, {'name': 'djmessenger.handling.SaveMessagingHandler'}]}, {'type': 'STICKER', 'filters': [{'name': 'djmessenger.filtering.ThumbUpFilter'}], 'repliers': [{'args': {'localized_string': {'translations': {'zh_TW': '謝謝您的讚'}, 'base_string': 'Thank you for your thumb!!!'}}, 'name': 'djmessenger.replying.SimpleMessageReplier'}, {'args': {'url': 'https://dl.dropboxusercontent.com/u/717667/icons/betatesting.png'}, 'name': 'djmessenger.replying.ImageReplier'}], 'name': 'ThumbUp Rule', 'handlers': [{'name': 'djmessenger.handling.ThumbUpHandler'}]}, {'type': 'SIMPLE_TEXT', 'filters': [{'args': {'regex': '^你好$'}, 'name': 'djmessenger.filtering.SimpleTextRegexFilter'}], 'repliers': [{'args': {'localized_string': {'translations': {'en_US': 'Hello to you, too!'}, 'base_string': '您也好'}}, 'name': 'djmessenger.replying.SimpleMessageReplier'}], 'name': 'Hello Chinese Rule'}, {'type': 'SIMPLE_TEXT', 'filters': [{'args': {'regex': '^hi$'}, 'name': 'djmessenger.filtering.SimpleTextRegexFilter'}], 'repliers': [{'args': {'localized_string': {'translations': {'zh_TW': '您也好'}, 'base_string': 'Hi you, too!'}}, 'name': 'djmessenger.replying.SimpleMessageReplier'}], 'name': 'Hi Rule'}, {'type': 'SIMPLE_TEXT', 'filters': [{'args': {'regex': '^buttons$'}, 'name': 'djmessenger.filtering.SimpleTextRegexFilter'}], 'repliers': [{'args': {'localized_string': {'translations': {'zh_TW': '請選擇一個按鈕'}, 'base_string': 'Please choose a button'}}, 'name': 'testapp.sending.MyButtonSender'}], 'name': 'buttons Rule'}, {'type': 'LOCATION', 'filters': [{'name': 'djmessenger.filtering.LocationFilter'}], 'name': 'Location Rule', 'handlers': [{'name': 'djmessenger.handling.LocationHandler'}]}, {'type': 'PERSISTENT_MENU_ONE', 'repliers': [{'args': {'localized_string': {'translations': {'zh_TW': '這是幫助訊息'}, 'base_string': 'This is help info'}}, 'name': 'djmessenger.replying.SimpleMessageReplier'}, {'args': {'localized_string': {'translations': {'zh_TW': '請選擇快速回覆'}, 'base_string': 'Please choose a QR'}}, 'name': 'testapp.sending.MyQuickReplySender'}], 'name': 'PM1 Rule'}, {'type': 'QUICK_REPLY', 'repliers': [{'args': {'localized_string': {'translations': {'zh_TW': '謝謝您的快速回覆'}, 'base_string': 'Thanks for the quick reply'}}, 'name': 'djmessenger.replying.SimpleMessageReplier'}], 'name': 'QR1 Rule', 'handlers': [{'name': 'testapp.handling.MyQuickReplyHandler'}]}]}

  DJM_ROUTING_POLICY defines the lifecycle of a message being received, handling it and lastly send some response back. In the following format, each router represents a type of message received, handlers represent which handlers should be used to handle this type of message and lastly which senders should be used to send the response back.

  See `djmessenger.utils.default_routing_policy` for more details

`djmessenger.settings.``DJM_SAVE_USER_PROFILE` = True

  Whether DJM should automatically fetch and save user profile for any user that sends message to the page and observed by BOT, default to True

`djmessenger.settings.``DJM_THREAD_ENABLE_AUTO_CONFIG` = False

  If set to True, djmessenger will send POST request to configure thread automatically. If set to False, you need to configure it by using management commands

`djmessenger.settings.``DJM_THREAD_ENABLE_GET_STARTED_BUTTON` = True

  https://developers.facebook.com/docs/messenger-platform/thread-settings/get-started-button Whether to enable get started button feature

`djmessenger.settings.``DJM_THREAD_GREETINGS_TEXT` = <django.utils.functional.lazy.<locals>.__proxy__ object>

  https://developers.facebook.com/docs/messenger-platform/thread-settings/greeting-text

  1. length must be <= 160
  2. only first time visitor sees this greeting text

## djmessenger.urls module

djmessenger URL Configuration

The urlpatterns list routes URLs to views. For more information please see:
  https://docs.djangoproject.com/en/1.9/topics/http/urls/

Examples: Function views

> 1. Add an import: from my_app import views
> 2. Add a URL to urlpatterns: url(r’^$’, views.home, name=’home’)

Class-based views
  1. Add an import: from other_app.views import Home
  2. Add a URL to urlpatterns: url(r’^$’, Home.as_view(), name=’home’)
Including another URLconf
  1. Import the include() function: from django.conf.urls import url, include
  2. Add a URL to urlpatterns: url(r’^blog/’, include(‘blog.urls’))

## djmessenger.views module

class `djmessenger.views.``DJMBotView`(**kwargs)

  Bases: `django.views.generic.base.View`

  `dispatch`(request, *args, **kwargs)
  `get`(request, *args, **kwargs)

    This will be called when Facebook verifies the endpoint

  `post`(request, *args, **kwargs)

    All Facebook callback will reach here

## Module contents

`djmessenger.``get_git_changeset`()

  Returns a numeric identifier of the latest git changeset. The result is the UTC timestamp of the changeset in YYYYMMDDHHMMSS format. This value isn’t guaranteed to be unique, but collisions are very unlikely, so it’s sufficient for generating the development version numbers. TODO: Check if we can rely on services like read-the-docs to pick this up.

`djmessenger.``get_version`(version=None)

  Returns a PEP 386-compliant version number from VERSION.