mastodon_source.py 5.1 KB

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