11import abc
22import base64
3+ import dataclasses
34import logging
45import sys
56import typing as t
67
78from . import ahttp , domain , http
89from .constants import DEFAULT_URL , VERSION
10+ from .encryption import BaseEncryptor
911
1012logger = logging .getLogger (__name__ )
1113
1214
1315class BaseClient (abc .ABC ):
1416 def __init__ (
15- self , login : str , password : str , * , base_url : str = DEFAULT_URL
17+ self ,
18+ login : str ,
19+ password : str ,
20+ * ,
21+ base_url : str = DEFAULT_URL ,
22+ encryptor : t .Optional [BaseEncryptor ] = None ,
1623 ) -> None :
1724 credentials = base64 .b64encode (f"{ login } :{ password } " .encode ("utf-8" )).decode (
1825 "utf-8"
@@ -23,6 +30,44 @@ def __init__(
2330 "User-Agent" : f"android-sms-gateway/{ VERSION } (client; python { sys .version_info .major } .{ sys .version_info .minor } )" ,
2431 }
2532 self .base_url = base_url .rstrip ("/" )
33+ self .encryptor = encryptor
34+
35+ def _encrypt (self , message : domain .Message ) -> domain .Message :
36+ if self .encryptor is None :
37+ return message
38+
39+ if message .is_encrypted :
40+ raise ValueError ("Message is already encrypted" )
41+
42+ message = dataclasses .replace (
43+ message ,
44+ is_encrypted = True ,
45+ message = self .encryptor .encrypt (message .message ),
46+ phone_numbers = [
47+ self .encryptor .encrypt (phone ) for phone in message .phone_numbers
48+ ],
49+ )
50+
51+ return message
52+
53+ def _decrypt (self , state : domain .MessageState ) -> domain .MessageState :
54+ if state .is_encrypted and self .encryptor is None :
55+ raise ValueError ("Message is encrypted but encryptor is not set" )
56+
57+ if self .encryptor is None :
58+ return state
59+
60+ return dataclasses .replace (
61+ state ,
62+ recipients = [
63+ dataclasses .replace (
64+ recipient ,
65+ phone_number = self .encryptor .decrypt (recipient .phone_number ),
66+ )
67+ for recipient in state .recipients
68+ ],
69+ is_encrypted = False ,
70+ )
2671
2772
2873class APIClient (BaseClient ):
@@ -32,10 +77,11 @@ def __init__(
3277 password : str ,
3378 * ,
3479 base_url : str = DEFAULT_URL ,
35- http_client : t .Optional [http .HttpClient ] = None ,
80+ encryptor : t .Optional [BaseEncryptor ] = None ,
81+ http : t .Optional [http .HttpClient ] = None ,
3682 ) -> None :
37- super ().__init__ (login , password , base_url = base_url )
38- self .http = http_client
83+ super ().__init__ (login , password , base_url = base_url , encryptor = encryptor )
84+ self .http = http
3985
4086 def __enter__ (self ):
4187 if self .http is not None :
@@ -50,17 +96,22 @@ def __exit__(self, exc_type, exc_val, exc_tb):
5096 self .http = None
5197
5298 def send (self , message : domain .Message ) -> domain .MessageState :
53- return domain .MessageState .from_dict (
54- self .http .post (
55- f"{ self .base_url } /message" ,
56- payload = message .asdict (),
57- headers = self .headers ,
99+ message = self ._encrypt (message )
100+ return self ._decrypt (
101+ domain .MessageState .from_dict (
102+ self .http .post (
103+ f"{ self .base_url } /message" ,
104+ payload = message .asdict (),
105+ headers = self .headers ,
106+ )
58107 )
59108 )
60109
61110 def get_state (self , _id : str ) -> domain .MessageState :
62- return domain .MessageState .from_dict (
63- self .http .get (f"{ self .base_url } /message/{ _id } " , headers = self .headers )
111+ return self ._decrypt (
112+ domain .MessageState .from_dict (
113+ self .http .get (f"{ self .base_url } /message/{ _id } " , headers = self .headers )
114+ )
64115 )
65116
66117
@@ -71,9 +122,10 @@ def __init__(
71122 password : str ,
72123 * ,
73124 base_url : str = DEFAULT_URL ,
125+ encryptor : t .Optional [BaseEncryptor ] = None ,
74126 http_client : t .Optional [ahttp .AsyncHttpClient ] = None ,
75127 ) -> None :
76- super ().__init__ (login , password , base_url = base_url )
128+ super ().__init__ (login , password , base_url = base_url , encryptor = encryptor )
77129 self .http = http_client
78130
79131 async def __aenter__ (self ):
@@ -89,15 +141,22 @@ async def __aexit__(self, exc_type, exc_val, exc_tb):
89141 self .http = None
90142
91143 async def send (self , message : domain .Message ) -> domain .MessageState :
92- return domain .MessageState .from_dict (
93- await self .http .post (
94- f"{ self .base_url } /message" ,
95- payload = message .asdict (),
96- headers = self .headers ,
144+ message = self ._encrypt (message )
145+ return self ._decrypt (
146+ domain .MessageState .from_dict (
147+ await self .http .post (
148+ f"{ self .base_url } /message" ,
149+ payload = message .asdict (),
150+ headers = self .headers ,
151+ )
97152 )
98153 )
99154
100155 async def get_state (self , _id : str ) -> domain .MessageState :
101- return domain .MessageState .from_dict (
102- await self .http .get (f"{ self .base_url } /message/{ _id } " , headers = self .headers )
156+ return self ._decrypt (
157+ domain .MessageState .from_dict (
158+ await self .http .get (
159+ f"{ self .base_url } /message/{ _id } " , headers = self .headers
160+ )
161+ )
103162 )
0 commit comments