api.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. from typing import List
  2. from dataclasses import asdict, replace
  3. from dacite import from_dict
  4. import json
  5. import requests
  6. from mastodon_v2.types import Status, Conversation, Context
  7. # https://github.com/mastodon/documentation/blob/master/content/en/api/guidelines.md#paginating-through-api-responses-pagination
  8. class MastodonAPSource:
  9. def __init__ (self, endpoint, token):
  10. self.endpoint = endpoint
  11. self.token = token
  12. super().__init__()
  13. def get_status_collection (self, path,
  14. params = {}, return_dataclasses = False):
  15. url = self.endpoint + path
  16. params = {
  17. **params
  18. }
  19. headers = {}
  20. if self.token:
  21. headers.update({
  22. 'Authorization': f'Bearer {self.token}'
  23. })
  24. response = requests.get(url, params=params, headers=headers)
  25. print(url)
  26. #print(response.status_code)
  27. #print(response.text)
  28. response_json = json.loads(response.text)
  29. try:
  30. print('trying to force timeline response to Status type')
  31. typed_resp = list(map(lambda s: from_dict(data=s, data_class=Status), response_json))
  32. except:
  33. raise 'error forcing status to type'
  34. if return_dataclasses:
  35. return typed_resp
  36. else:
  37. response_json = list(map(lambda s: asdict(s), typed_resp))
  38. return response_json
  39. def get_timeline (self, path = "home", params = {}, return_dataclasses=False):
  40. collection_path = "/api/v1/timelines/" + path
  41. params = {
  42. **params
  43. }
  44. return self.get_status_collection(collection_path, params = params, return_dataclasses=return_dataclasses)
  45. # nice parallel is validation
  46. # https://github.com/moko256/twitlatte/blob/master/component_client_mastodon/src/main/java/com/github/moko256/latte/client/mastodon/MastodonApiClientImpl.kt
  47. def get_mentions_timeline (self):
  48. # /api/v1/notifications?exclude_types=follow,favourite,reblog,poll,follow_request
  49. return
  50. def get_favorited_timeline (self):
  51. # /api/v1/notifications?exclude_types=follow,reblog,mention,poll,follow_request
  52. return
  53. def get_conversations (self,
  54. max_id = None, since_id = None, min_id = None, limit = None
  55. ) -> List[Conversation]:
  56. """
  57. Use get_status_context on the last_status for a conversation.
  58. """
  59. url = f'{self.instance}/api/v1/conversations'
  60. # https://docs.joinmastodon.org/methods/conversations/
  61. # Use HTTP Link header for pagination.
  62. headers = {
  63. 'Authorization': f'Bearer {self.token}'
  64. }
  65. params = cleandict({
  66. 'max_id': max_id,
  67. 'since_id': since_id,
  68. 'min_id': min_id,
  69. 'limit': limit
  70. })
  71. resp = requests.get(url, params=params, headers=headers)
  72. response_json = json.loads(resp.text)
  73. conversations = list(map(lambda c: from_dict(data=c, data_class=Conversation), response_json))
  74. return conversations
  75. def get_status_context (self, status_id) -> Context:
  76. """
  77. Returns all parents and direct children.
  78. """
  79. url = f'{self.instance}/api/v1/statuses/{status_id}/context'
  80. headers = {
  81. 'Authorization': f'Bearer {self.token}'
  82. }
  83. resp = requests.get(url, headers=headers)
  84. response_json = json.loads(resp.text)
  85. context = from_dict(data=response_json, data_class=Context)
  86. return context
  87. def get_bookmarks (self,
  88. max_id=None, return_dataclasses=False):
  89. collection_path = '/api/v1/bookmarks'
  90. params = {}
  91. if max_id:
  92. params['max_id'] = max_id
  93. return self.get_status_collection(collection_path, params = params, return_dataclasses=return_dataclasses)
  94. def get_favorites_timeline (self):
  95. # /account/favourites
  96. return
  97. def get_user_statuses (self, account_id,
  98. max_id = None, return_dataclasses=False):
  99. # /api/v2/search?type=statuses&account_id=
  100. # /api/v1/accounts/:id/statuses
  101. collection_path = f'/api/v1/accounts/{account_id}/statuses'
  102. params = {}
  103. if max_id:
  104. params['max_id'] = max_id
  105. return self.get_status_collection(collection_path, params = params, return_dataclasses=return_dataclasses)
  106. def get_statuses (self, ids):
  107. # /api/v1/statuses/:id
  108. return
  109. def get_profile (self, username):
  110. # /api/v1/accounts/search
  111. return self.search('@' + username, result_type='accounts')
  112. def search (q, result_type = None, following = False):
  113. # /api/v2/search
  114. return
  115. def cleandict(d):
  116. if isinstance(d, dict):
  117. return {k: cleandict(v) for k, v in d.items() if v is not None}
  118. elif isinstance(d, list):
  119. return [cleandict(v) for v in d]
  120. else:
  121. return d