hat.drivers.smpp
async def
connect( addr: hat.drivers.tcp.Address, system_id: str = '', password: str = '', close_timeout: float = 0.1, enquire_link_delay: float | None = None, enquire_link_timeout: float = 10, **kwargs) -> Client:
18async def connect(addr: tcp.Address, 19 system_id: str = '', 20 password: str = '', 21 close_timeout: float = 0.1, 22 enquire_link_delay: float | None = None, 23 enquire_link_timeout: float = 10, 24 **kwargs 25 ) -> 'Client': 26 """Connect to remote SMPP server 27 28 Additional arguments are passed directly to `tcp.connect`. 29 30 """ 31 client = Client() 32 client._async_group = aio.Group() 33 client._equire_link_event = asyncio.Event() 34 client._bound = False 35 36 conn = await tcp.connect(addr, **kwargs) 37 client._conn = transport.Connection( 38 conn=conn, 39 request_cb=client._on_request, 40 notification_cb=client._on_notification) 41 42 try: 43 client.async_group.spawn(aio.call_on_cancel, client._on_close, 44 close_timeout) 45 client.async_group.spawn(aio.call_on_done, conn.wait_closing(), 46 client.close) 47 48 req = transport.BindReq( 49 bind_type=transport.BindType.TRANSCEIVER, 50 system_id=system_id, 51 password=password, 52 system_type='', 53 interface_version=0x34, 54 addr_ton=transport.TypeOfNumber.UNKNOWN, 55 addr_npi=transport.NumericPlanIndicator.UNKNOWN, 56 address_range='') 57 await client._send(req) 58 59 client._bound = True 60 61 if enquire_link_delay is not None: 62 client.async_group.spawn(client._enquire_link_loop, 63 enquire_link_delay, enquire_link_timeout) 64 65 except BaseException: 66 await aio.uncancellable(client.async_close()) 67 raise 68 69 return client
Connect to remote SMPP server
Additional arguments are passed directly to tcp.connect
.
class
Client(hat.aio.group.Resource):
72class Client(aio.Resource): 73 74 @property 75 def async_group(self) -> aio.Group: 76 """Async group""" 77 return self._async_group 78 79 async def send_message(self, 80 dst_addr: str, 81 msg: util.Bytes, 82 *, 83 short_message: bool = True, 84 priority: common.Priority = common.Priority.BULK, 85 udhi: bool = False, 86 dst_ton: common.TypeOfNumber = common.TypeOfNumber.UNKNOWN, # NOQA 87 src_ton: common.TypeOfNumber = common.TypeOfNumber.UNKNOWN, # NOQA 88 src_addr: str = '', 89 data_coding: common.DataCoding = common.DataCoding.DEFAULT # NOQA 90 ) -> common.MessageId: 91 """Send message""" 92 optional_params = {} 93 gsm_features = set() 94 95 if not short_message: 96 optional_params[transport.OptionalParamTag.MESSAGE_PAYLOAD] = msg 97 98 if udhi: 99 gsm_features.add(transport.GsmFeature.UDHI) 100 101 req = transport.SubmitSmReq( 102 service_type='', 103 source_addr_ton=src_ton, 104 source_addr_npi=transport.NumericPlanIndicator.UNKNOWN, 105 source_addr=src_addr, 106 dest_addr_ton=dst_ton, 107 dest_addr_npi=transport.NumericPlanIndicator.UNKNOWN, 108 destination_addr=dst_addr, 109 esm_class=transport.EsmClass( 110 messaging_mode=transport.MessagingMode.DEFAULT, 111 message_type=transport.MessageType.DEFAULT, 112 gsm_features=gsm_features), 113 protocol_id=0, 114 priority_flag=priority, 115 schedule_delivery_time=None, 116 validity_period=None, 117 registered_delivery=transport.RegisteredDelivery( 118 delivery_receipt=transport.DeliveryReceipt.NO_RECEIPT, 119 acknowledgements=set(), 120 intermediate_notification=False), 121 replace_if_present_flag=False, 122 data_coding=data_coding, 123 sm_default_msg_id=0, 124 short_message=(msg if short_message else b''), 125 optional_params=optional_params) 126 127 res = await self._send(req) 128 129 return res.message_id 130 131 async def _on_close(self, timeout): 132 if self._bound: 133 with contextlib.suppress(Exception): 134 await aio.wait_for(self._conn.send(transport.UnbindReq()), 135 timeout) 136 137 await self._conn.async_close() 138 139 async def _on_request(self, req): 140 self._equire_link_event.set() 141 142 if isinstance(req, transport.UnbindReq): 143 self._bound = False 144 self.async_group.spawn(aio.call, self.close) 145 return transport.UnbindRes() 146 147 if isinstance(req, transport.DataSmReq): 148 # TODO 149 return transport.CommandStatus.ESME_RINVCMDID 150 151 if isinstance(req, transport.DeliverSmReq): 152 # TODO 153 return transport.DeliverSmRes() 154 155 if isinstance(req, transport.EnquireLinkReq): 156 return transport.EnquireLinkRes() 157 158 return transport.CommandStatus.ESME_RINVCMDID 159 160 async def _on_notification(self, notification): 161 self._equire_link_event.set() 162 163 if isinstance(notification, transport.OutbindNotification): 164 # TODO 165 pass 166 167 if isinstance(notification, transport.AlertNotification): 168 # TODO 169 pass 170 171 async def _enquire_link_loop(self, delay, timeout): 172 try: 173 while True: 174 self._equire_link_event.clear() 175 176 with contextlib.suppress(asyncio.TimeoutError): 177 await aio.wait_for(self._equire_link_event.wait(), delay) 178 continue 179 180 if self._bound: 181 await aio.wait_for( 182 self._conn.send(transport.EnquireLinkReq()), timeout) 183 184 except ConnectionError: 185 pass 186 187 except asyncio.TimeoutError: 188 mlog.warning('enquire link timeout') 189 190 except Exception as e: 191 mlog.error('equire link loop error: %s', e, exc_info=e) 192 193 finally: 194 self.close() 195 196 async def _send(self, req): 197 res = await self._conn.send(req) 198 self._equire_link_event.set() 199 200 if isinstance(res, transport.CommandStatus): 201 error_str = transport.command_status_descriptions[res] 202 raise Exception(f'command error response: {error_str}') 203 204 return res
Resource with lifetime control based on Group
.
async_group: hat.aio.group.Group
74 @property 75 def async_group(self) -> aio.Group: 76 """Async group""" 77 return self._async_group
Async group
async def
send_message( self, dst_addr: str, msg: bytes | bytearray | memoryview, *, short_message: bool = True, priority: Priority = <Priority.BULK: 0>, udhi: bool = False, dst_ton: TypeOfNumber = <TypeOfNumber.UNKNOWN: 0>, src_ton: TypeOfNumber = <TypeOfNumber.UNKNOWN: 0>, src_addr: str = '', data_coding: DataCoding = <DataCoding.DEFAULT: 0>) -> bytes | bytearray | memoryview:
79 async def send_message(self, 80 dst_addr: str, 81 msg: util.Bytes, 82 *, 83 short_message: bool = True, 84 priority: common.Priority = common.Priority.BULK, 85 udhi: bool = False, 86 dst_ton: common.TypeOfNumber = common.TypeOfNumber.UNKNOWN, # NOQA 87 src_ton: common.TypeOfNumber = common.TypeOfNumber.UNKNOWN, # NOQA 88 src_addr: str = '', 89 data_coding: common.DataCoding = common.DataCoding.DEFAULT # NOQA 90 ) -> common.MessageId: 91 """Send message""" 92 optional_params = {} 93 gsm_features = set() 94 95 if not short_message: 96 optional_params[transport.OptionalParamTag.MESSAGE_PAYLOAD] = msg 97 98 if udhi: 99 gsm_features.add(transport.GsmFeature.UDHI) 100 101 req = transport.SubmitSmReq( 102 service_type='', 103 source_addr_ton=src_ton, 104 source_addr_npi=transport.NumericPlanIndicator.UNKNOWN, 105 source_addr=src_addr, 106 dest_addr_ton=dst_ton, 107 dest_addr_npi=transport.NumericPlanIndicator.UNKNOWN, 108 destination_addr=dst_addr, 109 esm_class=transport.EsmClass( 110 messaging_mode=transport.MessagingMode.DEFAULT, 111 message_type=transport.MessageType.DEFAULT, 112 gsm_features=gsm_features), 113 protocol_id=0, 114 priority_flag=priority, 115 schedule_delivery_time=None, 116 validity_period=None, 117 registered_delivery=transport.RegisteredDelivery( 118 delivery_receipt=transport.DeliveryReceipt.NO_RECEIPT, 119 acknowledgements=set(), 120 intermediate_notification=False), 121 replace_if_present_flag=False, 122 data_coding=data_coding, 123 sm_default_msg_id=0, 124 short_message=(msg if short_message else b''), 125 optional_params=optional_params) 126 127 res = await self._send(req) 128 129 return res.message_id
Send message
MessageId =
bytes | bytearray | memoryview
class
Priority(enum.Enum):
BULK =
<Priority.BULK: 0>
NORMAL =
<Priority.NORMAL: 1>
URGENT =
<Priority.URGENT: 2>
VERY_URGENT =
<Priority.VERY_URGENT: 3>
class
TypeOfNumber(enum.Enum):
19class TypeOfNumber(enum.Enum): 20 UNKNOWN = 0 21 INTERNATIONAL = 1 22 NATIONAL = 2 23 NETWORK_SPECIFIC = 3 24 SUBSCRIBER_NUMBER = 4 25 ALPHANUMERIC = 5 26 ABBREVIATED = 6
UNKNOWN =
<TypeOfNumber.UNKNOWN: 0>
INTERNATIONAL =
<TypeOfNumber.INTERNATIONAL: 1>
NATIONAL =
<TypeOfNumber.NATIONAL: 2>
NETWORK_SPECIFIC =
<TypeOfNumber.NETWORK_SPECIFIC: 3>
SUBSCRIBER_NUMBER =
<TypeOfNumber.SUBSCRIBER_NUMBER: 4>
ALPHANUMERIC =
<TypeOfNumber.ALPHANUMERIC: 5>
ABBREVIATED =
<TypeOfNumber.ABBREVIATED: 6>
class
DataCoding(enum.Enum):
29class DataCoding(enum.Enum): 30 DEFAULT = 0 31 ASCII = 1 32 UNSPECIFIED_1 = 2 33 LATIN_1 = 3 34 UNSPECIFIED_2 = 4 35 JIS = 5 36 CYRLLIC = 6 37 LATIN_HEBREW = 7 38 UCS2 = 8 39 PICTOGRAM = 9 40 MUSIC = 10 41 EXTENDED_KANJI = 13 42 KS = 14
DEFAULT =
<DataCoding.DEFAULT: 0>
ASCII =
<DataCoding.ASCII: 1>
UNSPECIFIED_1 =
<DataCoding.UNSPECIFIED_1: 2>
LATIN_1 =
<DataCoding.LATIN_1: 3>
UNSPECIFIED_2 =
<DataCoding.UNSPECIFIED_2: 4>
JIS =
<DataCoding.JIS: 5>
CYRLLIC =
<DataCoding.CYRLLIC: 6>
LATIN_HEBREW =
<DataCoding.LATIN_HEBREW: 7>
UCS2 =
<DataCoding.UCS2: 8>
PICTOGRAM =
<DataCoding.PICTOGRAM: 9>
MUSIC =
<DataCoding.MUSIC: 10>
EXTENDED_KANJI =
<DataCoding.EXTENDED_KANJI: 13>
KS =
<DataCoding.KS: 14>