mailbox-org-api 2.3__tar.gz → 2.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/PKG-INFO +14 -8
  2. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/README.md +13 -7
  3. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api/APIClient.py +170 -146
  4. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api/APIError.py +2 -1
  5. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api/Invoice.py +1 -1
  6. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api.egg-info/PKG-INFO +14 -8
  7. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/pyproject.toml +1 -1
  8. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/setup.py +1 -1
  9. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/tests/TestAPIClient.py +46 -14
  10. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/tests/TestAccount.py +3 -2
  11. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/tests/TestInvoice.py +2 -1
  12. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/tests/TestMail.py +3 -1
  13. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/LICENSE +0 -0
  14. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api/Account.py +0 -0
  15. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api/Mail.py +0 -0
  16. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api/__init__.py +0 -0
  17. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api.egg-info/SOURCES.txt +0 -0
  18. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api.egg-info/dependency_links.txt +0 -0
  19. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/mailbox_org_api.egg-info/top_level.txt +0 -0
  20. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/setup.cfg +0 -0
  21. {mailbox_org_api-2.3 → mailbox_org_api-2.5}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mailbox-org-api
3
- Version: 2.3
3
+ Version: 2.5
4
4
  Summary: A library to access the mailbox Business API.
5
5
  Author: Hendrik Schlange
6
6
  Author-email: Hendrik Schlange <mail@heshsum.de>
@@ -36,11 +36,15 @@ pip install mailbox-org-api
36
36
  pip install git+https://github.com/heshsum/mailbox-org-api
37
37
  ```
38
38
 
39
- ## Usage
39
+ ## Usage and documentation
40
40
  Basic usage is fairly straightforward. The naming scheme of the functions is similar to the naming at mailbox.org,
41
- but instead of points, it uses underscores (e.g. instead of `mail.add` it's `mail_add`).
41
+ but instead of points, it uses underscores (e.g. instead of `mail.add` it's `mail_add`).
42
+
43
+ Therefore, the functions mirror the functions as provided and documented at mailbox:
44
+ [api.mailbox.org](https://api.mailbox.org)
45
+
42
46
  Additionally, some helper functions for common or more complicated tasks are included to make life a bit easier,
43
- e.g. for changing plans, password and to retrieve invoices.
47
+ e.g. for changing plans, password and to retrieving invoices.
44
48
 
45
49
  ```python
46
50
  from mailbox_org_api import APIClient
@@ -87,6 +91,8 @@ api.mail_set_forwards('foo@bar.com', ['forward1@bar.com', 'forward2@bar.com'])
87
91
  api.deauth()
88
92
  ```
89
93
 
94
+ More information can be found in the [Wiki](https://github.com/heshsum/mailbox-org-api/wiki)
95
+
90
96
  ## Common tasks
91
97
  mailbox_org_api includes a number of helper functions to make common tasks simpler. These include:
92
98
 
@@ -163,21 +169,21 @@ Usage:
163
169
  api.account_invoice_get_token('BMBO-1234-24')
164
170
  ```
165
171
 
166
- ### account_invoice_get_pdf
172
+ ### account_invoice_get_file
167
173
  Invoices are provided as Based64 encoded gz Strings. This function
168
174
  1. takes the invoice ID
169
175
  2. retrieves the token for the invoice
170
176
  3. gets the binary data
171
177
  4. decodes the Base64
172
178
  5. decompresses it
173
- 6. returns the bytes of the actual PDF
179
+ 6. returns the bytes of the actual invoice file
174
180
 
175
181
  Usage
176
182
  ```python
177
183
  invoice_id = 'BMBO-1234-24'
178
- account_name = 'some_user'
184
+ account_name = 'foo'
179
185
  with open(invoice_id + '.pdf', 'w') as file:
180
- file.write(api.account_invoice_get_pdf(account_name, invoice_id))
186
+ file.write(api.account_invoice_get_file(account_name, invoice_id, 'PDF'))
181
187
  ```
182
188
 
183
189
  ## Here be dragons
@@ -19,11 +19,15 @@ pip install mailbox-org-api
19
19
  pip install git+https://github.com/heshsum/mailbox-org-api
20
20
  ```
21
21
 
22
- ## Usage
22
+ ## Usage and documentation
23
23
  Basic usage is fairly straightforward. The naming scheme of the functions is similar to the naming at mailbox.org,
24
- but instead of points, it uses underscores (e.g. instead of `mail.add` it's `mail_add`).
24
+ but instead of points, it uses underscores (e.g. instead of `mail.add` it's `mail_add`).
25
+
26
+ Therefore, the functions mirror the functions as provided and documented at mailbox:
27
+ [api.mailbox.org](https://api.mailbox.org)
28
+
25
29
  Additionally, some helper functions for common or more complicated tasks are included to make life a bit easier,
26
- e.g. for changing plans, password and to retrieve invoices.
30
+ e.g. for changing plans, password and to retrieving invoices.
27
31
 
28
32
  ```python
29
33
  from mailbox_org_api import APIClient
@@ -70,6 +74,8 @@ api.mail_set_forwards('foo@bar.com', ['forward1@bar.com', 'forward2@bar.com'])
70
74
  api.deauth()
71
75
  ```
72
76
 
77
+ More information can be found in the [Wiki](https://github.com/heshsum/mailbox-org-api/wiki)
78
+
73
79
  ## Common tasks
74
80
  mailbox_org_api includes a number of helper functions to make common tasks simpler. These include:
75
81
 
@@ -146,21 +152,21 @@ Usage:
146
152
  api.account_invoice_get_token('BMBO-1234-24')
147
153
  ```
148
154
 
149
- ### account_invoice_get_pdf
155
+ ### account_invoice_get_file
150
156
  Invoices are provided as Based64 encoded gz Strings. This function
151
157
  1. takes the invoice ID
152
158
  2. retrieves the token for the invoice
153
159
  3. gets the binary data
154
160
  4. decodes the Base64
155
161
  5. decompresses it
156
- 6. returns the bytes of the actual PDF
162
+ 6. returns the bytes of the actual invoice file
157
163
 
158
164
  Usage
159
165
  ```python
160
166
  invoice_id = 'BMBO-1234-24'
161
- account_name = 'some_user'
167
+ account_name = 'foo'
162
168
  with open(invoice_id + '.pdf', 'w') as file:
163
- file.write(api.account_invoice_get_pdf(account_name, invoice_id))
169
+ file.write(api.account_invoice_get_file(account_name, invoice_id, 'PDF'))
164
170
  ```
165
171
 
166
172
  ## Here be dragons
@@ -5,7 +5,6 @@ import json
5
5
  from typing import Any
6
6
 
7
7
  import requests
8
- import typing_extensions
9
8
  from requests.adapters import HTTPAdapter
10
9
  from requests.exceptions import RequestException
11
10
  from urllib3.util.retry import Retry
@@ -19,11 +18,13 @@ headers = {'content-type': 'application/json'}
19
18
 
20
19
  keys_to_string = ['additional_cloud_quota', 'additional_mail_quota']
21
20
 
21
+
22
22
  class APIClient:
23
23
  """
24
24
  Object for API Client
25
25
  """
26
- def __init__(self, debug_output=False, max_retries=5):
26
+
27
+ def __init__(self, debug_output=False, max_retries=5, request_timeout: int = 30):
27
28
  # URL of the API
28
29
  self.url = "https://api.mailbox.org/v1/"
29
30
 
@@ -59,6 +60,8 @@ class APIClient:
59
60
  adapter = HTTPAdapter(max_retries=retry_strategy)
60
61
  self.session.mount('https://', adapter)
61
62
 
63
+ self.request_timeout = request_timeout
64
+
62
65
  # Increment the request ID
63
66
  def get_jsonrpc_id(self):
64
67
  """Method to create the JSON RPC request ID. """
@@ -89,20 +92,26 @@ class APIClient:
89
92
  # Check if any parameter is 'password' or 'pass'.
90
93
  # Check explicit for these strings as not to interfere with possible other
91
94
  # parameters containing a substring.
95
+ sensitive_keys = ['password', 'pass', 'password_hash', 'new_account_password', 'token',
96
+ 'HPLS-AUTH', 'auth_id', 'telephone_password']
92
97
  for param in print_request['params']:
93
- if param == 'password' or param == 'pass':
94
- # If True, replace the logged value with 'xxx'
98
+ if param in sensitive_keys:
95
99
  print_request['params'][param] = 'xxx'
96
100
 
97
101
  # Print the clean version of the request
98
102
  print('API full request:\t', print_request)
99
103
 
100
- api_response = self.session.post(self.url, data=json.dumps(request))
101
104
  try:
102
- api_response = api_response.json()
105
+ response = self.session.post(self.url, json=request, timeout=self.request_timeout)
106
+ response.raise_for_status()
103
107
  except RequestException as error:
104
- print('Non-JSON response received.\nFull response:\t', api_response,
105
- '\nError:', {error})
108
+ raise APIError(message=f"HTTP request failed: {error}", code=-32000) from error
109
+
110
+ try:
111
+ api_response = response.json()
112
+ except (ValueError, requests.exceptions.JSONDecodeError) as error:
113
+ print(f'API Full response: {response.content}')
114
+ raise APIError(message="Non-JSON response received from API", code=-32700) from error
106
115
  if self.debug_output:
107
116
  print('API full response:\t', api_response)
108
117
 
@@ -128,7 +137,7 @@ class APIClient:
128
137
  :param password: the password
129
138
  :return: the API response for the request
130
139
  """
131
- api_response = self.api_request('auth', {'user':username, 'pass':password})
140
+ api_response = self.api_request('auth', {'user': username, 'pass': password})
132
141
  if api_response['session']:
133
142
  # Level gives information about the calls available
134
143
  self.level = api_response["level"]
@@ -146,7 +155,7 @@ class APIClient:
146
155
  Function to close the current API session
147
156
  :return: True if the API session is closed, False otherwise
148
157
  """
149
- api_response = self.api_request('deauth',{})
158
+ api_response = self.api_request('deauth', {})
150
159
  if api_response:
151
160
  # The auth header is stripped
152
161
  self.session.headers.pop("HPLS-AUTH")
@@ -159,7 +168,7 @@ class APIClient:
159
168
  Function for hello world, just to test the connection
160
169
  :return: The response from the mailbox.org Business API
161
170
  """
162
- return self.api_request('hello.world',{})
171
+ return self.api_request('hello.world', {})
163
172
 
164
173
  def hello_innerworld(self):
165
174
  """
@@ -213,9 +222,9 @@ class APIClient:
213
222
  :param account: the account name to get
214
223
  :return: the response from the mailbox.org Business API
215
224
  """
216
- return self.api_request('account.get', {'account':account})
225
+ return self.api_request('account.get', {'account': account})
217
226
 
218
- def account_get_object(self, account:str) -> Account:
227
+ def account_get_object(self, account: str) -> Account:
219
228
  result = self.api_request('account.get', {'account': account})
220
229
  account_object = Account(account)
221
230
  for k, v in result.items():
@@ -259,7 +268,7 @@ class APIClient:
259
268
  :param account: the account name to delete
260
269
  :return: the response from the mailbox.org Business API
261
270
  """
262
- return self.api_request('account.del', {'account':account})
271
+ return self.api_request('account.del', {'account': account})
263
272
 
264
273
  def account_invoice_list(self, account: str) -> dict:
265
274
  """
@@ -267,7 +276,7 @@ class APIClient:
267
276
  :param account: the account name to list
268
277
  :return: the response from the mailbox.org Business API
269
278
  """
270
- return self.api_request('account.invoice.list', {'account':account})
279
+ return self.api_request('account.invoice.list', {'account': account})
271
280
 
272
281
  def account_invoice_get(self, account: str, token: str) -> dict:
273
282
  """
@@ -276,7 +285,7 @@ class APIClient:
276
285
  :param token: the token for the invoice
277
286
  :return: the response from the mailbox.org Business API - the invoice as a Base64 encoded gzipped string
278
287
  """
279
- return self.api_request('account.invoice.get', {'account':account, 'token':token})
288
+ return self.api_request('account.invoice.get', {'account': account, 'token': token})
280
289
 
281
290
  def account_invoice_get_object(self, account: str, invoice_id: str) -> Invoice:
282
291
  """
@@ -303,7 +312,7 @@ class APIClient:
303
312
  """
304
313
  Function to get a list of all invoice ids for a specific account
305
314
  """
306
- response = self.api_request('account.invoice.list', {'account':account})
315
+ response = self.api_request('account.invoice.list', {'account': account})
307
316
  invoices = []
308
317
 
309
318
  for invoice in response:
@@ -316,7 +325,7 @@ class APIClient:
316
325
  """
317
326
  invoices = self.account_invoice_list(account)
318
327
  open_invoices = []
319
-
328
+
320
329
  for i in invoices:
321
330
  if i['status'] == 'open':
322
331
  open_invoices.append(i)
@@ -335,22 +344,12 @@ class APIClient:
335
344
  return invoice['token']
336
345
  raise ValueError('Invoice not found')
337
346
 
338
- @typing_extensions.deprecated('Use account_invoice_get_file instead')
339
- def account_invoice_get_pdf(self, account: str, invoice_id: str) -> bytes:
340
- """
341
- Function to get a specific invoice as a PDf-file
342
- :param account: the account name
343
- :param invoice_id: the invoice ID
344
- :return: the PDF as bytes
345
- """
346
- return self.account_invoice_get_file(account, invoice_id, 'pdf')
347
-
348
347
  def account_invoice_get_file(self, account: str, invoice_id: str, file_type: str) -> bytes:
349
348
  """
350
349
  Function to get a specific invoice as a PDf-file
351
350
  :param account: the account name
352
351
  :param invoice_id: the invoice ID
353
- :param file_type: The file type to return. Valid: csv, pdf and xml
352
+ :param file_type: The file type to return. Valid: CSV, PDF and XML
354
353
  :return: the file as bytes
355
354
  """
356
355
  if file_type not in ('csv', 'pdf', 'xml'):
@@ -368,17 +367,30 @@ class APIClient:
368
367
  # Take the Base64 encoded data (response['bin']), decode the Base 64, decompress the gz and return the bytes
369
368
  return zlib.decompress(base64.b64decode(response['bin']))
370
369
 
371
- def domain_list(self, account:str, search_filter:str | None = None) -> dict:
370
+ def domain_list(self, account: str, search_filter: str | None = None) -> dict:
372
371
  """
373
372
  Function to list all domains
374
373
  :param account: the account to list domains for
375
374
  :param search_filter: String for optional search filter
376
375
  :return: the API response
377
376
  """
378
- params = {'account':account}
377
+ params = {'account': account}
379
378
  if isinstance(search_filter, str):
380
- params.update({'filter':str(search_filter)})
381
- return self.api_request('domain.list',params)
379
+ params.update({'filter': str(search_filter)})
380
+ return self.api_request('domain.list', params)
381
+
382
+ def domain_get_list(self, account: str, search_filter: str | None = None) -> list:
383
+ """
384
+ Function to get a List object with domain names for a given account
385
+ :param account: the account to list domains for
386
+ :param search_filter: String for optional search filter
387
+ :return: a List object containing the domain names
388
+ """
389
+ result = self.domain_list(account, search_filter)
390
+ domains = []
391
+ for i in result:
392
+ domains.append(i['domain'])
393
+ return domains
382
394
 
383
395
  def domain_add(self, account: str, domain: str, password: str, **kwargs) -> dict:
384
396
  """
@@ -390,7 +402,7 @@ class APIClient:
390
402
  See documentation here: https://api.mailbox.org/v1/doc/methods/index.html#domain-add
391
403
  :return: the API response
392
404
  """
393
- params = {'account':account, 'domain':domain, 'password':password}
405
+ params = {'account': account, 'domain': domain, 'password': password}
394
406
  params.update({k: v for k, v in kwargs.items() if v is not None})
395
407
  return self.api_request('domain.add', params)
396
408
 
@@ -400,7 +412,7 @@ class APIClient:
400
412
  :param domain: the domain to get
401
413
  :return: the API response
402
414
  """
403
- return self.api_request('domain.get',{'domain':domain})
415
+ return self.api_request('domain.get', {'domain': domain})
404
416
 
405
417
  def domain_capabilities_set(self, domain: str, capabilities: list) -> dict:
406
418
  """
@@ -445,7 +457,7 @@ class APIClient:
445
457
  :param domain: the domain to delete
446
458
  :return: the API response
447
459
  """
448
- return self.api_request('domain.del', {'account':account, 'domain':domain})
460
+ return self.api_request('domain.del', {'account': account, 'domain': domain})
449
461
 
450
462
  def domain_validate_spf(self, domain: str) -> dict:
451
463
  """
@@ -453,9 +465,9 @@ class APIClient:
453
465
  :param domain: the domain to validate
454
466
  :return: the API response - information about the SPF config
455
467
  """
456
- return self.api_request('domain.validate.spf', {'domain':domain})
468
+ return self.api_request('domain.validate.spf', {'domain': domain})
457
469
 
458
- def mail_list(self, domain: str, details: bool = False, page_size: int | None= None, page: int | None = None,
470
+ def mail_list(self, domain: str, details: bool = False, page_size: int | None = None, page: int | None = None,
459
471
  sort_field: str | None = None, sort_order: str | None = None) -> dict:
460
472
  """
461
473
  Function to list all mailboxes
@@ -467,8 +479,8 @@ class APIClient:
467
479
  :param sort_order: the order to sort by. Possible values: 'asc', 'desc'
468
480
  :return: the response from the mailbox.org Business API
469
481
  """
470
- args = {'domain':domain, 'details':details, 'page_size':page_size, 'page':page, 'sort_field':sort_field,
471
- 'sort_order':sort_order}
482
+ args = {'domain': domain, 'details': details, 'page_size': page_size, 'page': page, 'sort_field': sort_field,
483
+ 'sort_order': sort_order}
472
484
 
473
485
  # Allowed sort fields as documented here: https://api.mailbox.org/v1/doc/methods/index.html#mail-list
474
486
  mail_list_sort_field = ['mail', 'first_name', 'last_name', 'status', 'domain', 'plan', 'type', 'creation_date']
@@ -490,8 +502,20 @@ class APIClient:
490
502
 
491
503
  return self.api_request('mail.list', params)
492
504
 
493
- def mail_add(self, mail:str, password: str, plan: str, first_name: str, last_name: str, inboxsave: bool = True,
494
- forwards: list | None= None, **kwargs) -> dict:
505
+ def mail_get_list(self, domain: str) -> list:
506
+ """
507
+ Function to get a list of all mailboxes of a domain as a List object.
508
+ :param domain: the domain to list all mailboxes for.
509
+ :return: a List object containing all mail addresses of the given domain.
510
+ """
511
+ result = self.mail_list(domain)
512
+ mails = []
513
+ for i in result:
514
+ mails.append(i['mail'])
515
+ return mails
516
+
517
+ def mail_add(self, mail: str, password: str, plan: str, first_name: str, last_name: str, inboxsave: bool = True,
518
+ forwards: list | None = None, **kwargs) -> dict:
495
519
  """
496
520
  Function to add a mail
497
521
  :param mail: the mail to add
@@ -510,10 +534,10 @@ class APIClient:
510
534
  'create_own_context': bool, 'title': str, 'birthday': str, 'position': str,
511
535
  'department': str, 'company': str, 'street': str, 'postal_code': str, 'city': str,
512
536
  'phone': str, 'fax': str, 'cell_phone': str, 'recover': bool, 'skip_welcome_mail': bool,
513
- 'uid_extern': str, 'language': str, 'evac_force_activation':bool}
537
+ 'uid_extern': str, 'language': str, 'evac_force_activation': bool}
514
538
 
515
539
  if password and 'password_hash' in kwargs:
516
- raise KeyError('''Simultaneous usage of 'password' and 'password_hash' not allowed.
540
+ raise KeyError('''Simultaneous usage of 'password' and 'password_hash' not allowed.
517
541
  Use 'password' = None if password_hash is used.''')
518
542
 
519
543
  if forwards is None:
@@ -526,8 +550,8 @@ class APIClient:
526
550
  kwargs[k] = str(kwargs[k])
527
551
 
528
552
  # After validation, build parameter list from mail and kwargs
529
- params = {'mail':mail, 'password':password, 'plan':plan, 'first_name':first_name, 'last_name':last_name,
530
- 'inboxsave':inboxsave, 'forwards':forwards}
553
+ params = {'mail': mail, 'password': password, 'plan': plan, 'first_name': first_name, 'last_name': last_name,
554
+ 'inboxsave': inboxsave, 'forwards': forwards}
531
555
  params.update({k: v for k, v in kwargs.items() if v is not None})
532
556
  return self.api_request('mail.add', params)
533
557
 
@@ -538,10 +562,10 @@ class APIClient:
538
562
  :param include_quota_usage: True if the quota usage should be included in the request
539
563
  :return the response for the request
540
564
  """
541
- return self.api_request('mail.get', {'mail':mail, 'include_quota_usage':include_quota_usage})
565
+ return self.api_request('mail.get', {'mail': mail, 'include_quota_usage': include_quota_usage})
542
566
 
543
- def mail_get_object(self, mail:str) -> Mail:
544
- result = self.api_request('mail.get', {'mail':mail, 'include_quota_usage':False})
567
+ def mail_get_object(self, mail: str) -> Mail:
568
+ result = self.api_request('mail.get', {'mail': mail, 'include_quota_usage': False})
545
569
  mail_object = Mail(mail)
546
570
  for k, v in result.items():
547
571
  if v is not None:
@@ -558,17 +582,17 @@ class APIClient:
558
582
  """
559
583
  # Allowed attributes as documented here: https://api.mailbox.org/v1/doc/methods/index.html#mail-set
560
584
  allowed_parameters = {'password': str, 'password_hash': str, 'same_password_allowed': bool,
561
- 'require_reset_password': bool, 'plan': str, 'additional_mail_quota': int,
562
- 'additional_cloud_quota': int, 'first_name': str, 'last_name': str, 'inboxsave': bool,
563
- 'forwards': list, 'aliases': list, 'alternate_mail': str, 'memo': str, 'allow_nets': str,
564
- 'active': bool, 'title': str, 'birthday': str, 'position': str, 'department': str,
565
- 'company': str, 'street': str, 'postal_code': str, 'city': str, 'phone': str, 'fax': str,
566
- 'cell_phone': str, 'uid_extern': str, 'language': str, 'deletion_date': str}
585
+ 'require_reset_password': bool, 'plan': str, 'additional_mail_quota': int,
586
+ 'additional_cloud_quota': int, 'first_name': str, 'last_name': str, 'inboxsave': bool,
587
+ 'forwards': list, 'aliases': list, 'alternate_mail': str, 'memo': str, 'allow_nets': str,
588
+ 'active': bool, 'title': str, 'birthday': str, 'position': str, 'department': str,
589
+ 'company': str, 'street': str, 'postal_code': str, 'city': str, 'phone': str, 'fax': str,
590
+ 'cell_phone': str, 'uid_extern': str, 'language': str, 'deletion_date': str}
567
591
 
568
592
  if 'password' in kwargs and 'password_hash' in kwargs:
569
- raise KeyError('''Simultaneous usage of 'password' and 'password_hash' not allowed.''')
593
+ raise KeyError('''Simultaneous usage of 'password' and 'password_hash' not allowed.''')
570
594
 
571
- if 'additional_mail_quota' in kwargs or 'additional_cloud_quota' in kwargs and 'plan' not in kwargs:
595
+ if ('additional_mail_quota' in kwargs or 'additional_cloud_quota' in kwargs) and 'plan' not in kwargs:
572
596
  raise KeyError('''If setting additional quota, 'plan' must be given.''')
573
597
 
574
598
  # Check for each argument in kwargs if it is a valid function parameter.
@@ -581,9 +605,9 @@ class APIClient:
581
605
  kwargs[k] = str(kwargs[k])
582
606
 
583
607
  # After validation, build parameter list from mail and kwargs
584
- params = {'mail':mail}
608
+ params = {'mail': mail}
585
609
  params.update({k: v for k, v in kwargs.items() if v is not None})
586
-
610
+
587
611
  return self.api_request('mail.set', params)
588
612
 
589
613
  def mail_set_password(self, mail: str, password: str) -> dict:
@@ -593,7 +617,7 @@ class APIClient:
593
617
  :param password: the password to set
594
618
  :return: the response for the request
595
619
  """
596
- return self.api_request('mail.set', {'mail':mail, 'password':password})
620
+ return self.api_request('mail.set', {'mail': mail, 'password': password})
597
621
 
598
622
  def mail_set_password_require_reset(self, mail: str, password: str) -> dict:
599
623
  """
@@ -611,7 +635,7 @@ class APIClient:
611
635
  :param plan: the plan to set
612
636
  :return: the response for the request
613
637
  """
614
- return self.api_request('mail.set', {'mail':mail, 'plan':plan})
638
+ return self.api_request('mail.set', {'mail': mail, 'plan': plan})
615
639
 
616
640
  def mail_set_forwards(self, mail: str, forwards: list) -> dict:
617
641
  """
@@ -620,7 +644,7 @@ class APIClient:
620
644
  :param forwards: a list of addresses to forwards mails to
621
645
  :return: the response for the request
622
646
  """
623
- return self.api_request('mail.set', {'mail':mail, 'forwards':forwards})
647
+ return self.api_request('mail.set', {'mail': mail, 'forwards': forwards})
624
648
 
625
649
  def mail_set_aliases(self, mail: str, aliases: list) -> dict:
626
650
  """
@@ -629,7 +653,7 @@ class APIClient:
629
653
  :param aliases: a list of aliases to set
630
654
  :return: the response for the request
631
655
  """
632
- return self.api_request('mail.set', {'mail':mail, 'aliases':aliases})
656
+ return self.api_request('mail.set', {'mail': mail, 'aliases': aliases})
633
657
 
634
658
  def mail_set_state(self, mail: str, active: bool) -> dict:
635
659
  """
@@ -638,7 +662,7 @@ class APIClient:
638
662
  :param active: True if the mail should be active, False if it shall be deactivated
639
663
  :return: the response for the request
640
664
  """
641
- return self.api_request('mail.set', {'mail':mail, 'active':active})
665
+ return self.api_request('mail.set', {'mail': mail, 'active': active})
642
666
 
643
667
  def mail_set_additional_mail_quota(self, mail: str, quota: int) -> dict:
644
668
  """
@@ -648,7 +672,7 @@ class APIClient:
648
672
  :return: the response for the request
649
673
  """
650
674
  plan = self.mail_get(mail)['plan']
651
- return self.api_request('mail.set', {'mail': mail, 'plan':plan, 'additional_mail_quota':quota})
675
+ return self.api_request('mail.set', {'mail': mail, 'plan': plan, 'additional_mail_quota': quota})
652
676
 
653
677
  def mail_set_additional_cloud_quota(self, mail: str, quota: int) -> dict:
654
678
  """
@@ -670,7 +694,7 @@ class APIClient:
670
694
  :return: the response for the request
671
695
  """
672
696
  return self.api_request('mail.set', {'mail': mail, 'deletion_date': deletion_date,
673
- 'active': False})
697
+ 'active': False})
674
698
 
675
699
  def mail_capabilities_set(self, mail: str, capabilities: list) -> dict:
676
700
  """
@@ -687,7 +711,7 @@ class APIClient:
687
711
  invalid = set(capabilities) - set(mail_capabilities)
688
712
  if invalid:
689
713
  raise ValueError(f'Invalid capabilities found: {", ".join(invalid)}')
690
- params = {'mail':mail, 'capabilities':list(capabilities)}
714
+ params = {'mail': mail, 'capabilities': list(capabilities)}
691
715
  return self.api_request('mail.capabilities.set', params)
692
716
 
693
717
  def mail_del(self, mail: str) -> dict:
@@ -696,17 +720,17 @@ class APIClient:
696
720
  :param mail: the mail to delete
697
721
  :return: the response for the request
698
722
  """
699
- return self.api_request('mail.del', {'mail':mail})
723
+ return self.api_request('mail.del', {'mail': mail})
700
724
 
701
- def mail_apppassword_list(self, mail:str) -> dict:
725
+ def mail_apppassword_list(self, mail: str) -> dict:
702
726
  """
703
727
  Function to list all app passwords of a given mail
704
728
  :param mail: the mail to list app passwords for
705
729
  :return: the response for the request
706
730
  """
707
- return self.api_request('mail.apppassword.list', {'mail':mail})
731
+ return self.api_request('mail.apppassword.list', {'mail': mail})
708
732
 
709
- def mail_apppassword_add(self, mail:str, memo:str, imap_allowed:bool = True, smtp_allowed:bool = True) -> dict:
733
+ def mail_apppassword_add(self, mail: str, memo: str, imap_allowed: bool = True, smtp_allowed: bool = True) -> dict:
710
734
  """
711
735
  Function to generate a new mail app password for a mail
712
736
  :param mail: the mail to generate a new mail app password
@@ -715,8 +739,8 @@ class APIClient:
715
739
  :param smtp_allowed: True if the app password should be allowed to use an SMTP server. Default: True
716
740
  :return: the response for the request
717
741
  """
718
- return self.api_request('mail.apppassword.add', {'mail':mail, 'memo':memo,
719
- 'imap_allowed':imap_allowed, 'smtp_allowed':smtp_allowed})
742
+ return self.api_request('mail.apppassword.add', {'mail': mail, 'memo': memo,
743
+ 'imap_allowed': imap_allowed, 'smtp_allowed': smtp_allowed})
720
744
 
721
745
  def mail_apppassword_del(self, apppassword_id: int) -> dict:
722
746
  """
@@ -724,7 +748,7 @@ class APIClient:
724
748
  :param apppassword_id: the id of the mail app password
725
749
  :return: the response for the request
726
750
  """
727
- return self.api_request('mail.apppassword.del', {'id':apppassword_id})
751
+ return self.api_request('mail.apppassword.del', {'id': apppassword_id})
728
752
 
729
753
  def mail_externaluid(self, account: str, uid_extern: str) -> dict:
730
754
  """
@@ -733,7 +757,7 @@ class APIClient:
733
757
  :param uid_extern: the external UID to get a mail for
734
758
  :return: mailbox API response - an array with the mail details
735
759
  """
736
- return self.api_request('mail.externaluid', {'account':account, 'uid_extern':uid_extern})
760
+ return self.api_request('mail.externaluid', {'account': account, 'uid_extern': uid_extern})
737
761
 
738
762
  def mail_backup_list(self, mail: str) -> dict:
739
763
  """
@@ -741,7 +765,7 @@ class APIClient:
741
765
  :param mail: the mail to list backups for
742
766
  :return: mailbox API response - an array with the backup numbers and dates
743
767
  """
744
- return self.api_request('mail.backup.list', {'mail':mail})
768
+ return self.api_request('mail.backup.list', {'mail': mail})
745
769
 
746
770
  def mail_backup_import(self, mail: str, backup_id: str, time: str, backup_filter: str) -> dict:
747
771
  """
@@ -753,7 +777,7 @@ class APIClient:
753
777
  :return: mailbox API response - an array with the backup numbers and dates
754
778
  """
755
779
  return self.api_request('mail.backup.import',
756
- {'mail':mail, 'id':backup_id, 'time':time, 'filter':backup_filter})
780
+ {'mail': mail, 'id': backup_id, 'time': time, 'filter': backup_filter})
757
781
 
758
782
  def mail_spamprotect_get(self, mail: str) -> dict:
759
783
  """
@@ -761,7 +785,7 @@ class APIClient:
761
785
  :param mail: the mail to get the spam settings for
762
786
  :return: mailbox API response - an array with the spam settings
763
787
  """
764
- return self.api_request('mail.spamprotect.get', {'mail':mail})
788
+ return self.api_request('mail.spamprotect.get', {'mail': mail})
765
789
 
766
790
  def mail_spamprotect_set(self, mail: str, greylist: bool, smtp_plausibility: bool, rbl: bool,
767
791
  bypass_banned_checks: bool, tag2level: float, killlevel: str, route_to: str) -> dict:
@@ -781,27 +805,28 @@ class APIClient:
781
805
  raise ValueError('''Invalid value for killlevel. Only 'reject' or 'route' are allowed''')
782
806
 
783
807
  return self.api_request('mail.spamprotect.set',
784
- {'mail':mail, 'greylist': bool2str(greylist),
785
- 'smtp_plausibility':bool2str(smtp_plausibility), 'rbl':bool2str(rbl),
786
- 'bypass_banned_checks':bool2str(bypass_banned_checks), 'tag2level':round(tag2level, 1),
787
- 'killevel':killlevel, 'route_to':route_to})
808
+ {'mail': mail, 'greylist': bool2str(greylist),
809
+ 'smtp_plausibility': bool2str(smtp_plausibility), 'rbl': bool2str(rbl),
810
+ 'bypass_banned_checks': bool2str(bypass_banned_checks),
811
+ 'tag2level': round(tag2level, 1),
812
+ 'killevel': killlevel, 'route_to': route_to})
788
813
 
789
814
  def mail_blacklist_list(self, mail: str) -> dict:
790
815
  """
791
- Function to list the mail blacklist for a given mail address
792
- :param mail: the mail to list the blacklist for
793
- :return: mailbox API response - an array with the complete blacklist of the mail
816
+ Function to list the mail blacklist for a given mail address.
817
+ :param mail: the mail to list the blacklist for.
818
+ :return: mailbox API response - an array with the complete blacklist of the mail.
794
819
  """
795
- return self.api_request('mail.blacklist.list', {'mail':mail})
820
+ return self.api_request('mail.blacklist.list', {'mail': mail})
796
821
 
797
822
  def mail_blacklist_add(self, mail: str, add_address: str) -> dict:
798
823
  """
799
- Function to add a mail to a blacklist of a mail address
800
- :param mail: the mail of the owner of the blacklist
801
- :param add_address: the address to add to the blacklist
802
- :return: mailbox API response - an array with the complete blacklist of the mail
824
+ Function to add a mail to a blacklist of a mail address.
825
+ :param mail: the mail of the owner of the blacklist.
826
+ :param add_address: the address to add to the blacklist.
827
+ :return: mailbox API response - an array with the complete blacklist of the mail.
803
828
  """
804
- return self.api_request('mail.blacklist.add', {'mail':mail, 'add_address':add_address})
829
+ return self.api_request('mail.blacklist.add', {'mail': mail, 'add_address': add_address})
805
830
 
806
831
  def mail_blacklist_del(self, mail: str, delete_address: str) -> dict:
807
832
  """
@@ -810,7 +835,7 @@ class APIClient:
810
835
  :param delete_address: the address to delete from the blacklist
811
836
  :return: mailbox API response - an array with the complete blacklist of the mail
812
837
  """
813
- return self.api_request('mail.blacklist.del', {'mail':mail, 'delete_address':delete_address})
838
+ return self.api_request('mail.blacklist.del', {'mail': mail, 'delete_address': delete_address})
814
839
 
815
840
  def mail_vacation_get(self, mail: str) -> dict:
816
841
  """
@@ -818,7 +843,7 @@ class APIClient:
818
843
  :param mail: the mail to get the vacation notice for
819
844
  :return: mailbox API response - the vacation notice of the mail
820
845
  """
821
- return self.api_request('mail.vacation.get', {'mail':mail})
846
+ return self.api_request('mail.vacation.get', {'mail': mail})
822
847
 
823
848
  def mail_vacation_set(self, mail: str, subject: str, body: str, start_date: str, end_date: str,
824
849
  additional_mail_addresses: list | None = None) -> dict:
@@ -832,8 +857,8 @@ class APIClient:
832
857
  :param additional_mail_addresses: list of addresses to add to the vacation notice (optional)
833
858
  :return: mailbox API response - array with result 'true' of the request, code and message in case of an error
834
859
  """
835
- params = {'mail':mail, 'subject':subject, 'body':body,'start_date':start_date, 'end_date':end_date,
836
- 'additional_mail_addresses':additional_mail_addresses}
860
+ params = {'mail': mail, 'subject': subject, 'body': body, 'start_date': start_date, 'end_date': end_date,
861
+ 'additional_mail_addresses': additional_mail_addresses}
837
862
 
838
863
  # If no additional_mail_addresses are given, remove the parameter from the request
839
864
  if not additional_mail_addresses:
@@ -841,7 +866,7 @@ class APIClient:
841
866
 
842
867
  return self.api_request('mail.vacation.set', params)
843
868
 
844
- def group_list(self, account:str | None = None) -> dict:
869
+ def group_list(self, account: str | None = None) -> dict:
845
870
  """
846
871
  Function to list all groups for an account
847
872
  :param account: optional parameter for the account to list the groups for
@@ -885,13 +910,13 @@ class APIClient:
885
910
  :param account: optional parameter for the account to add the group for
886
911
  :return: mailbox API response - True if the group was added, False otherwise
887
912
  """
888
- params = {'name':name, 'display_name':display_name, 'mail_addresses_to_add':mail_addresses_to_add}
913
+ params = {'name': name, 'display_name': display_name, 'mail_addresses_to_add': mail_addresses_to_add}
889
914
  if account:
890
915
  params['account'] = account
891
916
  return self.api_request('group.add', params)
892
917
 
893
918
  def group_set(self, group_id: int, display_name: str, mail_addresses_to_add: list | None = None,
894
- mail_addresses_to_remove: list | None = None, account: str | None= None) -> dict:
919
+ mail_addresses_to_remove: list | None = None, account: str | None = None) -> dict:
895
920
  """
896
921
  Function to modify a group. Either mail_addresses_to_add or mail_addresses_to_remove have to be specified.
897
922
  :param group_id: the group's id of the group to modify
@@ -905,8 +930,8 @@ class APIClient:
905
930
  if mail_addresses_to_add is None and mail_addresses_to_remove is None:
906
931
  raise ValueError('mail_addresses_to_add or mail_addresses_to_remove are required')
907
932
 
908
- params = {'group_id':group_id, 'display_name':display_name, 'mail_addresses_to_add':mail_addresses_to_add,
909
- 'mail_addresses_to_remove':mail_addresses_to_remove}
933
+ params = {'group_id': group_id, 'display_name': display_name, 'mail_addresses_to_add': mail_addresses_to_add,
934
+ 'mail_addresses_to_remove': mail_addresses_to_remove}
910
935
  if account:
911
936
  params['account'] = account
912
937
  return self.api_request('group.set', params)
@@ -917,16 +942,16 @@ class APIClient:
917
942
  :param mail: the mail to query
918
943
  :return: mailbox API response - a list of available password reset methods
919
944
  """
920
- return self.api_request('mail.passwordreset.listmethods', {'mail':mail})
945
+ return self.api_request('mail.passwordreset.listmethods', {'mail': mail})
921
946
 
922
947
  def mail_passwordreset_sendsms(self, mail: str, cell_phone: str) -> dict:
923
948
  """
924
949
  Function to send a password reset for a mail via SMS
925
950
  :param mail: the mail to send the SMS for
926
951
  :param cell_phone: the cell phone number of the mailbox
927
- :return: mailbox API response - True if the SMS was sent, False otherwise
952
+ :return: API response from mailbox - True if the SMS was sent, False otherwise
928
953
  """
929
- return self.api_request('mail.passwordreset.sendsms',{'mail':mail, 'cell_phone':cell_phone})
954
+ return self.api_request('mail.passwordreset.sendsms', {'mail': mail, 'cell_phone': cell_phone})
930
955
 
931
956
  def mail_passwordreset_setpassword(self, mail: str, token: str, password: str) -> dict:
932
957
  """
@@ -937,7 +962,7 @@ class APIClient:
937
962
  :return: mailbox API response - True if the password was set, False otherwise
938
963
  """
939
964
  return self.api_request('mail.passwordreset.setpassword',
940
- {'mail':mail, 'token':token, 'password':password})
965
+ {'mail': mail, 'token': token, 'password': password})
941
966
 
942
967
  def context_list(self, account: str) -> dict:
943
968
  """
@@ -945,7 +970,7 @@ class APIClient:
945
970
  :param account: the account to list all contexts for
946
971
  :return: mailbox API response - an array with key 'context id' and value 'associated domains'
947
972
  """
948
- return self.api_request('context.list', {'account':account})
973
+ return self.api_request('context.list', {'account': account})
949
974
 
950
975
  def search(self, query: str, get_account_summary: bool = False, get_extended_mail_result: bool = False) -> dict:
951
976
  """
@@ -955,8 +980,8 @@ class APIClient:
955
980
  :param get_extended_mail_result: whether to return more information about mailboxes found
956
981
  :return: the mailbox API response for the request - an array with results for accounts, domains and mailboxes
957
982
  """
958
- return self.api_request('search', {'query':query, 'get_account_summary':get_account_summary,
959
- 'get_extended_mail_result':get_extended_mail_result})
983
+ return self.api_request('search', {'query': query, 'get_account_summary': get_account_summary,
984
+ 'get_extended_mail_result': get_extended_mail_result})
960
985
 
961
986
  def mailinglist_list(self, account: str) -> dict:
962
987
  """
@@ -964,7 +989,7 @@ class APIClient:
964
989
  :param account: the account to list all mailing lists for
965
990
  :return: a dict containing the list of mailing lists
966
991
  """
967
- return self.api_request('mailinglist.list', {'account':account})
992
+ return self.api_request('mailinglist.list', {'account': account})
968
993
 
969
994
  def mailinglist_add(self, mailinglist: str, password: str, account: str, adminmail: str | None = None) -> dict:
970
995
  """
@@ -975,8 +1000,8 @@ class APIClient:
975
1000
  :param adminmail: admin email address of the mailing list (optional)
976
1001
  :return: True if the mailing list was added, error code otherwise
977
1002
  """
978
- return self.api_request('mailinglist.add', {'mailinglist':mailinglist, 'password':password,
979
- 'account':account, 'adminmail':adminmail})
1003
+ return self.api_request('mailinglist.add', {'mailinglist': mailinglist, 'password': password,
1004
+ 'account': account, 'adminmail': adminmail})
980
1005
 
981
1006
  def mailinglist_get(self, mailinglist: str, account: str) -> dict:
982
1007
  """
@@ -985,20 +1010,20 @@ class APIClient:
985
1010
  :param account: the account of the mailing list
986
1011
  :return: the mailbox API response for the request - a dict of the mailing list
987
1012
  """
988
- return self.api_request('mailinglist.get', {'mailinglist':mailinglist, 'account':account})
1013
+ return self.api_request('mailinglist.get', {'mailinglist': mailinglist, 'account': account})
989
1014
 
990
1015
  def mailinglist_set(self, mailinglist: str, account: str, password: str | None = None,
991
1016
  adminmail: str | None = None) -> dict:
992
1017
  """
993
- Function to change a mailing list
994
- :param mailinglist: the mailing list to change
995
- :param password: the password of the mailing list
996
- :param account: the account of the mailing list (optional)
997
- :param adminmail: admin email address of the mailing list (optional)
998
- :return: the mailbox API response for the request - True if the mailing list was changed, error code otherwise
1018
+ Function to change a mailing list.
1019
+ :param mailinglist: the mailing list to change.
1020
+ :param password: the password of the mailing list.
1021
+ :param account: the account of the mailing list (optional).
1022
+ :param adminmail: admin email address of the mailing list (optional).
1023
+ :return: the mailbox API response for the request - True if the mailing list was changed, error code otherwise.
999
1024
  """
1000
- return self.api_request('mailinglist.set', {'mailinglist':mailinglist, 'account':account,
1001
- 'password':password, 'adminmail':adminmail})
1025
+ return self.api_request('mailinglist.set', {'mailinglist': mailinglist, 'account': account,
1026
+ 'password': password, 'adminmail': adminmail})
1002
1027
 
1003
1028
  def mailinglist_del(self, mailinglist: str, account: str) -> dict:
1004
1029
  """
@@ -1007,7 +1032,7 @@ class APIClient:
1007
1032
  :param account: the account of the mailing list
1008
1033
  :return: the mailbox API response for the request - True if the mailing list was deleted, error code otherwise
1009
1034
  """
1010
- return self.api_request('mailinglist.del', {'mailinglist':mailinglist, 'account':account})
1035
+ return self.api_request('mailinglist.del', {'mailinglist': mailinglist, 'account': account})
1011
1036
 
1012
1037
  def additionalmailaccount_list(self, parent_mail: str) -> dict:
1013
1038
  """
@@ -1015,7 +1040,7 @@ class APIClient:
1015
1040
  :param parent_mail: the parent mail to list additional mail accounts for
1016
1041
  :return: a dict containing the parent mail account and a list of additional mail accounts
1017
1042
  """
1018
- return self.api_request('additionalmailaccount.list', {'parent_mail':parent_mail})
1043
+ return self.api_request('additionalmailaccount.list', {'parent_mail': parent_mail})
1019
1044
 
1020
1045
  def additionalmailaccount_add(self, parent_mail: str, new_account_mail: str, new_account_password: str,
1021
1046
  primary_address: str | None = None, mail_server: str = 'imap.mailbox.org',
@@ -1031,23 +1056,23 @@ class APIClient:
1031
1056
  2. mail server settings are grouped
1032
1057
  3. transport server settings are grouped
1033
1058
  4. Ports are integers
1034
- :param new_account_mail: the additional mail address to add
1035
- :param new_account_password: the password of the additional mail address
1036
- :param parent_mail: the mail address to add the additional mail account to
1037
- :param primary_address: the primary 'address from' for the additional mail address
1038
- :param mail_server: the IMAP server to use
1039
- :param mail_port: the port of the IMAP server
1040
- :param transport_server: the SMTP server to use
1041
- :param transport_port: the port of the SMTP server
1042
- :param mail_secure: whether to use SSL for IMAP
1043
- :param mail_starttls: whether to use STARTTLS for IMAP
1044
- :param transport_secure: whether to use SSL for SMTP
1045
- :param transport_starttls: whether to use STARTTLS for SMTP
1046
- :param trash_folder: name of the trash folder
1047
- :param sent_folder: name of the sent folder
1048
- :param drafts_folder: name of the drafts folder
1049
- :param spam_folder: name of the spam folder
1050
- :return: the response for the request - True if adding was successful, error code otherwise
1059
+ :param new_account_mail: the additional mail address to add.
1060
+ :param new_account_password: the password of the additional mail address.
1061
+ :param parent_mail: the mail address to add the additional mail account to.
1062
+ :param primary_address: the primary 'address from' for the additional mail address.
1063
+ :param mail_server: the IMAP server to use.
1064
+ :param mail_port: the port of the IMAP server.
1065
+ :param transport_server: the SMTP server to use.
1066
+ :param transport_port: the port of the SMTP server.
1067
+ :param mail_secure: whether to use SSL for IMAP.
1068
+ :param mail_starttls: whether to use STARTTLS for IMAP.
1069
+ :param transport_secure: whether to use SSL for SMTP.
1070
+ :param transport_starttls: whether to use STARTTLS for SMTP.
1071
+ :param trash_folder: name of the trash folder.
1072
+ :param sent_folder: name of the sent folder.
1073
+ :param drafts_folder: name of the drafts folder.
1074
+ :param spam_folder: name of the spam folder.
1075
+ :return: the response for the request - True if adding was successful, error code otherwise.
1051
1076
  """
1052
1077
  return self.api_request('additionalmailaccount.add', {'new_account_mail': new_account_mail,
1053
1078
  'new_account_password': new_account_password,
@@ -1073,7 +1098,7 @@ class APIClient:
1073
1098
  :return: True if the account was deleted, error code otherwise
1074
1099
  """
1075
1100
  return self.api_request('additionalmailaccount.delete',
1076
- {'parent_mail':parent_mail, 'account_mail':account_mail})
1101
+ {'parent_mail': parent_mail, 'account_mail': account_mail})
1077
1102
 
1078
1103
  def evac_activate(self):
1079
1104
  """
@@ -1083,7 +1108,7 @@ class APIClient:
1083
1108
  """
1084
1109
  return self.api_request('evac_activate', {})
1085
1110
 
1086
- def evac_resetaccount(self, delete_mail_accounts_and_domains:bool = False) -> dict:
1111
+ def evac_resetaccount(self, delete_mail_accounts_and_domains: bool = False) -> dict:
1087
1112
  """
1088
1113
  Function to reset a mailbox EVAC account.
1089
1114
  Note: this needs a special permission from mailbox.
@@ -1093,16 +1118,15 @@ class APIClient:
1093
1118
  return self.api_request('evac_resetaccount',
1094
1119
  {'delete_mail_accounts_and_domains': delete_mail_accounts_and_domains})
1095
1120
 
1096
- def validate_params(allowed: dict, actual: dict) -> bool | None:
1121
+
1122
+ def validate_params(allowed: dict, actual: dict) -> bool:
1097
1123
  for arg in actual:
1098
1124
  if arg not in allowed:
1099
1125
  raise ValueError(f'Parameter {arg} not a valid parameter.')
1100
-
1101
1126
  if not isinstance(actual[arg], allowed[arg]):
1102
1127
  raise TypeError(f'Attribute {arg} must be of type {str(allowed[arg])}. {str(type(actual[arg]))} given')
1103
- else:
1104
- return True
1105
- return None
1128
+ return True
1129
+
1106
1130
 
1107
1131
  def bool2str(state: bool) -> str:
1108
1132
  """
@@ -1111,4 +1135,4 @@ def bool2str(state: bool) -> str:
1111
1135
  """
1112
1136
  if state:
1113
1137
  return '1'
1114
- return '0'
1138
+ return '0'
@@ -1,4 +1,5 @@
1
1
  class APIError(Exception):
2
2
  """Custom exception for API errors."""
3
+
3
4
  def __init__(self, message, code=None):
4
- super().__init__(f'Error {code} - {message}')
5
+ super().__init__(f'Error {code} - {message}')
@@ -80,4 +80,4 @@ class Invoice:
80
80
  # Add each attribute to the String.
81
81
  # As the attribute name is '_attribute', remove the leading character
82
82
  print_string += f'{k[1:]}: {v}\n'
83
- return print_string
83
+ return print_string
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mailbox-org-api
3
- Version: 2.3
3
+ Version: 2.5
4
4
  Summary: A library to access the mailbox Business API.
5
5
  Author: Hendrik Schlange
6
6
  Author-email: Hendrik Schlange <mail@heshsum.de>
@@ -36,11 +36,15 @@ pip install mailbox-org-api
36
36
  pip install git+https://github.com/heshsum/mailbox-org-api
37
37
  ```
38
38
 
39
- ## Usage
39
+ ## Usage and documentation
40
40
  Basic usage is fairly straightforward. The naming scheme of the functions is similar to the naming at mailbox.org,
41
- but instead of points, it uses underscores (e.g. instead of `mail.add` it's `mail_add`).
41
+ but instead of points, it uses underscores (e.g. instead of `mail.add` it's `mail_add`).
42
+
43
+ Therefore, the functions mirror the functions as provided and documented at mailbox:
44
+ [api.mailbox.org](https://api.mailbox.org)
45
+
42
46
  Additionally, some helper functions for common or more complicated tasks are included to make life a bit easier,
43
- e.g. for changing plans, password and to retrieve invoices.
47
+ e.g. for changing plans, password and to retrieving invoices.
44
48
 
45
49
  ```python
46
50
  from mailbox_org_api import APIClient
@@ -87,6 +91,8 @@ api.mail_set_forwards('foo@bar.com', ['forward1@bar.com', 'forward2@bar.com'])
87
91
  api.deauth()
88
92
  ```
89
93
 
94
+ More information can be found in the [Wiki](https://github.com/heshsum/mailbox-org-api/wiki)
95
+
90
96
  ## Common tasks
91
97
  mailbox_org_api includes a number of helper functions to make common tasks simpler. These include:
92
98
 
@@ -163,21 +169,21 @@ Usage:
163
169
  api.account_invoice_get_token('BMBO-1234-24')
164
170
  ```
165
171
 
166
- ### account_invoice_get_pdf
172
+ ### account_invoice_get_file
167
173
  Invoices are provided as Based64 encoded gz Strings. This function
168
174
  1. takes the invoice ID
169
175
  2. retrieves the token for the invoice
170
176
  3. gets the binary data
171
177
  4. decodes the Base64
172
178
  5. decompresses it
173
- 6. returns the bytes of the actual PDF
179
+ 6. returns the bytes of the actual invoice file
174
180
 
175
181
  Usage
176
182
  ```python
177
183
  invoice_id = 'BMBO-1234-24'
178
- account_name = 'some_user'
184
+ account_name = 'foo'
179
185
  with open(invoice_id + '.pdf', 'w') as file:
180
- file.write(api.account_invoice_get_pdf(account_name, invoice_id))
186
+ file.write(api.account_invoice_get_file(account_name, invoice_id, 'PDF'))
181
187
  ```
182
188
 
183
189
  ## Here be dragons
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mailbox-org-api"
3
- version = "2.3"
3
+ version = "2.5"
4
4
  authors = [
5
5
  { name="Hendrik Schlange", email="mail@heshsum.de" },
6
6
  ]
@@ -3,7 +3,7 @@ from setuptools import find_packages, setup
3
3
  setup(
4
4
  name='mailbox_org_api',
5
5
  packages=find_packages(),
6
- version='2.3',
6
+ version='2.5',
7
7
  description='A library to access the mailbox Business API',
8
8
  author='Hendrik Schlange',
9
9
  install_requires=['requests'],
@@ -1,19 +1,23 @@
1
- from mailbox_org_api import APIClient
2
- from mailbox_org_api.APIError import APIError
3
- import pytest
4
1
  import os
5
- import time
6
2
  import secrets
7
3
  import string
4
+ import time
5
+
6
+ import pytest
7
+
8
+ from mailbox_org_api import APIClient
9
+ from mailbox_org_api.APIError import APIError
8
10
 
9
11
  api_test_user = os.environ['API_TEST_USER']
10
12
  api_test_pass = os.environ['API_TEST_PASS']
11
13
 
14
+
12
15
  # Create a unique ID String by using the Unix time,
13
16
  # converted to int (to get rid of the decimal) and then to String
14
17
  def generate_id():
15
18
  return str(int(time.time()))
16
19
 
20
+
17
21
  def generate_pw():
18
22
  # Length of password
19
23
  length = 42
@@ -36,6 +40,7 @@ def generate_pw():
36
40
  any(c in special for c in pw)):
37
41
  return pw
38
42
 
43
+
39
44
  def get_domain():
40
45
  api = APIClient.APIClient()
41
46
  api.auth(api_test_user, api_test_pass)
@@ -43,16 +48,18 @@ def get_domain():
43
48
  api.deauth()
44
49
  return domain
45
50
 
51
+
46
52
  test_id = generate_id()
47
53
  domain = get_domain()
48
54
 
55
+
49
56
  class TestAPIClient:
50
57
  def test_validate_params(self):
51
58
  allowed = {'string': str}
52
59
  with pytest.raises(ValueError):
53
- APIClient.validate_params(allowed, {'integer':123})
60
+ APIClient.validate_params(allowed, {'integer': 123})
54
61
  with pytest.raises(TypeError):
55
- APIClient.validate_params(allowed, {'string':123})
62
+ APIClient.validate_params(allowed, {'string': 123})
56
63
 
57
64
  def test_headers(self):
58
65
  api = APIClient.APIClient()
@@ -195,7 +202,7 @@ class TestAPIClient:
195
202
  api.auth(api_test_user, api_test_pass)
196
203
  invoice = api.account_invoice_get_list(api_test_user)[0]
197
204
  token = api.account_invoice_get_token(api_test_user, invoice_id=invoice)
198
- assert len(token) >0
205
+ assert len(token) > 0
199
206
  assert isinstance(token, str)
200
207
  api.deauth()
201
208
 
@@ -210,6 +217,16 @@ class TestAPIClient:
210
217
  assert isinstance(d['count_mails'], int)
211
218
  api.deauth()
212
219
 
220
+ def test_domain_get_list(self):
221
+ api = APIClient.APIClient()
222
+ api.auth(api_test_user, api_test_pass)
223
+ domain_names = api.domain_get_list(api_test_user)
224
+ domains = api.domain_list(api_test_user)
225
+ assert len(domain_names) == len(domains)
226
+ for d in domains:
227
+ assert d['domain'] in domain_names
228
+ api.deauth()
229
+
213
230
  def test_domain_get(self):
214
231
  api = APIClient.APIClient()
215
232
  api.auth(api_test_user, api_test_pass)
@@ -257,13 +274,24 @@ class TestAPIClient:
257
274
  assert paginated_mails['totalPages'] is not None
258
275
  assert paginated_mails['results'] is not None
259
276
  with pytest.raises(APIError):
260
- api.mail_list(domain, page = 2)
277
+ api.mail_list(domain, page=2)
261
278
  with pytest.raises(APIError):
262
279
  api.mail_list(domain, page_size=-1)
263
280
  with pytest.raises(ValueError):
264
281
  api.mail_list(domain, sort_order='wröng')
265
282
  api.deauth()
266
283
 
284
+ def test_mail_get_list(self):
285
+ api = APIClient.APIClient()
286
+ api.auth(api_test_user, api_test_pass)
287
+ mails = api.mail_list(domain)
288
+ mail_addresses = api.mail_get_list(domain)
289
+ assert len(mail_addresses) == len(mails)
290
+ for i in mails:
291
+ assert i['mail'] is not None
292
+ assert i['mail'] in mail_addresses
293
+ api.deauth()
294
+
267
295
  def test_mail_get(self):
268
296
  api = APIClient.APIClient()
269
297
  api.auth(api_test_user, api_test_pass)
@@ -318,8 +346,8 @@ class TestAPIClient:
318
346
  'aliases': [test_id + '_alias@' + domain], 'alternate_mail': test_id + '_alternate@' + domain,
319
347
  'memo': 'memo_string', 'active': True, 'title': 'Title', 'position': 'Job Position',
320
348
  'department': 'Department', 'company': 'Company', 'street': 'Street 1',
321
- 'postal_code': '12345', 'city': 'City', 'phone':'+492345678', 'fax':'+492345678',
322
- 'cell_phone':'+492345678', 'uid_extern': 'external_uid_value', 'language': 'de_DE'}
349
+ 'postal_code': '12345', 'city': 'City', 'phone': '+492345678', 'fax': '+492345678',
350
+ 'cell_phone': '+492345678', 'uid_extern': 'external_uid_value', 'language': 'de_DE'}
323
351
 
324
352
  # Adding parameters to call
325
353
  params = {}
@@ -340,7 +368,7 @@ class TestAPIClient:
340
368
  api.mail_set(mail, additional_cloud_quota=5)
341
369
 
342
370
  with pytest.raises(KeyError):
343
- api.mail_set(mail, additional_mail_quota = 5, additional_cloud_quota=5)
371
+ api.mail_set(mail, additional_mail_quota=5, additional_cloud_quota=5)
344
372
 
345
373
  api.deauth()
346
374
 
@@ -351,6 +379,9 @@ class TestAPIClient:
351
379
  api = APIClient.APIClient()
352
380
  api.auth(api_test_user, api_test_pass)
353
381
  mail = test_id + '@' + domain
382
+
383
+ # Ensure that the plan supports capabilities
384
+ api.mail_set_plan(mail, 'standard')
354
385
  for i in capabilities:
355
386
  api.mail_capabilities_set(mail, [i])
356
387
  # The API returns a list of capabilities
@@ -378,7 +409,7 @@ class TestAPIClient:
378
409
  api.auth(api_test_user, api_test_pass)
379
410
  mails = api.mail_list(domain)
380
411
  mail = mails[0]['mail']
381
- plans = ['premium', 'light', 'standard']
412
+ plans = ['premium', 'standard']
382
413
  for plan in plans:
383
414
  api.mail_set_plan(mail, plan)
384
415
  assert str(api.mail_get(mail)['plan']).lower() == plan
@@ -440,16 +471,17 @@ class TestAPIClient:
440
471
  mail = test_id + '@' + domain
441
472
  additional_mail_quota = 23
442
473
  api.mail_set_additional_mail_quota(mail, additional_mail_quota)
443
- assert additional_mail_quota == api.mail_get(mail)['additional_mail_quota']
474
+ assert int(api.mail_get(mail)['additional_mail_quota']) == additional_mail_quota
444
475
  api.deauth()
445
476
 
477
+ @pytest.mark.depends(name="test_mail_add")
446
478
  def test_mail_set_additional_cloud_quota(self):
447
479
  api = APIClient.APIClient()
448
480
  api.auth(api_test_user, api_test_pass)
449
481
  mail = test_id + '@' + domain
450
482
  additional_cloud_quota = 42
451
483
  api.mail_set_additional_cloud_quota(mail, additional_cloud_quota)
452
- assert additional_cloud_quota == api.mail_get(mail)['additional_cloud_quota']
484
+ assert int(api.mail_get(mail)['additional_cloud_quota']) == additional_cloud_quota
453
485
  api.deauth()
454
486
 
455
487
  @pytest.mark.depends(name="test_mail_add")
@@ -2,6 +2,7 @@ from mailbox_org_api import Account
2
2
 
3
3
  account_name = 'heiner.hansen'
4
4
 
5
+
5
6
  class TestAccount:
6
7
  def test_account(self):
7
8
  account = Account.Account(account_name)
@@ -80,8 +81,8 @@ class TestAccount:
80
81
  def test_account_contact(self):
81
82
  account = Account.Account(account_name)
82
83
  assert account.contact == {}
83
- contact = {'mail':'contact@tests.internal', 'first_name': 'Test', 'last_name': 'Contact', 'birthday': '',
84
- 'street':'Teststr. 1', 'zipcode':'12345', 'town':'Testtown', 'country':'DE'}
84
+ contact = {'mail': 'contact@tests.internal', 'first_name': 'Test', 'last_name': 'Contact', 'birthday': '',
85
+ 'street': 'Teststr. 1', 'zipcode': '12345', 'town': 'Testtown', 'country': 'DE'}
85
86
  account.contact = contact
86
87
  assert account.contact == contact
87
88
 
@@ -3,6 +3,7 @@ from mailbox_org_api import Invoice
3
3
  test_account = 'test_account'
4
4
  test_id = 'BMBO-1234-2025'
5
5
 
6
+
6
7
  class TestMail:
7
8
 
8
9
  def test_invoice_create(self):
@@ -43,4 +44,4 @@ class TestMail:
43
44
  assert invoice.token is None
44
45
  token = '123456789'
45
46
  invoice.token = token
46
- assert invoice.token == token
47
+ assert invoice.token == token
@@ -1,8 +1,10 @@
1
- from mailbox_org_api import Mail
2
1
  import unittest
3
2
 
3
+ from mailbox_org_api import Mail
4
+
4
5
  mail_address = 'tests@tests.tests'
5
6
 
7
+
6
8
  class TestMail(unittest.TestCase):
7
9
 
8
10
  def test_init(self):
File without changes
File without changes