D7 Networks Rewrite in lieu of its new API (#791)

This commit is contained in:
Chris Caron 2022-12-20 17:57:40 -05:00 committed by GitHub
parent d21a1cb1f7
commit 0b1655c5eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 205 additions and 175 deletions

View File

@ -130,7 +130,7 @@ The table below identifies the services this tool supports and some example serv
| [BulkSMS](https://github.com/caronc/apprise/wiki/Notify_bulksms) | bulksms:// | (TCP) 443 | bulksms://user:password@ToPhoneNo<br/>bulksms://User:Password@ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/ | [BulkSMS](https://github.com/caronc/apprise/wiki/Notify_bulksms) | bulksms:// | (TCP) 443 | bulksms://user:password@ToPhoneNo<br/>bulksms://User:Password@ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/
| [ClickSend](https://github.com/caronc/apprise/wiki/Notify_clicksend) | clicksend:// | (TCP) 443 | clicksend://user:pass@PhoneNo<br/>clicksend://user:pass@ToPhoneNo1/ToPhoneNo2/ToPhoneNoN | [ClickSend](https://github.com/caronc/apprise/wiki/Notify_clicksend) | clicksend:// | (TCP) 443 | clicksend://user:pass@PhoneNo<br/>clicksend://user:pass@ToPhoneNo1/ToPhoneNo2/ToPhoneNoN
| [DAPNET](https://github.com/caronc/apprise/wiki/Notify_dapnet) | dapnet:// | (TCP) 80 | dapnet://user:pass@callsign<br/>dapnet://user:pass@callsign1/callsign2/callsignN | [DAPNET](https://github.com/caronc/apprise/wiki/Notify_dapnet) | dapnet:// | (TCP) 80 | dapnet://user:pass@callsign<br/>dapnet://user:pass@callsign1/callsign2/callsignN
| [D7 Networks](https://github.com/caronc/apprise/wiki/Notify_d7networks) | d7sms:// | (TCP) 443 | d7sms://user:pass@PhoneNo<br/>d7sms://user:pass@ToPhoneNo1/ToPhoneNo2/ToPhoneNoN | [D7 Networks](https://github.com/caronc/apprise/wiki/Notify_d7networks) | d7sms:// | (TCP) 443 | d7sms://token@PhoneNo<br/>d7sms://token@ToPhoneNo1/ToPhoneNo2/ToPhoneNoN
| [DingTalk](https://github.com/caronc/apprise/wiki/Notify_dingtalk) | dingtalk:// | (TCP) 443 | dingtalk://token/<br />dingtalk://token/ToPhoneNo<br />dingtalk://token/ToPhoneNo1/ToPhoneNo2/ToPhoneNo1/ | [DingTalk](https://github.com/caronc/apprise/wiki/Notify_dingtalk) | dingtalk:// | (TCP) 443 | dingtalk://token/<br />dingtalk://token/ToPhoneNo<br />dingtalk://token/ToPhoneNo1/ToPhoneNo2/ToPhoneNo1/
| [Kavenegar](https://github.com/caronc/apprise/wiki/Notify_kavenegar) | kavenegar:// | (TCP) 443 | kavenegar://ApiKey/ToPhoneNo<br/>kavenegar://FromPhoneNo@ApiKey/ToPhoneNo<br/>kavenegar://ApiKey/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN | [Kavenegar](https://github.com/caronc/apprise/wiki/Notify_kavenegar) | kavenegar:// | (TCP) 443 | kavenegar://ApiKey/ToPhoneNo<br/>kavenegar://FromPhoneNo@ApiKey/ToPhoneNo<br/>kavenegar://ApiKey/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN
| [MessageBird](https://github.com/caronc/apprise/wiki/Notify_messagebird) | msgbird:// | (TCP) 443 | msgbird://ApiKey/FromPhoneNo<br/>msgbird://ApiKey/FromPhoneNo/ToPhoneNo<br/>msgbird://ApiKey/FromPhoneNo/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/ | [MessageBird](https://github.com/caronc/apprise/wiki/Notify_messagebird) | msgbird:// | (TCP) 443 | msgbird://ApiKey/FromPhoneNo<br/>msgbird://ApiKey/FromPhoneNo/ToPhoneNo<br/>msgbird://ApiKey/FromPhoneNo/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/

View File

@ -29,17 +29,18 @@
# After you've established your account you can get your api login credentials # After you've established your account you can get your api login credentials
# (both user and password) from the API Details section from within your # (both user and password) from the API Details section from within your
# account profile area: https://d7networks.com/accounts/profile/ # account profile area: https://d7networks.com/accounts/profile/
#
# API Reference: https://d7networks.com/docs/Messages/Send_Message/
import requests import requests
import base64
from json import dumps from json import dumps
from json import loads from json import loads
from .NotifyBase import NotifyBase from .NotifyBase import NotifyBase
from ..URLBase import PrivacyMode
from ..common import NotifyType from ..common import NotifyType
from ..utils import is_phone_no from ..utils import is_phone_no
from ..utils import parse_phone_no from ..utils import parse_phone_no
from ..utils import validate_regex
from ..utils import parse_bool from ..utils import parse_bool
from ..AppriseLocale import gettext_lazy as _ from ..AppriseLocale import gettext_lazy as _
@ -52,25 +53,6 @@ D7NETWORKS_HTTP_ERROR_MAP = {
} }
# Priorities
class D7SMSPriority:
"""
D7 Networks SMS Message Priority
"""
LOW = 0
MODERATE = 1
NORMAL = 2
HIGH = 3
D7NETWORK_SMS_PRIORITIES = (
D7SMSPriority.LOW,
D7SMSPriority.MODERATE,
D7SMSPriority.NORMAL,
D7SMSPriority.HIGH,
)
class NotifyD7Networks(NotifyBase): class NotifyD7Networks(NotifyBase):
""" """
A wrapper for D7 Networks Notifications A wrapper for D7 Networks Notifications
@ -92,11 +74,8 @@ class NotifyD7Networks(NotifyBase):
# A URL that takes you to the setup/help of the specific protocol # A URL that takes you to the setup/help of the specific protocol
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_d7networks' setup_url = 'https://github.com/caronc/apprise/wiki/Notify_d7networks'
# D7 Networks batch notification URL
notify_batch_url = 'http://rest-api.d7networks.com/secure/sendbatch'
# D7 Networks single notification URL # D7 Networks single notification URL
notify_url = 'http://rest-api.d7networks.com/secure/send' notify_url = 'https://api.d7networks.com/messages/v1/send'
# The maximum length of the body # The maximum length of the body
body_maxlen = 160 body_maxlen = 160
@ -107,21 +86,16 @@ class NotifyD7Networks(NotifyBase):
# Define object templates # Define object templates
templates = ( templates = (
'{schema}://{user}:{password}@{targets}', '{schema}://{token}@{targets}',
) )
# Define our template tokens # Define our template tokens
template_tokens = dict(NotifyBase.template_tokens, **{ template_tokens = dict(NotifyBase.template_tokens, **{
'user': { 'token': {
'name': _('Username'), 'name': _('API Access Token'),
'type': 'string', 'type': 'string',
'required': True, 'required': True,
},
'password': {
'name': _('Password'),
'type': 'string',
'private': True, 'private': True,
'required': True,
}, },
'target_phone': { 'target_phone': {
'name': _('Target Phone No'), 'name': _('Target Phone No'),
@ -138,16 +112,11 @@ class NotifyD7Networks(NotifyBase):
# Define our template arguments # Define our template arguments
template_args = dict(NotifyBase.template_args, **{ template_args = dict(NotifyBase.template_args, **{
'priority': { 'unicode': {
'name': _('Priority'), # Unicode characters (default is 'auto')
'type': 'choice:int', 'name': _('Unicode Characters'),
'min': D7SMSPriority.LOW, 'type': 'bool',
'max': D7SMSPriority.HIGH, 'default': False,
'values': D7NETWORK_SMS_PRIORITIES,
# The website identifies that the default priority is low; so
# this plugin will honor that same default
'default': D7SMSPriority.LOW,
}, },
'batch': { 'batch': {
'name': _('Batch Mode'), 'name': _('Batch Mode'),
@ -172,20 +141,13 @@ class NotifyD7Networks(NotifyBase):
}, },
}) })
def __init__(self, targets=None, priority=None, source=None, batch=False, def __init__(self, token=None, targets=None, source=None,
**kwargs): batch=False, unicode=None, **kwargs):
""" """
Initialize D7 Networks Object Initialize D7 Networks Object
""" """
super().__init__(**kwargs) super().__init__(**kwargs)
# The Priority of the message
if priority not in D7NETWORK_SMS_PRIORITIES:
self.priority = self.template_args['priority']['default']
else:
self.priority = priority
# Prepare Batch Mode Flag # Prepare Batch Mode Flag
self.batch = batch self.batch = batch
@ -193,8 +155,15 @@ class NotifyD7Networks(NotifyBase):
self.source = None \ self.source = None \
if not isinstance(source, str) else source.strip() if not isinstance(source, str) else source.strip()
if not (self.user and self.password): # Define whether or not we should set the unicode flag
msg = 'A D7 Networks user/pass was not provided.' self.unicode = self.template_args['unicode']['default'] \
if unicode is None else bool(unicode)
# The token associated with the account
self.token = validate_regex(token)
if not self.token:
msg = 'The D7 Networks token specified ({}) is invalid.'\
.format(token)
self.logger.warning(msg) self.logger.warning(msg)
raise TypeError(msg) raise TypeError(msg)
@ -229,40 +198,41 @@ class NotifyD7Networks(NotifyBase):
# error tracking (used for function return) # error tracking (used for function return)
has_error = False has_error = False
auth = '{user}:{password}'.format(
user=self.user, password=self.password)
# Python 3's versio of b64encode() expects a byte array and not
# a string. To accommodate this, we encode the content here
auth = auth.encode('utf-8')
# Prepare our headers # Prepare our headers
headers = { headers = {
'User-Agent': self.app_id, 'User-Agent': self.app_id,
'Content-Type': 'application/json',
'Accept': 'application/json', 'Accept': 'application/json',
'Authorization': 'Basic {}'.format(base64.b64encode(auth)) 'Authorization': f'Bearer {self.token}',
} }
# Our URL varies depending if we're doing a batch mode or not payload = {
url = self.notify_batch_url if self.batch else self.notify_url 'message_globals': {
'channel': 'sms',
},
'messages': [{
# Populated later on
'recipients': None,
'content': body,
'data_coding':
# auto is a better substitute over 'text' as text is easier to
# detect from a post than `unicode` is.
'auto' if not self.unicode else 'unicode',
}],
}
# use the list directly # use the list directly
targets = list(self.targets) targets = list(self.targets)
if self.source:
payload['message_globals']['originator'] = self.source
target = None
while len(targets): while len(targets):
if self.batch: if self.batch:
# Prepare our payload # Prepare our payload
payload = { payload['messages'][0]['recipients'] = self.targets
'globals': {
'priority': self.priority,
'from': self.source if self.source else self.app_id,
},
'messages': [{
'to': self.targets,
'content': body,
}],
}
# Reset our targets so we don't keep going. This is required # Reset our targets so we don't keep going. This is required
# because we're in batch mode; we only need to loop once. # because we're in batch mode; we only need to loop once.
@ -274,24 +244,19 @@ class NotifyD7Networks(NotifyBase):
target = targets.pop(0) target = targets.pop(0)
# Prepare our payload # Prepare our payload
payload = { payload['messages'][0]['recipients'] = [target]
'priority': self.priority,
'content': body,
'to': target,
'from': self.source if self.source else self.app_id,
}
# Some Debug Logging # Some Debug Logging
self.logger.debug( self.logger.debug(
'D7 Networks POST URL: {} (cert_verify={})'.format( 'D7 Networks POST URL: {} (cert_verify={})'.format(
url, self.verify_certificate)) self.notify_url, self.verify_certificate))
self.logger.debug('D7 Networks Payload: {}' .format(payload)) self.logger.debug('D7 Networks Payload: {}' .format(payload))
# Always call throttle before any remote server i/o is made # Always call throttle before any remote server i/o is made
self.throttle() self.throttle()
try: try:
r = requests.post( r = requests.post(
url, self.notify_url,
data=dumps(payload), data=dumps(payload),
headers=headers, headers=headers,
verify=self.verify_certificate, verify=self.verify_certificate,
@ -337,29 +302,9 @@ class NotifyD7Networks(NotifyBase):
else: else:
if self.batch: if self.batch:
count = len(self.targets)
try:
# Get our message delivery count if we can
json_response = loads(r.content)
count = int(json_response.get(
'data', {}).get('messageCount', -1))
except (AttributeError, TypeError, ValueError):
# ValueError = r.content is Unparsable
# TypeError = r.content is None
# AttributeError = r is None
# We could not parse JSON response. Assume that
# our delivery is okay for now.
pass
if count != len(self.targets):
has_error = True
self.logger.info( self.logger.info(
'Sent D7 Networks batch SMS notification to ' 'Sent D7 Networks batch SMS notification to '
'{} of {} target(s).'.format( '{} target(s).'.format(len(self.targets)))
count, len(self.targets)))
else: else:
self.logger.info( self.logger.info(
@ -389,22 +334,18 @@ class NotifyD7Networks(NotifyBase):
# Define any URL parameters # Define any URL parameters
params = { params = {
'batch': 'yes' if self.batch else 'no', 'batch': 'yes' if self.batch else 'no',
'unicode': 'yes' if self.unicode else 'no',
} }
# Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
if self.priority != self.template_args['priority']['default']:
params['priority'] = str(self.priority)
if self.source: if self.source:
params['from'] = self.source params['from'] = self.source
return '{schema}://{user}:{password}@{targets}/?{params}'.format( # Extend our parameters
params.update(self.url_parameters(privacy=privacy, *args, **kwargs))
return '{schema}://{token}@{targets}/?{params}'.format(
schema=self.secure_protocol, schema=self.secure_protocol,
user=NotifyD7Networks.quote(self.user, safe=''), token=self.pprint(self.token, privacy, safe=''),
password=self.pprint(
self.password, privacy, mode=PrivacyMode.Secret, safe=''),
targets='/'.join( targets='/'.join(
[NotifyD7Networks.quote(x, safe='') for x in self.targets]), [NotifyD7Networks.quote(x, safe='') for x in self.targets]),
params=NotifyD7Networks.urlencode(params)) params=NotifyD7Networks.urlencode(params))
@ -421,6 +362,23 @@ class NotifyD7Networks(NotifyBase):
# We're done early as we couldn't load the results # We're done early as we couldn't load the results
return results return results
if 'token' in results['qsd'] and len(results['qsd']['token']):
results['token'] = \
NotifyD7Networks.unquote(results['qsd']['token'])
elif results['user']:
results['token'] = NotifyD7Networks.unquote(results['user'])
if results['password']:
# Support token containing a colon (:)
results['token'] += \
':' + NotifyD7Networks.unquote(results['password'])
elif results['password']:
# Support token starting with a colon (:)
results['token'] = \
':' + NotifyD7Networks.unquote(results['password'])
# Initialize our targets # Initialize our targets
results['targets'] = list() results['targets'] = list()
@ -432,44 +390,27 @@ class NotifyD7Networks(NotifyBase):
results['targets'].extend( results['targets'].extend(
NotifyD7Networks.split_path(results['fullpath'])) NotifyD7Networks.split_path(results['fullpath']))
# Set our priority
if 'priority' in results['qsd'] and len(results['qsd']['priority']):
_map = {
'l': D7SMSPriority.LOW,
'0': D7SMSPriority.LOW,
'm': D7SMSPriority.MODERATE,
'1': D7SMSPriority.MODERATE,
'n': D7SMSPriority.NORMAL,
'2': D7SMSPriority.NORMAL,
'h': D7SMSPriority.HIGH,
'3': D7SMSPriority.HIGH,
}
try:
results['priority'] = \
_map[results['qsd']['priority'][0].lower()]
except KeyError:
# No priority was set
pass
# Support the 'from' and 'source' variable so that we can support
# targets this way too.
# The 'from' makes it easier to use yaml configuration
if 'from' in results['qsd'] and len(results['qsd']['from']):
results['source'] = \
NotifyD7Networks.unquote(results['qsd']['from'])
if 'source' in results['qsd'] and len(results['qsd']['source']):
results['source'] = \
NotifyD7Networks.unquote(results['qsd']['source'])
# Get Batch Mode Flag # Get Batch Mode Flag
results['batch'] = \ results['batch'] = \
parse_bool(results['qsd'].get('batch', False)) parse_bool(results['qsd'].get('batch', False))
# Get Unicode Flag
results['unicode'] = \
parse_bool(results['qsd'].get('unicode', False))
# Support the 'to' variable so that we can support targets this way too # Support the 'to' variable so that we can support targets this way too
# The 'to' makes it easier to use yaml configuration # The 'to' makes it easier to use yaml configuration
if 'to' in results['qsd'] and len(results['qsd']['to']): if 'to' in results['qsd'] and len(results['qsd']['to']):
results['targets'] += \ results['targets'] += \
NotifyD7Networks.parse_phone_no(results['qsd']['to']) NotifyD7Networks.parse_phone_no(results['qsd']['to'])
# Support the 'from' and source variable
if 'from' in results['qsd'] and len(results['qsd']['from']):
results['source'] = \
NotifyD7Networks.unquote(results['qsd']['from'])
elif 'source' in results['qsd'] and len(results['qsd']['source']):
results['source'] = \
NotifyD7Networks.unquote(results['qsd']['source'])
return results return results

View File

@ -22,8 +22,13 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
import requests
from json import loads
from unittest import mock
from apprise.plugins.NotifyD7Networks import NotifyD7Networks from apprise.plugins.NotifyD7Networks import NotifyD7Networks
from helpers import AppriseURLTester from helpers import AppriseURLTester
from apprise import Apprise
from apprise import NotifyType
# Disable logging for a cleaner testing output # Disable logging for a cleaner testing output
import logging import logging
@ -39,66 +44,72 @@ apprise_url_tests = (
# We failed to identify any valid authentication # We failed to identify any valid authentication
'instance': TypeError, 'instance': TypeError,
}), }),
('d7sms://user:pass@{}/{}/{}'.format('1' * 9, '2' * 15, 'a' * 13), { ('d7sms://token@{}/{}/{}'.format('1' * 9, '2' * 15, 'a' * 13), {
# No valid targets to notify # No valid targets to notify
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
# Since there are no targets specified we expect a False return on # Since there are no targets specified we expect a False return on
# send() # send()
'notify_response': False, 'notify_response': False,
}), }),
('d7sms://user:pass@{}?batch=yes'.format('3' * 14), { ('d7sms://token1@{}?batch=yes'.format('3' * 14), {
# valid number # valid number
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
# Our expected url(privacy=True) startswith() response: # Our expected url(privacy=True) startswith() response:
'privacy_url': 'd7sms://user:****@', 'privacy_url': 'd7sms://t...1@',
}), }),
('d7sms://user:pass@{}?batch=yes'.format('7' * 14), { ('d7sms://token:colon2@{}?batch=yes'.format('3' * 14), {
# valid number # valid number - token containing a colon
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
# Test what happens if a batch send fails to return a messageCount # Our expected url(privacy=True) startswith() response:
'requests_response_text': { 'privacy_url': 'd7sms://t...2@',
'data': {
'messageCount': 0,
},
},
# Expected notify() response
'notify_response': False,
}), }),
('d7sms://user:pass@{}?batch=yes&to={}'.format('3' * 14, '6' * 14), { ('d7sms://:token3@{}?batch=yes'.format('3' * 14), {
# valid number - token starting wit a colon
'instance': NotifyD7Networks,
# Our expected url(privacy=True) startswith() response:
'privacy_url': 'd7sms://:...3@',
}),
('d7sms://{}?token=token6'.format('3' * 14), {
# valid number - token starting wit a colon
'instance': NotifyD7Networks,
# Our expected url(privacy=True) startswith() response:
'privacy_url': 'd7sms://t...6@',
}),
('d7sms://token4@{}?unicode=no'.format('3' * 14), {
# valid number - test unicode
'instance': NotifyD7Networks,
# Our expected url(privacy=True) startswith() response:
'privacy_url': 'd7sms://t...4@',
}),
('d7sms://token8@{}/{}/?unicode=yes'.format('3' * 14, '4' * 14), {
# valid number - test unicode
'instance': NotifyD7Networks,
# Our expected url(privacy=True) startswith() response:
'privacy_url': 'd7sms://t...8@',
}),
('d7sms://token@{}?batch=yes&to={}'.format('3' * 14, '6' * 14), {
# valid number # valid number
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
}), }),
('d7sms://user:pass@{}?batch=yes&from=apprise'.format('3' * 14), { ('d7sms://token@{}?batch=yes&from=apprise'.format('3' * 14), {
# valid number, utilizing the optional from= variable # valid number, utilizing the optional from= variable
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
}), }),
('d7sms://user:pass@{}?batch=yes&source=apprise'.format('3' * 14), { ('d7sms://token@{}?batch=yes&source=apprise'.format('3' * 14), {
# valid number, utilizing the optional source= variable (same as from) # valid number, utilizing the optional source= variable (same as from)
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
}), }),
('d7sms://user:pass@{}?priority=invalid'.format('3' * 14), { ('d7sms://token@{}?batch=no'.format('3' * 14), {
# valid number; invalid priority
'instance': NotifyD7Networks,
}),
('d7sms://user:pass@{}?priority=3'.format('3' * 14), {
# valid number; adjusted priority
'instance': NotifyD7Networks,
}),
('d7sms://user:pass@{}?priority=high'.format('3' * 14), {
# valid number; adjusted priority (string supported)
'instance': NotifyD7Networks,
}),
('d7sms://user:pass@{}?batch=no'.format('3' * 14), {
# valid number - no batch # valid number - no batch
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
}), }),
('d7sms://user:pass@{}'.format('3' * 14), { ('d7sms://token@{}'.format('3' * 14), {
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
# throw a bizzare code forcing us to fail to look it up # throw a bizzare code forcing us to fail to look it up
'response': False, 'response': False,
'requests_response_code': 999, 'requests_response_code': 999,
}), }),
('d7sms://user:pass@{}'.format('3' * 14), { ('d7sms://token@{}'.format('3' * 14), {
'instance': NotifyD7Networks, 'instance': NotifyD7Networks,
# Throws a series of connection and transfer exceptions when this flag # Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracfully handle them # is set and tests that we gracfully handle them
@ -115,3 +126,81 @@ def test_plugin_d7networks_urls():
# Run our general tests # Run our general tests
AppriseURLTester(tests=apprise_url_tests).run_all() AppriseURLTester(tests=apprise_url_tests).run_all()
@mock.patch('requests.post')
def test_plugin_d7networks_edge_cases(mock_post):
"""
NotifyD7Networks() Edge Cases tests
"""
# Prepare Mock
request = mock.Mock()
request.content = '{}'
request.status_code = requests.codes.ok
mock_post.return_value = request
# Initializations
aobj = Apprise()
assert aobj.add('d7sms://Token@15551231234/15551231236')
body = "test message"
# Send our notification
assert aobj.notify(
body=body, title='title', notify_type=NotifyType.INFO)
# Not set to batch, so we send 2 different messages
assert mock_post.call_count == 2
assert mock_post.call_args_list[0][0][0] == \
'https://api.d7networks.com/messages/v1/send'
assert mock_post.call_args_list[1][0][0] == \
'https://api.d7networks.com/messages/v1/send'
# our first post
data = loads(mock_post.call_args_list[0][1]['data'])
assert len(data['messages']) == 1
message = data['messages'][0]
assert len(message['recipients']) == 1
assert message['content'] == 'title\r\ntest message'
assert message['data_coding'] == 'auto'
# our second post
data = loads(mock_post.call_args_list[1][1]['data'])
assert len(data['messages']) == 1
message = data['messages'][0]
assert len(message['recipients']) == 1
assert message['content'] == 'title\r\ntest message'
assert message['data_coding'] == 'auto'
#
# Do a batch test now
#
mock_post.reset_mock()
# Initializations
aobj = Apprise()
assert aobj.add('d7sms://Token@15551231234/15551231236?batch=yes')
body = "test message"
# Send our notification
assert aobj.notify(
body=body, title='title', notify_type=NotifyType.INFO)
# All notifications go through in a batch
assert mock_post.call_count == 1
assert mock_post.call_args_list[0][0][0] == \
'https://api.d7networks.com/messages/v1/send'
data = loads(mock_post.call_args_list[0][1]['data'])
assert len(data['messages']) == 1
message = data['messages'][0]
# All of our phone numbers were added here
assert len(message['recipients']) == 2
assert '15551231234' in message['recipients']
assert '15551231236' in message['recipients']
assert message['content'] == 'title\r\ntest message'
assert message['data_coding'] == 'auto'