Add support for Twitter API v2 Direct Messages endpoints
authorHarmon <Harmon758@gmail.com>
Fri, 28 Oct 2022 02:54:28 +0000 (21:54 -0500)
committerHarmon <Harmon758@gmail.com>
Fri, 28 Oct 2022 02:54:28 +0000 (21:54 -0500)
Resolves #1995

12 files changed:
cassettes/test_asyncclient_manage_and_lookup_direct_messages.yaml [new file with mode: 0644]
cassettes/test_manage_and_lookup_direct_messages.yaml [new file with mode: 0644]
docs/asyncclient.rst
docs/client.rst
docs/v2_models.rst
tests/test_asyncclient.py
tests/test_client.py
tweepy/__init__.py
tweepy/asynchronous/client.py
tweepy/client.py
tweepy/direct_message_event.py [new file with mode: 0644]
tweepy/tweet.py

diff --git a/cassettes/test_asyncclient_manage_and_lookup_direct_messages.yaml b/cassettes/test_asyncclient_manage_and_lookup_direct_messages.yaml
new file mode 100644 (file)
index 0000000..4d7ecda
--- /dev/null
@@ -0,0 +1,410 @@
+interactions:
+- request:
+    body:
+      text: Testing 1
+    headers:
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Python/3.10.0 aiohttp/3.8.1 Tweepy/4.11.0
+    method: POST
+    uri: https://api.twitter.com/2/dm_conversations/with/750362064426721281/messages
+  response:
+    body:
+      string: '{"data":{"dm_conversation_id":"750362064426721281-1072250532645998596","dm_event_id":"1585823814058971140"}}'
+    headers:
+      Cache-Control:
+      - no-cache, no-store, max-age=0
+      Content-Disposition:
+      - attachment; filename=json.json
+      Content-Encoding:
+      - gzip
+      Content-Length:
+      - '116'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Fri, 28 Oct 2022 02:40:35 UTC
+      Location:
+      - https://api.twitter.com/2/dm_events/"1585823814058971140"
+      Server:
+      - tsa_b
+      Set-Cookie:
+      - guest_id_marketing=v1%3A166692483505142170; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id_ads=v1%3A166692483505142170; Max-Age=63072000; Expires=Sun, 27 Oct
+        2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - personalization_id="v1_s0sOrj9Aez25nmQGn2ueog=="; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id=v1%3A166692483505142170; Max-Age=63072000; Expires=Sun, 27 Oct 2024
+        02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      api-version:
+      - '2.55'
+      perf:
+      - '7626143928'
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - e64aa05d988c6b12f356b0b777b16e58067fd9f4f5b76e0aa03a755ded9ac882
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '200'
+      x-rate-limit-remaining:
+      - '196'
+      x-rate-limit-reset:
+      - '1666925626'
+      x-response-time:
+      - '99'
+      x-transaction-id:
+      - 1ba0e69dac2d14c9
+      x-xss-protection:
+      - '0'
+    status:
+      code: 201
+      message: Created
+    url: https://api.twitter.com/2/dm_conversations/with/750362064426721281/messages
+- request:
+    body:
+      text: Testing 2
+    headers:
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Python/3.10.0 aiohttp/3.8.1 Tweepy/4.11.0
+    method: POST
+    uri: https://api.twitter.com/2/dm_conversations/750362064426721281-1072250532645998596/messages
+  response:
+    body:
+      string: '{"data":{"dm_conversation_id":"750362064426721281-1072250532645998596","dm_event_id":"1585823814956482567"}}'
+    headers:
+      Cache-Control:
+      - no-cache, no-store, max-age=0
+      Content-Disposition:
+      - attachment; filename=json.json
+      Content-Encoding:
+      - gzip
+      Content-Length:
+      - '118'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Fri, 28 Oct 2022 02:40:35 UTC
+      Location:
+      - https://api.twitter.com/2/dm_events/"1585823814956482567"
+      Server:
+      - tsa_b
+      Set-Cookie:
+      - guest_id_marketing=v1%3A166692483526981171; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id_ads=v1%3A166692483526981171; Max-Age=63072000; Expires=Sun, 27 Oct
+        2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - personalization_id="v1_vOAhj7Rgpg4+d8XWHyuGvQ=="; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id=v1%3A166692483526981171; Max-Age=63072000; Expires=Sun, 27 Oct 2024
+        02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      api-version:
+      - '2.55'
+      perf:
+      - '7626143928'
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 816b3d3d7fa28b220cbf9fd64979c7aa9444e638bd19fa1521543b2607132975
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '200'
+      x-rate-limit-remaining:
+      - '195'
+      x-rate-limit-reset:
+      - '1666925626'
+      x-response-time:
+      - '92'
+      x-transaction-id:
+      - ab04790d9a0ad556
+      x-xss-protection:
+      - '0'
+    status:
+      code: 201
+      message: Created
+    url: https://api.twitter.com/2/dm_conversations/750362064426721281-1072250532645998596/messages
+- request:
+    body:
+      conversation_type: Group
+      message:
+        text: Testing
+      participant_ids:
+      - '145336962'
+      - '750362064426721281'
+    headers:
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Python/3.10.0 aiohttp/3.8.1 Tweepy/4.11.0
+    method: POST
+    uri: https://api.twitter.com/2/dm_conversations
+  response:
+    body:
+      string: '{"data":{"dm_conversation_id":"1585823815904395264","dm_event_id":"1585823815904395268"}}'
+    headers:
+      Cache-Control:
+      - no-cache, no-store, max-age=0
+      Content-Disposition:
+      - attachment; filename=json.json
+      Content-Encoding:
+      - gzip
+      Content-Length:
+      - '91'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Fri, 28 Oct 2022 02:40:35 UTC
+      Location:
+      - https://api.twitter.com/2/dm_events/"1585823815904395268"
+      Server:
+      - tsa_b
+      Set-Cookie:
+      - guest_id_marketing=v1%3A166692483548929307; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id_ads=v1%3A166692483548929307; Max-Age=63072000; Expires=Sun, 27 Oct
+        2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - personalization_id="v1_/SSSm+jQ8/m+T74h092ffw=="; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id=v1%3A166692483548929307; Max-Age=63072000; Expires=Sun, 27 Oct 2024
+        02:40:35 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      api-version:
+      - '2.55'
+      perf:
+      - '7626143928'
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 419970fa1e4f08780874d727938e832f372b494fd2b990a9d354f157420f7c2e
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '200'
+      x-rate-limit-remaining:
+      - '194'
+      x-rate-limit-reset:
+      - '1666925626'
+      x-response-time:
+      - '111'
+      x-transaction-id:
+      - 95312ec9aed6941f
+      x-xss-protection:
+      - '0'
+    status:
+      code: 201
+      message: Created
+    url: https://api.twitter.com/2/dm_conversations
+- request:
+    body: null
+    headers:
+      User-Agent:
+      - Python/3.10.0 aiohttp/3.8.1 Tweepy/4.11.0
+    method: GET
+    uri: https://api.twitter.com/2/dm_events
+  response:
+    body:
+      string: '{"meta":{"result_count":0}}'
+    headers:
+      Cache-Control:
+      - no-cache, no-store, max-age=0
+      Content-Disposition:
+      - attachment; filename=json.json
+      Content-Encoding:
+      - gzip
+      Content-Length:
+      - '53'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Fri, 28 Oct 2022 02:40:36 UTC
+      Server:
+      - tsa_b
+      Set-Cookie:
+      - guest_id_marketing=v1%3A166692483571694274; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id_ads=v1%3A166692483571694274; Max-Age=63072000; Expires=Sun, 27 Oct
+        2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - personalization_id="v1_JqIhrGbVz9y65V8gt0I0nw=="; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id=v1%3A166692483571694274; Max-Age=63072000; Expires=Sun, 27 Oct 2024
+        02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      api-version:
+      - '2.55'
+      perf:
+      - '7626143928'
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - c61ab77e6b2edde82e41279fe30614c360c83986f2486df3cd5379ff6ee81efc
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '300'
+      x-rate-limit-remaining:
+      - '295'
+      x-rate-limit-reset:
+      - '1666925575'
+      x-response-time:
+      - '450'
+      x-transaction-id:
+      - ca2238d8b3bb8180
+      x-xss-protection:
+      - '0'
+    status:
+      code: 200
+      message: OK
+    url: https://api.twitter.com/2/dm_events
+- request:
+    body: null
+    headers:
+      User-Agent:
+      - Python/3.10.0 aiohttp/3.8.1 Tweepy/4.11.0
+    method: GET
+    uri: https://api.twitter.com/2/dm_conversations/750362064426721281-1072250532645998596/dm_events
+  response:
+    body:
+      string: '{"data":[{"event_type":"MessageCreate","id":"1585823814956482567","text":"Testing
+        2"},{"event_type":"MessageCreate","id":"1585823814058971140","text":"Testing
+        1"}],"meta":{"result_count":2,"next_token":"18LAA5G1V9DLILOG0G00ZZZZ","previous_token":"1BLC45G1V9DOTLG00S00ZZZZ"}}'
+    headers:
+      Cache-Control:
+      - no-cache, no-store, max-age=0
+      Content-Disposition:
+      - attachment; filename=json.json
+      Content-Encoding:
+      - gzip
+      Content-Length:
+      - '201'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Fri, 28 Oct 2022 02:40:36 UTC
+      Server:
+      - tsa_b
+      Set-Cookie:
+      - guest_id_marketing=v1%3A166692483630137659; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id_ads=v1%3A166692483630137659; Max-Age=63072000; Expires=Sun, 27 Oct
+        2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - personalization_id="v1_q7EvxLEzivBP+d8d8n5iwQ=="; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id=v1%3A166692483630137659; Max-Age=63072000; Expires=Sun, 27 Oct 2024
+        02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      api-version:
+      - '2.55'
+      perf:
+      - '7626143928'
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 3eaad847b13a27123db04d3bc781a841f4f3a73b2da9fcdb1063276020d63e72
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '300'
+      x-rate-limit-remaining:
+      - '294'
+      x-rate-limit-reset:
+      - '1666925575'
+      x-response-time:
+      - '240'
+      x-transaction-id:
+      - 6f38f4836665784c
+      x-xss-protection:
+      - '0'
+    status:
+      code: 200
+      message: OK
+    url: https://api.twitter.com/2/dm_conversations/750362064426721281-1072250532645998596/dm_events
+- request:
+    body: null
+    headers:
+      User-Agent:
+      - Python/3.10.0 aiohttp/3.8.1 Tweepy/4.11.0
+    method: GET
+    uri: https://api.twitter.com/2/dm_conversations/with/750362064426721281/dm_events
+  response:
+    body:
+      string: '{"data":[{"event_type":"MessageCreate","id":"1585823814956482567","text":"Testing
+        2"},{"event_type":"MessageCreate","id":"1585823814058971140","text":"Testing
+        1"}],"meta":{"result_count":2,"next_token":"18LAA5G1V9DLILOG0G00ZZZZ","previous_token":"1BLC45G1V9DOTLG00S00ZZZZ"}}'
+    headers:
+      Cache-Control:
+      - no-cache, no-store, max-age=0
+      Content-Disposition:
+      - attachment; filename=json.json
+      Content-Encoding:
+      - gzip
+      Content-Length:
+      - '201'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Fri, 28 Oct 2022 02:40:36 UTC
+      Server:
+      - tsa_b
+      Set-Cookie:
+      - guest_id_marketing=v1%3A166692483667194018; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id_ads=v1%3A166692483667194018; Max-Age=63072000; Expires=Sun, 27 Oct
+        2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - personalization_id="v1_z5H6tv+pvYGBnAirlfFEDQ=="; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id=v1%3A166692483667194018; Max-Age=63072000; Expires=Sun, 27 Oct 2024
+        02:40:36 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      api-version:
+      - '2.55'
+      perf:
+      - '7626143928'
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - b46452f9f641533ca331b6f3fef745c7e077f6526cbca084d0042a1a9bbb9326
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '300'
+      x-rate-limit-remaining:
+      - '293'
+      x-rate-limit-reset:
+      - '1666925575'
+      x-response-time:
+      - '248'
+      x-transaction-id:
+      - 91411ce0949616ad
+      x-xss-protection:
+      - '0'
+    status:
+      code: 200
+      message: OK
+    url: https://api.twitter.com/2/dm_conversations/with/750362064426721281/dm_events
+version: 1
diff --git a/cassettes/test_manage_and_lookup_direct_messages.yaml b/cassettes/test_manage_and_lookup_direct_messages.yaml
new file mode 100644 (file)
index 0000000..f46eebd
--- /dev/null
@@ -0,0 +1,422 @@
+interactions:
+- request:
+    body: '{"text": "Testing 1"}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '21'
+      Content-Type:
+      - application/json
+      User-Agent:
+      - Python/3.10.0 Requests/2.27.1 Tweepy/4.11.0
+    method: POST
+    uri: https://api.twitter.com/2/dm_conversations/with/750362064426721281/messages
+  response:
+    body:
+      string: !!binary |
+        H4sIAAAAAAAAACTLQQqEMAwF0LtkPULy26Spl5Fiu3BhhZniRrz7IL79u6iWUWi+qO7LevSzfX9l
+        bEdftkozJeVgYIsRliBwmYQToKwBFjVn12z0eXY7Wx9vE3V1ZpMADS4AnO77DwAA//8DAK1K7bhs
+        AAAA
+    headers:
+      api-version:
+      - '2.55'
+      cache-control:
+      - no-cache, no-store, max-age=0
+      content-disposition:
+      - attachment; filename=json.json
+      content-encoding:
+      - gzip
+      content-length:
+      - '117'
+      content-type:
+      - application/json; charset=utf-8
+      date:
+      - Fri, 28 Oct 2022 01:08:23 UTC
+      location:
+      - https://api.twitter.com/2/dm_events/"1585800613253812228"
+      perf:
+      - '7626143928'
+      server:
+      - tsa_b
+      set-cookie:
+      - guest_id_marketing=v1%3A166691930357241307; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 01:08:23 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id_ads=v1%3A166691930357241307; Max-Age=63072000; Expires=Sun, 27 Oct
+        2024 01:08:23 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - personalization_id="v1_Q7bH83VtVAsTu/8rDJi07A=="; Max-Age=63072000; Expires=Sun,
+        27 Oct 2024 01:08:23 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      - guest_id=v1%3A166691930357241307; Max-Age=63072000; Expires=Sun, 27 Oct 2024
+        01:08:23 GMT; Path=/; Domain=.twitter.com; Secure; SameSite=None
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 6606c0b67f5c1549f59c56f029b51c3767fa3d7d2e303df11caf4fa499a0299e
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '200'
+      x-rate-limit-remaining:
+      - '199'
+      x-rate-limit-reset:
+      - '1666920203'
+      x-response-time:
+      - '82'
+      x-transaction-id:
+      - 8a5ee86aeacaa300
+      x-xss-protection:
+      - '0'
+    status:
+      code: 201
+      message: Created
+- request:
+    body: '{"text": "Testing 2"}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '21'
+      Content-Type:
+      - application/json
+      Cookie:
+      - guest_id=v1%3A166691930357241307; guest_id_ads=v1%3A166691930357241307; guest_id_marketing=v1%3A166691930357241307;
+        personalization_id="v1_Q7bH83VtVAsTu/8rDJi07A=="
+      User-Agent:
+      - Python/3.10.0 Requests/2.27.1 Tweepy/4.11.0
+    method: POST
+    uri: https://api.twitter.com/2/dm_conversations/750362064426721281-1072250532645998596/messages
+  response:
+    body:
+      string: !!binary |
+        H4sIAAAAAAAAACTLQQqEMAwF0LtkPUKS9qepl5Fiu3BhhZniRrz7IL79u6iWUWi+qO7LevSzfX9l
+        bEdftkozJXAwZYtRLamoyyScVMEIahE5O7LR59ntbH28TeBwZpOQoO4iMLrvPwAAAP//AwC4jkA1
+        bAAAAA==
+    headers:
+      api-version:
+      - '2.55'
+      cache-control:
+      - no-cache, no-store, max-age=0
+      content-disposition:
+      - attachment; filename=json.json
+      content-encoding:
+      - gzip
+      content-length:
+      - '118'
+      content-type:
+      - application/json; charset=utf-8
+      date:
+      - Fri, 28 Oct 2022 01:08:23 UTC
+      location:
+      - https://api.twitter.com/2/dm_events/"1585800613752881156"
+      perf:
+      - '7626143928'
+      server:
+      - tsa_b
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 6606c0b67f5c1549f59c56f029b51c3767fa3d7d2e303df11caf4fa499a0299e
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '200'
+      x-rate-limit-remaining:
+      - '198'
+      x-rate-limit-reset:
+      - '1666920203'
+      x-response-time:
+      - '71'
+      x-transaction-id:
+      - 8d2db836db90cb70
+      x-xss-protection:
+      - '0'
+    status:
+      code: 201
+      message: Created
+- request:
+    body: '{"conversation_type": "Group", "message": {"text": "Testing"}, "participant_ids":
+      ["145336962", "750362064426721281"]}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '118'
+      Content-Type:
+      - application/json
+      Cookie:
+      - guest_id=v1%3A166691930357241307; guest_id_ads=v1%3A166691930357241307; guest_id_marketing=v1%3A166691930357241307;
+        personalization_id="v1_Q7bH83VtVAsTu/8rDJi07A=="
+      User-Agent:
+      - Python/3.10.0 Requests/2.27.1 Tweepy/4.11.0
+    method: POST
+    uri: https://api.twitter.com/2/dm_conversations
+  response:
+    body:
+      string: !!binary |
+        H4sIAAAAAAAAAKpWSkksSVSyqlZKyY1Pzs8rSy0qTizJzM+Lz0xRslIyNLUwtTAwMDM0MTYzNTa2
+        NDOxUNIBKU0tS80rwaHG1EipthYAAAD//wMA/NX3P1kAAAA=
+    headers:
+      api-version:
+      - '2.55'
+      cache-control:
+      - no-cache, no-store, max-age=0
+      content-disposition:
+      - attachment; filename=json.json
+      content-encoding:
+      - gzip
+      content-length:
+      - '92'
+      content-type:
+      - application/json; charset=utf-8
+      date:
+      - Fri, 28 Oct 2022 01:08:23 UTC
+      location:
+      - https://api.twitter.com/2/dm_events/"1585800614365339652"
+      perf:
+      - '7626143928'
+      server:
+      - tsa_b
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 6606c0b67f5c1549f59c56f029b51c3767fa3d7d2e303df11caf4fa499a0299e
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '200'
+      x-rate-limit-remaining:
+      - '197'
+      x-rate-limit-reset:
+      - '1666920203'
+      x-response-time:
+      - '114'
+      x-transaction-id:
+      - bd8c8860e7589c88
+      x-xss-protection:
+      - '0'
+    status:
+      code: 201
+      message: Created
+- request:
+    body: null
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      Cookie:
+      - guest_id=v1%3A166691930357241307; guest_id_ads=v1%3A166691930357241307; guest_id_marketing=v1%3A166691930357241307;
+        personalization_id="v1_Q7bH83VtVAsTu/8rDJi07A=="
+      User-Agent:
+      - Python/3.10.0 Requests/2.27.1 Tweepy/4.11.0
+    method: GET
+    uri: https://api.twitter.com/2/dm_events
+  response:
+    body:
+      string: !!binary |
+        H4sIAAAAAAAAAKpWyk0tSVSyqlYqSi0uzSmJT84vzStRsjKorQUAAAD//wMAMxjNjxsAAAA=
+    headers:
+      api-version:
+      - '2.55'
+      cache-control:
+      - no-cache, no-store, max-age=0
+      content-disposition:
+      - attachment; filename=json.json
+      content-encoding:
+      - gzip
+      content-length:
+      - '53'
+      content-type:
+      - application/json; charset=utf-8
+      date:
+      - Fri, 28 Oct 2022 01:08:24 UTC
+      perf:
+      - '7626143928'
+      server:
+      - tsa_b
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 6606c0b67f5c1549f59c56f029b51c3767fa3d7d2e303df11caf4fa499a0299e
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '300'
+      x-rate-limit-remaining:
+      - '299'
+      x-rate-limit-reset:
+      - '1666920203'
+      x-response-time:
+      - '402'
+      x-transaction-id:
+      - 532b9bbaaab076f0
+      x-xss-protection:
+      - '0'
+    status:
+      code: 200
+      message: OK
+- request:
+    body: null
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      Cookie:
+      - guest_id=v1%3A166691930357241307; guest_id_ads=v1%3A166691930357241307; guest_id_marketing=v1%3A166691930357241307;
+        personalization_id="v1_Q7bH83VtVAsTu/8rDJi07A=="
+      User-Agent:
+      - Python/3.10.0 Requests/2.27.1 Tweepy/4.11.0
+    method: GET
+    uri: https://api.twitter.com/2/dm_conversations/750362064426721281-1072250532645998596/dm_events
+  response:
+    body:
+      string: !!binary |
+        H4sIAAAAAAAAAJSOwQqCQBRFfyXe2sXM2NjgzlxIMFZUqyJE8iFSjeI8pRD/vZGgFq26y3sPhztA
+        kVMO4WkA7NFQRs8GIYQUrc1LjFvMCcGDqnAll0oqxgLuL6RQinMZuInwQW48oKXKlDMBo/efTEhf
+        cSGE+pVxGM8e3HG6OECLtrtRdqk74yDhgXF0RvUVzSRUOopkwvearXdyk7CEsaOLszYt9lXd2S+7
+        1PH8zW5TvWIfdhxfAAAA//8DAPQRV/YSAQAA
+    headers:
+      api-version:
+      - '2.55'
+      cache-control:
+      - no-cache, no-store, max-age=0
+      content-disposition:
+      - attachment; filename=json.json
+      content-encoding:
+      - gzip
+      content-length:
+      - '198'
+      content-type:
+      - application/json; charset=utf-8
+      date:
+      - Fri, 28 Oct 2022 01:08:24 UTC
+      perf:
+      - '7626143928'
+      server:
+      - tsa_b
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 6606c0b67f5c1549f59c56f029b51c3767fa3d7d2e303df11caf4fa499a0299e
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '300'
+      x-rate-limit-remaining:
+      - '298'
+      x-rate-limit-reset:
+      - '1666920203'
+      x-response-time:
+      - '252'
+      x-transaction-id:
+      - 97e15c5a26d66f34
+      x-xss-protection:
+      - '0'
+    status:
+      code: 200
+      message: OK
+- request:
+    body: null
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      Cookie:
+      - guest_id=v1%3A166691930357241307; guest_id_ads=v1%3A166691930357241307; guest_id_marketing=v1%3A166691930357241307;
+        personalization_id="v1_Q7bH83VtVAsTu/8rDJi07A=="
+      User-Agent:
+      - Python/3.10.0 Requests/2.27.1 Tweepy/4.11.0
+    method: GET
+    uri: https://api.twitter.com/2/dm_conversations/with/750362064426721281/dm_events
+  response:
+    body:
+      string: !!binary |
+        H4sIAAAAAAAAAJSOwQqCQBRFfyXe2sXM2NjgzlxIMFZUqyJE8iFSjeI8pRD/vZGgFq26y3sPhztA
+        kVMO4WkA7NFQRs8GIYQUrc1LjFvMCcGDqnAll0oqxgLuL6RQinMZuInwQW48oKXKlDMBo/efTEhf
+        cSGE+pVxGM8e3HG6OECLtrtRdqk74yDhgXF0RvUVzSRUOopkwvearXdyk7CEsaOLszYt9lXd2S+7
+        1PH8zW5TvWIfdhxfAAAA//8DAPQRV/YSAQAA
+    headers:
+      api-version:
+      - '2.55'
+      cache-control:
+      - no-cache, no-store, max-age=0
+      content-disposition:
+      - attachment; filename=json.json
+      content-encoding:
+      - gzip
+      content-length:
+      - '198'
+      content-type:
+      - application/json; charset=utf-8
+      date:
+      - Fri, 28 Oct 2022 01:08:24 UTC
+      perf:
+      - '7626143928'
+      server:
+      - tsa_b
+      strict-transport-security:
+      - max-age=631138519
+      x-access-level:
+      - read-write-directmessages
+      x-connection-hash:
+      - 6606c0b67f5c1549f59c56f029b51c3767fa3d7d2e303df11caf4fa499a0299e
+      x-content-type-options:
+      - nosniff
+      x-frame-options:
+      - SAMEORIGIN
+      x-rate-limit-limit:
+      - '300'
+      x-rate-limit-remaining:
+      - '297'
+      x-rate-limit-reset:
+      - '1666920203'
+      x-response-time:
+      - '249'
+      x-transaction-id:
+      - 445b49d7f86564e1
+      x-xss-protection:
+      - '0'
+    status:
+      code: 200
+      message: OK
+version: 1
index 7d37f93897c1ed8e67c9a3587fea4ef6210e66e0..0f9f0f60aaebc1371c8d065ae97e0f5542441689 100644 (file)
 .. table::
     :align: center
 
-    +--------------------------------------------------------------+---------------------------------------------+
-    | Twitter API v2 Endpoint                                      | :class:`AsyncClient` Method                 |
-    +==============================================================+=============================================+
-    | .. centered:: :ref:`Tweets`                                                                                |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Bookmarks|_                                                                                 |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:id/bookmarks/:tweet_id`_                   | :meth:`AsyncClient.remove_bookmark`         |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/bookmarks`_                                | :meth:`AsyncClient.get_bookmarks`           |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/bookmarks`_                               | :meth:`AsyncClient.bookmark`                |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Hide replies|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`AsyncClient.hide_reply`              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`AsyncClient.unhide_reply`            |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Likes|_                                                                                     |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:id/likes/:tweet_id`_                       | :meth:`AsyncClient.unlike`                  |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/:id/liking_users`_                            | :meth:`AsyncClient.get_liking_users`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/liked_tweets`_                             | :meth:`AsyncClient.get_liked_tweets`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/likes`_                                   | :meth:`AsyncClient.like`                    |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Manage Tweets|_                                                                             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/tweets/:id`_                                      | :meth:`AsyncClient.delete_tweet`            |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/tweets`_                                            | :meth:`AsyncClient.create_tweet`            |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Quote Tweets|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/:id/quote_tweets`_                            | :meth:`AsyncClient.get_quote_tweets`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Retweets|_                                                                                  |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:id/retweets/:source_tweet_id`_             | :meth:`AsyncClient.unretweet`               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/:id/retweeted_by`_                            | :meth:`AsyncClient.get_retweeters`          |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/retweets`_                                | :meth:`AsyncClient.retweet`                 |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Search Tweets|_                                                                             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/search/all`_                                  | :meth:`AsyncClient.search_all_tweets`       |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/search/recent`_                               | :meth:`AsyncClient.search_recent_tweets`    |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Timelines|_                                                                                 |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/mentions`_                                 | :meth:`AsyncClient.get_users_mentions`      |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/timelines/reverse_chronological`_          | :meth:`AsyncClient.get_home_timeline`       |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/tweets`_                                   | :meth:`AsyncClient.get_users_tweets`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Tweet counts|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/counts/all`_                                  | :meth:`AsyncClient.get_all_tweets_count`    |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/counts/recent`_                               | :meth:`AsyncClient.get_recent_tweets_count` |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Tweet lookup|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets/:id`_                                         | :meth:`AsyncClient.get_tweet`               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/tweets`_                                             | :meth:`AsyncClient.get_tweets`              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: :ref:`Users`                                                                                 |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Blocks|_                                                                                    |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:source_user_id/blocking/:target_user_id`_  | :meth:`AsyncClient.unblock`                 |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/blocking`_                                 | :meth:`AsyncClient.get_blocked`             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/blocking`_                                | :meth:`AsyncClient.block`                   |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Follows|_                                                                                   |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:source_user_id/following/:target_user_id`_ | :meth:`AsyncClient.unfollow_user`           |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/followers`_                                | :meth:`AsyncClient.get_users_followers`     |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/following`_                                | :meth:`AsyncClient.get_users_following`     |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/following`_                               | :meth:`AsyncClient.follow_user`             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Mutes|_                                                                                     |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:source_user_id/muting/:target_user_id`_    | :meth:`AsyncClient.unmute`                  |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/muting`_                                   | :meth:`AsyncClient.get_muted`               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/muting`_                                  | :meth:`AsyncClient.mute`                    |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |User lookup|_                                                                               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id`_                                          | :meth:`AsyncClient.get_user`                |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/by/username/:username`_                        | :meth:`AsyncClient.get_user`                |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users`_                                              | :meth:`AsyncClient.get_users`               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/by`_                                           | :meth:`AsyncClient.get_users`               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/me`_                                           | :meth:`AsyncClient.get_me`                  |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: :ref:`Spaces`                                                                                |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Search Spaces|_                                                                             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/spaces/search`_                                      | :meth:`AsyncClient.search_spaces`           |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Spaces lookup|_                                                                             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/spaces`_                                             | :meth:`AsyncClient.get_spaces`              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/spaces/:id`_                                         | :meth:`AsyncClient.get_space`               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/spaces/:id/buyers`_                                  | :meth:`AsyncClient.get_space_buyers`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/spaces/:id/tweets`_                                  | :meth:`AsyncClient.get_space_tweets`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/spaces/by/creator_ids`_                              | :meth:`AsyncClient.get_spaces`              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: :ref:`Lists`                                                                                 |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |List Tweets lookup|_                                                                        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/lists/:id/tweets`_                                   | :meth:`AsyncClient.get_list_tweets`         |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |List follows|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:id/followed_lists/:list_id`_               | :meth:`AsyncClient.unfollow_list`           |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/lists/:id/followers`_                                | :meth:`AsyncClient.get_list_followers`      |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/followed_lists`_                           | :meth:`AsyncClient.get_followed_lists`      |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/followed_lists`_                          | :meth:`AsyncClient.follow_list`             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |List lookup|_                                                                               |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/lists/:id`_                                          | :meth:`AsyncClient.get_list`                |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/owned_lists`_                              | :meth:`AsyncClient.get_owned_lists`         |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |List members|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/lists/:id/members/:user_id`_                      | :meth:`AsyncClient.remove_list_member`      |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/lists/:id/members`_                                  | :meth:`AsyncClient.get_list_members`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/list_memberships`_                         | :meth:`AsyncClient.get_list_memberships`    |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/lists/:id/members`_                                 | :meth:`AsyncClient.add_list_member`         |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Manage Lists|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/lists/:id`_                                       | :meth:`AsyncClient.delete_list`             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `PUT /2/lists/:id`_                                          | :meth:`AsyncClient.update_list`             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/lists`_                                             | :meth:`AsyncClient.create_list`             |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Pinned Lists|_                                                                              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `DELETE /2/users/:id/pinned_lists/:list_id`_                 | :meth:`AsyncClient.unpin_list`              |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/users/:id/pinned_lists`_                             | :meth:`AsyncClient.get_pinned_lists`        |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/users/:id/pinned_lists`_                            | :meth:`AsyncClient.pin_list`                |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: :ref:`Compliance`                                                                            |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | .. centered:: |Batch Compliance|_                                                                          |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/compliance/jobs`_                                    | :meth:`AsyncClient.get_compliance_jobs`     |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `GET /2/compliance/jobs/:id`_                                | :meth:`AsyncClient.get_compliance_job`      |
-    +--------------------------------------------------------------+---------------------------------------------+
-    | `POST /2/compliance/jobs`_                                   | :meth:`AsyncClient.create_compliance_job`   |
-    +--------------------------------------------------------------+---------------------------------------------+
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | Twitter API v2 Endpoint                                      | :class:`AsyncClient` Method                            |
+    +==============================================================+========================================================+
+    | .. centered:: :ref:`Tweets`                                                                                           |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Bookmarks|_                                                                                            |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:id/bookmarks/:tweet_id`_                   | :meth:`AsyncClient.remove_bookmark`                    |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/bookmarks`_                                | :meth:`AsyncClient.get_bookmarks`                      |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/bookmarks`_                               | :meth:`AsyncClient.bookmark`                           |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Hide replies|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`AsyncClient.hide_reply`                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`AsyncClient.unhide_reply`                       |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Likes|_                                                                                                |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:id/likes/:tweet_id`_                       | :meth:`AsyncClient.unlike`                             |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/:id/liking_users`_                            | :meth:`AsyncClient.get_liking_users`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/liked_tweets`_                             | :meth:`AsyncClient.get_liked_tweets`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/likes`_                                   | :meth:`AsyncClient.like`                               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Manage Tweets|_                                                                                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/tweets/:id`_                                      | :meth:`AsyncClient.delete_tweet`                       |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/tweets`_                                            | :meth:`AsyncClient.create_tweet`                       |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Quote Tweets|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/:id/quote_tweets`_                            | :meth:`AsyncClient.get_quote_tweets`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Retweets|_                                                                                             |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:id/retweets/:source_tweet_id`_             | :meth:`AsyncClient.unretweet`                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/:id/retweeted_by`_                            | :meth:`AsyncClient.get_retweeters`                     |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/retweets`_                                | :meth:`AsyncClient.retweet`                            |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Search Tweets|_                                                                                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/search/all`_                                  | :meth:`AsyncClient.search_all_tweets`                  |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/search/recent`_                               | :meth:`AsyncClient.search_recent_tweets`               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Timelines|_                                                                                            |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/mentions`_                                 | :meth:`AsyncClient.get_users_mentions`                 |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/timelines/reverse_chronological`_          | :meth:`AsyncClient.get_home_timeline`                  |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/tweets`_                                   | :meth:`AsyncClient.get_users_tweets`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Tweet counts|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/counts/all`_                                  | :meth:`AsyncClient.get_all_tweets_count`               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/counts/recent`_                               | :meth:`AsyncClient.get_recent_tweets_count`            |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Tweet lookup|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets/:id`_                                         | :meth:`AsyncClient.get_tweet`                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/tweets`_                                             | :meth:`AsyncClient.get_tweets`                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: :ref:`Users`                                                                                            |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Blocks|_                                                                                               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:source_user_id/blocking/:target_user_id`_  | :meth:`AsyncClient.unblock`                            |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/blocking`_                                 | :meth:`AsyncClient.get_blocked`                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/blocking`_                                | :meth:`AsyncClient.block`                              |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Follows|_                                                                                              |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:source_user_id/following/:target_user_id`_ | :meth:`AsyncClient.unfollow_user`                      |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/followers`_                                | :meth:`AsyncClient.get_users_followers`                |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/following`_                                | :meth:`AsyncClient.get_users_following`                |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/following`_                               | :meth:`AsyncClient.follow_user`                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Mutes|_                                                                                                |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:source_user_id/muting/:target_user_id`_    | :meth:`AsyncClient.unmute`                             |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/muting`_                                   | :meth:`AsyncClient.get_muted`                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/muting`_                                  | :meth:`AsyncClient.mute`                               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |User lookup|_                                                                                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id`_                                          | :meth:`AsyncClient.get_user`                           |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/by/username/:username`_                        | :meth:`AsyncClient.get_user`                           |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users`_                                              | :meth:`AsyncClient.get_users`                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/by`_                                           | :meth:`AsyncClient.get_users`                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/me`_                                           | :meth:`AsyncClient.get_me`                             |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: :ref:`Spaces`                                                                                           |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Search Spaces|_                                                                                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/spaces/search`_                                      | :meth:`AsyncClient.search_spaces`                      |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Spaces lookup|_                                                                                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/spaces`_                                             | :meth:`AsyncClient.get_spaces`                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/spaces/:id`_                                         | :meth:`AsyncClient.get_space`                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/spaces/:id/buyers`_                                  | :meth:`AsyncClient.get_space_buyers`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/spaces/:id/tweets`_                                  | :meth:`AsyncClient.get_space_tweets`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/spaces/by/creator_ids`_                              | :meth:`AsyncClient.get_spaces`                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: :ref:`Direct Messages`                                                                                  |
+    +-----------------------------------------------------------------------------------------------------------------------+
+    | .. centered:: |Direct Messages lookup|_                                                                               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/dm_conversations/:dm_conversation_id/dm_events`_     | :meth:`AsyncClient.get_direct_message_events`          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/dm_conversations/with/:participant_id/dm_events`_    | :meth:`AsyncClient.get_direct_message_events`          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/dm_events`_                                          | :meth:`AsyncClient.get_direct_message_events`          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Manage Direct Messages|_                                                                               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/dm_conversations`_                                  | :meth:`AsyncClient.create_direct_message_conversation` |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/dm_conversations/:dm_conversation_id/messages`_     | :meth:`AsyncClient.create_direct_message`              |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/dm_conversations/with/:participant_id/messages`_    | :meth:`AsyncClient.create_direct_message`              |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: :ref:`Lists`                                                                                            |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |List Tweets lookup|_                                                                                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/lists/:id/tweets`_                                   | :meth:`AsyncClient.get_list_tweets`                    |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |List follows|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:id/followed_lists/:list_id`_               | :meth:`AsyncClient.unfollow_list`                      |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/lists/:id/followers`_                                | :meth:`AsyncClient.get_list_followers`                 |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/followed_lists`_                           | :meth:`AsyncClient.get_followed_lists`                 |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/followed_lists`_                          | :meth:`AsyncClient.follow_list`                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |List lookup|_                                                                                          |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/lists/:id`_                                          | :meth:`AsyncClient.get_list`                           |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/owned_lists`_                              | :meth:`AsyncClient.get_owned_lists`                    |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |List members|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/lists/:id/members/:user_id`_                      | :meth:`AsyncClient.remove_list_member`                 |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/lists/:id/members`_                                  | :meth:`AsyncClient.get_list_members`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/list_memberships`_                         | :meth:`AsyncClient.get_list_memberships`               |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/lists/:id/members`_                                 | :meth:`AsyncClient.add_list_member`                    |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Manage Lists|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/lists/:id`_                                       | :meth:`AsyncClient.delete_list`                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `PUT /2/lists/:id`_                                          | :meth:`AsyncClient.update_list`                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/lists`_                                             | :meth:`AsyncClient.create_list`                        |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Pinned Lists|_                                                                                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `DELETE /2/users/:id/pinned_lists/:list_id`_                 | :meth:`AsyncClient.unpin_list`                         |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/users/:id/pinned_lists`_                             | :meth:`AsyncClient.get_pinned_lists`                   |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/users/:id/pinned_lists`_                            | :meth:`AsyncClient.pin_list`                           |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: :ref:`Compliance`                                                                                       |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | .. centered:: |Batch Compliance|_                                                                                     |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/compliance/jobs`_                                    | :meth:`AsyncClient.get_compliance_jobs`                |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `GET /2/compliance/jobs/:id`_                                | :meth:`AsyncClient.get_compliance_job`                 |
+    +--------------------------------------------------------------+--------------------------------------------------------+
+    | `POST /2/compliance/jobs`_                                   | :meth:`AsyncClient.create_compliance_job`              |
+    +--------------------------------------------------------------+--------------------------------------------------------+
 
 .. |Bookmarks| replace:: *Bookmarks*
 .. _DELETE /2/users/:id/bookmarks/:tweet_id: https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/delete-users-id-bookmarks-tweet_id
 .. _GET /2/compliance/jobs: https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs
 .. _GET /2/compliance/jobs/:id: https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs-id
 .. _POST /2/compliance/jobs: https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs
+.. |Direct Messages lookup| replace:: *Direct Messages lookup*
+.. _GET /2/dm_conversations/:dm_conversation_id/dm_events: https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-dm_conversation_id-dm_events
+.. _GET /2/dm_conversations/with/:participant_id/dm_events: https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-with-participant_id-dm_events
+.. _GET /2/dm_events: https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_events
+.. |Manage Direct Messages| replace:: *Manage Direct Messages*
+.. _POST /2/dm_conversations: https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations
+.. _POST /2/dm_conversations/:dm_conversation_id/messages: https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-dm_conversation_id-messages
+.. _POST /2/dm_conversations/with/:participant_id/messages: https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-with-participant_id-messages
 
 Tweets
 ======
@@ -430,6 +456,21 @@ Spaces lookup
 
 .. automethod:: AsyncClient.get_space_tweets
 
+Direct Messages
+===============
+
+Direct Messages lookup
+----------------------
+
+.. automethod:: AsyncClient.get_direct_message_events
+
+Manage Direct Messages
+----------------------
+
+.. automethod:: AsyncClient.create_direct_message
+
+.. automethod:: AsyncClient.create_direct_message_conversation
+
 Lists
 =====
 
index fd4be3bb9f8bf8a61dabc8c17d54809d00f9cd72..a32ac2241a49cb599eb2d853610c49e6a74e898f 100644 (file)
 .. table::
     :align: center
 
-    +--------------------------------------------------------------+----------------------------------------+
-    | Twitter API v2 Endpoint                                      | :class:`Client` Method                 |
-    +==============================================================+========================================+
-    | .. centered:: :ref:`Tweets`                                                                           |
-    +-------------------------------------------------------------------------------------------------------+
-    | .. centered:: |Bookmarks|_                                                                            |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:id/bookmarks/:tweet_id`_                   | :meth:`Client.remove_bookmark`         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/bookmarks`_                                | :meth:`Client.get_bookmarks`           |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/bookmarks`_                               | :meth:`Client.bookmark`                |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Hide replies|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`Client.hide_reply`              |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`Client.unhide_reply`            |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Likes|_                                                                                |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:id/likes/:tweet_id`_                       | :meth:`Client.unlike`                  |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/:id/liking_users`_                            | :meth:`Client.get_liking_users`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/liked_tweets`_                             | :meth:`Client.get_liked_tweets`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/likes`_                                   | :meth:`Client.like`                    |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Manage Tweets|_                                                                        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/tweets/:id`_                                      | :meth:`Client.delete_tweet`            |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/tweets`_                                            | :meth:`Client.create_tweet`            |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Quote Tweets|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/:id/quote_tweets`_                            | :meth:`Client.get_quote_tweets`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Retweets|_                                                                             |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:id/retweets/:source_tweet_id`_             | :meth:`Client.unretweet`               |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/:id/retweeted_by`_                            | :meth:`Client.get_retweeters`          |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/retweets`_                                | :meth:`Client.retweet`                 |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Search Tweets|_                                                                        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/search/all`_                                  | :meth:`Client.search_all_tweets`       |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/search/recent`_                               | :meth:`Client.search_recent_tweets`    |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Timelines|_                                                                            |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/mentions`_                                 | :meth:`Client.get_users_mentions`      |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/timelines/reverse_chronological`_          | :meth:`Client.get_home_timeline`       |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/tweets`_                                   | :meth:`Client.get_users_tweets`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Tweet counts|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/counts/all`_                                  | :meth:`Client.get_all_tweets_count`    |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/counts/recent`_                               | :meth:`Client.get_recent_tweets_count` |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Tweet lookup|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets/:id`_                                         | :meth:`Client.get_tweet`               |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/tweets`_                                             | :meth:`Client.get_tweets`              |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: :ref:`Users`                                                                            |
-    +-------------------------------------------------------------------------------------------------------+
-    | .. centered:: |Blocks|_                                                                               |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:source_user_id/blocking/:target_user_id`_  | :meth:`Client.unblock`                 |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/blocking`_                                 | :meth:`Client.get_blocked`             |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/blocking`_                                | :meth:`Client.block`                   |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Follows|_                                                                              |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:source_user_id/following/:target_user_id`_ | :meth:`Client.unfollow_user`           |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/followers`_                                | :meth:`Client.get_users_followers`     |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/following`_                                | :meth:`Client.get_users_following`     |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/following`_                               | :meth:`Client.follow_user`             |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Mutes|_                                                                                |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:source_user_id/muting/:target_user_id`_    | :meth:`Client.unmute`                  |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/muting`_                                   | :meth:`Client.get_muted`               |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/muting`_                                  | :meth:`Client.mute`                    |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |User lookup|_                                                                          |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id`_                                          | :meth:`Client.get_user`                |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/by/username/:username`_                        | :meth:`Client.get_user`                |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users`_                                              | :meth:`Client.get_users`               |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/by`_                                           | :meth:`Client.get_users`               |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/me`_                                           | :meth:`Client.get_me`                  |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: :ref:`Spaces`                                                                           |
-    +-------------------------------------------------------------------------------------------------------+
-    | .. centered:: |Search Spaces|_                                                                        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/spaces/search`_                                      | :meth:`Client.search_spaces`           |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Spaces lookup|_                                                                        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/spaces`_                                             | :meth:`Client.get_spaces`              |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/spaces/:id`_                                         | :meth:`Client.get_space`               |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/spaces/:id/buyers`_                                  | :meth:`Client.get_space_buyers`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/spaces/:id/tweets`_                                  | :meth:`Client.get_space_tweets`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/spaces/by/creator_ids`_                              | :meth:`Client.get_spaces`              |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: :ref:`Lists`                                                                            |
-    +-------------------------------------------------------------------------------------------------------+
-    | .. centered:: |List Tweets lookup|_                                                                   |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/lists/:id/tweets`_                                   | :meth:`Client.get_list_tweets`         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |List follows|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:id/followed_lists/:list_id`_               | :meth:`Client.unfollow_list`           |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/lists/:id/followers`_                                | :meth:`Client.get_list_followers`      |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/followed_lists`_                           | :meth:`Client.get_followed_lists`      |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/followed_lists`_                          | :meth:`Client.follow_list`             |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |List lookup|_                                                                          |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/lists/:id`_                                          | :meth:`Client.get_list`                |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/owned_lists`_                              | :meth:`Client.get_owned_lists`         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |List members|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/lists/:id/members/:user_id`_                      | :meth:`Client.remove_list_member`      |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/lists/:id/members`_                                  | :meth:`Client.get_list_members`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/list_memberships`_                         | :meth:`Client.get_list_memberships`    |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/lists/:id/members`_                                 | :meth:`Client.add_list_member`         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Manage Lists|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/lists/:id`_                                       | :meth:`Client.delete_list`             |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `PUT /2/lists/:id`_                                          | :meth:`Client.update_list`             |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/lists`_                                             | :meth:`Client.create_list`             |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: |Pinned Lists|_                                                                         |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `DELETE /2/users/:id/pinned_lists/:list_id`_                 | :meth:`Client.unpin_list`              |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/users/:id/pinned_lists`_                             | :meth:`Client.get_pinned_lists`        |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/users/:id/pinned_lists`_                            | :meth:`Client.pin_list`                |
-    +--------------------------------------------------------------+----------------------------------------+
-    | .. centered:: :ref:`Compliance`                                                                       |
-    +-------------------------------------------------------------------------------------------------------+
-    | .. centered:: |Batch Compliance|_                                                                     |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/compliance/jobs`_                                    | :meth:`Client.get_compliance_jobs`     |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `GET /2/compliance/jobs/:id`_                                | :meth:`Client.get_compliance_job`      |
-    +--------------------------------------------------------------+----------------------------------------+
-    | `POST /2/compliance/jobs`_                                   | :meth:`Client.create_compliance_job`   |
-    +--------------------------------------------------------------+----------------------------------------+
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | Twitter API v2 Endpoint                                      | :class:`Client` Method                            |
+    +==============================================================+===================================================+
+    | .. centered:: :ref:`Tweets`                                                                                      |
+    +------------------------------------------------------------------------------------------------------------------+
+    | .. centered:: |Bookmarks|_                                                                                       |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:id/bookmarks/:tweet_id`_                   | :meth:`Client.remove_bookmark`                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/bookmarks`_                                | :meth:`Client.get_bookmarks`                      |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/bookmarks`_                               | :meth:`Client.bookmark`                           |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Hide replies|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`Client.hide_reply`                         |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `PUT /2/tweets/:id/hidden`_                                  | :meth:`Client.unhide_reply`                       |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Likes|_                                                                                           |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:id/likes/:tweet_id`_                       | :meth:`Client.unlike`                             |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/:id/liking_users`_                            | :meth:`Client.get_liking_users`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/liked_tweets`_                             | :meth:`Client.get_liked_tweets`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/likes`_                                   | :meth:`Client.like`                               |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Manage Tweets|_                                                                                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/tweets/:id`_                                      | :meth:`Client.delete_tweet`                       |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/tweets`_                                            | :meth:`Client.create_tweet`                       |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Quote Tweets|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/:id/quote_tweets`_                            | :meth:`Client.get_quote_tweets`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Retweets|_                                                                                        |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:id/retweets/:source_tweet_id`_             | :meth:`Client.unretweet`                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/:id/retweeted_by`_                            | :meth:`Client.get_retweeters`                     |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/retweets`_                                | :meth:`Client.retweet`                            |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Search Tweets|_                                                                                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/search/all`_                                  | :meth:`Client.search_all_tweets`                  |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/search/recent`_                               | :meth:`Client.search_recent_tweets`               |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Timelines|_                                                                                       |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/mentions`_                                 | :meth:`Client.get_users_mentions`                 |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/timelines/reverse_chronological`_          | :meth:`Client.get_home_timeline`                  |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/tweets`_                                   | :meth:`Client.get_users_tweets`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Tweet counts|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/counts/all`_                                  | :meth:`Client.get_all_tweets_count`               |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/counts/recent`_                               | :meth:`Client.get_recent_tweets_count`            |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Tweet lookup|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets/:id`_                                         | :meth:`Client.get_tweet`                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/tweets`_                                             | :meth:`Client.get_tweets`                         |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: :ref:`Users`                                                                                       |
+    +------------------------------------------------------------------------------------------------------------------+
+    | .. centered:: |Blocks|_                                                                                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:source_user_id/blocking/:target_user_id`_  | :meth:`Client.unblock`                            |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/blocking`_                                 | :meth:`Client.get_blocked`                        |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/blocking`_                                | :meth:`Client.block`                              |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Follows|_                                                                                         |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:source_user_id/following/:target_user_id`_ | :meth:`Client.unfollow_user`                      |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/followers`_                                | :meth:`Client.get_users_followers`                |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/following`_                                | :meth:`Client.get_users_following`                |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/following`_                               | :meth:`Client.follow_user`                        |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Mutes|_                                                                                           |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:source_user_id/muting/:target_user_id`_    | :meth:`Client.unmute`                             |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/muting`_                                   | :meth:`Client.get_muted`                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/muting`_                                  | :meth:`Client.mute`                               |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |User lookup|_                                                                                     |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id`_                                          | :meth:`Client.get_user`                           |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/by/username/:username`_                        | :meth:`Client.get_user`                           |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users`_                                              | :meth:`Client.get_users`                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/by`_                                           | :meth:`Client.get_users`                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/me`_                                           | :meth:`Client.get_me`                             |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: :ref:`Spaces`                                                                                      |
+    +------------------------------------------------------------------------------------------------------------------+
+    | .. centered:: |Search Spaces|_                                                                                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/spaces/search`_                                      | :meth:`Client.search_spaces`                      |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Spaces lookup|_                                                                                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/spaces`_                                             | :meth:`Client.get_spaces`                         |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/spaces/:id`_                                         | :meth:`Client.get_space`                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/spaces/:id/buyers`_                                  | :meth:`Client.get_space_buyers`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/spaces/:id/tweets`_                                  | :meth:`Client.get_space_tweets`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/spaces/by/creator_ids`_                              | :meth:`Client.get_spaces`                         |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: :ref:`Direct Messages`                                                                             |
+    +------------------------------------------------------------------------------------------------------------------+
+    | .. centered:: |Direct Messages lookup|_                                                                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/dm_conversations/:dm_conversation_id/dm_events`_     | :meth:`Client.get_direct_message_events`          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/dm_conversations/with/:participant_id/dm_events`_    | :meth:`Client.get_direct_message_events`          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/dm_events`_                                          | :meth:`Client.get_direct_message_events`          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Manage Direct Messages|_                                                                          |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/dm_conversations`_                                  | :meth:`Client.create_direct_message_conversation` |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/dm_conversations/:dm_conversation_id/messages`_     | :meth:`Client.create_direct_message`              |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/dm_conversations/with/:participant_id/messages`_    | :meth:`Client.create_direct_message`              |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: :ref:`Lists`                                                                                       |
+    +------------------------------------------------------------------------------------------------------------------+
+    | .. centered:: |List Tweets lookup|_                                                                              |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/lists/:id/tweets`_                                   | :meth:`Client.get_list_tweets`                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |List follows|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:id/followed_lists/:list_id`_               | :meth:`Client.unfollow_list`                      |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/lists/:id/followers`_                                | :meth:`Client.get_list_followers`                 |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/followed_lists`_                           | :meth:`Client.get_followed_lists`                 |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/followed_lists`_                          | :meth:`Client.follow_list`                        |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |List lookup|_                                                                                     |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/lists/:id`_                                          | :meth:`Client.get_list`                           |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/owned_lists`_                              | :meth:`Client.get_owned_lists`                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |List members|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/lists/:id/members/:user_id`_                      | :meth:`Client.remove_list_member`                 |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/lists/:id/members`_                                  | :meth:`Client.get_list_members`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/list_memberships`_                         | :meth:`Client.get_list_memberships`               |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/lists/:id/members`_                                 | :meth:`Client.add_list_member`                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Manage Lists|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/lists/:id`_                                       | :meth:`Client.delete_list`                        |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `PUT /2/lists/:id`_                                          | :meth:`Client.update_list`                        |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/lists`_                                             | :meth:`Client.create_list`                        |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: |Pinned Lists|_                                                                                    |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `DELETE /2/users/:id/pinned_lists/:list_id`_                 | :meth:`Client.unpin_list`                         |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/users/:id/pinned_lists`_                             | :meth:`Client.get_pinned_lists`                   |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/users/:id/pinned_lists`_                            | :meth:`Client.pin_list`                           |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | .. centered:: :ref:`Compliance`                                                                                  |
+    +------------------------------------------------------------------------------------------------------------------+
+    | .. centered:: |Batch Compliance|_                                                                                |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/compliance/jobs`_                                    | :meth:`Client.get_compliance_jobs`                |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `GET /2/compliance/jobs/:id`_                                | :meth:`Client.get_compliance_job`                 |
+    +--------------------------------------------------------------+---------------------------------------------------+
+    | `POST /2/compliance/jobs`_                                   | :meth:`Client.create_compliance_job`              |
+    +--------------------------------------------------------------+---------------------------------------------------+
 
 .. |Bookmarks| replace:: *Bookmarks*
 .. _DELETE /2/users/:id/bookmarks/:tweet_id: https://developer.twitter.com/en/docs/twitter-api/tweets/bookmarks/api-reference/delete-users-id-bookmarks-tweet_id
 .. _GET /2/compliance/jobs: https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs
 .. _GET /2/compliance/jobs/:id: https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/get-compliance-jobs-id
 .. _POST /2/compliance/jobs: https://developer.twitter.com/en/docs/twitter-api/compliance/batch-compliance/api-reference/post-compliance-jobs
+.. |Direct Messages lookup| replace:: *Direct Messages lookup*
+.. _GET /2/dm_conversations/:dm_conversation_id/dm_events: https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-dm_conversation_id-dm_events
+.. _GET /2/dm_conversations/with/:participant_id/dm_events: https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-with-participant_id-dm_events
+.. _GET /2/dm_events: https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_events
+.. |Manage Direct Messages| replace:: *Manage Direct Messages*
+.. _POST /2/dm_conversations: https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations
+.. _POST /2/dm_conversations/:dm_conversation_id/messages: https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-dm_conversation_id-messages
+.. _POST /2/dm_conversations/with/:participant_id/messages: https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-with-participant_id-messages
 
 Tweets
 ======
@@ -434,6 +460,21 @@ Spaces lookup
 
 .. automethod:: Client.get_space_tweets
 
+Direct Messages
+===============
+
+Direct Messages lookup
+----------------------
+
+.. automethod:: Client.get_direct_message_events
+
+Manage Direct Messages
+----------------------
+
+.. automethod:: Client.create_direct_message
+
+.. automethod:: Client.create_direct_message_conversation
+
 Lists
 =====
 
index dfe5332d7022a8b881bb7c841912ee5d30e49b8a..f0da251b413214dce8af662ce9c0ac35b9e5029b 100644 (file)
@@ -6,6 +6,11 @@
 Models
 ******
 
+:class:`DirectMessageEvent`
+===========================
+
+.. autoclass:: DirectMessageEvent()
+
 :class:`List`
 =============
 
index 4aaa6c51256011c9c1fe80a323c1a68c1a66c550..f7fa1b82cb19ef6db71d9a7b0b55c90a438e970d 100644 (file)
@@ -185,6 +185,29 @@ class TweepyAsyncClientTests(IsolatedAsyncioTestCase):
 
     # TODO: Test AsyncClient.get_space_tweets
 
+    @tape.use_cassette(
+        "test_asyncclient_manage_and_lookup_direct_messages.yaml"
+    )
+    async def test_manage_and_lookup_direct_messages(self):
+        user_ids = [145336962, 750362064426721281]
+        # User IDs for @Harmon758 and @Harmon758Public
+        response = await self.client.create_direct_message(
+            participant_id=user_ids[1],
+            text="Testing 1"
+        )
+        dm_conversation_id = response.data["dm_conversation_id"]
+        await self.client.create_direct_message(
+            dm_conversation_id=dm_conversation_id,
+            text="Testing 2"
+        )
+        await self.client.create_direct_message_conversation(
+            text="Testing",
+            participant_ids=user_ids
+        )
+        await self.client.get_dm_events()
+        await self.client.get_dm_events(dm_conversation_id=dm_conversation_id)
+        await self.client.get_dm_events(participant_id=user_ids[1])
+
     @tape.use_cassette("test_asyncclient_get_list_tweets.yaml")
     async def test_get_list_tweets(self):
         list_id = 84839422  # List ID for Official Twitter Accounts (@Twitter)
index 054f3d5521c36f72616d9ca1de52be0df7aa84db..faa3a41be16b692e423c709fd0cf3187c9e318ce 100644 (file)
@@ -179,6 +179,27 @@ class TweepyClientTests(unittest.TestCase):
 
     # TODO: Test Client.get_space_tweets
 
+    @tape.use_cassette("test_manage_and_lookup_direct_messages.yaml")
+    def test_manage_and_lookup_direct_messages(self):
+        user_ids = [145336962, 750362064426721281]
+        # User IDs for @Harmon758 and @Harmon758Public
+        response = self.client.create_direct_message(
+            participant_id=user_ids[1],
+            text="Testing 1"
+        )
+        dm_conversation_id = response.data["dm_conversation_id"]
+        self.client.create_direct_message(
+            dm_conversation_id=dm_conversation_id,
+            text="Testing 2"
+        )
+        self.client.create_direct_message_conversation(
+            text="Testing",
+            participant_ids=user_ids
+        )
+        self.client.get_dm_events()
+        self.client.get_dm_events(dm_conversation_id=dm_conversation_id)
+        self.client.get_dm_events(participant_id=user_ids[1])
+
     @tape.use_cassette("test_client_get_list_tweets.yaml")
     def test_get_list_tweets(self):
         list_id = 84839422  # List ID for Official Twitter Accounts (@Twitter)
index 106921f9d769096c397e0f16035b96c6b0305e3f..78af3b9066044d55cfbc2a98c9e639c9f983f823 100644 (file)
@@ -17,6 +17,7 @@ from tweepy.auth import (
 from tweepy.cache import Cache, FileCache, MemoryCache
 from tweepy.client import Client, Response
 from tweepy.cursor import Cursor
+from tweepy.direct_message_event import DirectMessageEvent
 from tweepy.errors import (
     BadRequest, Forbidden, HTTPException, NotFound, TooManyRequests,
     TweepyException, TwitterServerError, Unauthorized
index 27a2e74dffe918ffc92a42351c10d82050ca46de..be256aea315d6077772cbafd9dda6a52fc110e8c 100644 (file)
@@ -20,6 +20,7 @@ from yarl import URL
 
 import tweepy
 from tweepy.client import BaseClient, Response
+from tweepy.direct_message_event import DirectMessageEvent
 from tweepy.errors import (
     BadRequest, Forbidden, HTTPException, NotFound, TooManyRequests,
     TwitterServerError, Unauthorized
@@ -138,7 +139,7 @@ class AsyncBaseClient(BaseClient):
         return response
 
     async def _make_request(
-        self, method, route, params={}, endpoint_parameters=None, json=None,
+        self, method, route, params={}, endpoint_parameters=(), json=None,
         data_type=None, user_auth=False
     ):
         request_params = self._process_params(params, endpoint_parameters)
@@ -2569,6 +2570,235 @@ class AsyncClient(AsyncBaseClient):
             ), data_type=Tweet
         )
 
+    # Direct Messages lookup
+
+    async def get_direct_message_events(
+        self, *, dm_conversation_id=None, participant_id=None, user_auth=True,
+        **params
+    ):
+        """get_direct_message_events( \
+            *, dm_conversation_id=None, participant_id=None, \
+            dm_event_fields=None, event_types=None, expansions=None, \
+            max_results=None, media_fields=None, pagination_token=None, \
+            tweet_fields=None, user_fields=None, user_auth=True \
+        )
+
+        If ``dm_conversation_id`` is passed, returns a list of Direct Messages
+        within the conversation specified. Messages are returned in reverse
+        chronological order.
+
+        If ``participant_id`` is passed, returns a list of Direct Messages (DM)
+        events within a 1-1 conversation with the user specified. Messages are
+        returned in reverse chronological order.
+
+        If neither is passed, returns a list of Direct Messages for the
+        authenticated user, both sent and received. Direct Message events are
+        returned in reverse chronological order. Supports retrieving events
+        from the previous 30 days.
+
+        .. note::
+        
+            There is an alias for this method named ``get_dm_events``.
+
+        .. versionadded:: 4.12
+
+        Parameters
+        ----------
+        dm_conversation_id : str | None
+            The ``id`` of the Direct Message conversation for which events are
+            being retrieved.
+        participant_id : int | str | None
+            The ``participant_id`` of the user that the authenicating user is
+            having a 1-1 conversation with.
+        dm_event_fields : list[str] | str | None
+            Extra fields to include in the event payload. ``id``, ``text``, and
+            ``event_type`` are returned by default.
+        event_types : str
+            The type of Direct Message event to returm. If not included, all
+            types are returned.
+        expansions : list[str] | str | None
+            :ref:`expansions_parameter`
+        max_results : int | None
+            The maximum number of results to be returned in a page. Must be
+            between 1 and 100. The default is 100.
+        media_fields : list[str] | str | None
+            :ref:`media_fields_parameter`
+        pagination_token : str | None
+            Contains either the ``next_token`` or ``previous_token`` value.
+        tweet_fields : list[str] | str | None
+            :ref:`tweet_fields_parameter`
+        user_fields : list[str] | str | None
+            :ref:`user_fields_parameter`
+        user_auth : bool
+            Whether or not to use OAuth 1.0a User Context to authenticate
+
+        Raises
+        ------
+        TypeError
+            If both ``dm_conversation_id`` and ``participant_id`` are passed
+
+        Returns
+        -------
+        dict | aiohttp.ClientResponse | Response
+
+        References
+        ----------
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_events
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-with-participant_id-dm_events
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-dm_conversation_id-dm_events
+        """
+        if dm_conversation_id is not None and participant_id is not None:
+            raise TypeError(
+                "Expected DM conversation ID or participant ID, not both"
+            )
+        elif dm_conversation_id is not None:
+            path = f"/2/dm_conversations/{dm_conversation_id}/dm_events"
+        elif participant_id is not None:
+            path = f"/2/dm_conversations/with/{participant_id}/dm_events"
+        else:
+            path = "/2/dm_events"
+
+        return await self._make_request(
+            "GET", path, params=params,
+            endpoint_parameters=(
+                "dm_event.fields", "event_types", "expansions", "max_results",
+                "media.fields", "pagination_token", "tweet.fields",
+                "user.fields"
+            ), data_type=DirectMessageEvent, user_auth=user_auth
+        )
+
+    get_dm_events = get_direct_message_events
+
+    # Manage Direct Messages
+
+    async def create_direct_message(
+        self, *, dm_conversation_id=None, participant_id=None, media_id=None,
+        text=None, user_auth=True
+    ):
+        """If ``dm_conversation_id`` is passed, creates a Direct Message on
+        behalf of the authenticated user, and adds it to the specified
+        conversation.
+
+        If ``participant_id`` is passed, creates a one-to-one Direct Message
+        and adds it to the one-to-one conversation. This method either creates
+        a new one-to-one conversation or retrieves the current conversation and
+        adds the Direct Message to it.
+
+        .. note::
+        
+            There is an alias for this method named ``create_dm``.
+
+        .. versionadded:: 4.12
+
+        Parameters
+        ----------
+        dm_conversation_id : str | None
+            The ``dm_conversation_id`` of the conversation to add the Direct
+            Message to. Supports both 1-1 and group conversations.
+        participant_id : int | str | None
+            The User ID of the account this one-to-one Direct Message is to be
+            sent to.
+        media_id : int | str | None
+            A single Media ID being attached to the Direct Message. This field
+            is required if ``text`` is not present. For this launch, only 1
+            attachment is supported.
+        text : str | None
+            Text of the Direct Message being created. This field is required if
+            ``media_id`` is not present. Text messages support up to 10,000
+            characters.
+        user_auth : bool
+            Whether or not to use OAuth 1.0a User Context to authenticate
+
+        Raises
+        ------
+        TypeError
+            If ``dm_conversation_id`` and ``participant_id`` are not passed or
+            both are passed
+
+        Returns
+        -------
+        dict | aiohttp.ClientResponse | Response
+
+        References
+        ----------
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-dm_conversation_id-messages
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-with-participant_id-messages
+        """
+        if dm_conversation_id is not None and participant_id is not None:
+            raise TypeError(
+                "Expected DM conversation ID or participant ID, not both"
+            )
+        elif dm_conversation_id is not None:
+            path = f"/2/dm_conversations/{dm_conversation_id}/messages"
+        elif participant_id is not None:
+            path = f"/2/dm_conversations/with/{participant_id}/messages"
+        else:
+            raise TypeError("DM conversation ID or participant ID is required")
+
+        json = {}
+        if media_id is not None:
+            json["attachments"] = [{"media_id": str(media_id)}]
+        if text is not None:
+            json["text"] = text
+
+        return await self._make_request(
+            "POST", path, json=json, user_auth=user_auth
+        )
+
+    create_dm = create_direct_message
+
+    async def create_direct_message_conversation(
+        self, *, media_id=None, text=None, participant_ids, user_auth=True
+    ):
+        """Creates a new group conversation and adds a Direct Message to it on
+        behalf of the authenticated user.
+
+        .. note::
+        
+            There is an alias for this method named ``create_dm_conversation``.
+
+        .. versionadded:: 4.12
+
+        Parameters
+        ----------
+        media_id : int | str | None
+            A single Media ID being attached to the Direct Message. This field
+            is required if ``text`` is not present. For this launch, only 1
+            attachment is supported.
+        text : str | None
+            Text of the Direct Message being created. This field is required if
+            ``media_id`` is not present. Text messages support up to 10,000
+            characters.
+        participant_ids : list[int | str]
+            An array of User IDs that the conversation is created with.
+            Conversations can have up to 50 participants.
+        user_auth : bool
+            Whether or not to use OAuth 1.0a User Context to authenticate
+
+        Returns
+        -------
+        dict | aiohttp.ClientResponse | Response
+
+        References
+        ----------
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations
+        """
+        json = {
+            "conversation_type": "Group",
+            "message": {},
+            "participant_ids": list(map(str, participant_ids))
+        }
+        if media_id is not None:
+            json["message"]["attachments"] = [{"media_id": str(media_id)}]
+        if text is not None:
+            json["message"]["text"] = text
+
+        return await self._make_request(
+            "POST", "/2/dm_conversations", json=json, user_auth=user_auth
+        )
+
+    create_dm_conversation = create_direct_message_conversation
+
     # List Tweets lookup
 
     async def get_list_tweets(self, id, *, user_auth=False, **params):
index 6aace325ba7475d94bc1384f6a658d338523a6ec..c7db560caa317b280a73f59ea2cf1d9123d0b0d0 100644 (file)
@@ -20,6 +20,7 @@ import requests
 
 import tweepy
 from tweepy.auth import OAuth1UserHandler
+from tweepy.direct_message_event import DirectMessageEvent
 from tweepy.errors import (
     BadRequest, Forbidden, HTTPException, NotFound, TooManyRequests,
     TwitterServerError, Unauthorized
@@ -119,8 +120,10 @@ class BaseClient:
 
             return response
 
-    def _make_request(self, method, route, params={}, endpoint_parameters=None,
-                      json=None, data_type=None, user_auth=False):
+    def _make_request(
+        self, method, route, params={}, endpoint_parameters=(), json=None,
+        data_type=None, user_auth=False
+    ):
         request_params = self._process_params(params, endpoint_parameters)
 
         response = self.request(method, route, params=request_params,
@@ -167,10 +170,17 @@ class BaseClient:
         return includes
 
     def _process_params(self, params, endpoint_parameters):
+        endpoint_parameters = {
+            endpoint_parameter.replace('.', '_'): endpoint_parameter
+            for endpoint_parameter in endpoint_parameters
+        }
+
         request_params = {}
         for param_name, param_value in params.items():
-            if param_name.replace('_', '.') in endpoint_parameters:
-                param_name = param_name.replace('_', '.')
+            try:
+                param_name = endpoint_parameters[param_name]
+            except KeyError:
+                log.warn(f"Unexpected parameter: {param_name}")
 
             if isinstance(param_value, list):
                 request_params[param_name] = ','.join(map(str, param_value))
@@ -184,8 +194,6 @@ class BaseClient:
             elif param_value is not None:
                 request_params[param_name] = param_value
 
-            if param_name not in endpoint_parameters:
-                log.warn(f"Unexpected parameter: {param_name}")
         return request_params
 
 
@@ -2760,6 +2768,233 @@ class Client(BaseClient):
             ), data_type=Tweet
         )
 
+    # Direct Messages lookup
+
+    def get_direct_message_events(
+        self, *, dm_conversation_id=None, participant_id=None, user_auth=True,
+        **params
+    ):
+        """get_direct_message_events( \
+            *, dm_conversation_id=None, participant_id=None, \
+            dm_event_fields=None, event_types=None, expansions=None, \
+            max_results=None, media_fields=None, pagination_token=None, \
+            tweet_fields=None, user_fields=None, user_auth=True \
+        )
+
+        If ``dm_conversation_id`` is passed, returns a list of Direct Messages
+        within the conversation specified. Messages are returned in reverse
+        chronological order.
+
+        If ``participant_id`` is passed, returns a list of Direct Messages (DM)
+        events within a 1-1 conversation with the user specified. Messages are
+        returned in reverse chronological order.
+
+        If neither is passed, returns a list of Direct Messages for the
+        authenticated user, both sent and received. Direct Message events are
+        returned in reverse chronological order. Supports retrieving events
+        from the previous 30 days.
+
+        .. note::
+        
+            There is an alias for this method named ``get_dm_events``.
+
+        .. versionadded:: 4.12
+
+        Parameters
+        ----------
+        dm_conversation_id : str | None
+            The ``id`` of the Direct Message conversation for which events are
+            being retrieved.
+        participant_id : int | str | None
+            The ``participant_id`` of the user that the authenicating user is
+            having a 1-1 conversation with.
+        dm_event_fields : list[str] | str | None
+            Extra fields to include in the event payload. ``id``, ``text``, and
+            ``event_type`` are returned by default.
+        event_types : str
+            The type of Direct Message event to returm. If not included, all
+            types are returned.
+        expansions : list[str] | str | None
+            :ref:`expansions_parameter`
+        max_results : int | None
+            The maximum number of results to be returned in a page. Must be
+            between 1 and 100. The default is 100.
+        media_fields : list[str] | str | None
+            :ref:`media_fields_parameter`
+        pagination_token : str | None
+            Contains either the ``next_token`` or ``previous_token`` value.
+        tweet_fields : list[str] | str | None
+            :ref:`tweet_fields_parameter`
+        user_fields : list[str] | str | None
+            :ref:`user_fields_parameter`
+        user_auth : bool
+            Whether or not to use OAuth 1.0a User Context to authenticate
+
+        Raises
+        ------
+        TypeError
+            If both ``dm_conversation_id`` and ``participant_id`` are passed
+
+        Returns
+        -------
+        dict | requests.Response | Response
+
+        References
+        ----------
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_events
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-with-participant_id-dm_events
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/lookup/api-reference/get-dm_conversations-dm_conversation_id-dm_events
+        """
+        if dm_conversation_id is not None and participant_id is not None:
+            raise TypeError(
+                "Expected DM conversation ID or participant ID, not both"
+            )
+        elif dm_conversation_id is not None:
+            path = f"/2/dm_conversations/{dm_conversation_id}/dm_events"
+        elif participant_id is not None:
+            path = f"/2/dm_conversations/with/{participant_id}/dm_events"
+        else:
+            path = "/2/dm_events"
+
+        return self._make_request(
+            "GET", path, params=params,
+            endpoint_parameters=(
+                "dm_event.fields", "event_types", "expansions", "max_results",
+                "media.fields", "pagination_token", "tweet.fields",
+                "user.fields"
+            ), data_type=DirectMessageEvent, user_auth=user_auth
+        )
+
+    get_dm_events = get_direct_message_events
+
+    # Manage Direct Messages
+
+    def create_direct_message(
+        self, *, dm_conversation_id=None, participant_id=None, media_id=None,
+        text=None, user_auth=True
+    ):
+        """If ``dm_conversation_id`` is passed, creates a Direct Message on
+        behalf of the authenticated user, and adds it to the specified
+        conversation.
+
+        If ``participant_id`` is passed, creates a one-to-one Direct Message
+        and adds it to the one-to-one conversation. This method either creates
+        a new one-to-one conversation or retrieves the current conversation and
+        adds the Direct Message to it.
+
+        .. note::
+        
+            There is an alias for this method named ``create_dm``.
+
+        .. versionadded:: 4.12
+
+        Parameters
+        ----------
+        dm_conversation_id : str | None
+            The ``dm_conversation_id`` of the conversation to add the Direct
+            Message to. Supports both 1-1 and group conversations.
+        participant_id : int | str | None
+            The User ID of the account this one-to-one Direct Message is to be
+            sent to.
+        media_id : int | str | None
+            A single Media ID being attached to the Direct Message. This field
+            is required if ``text`` is not present. For this launch, only 1
+            attachment is supported.
+        text : str | None
+            Text of the Direct Message being created. This field is required if
+            ``media_id`` is not present. Text messages support up to 10,000
+            characters.
+        user_auth : bool
+            Whether or not to use OAuth 1.0a User Context to authenticate
+
+        Raises
+        ------
+        TypeError
+            If ``dm_conversation_id`` and ``participant_id`` are not passed or
+            both are passed
+
+        Returns
+        -------
+        dict | requests.Response | Response
+
+        References
+        ----------
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-dm_conversation_id-messages
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations-with-participant_id-messages
+        """
+        if dm_conversation_id is not None and participant_id is not None:
+            raise TypeError(
+                "Expected DM conversation ID or participant ID, not both"
+            )
+        elif dm_conversation_id is not None:
+            path = f"/2/dm_conversations/{dm_conversation_id}/messages"
+        elif participant_id is not None:
+            path = f"/2/dm_conversations/with/{participant_id}/messages"
+        else:
+            raise TypeError("DM conversation ID or participant ID is required")
+
+        json = {}
+        if media_id is not None:
+            json["attachments"] = [{"media_id": str(media_id)}]
+        if text is not None:
+            json["text"] = text
+
+        return self._make_request("POST", path, json=json, user_auth=user_auth)
+
+    create_dm = create_direct_message
+
+    def create_direct_message_conversation(
+        self, *, media_id=None, text=None, participant_ids, user_auth=True
+    ):
+        """Creates a new group conversation and adds a Direct Message to it on
+        behalf of the authenticated user.
+
+        .. note::
+        
+            There is an alias for this method named ``create_dm_conversation``.
+
+        .. versionadded:: 4.12
+
+        Parameters
+        ----------
+        media_id : int | str | None
+            A single Media ID being attached to the Direct Message. This field
+            is required if ``text`` is not present. For this launch, only 1
+            attachment is supported.
+        text : str | None
+            Text of the Direct Message being created. This field is required if
+            ``media_id`` is not present. Text messages support up to 10,000
+            characters.
+        participant_ids : list[int | str]
+            An array of User IDs that the conversation is created with.
+            Conversations can have up to 50 participants.
+        user_auth : bool
+            Whether or not to use OAuth 1.0a User Context to authenticate
+
+        Returns
+        -------
+        dict | requests.Response | Response
+
+        References
+        ----------
+        https://developer.twitter.com/en/docs/twitter-api/direct-messages/manage/api-reference/post-dm_conversations
+        """
+        json = {
+            "conversation_type": "Group",
+            "message": {},
+            "participant_ids": list(map(str, participant_ids))
+        }
+        if media_id is not None:
+            json["message"]["attachments"] = [{"media_id": str(media_id)}]
+        if text is not None:
+            json["message"]["text"] = text
+
+        return self._make_request(
+            "POST", "/2/dm_conversations", json=json, user_auth=user_auth
+        )
+
+    create_dm_conversation = create_direct_message_conversation
+
     # List Tweets lookup
 
     def get_list_tweets(self, id, *, user_auth=False, **params):
diff --git a/tweepy/direct_message_event.py b/tweepy/direct_message_event.py
new file mode 100644 (file)
index 0000000..6ff037a
--- /dev/null
@@ -0,0 +1,74 @@
+# Tweepy
+# Copyright 2009-2022 Joshua Roesslein
+# See LICENSE for details.
+
+from tweepy.mixins import DataMapping, HashableID
+from tweepy.tweet import ReferencedTweet
+from tweepy.utils import parse_datetime
+
+
+class DirectMessageEvent(HashableID, DataMapping):
+    """.. versionadded:: 4.12
+
+    Attributes
+    ----------
+    data : dict
+        The JSON data representing the Direct Message event.
+    id : int
+    event_type : str
+    attachments : dict | None
+    created_at : datetime.datetime | None
+    dm_conversation_id : str | None
+    participant_ids : list[int] | None
+    referenced_tweets : list[ReferencedTweet] | None
+    sender_id : int | None
+    text : str | None
+    """
+
+    __slots__ = (
+        "data", "id", "event_type", "attachments", "created_at",
+        "dm_conversation_id", "participant_ids", "referenced_tweets",
+        "sender_id", "text"
+    )
+
+    def __init__(self, data):
+        self.data = data
+        self.id = int(data["id"])
+        self.event_type = data["event_type"]
+
+        self.attachments = data.get("attachments")
+
+        self.created_at = data.get("created_at")
+        if self.created_at is not None:
+            self.created_at = parse_datetime(self.created_at)
+
+        self.dm_conversation_id = data.get("dm_conversation_id")
+
+        self.participant_ids = data.get("participant_ids")
+        if self.participant_ids is not None:
+            self.participant_ids = list(map(int, self.participant_ids))
+
+        self.referenced_tweets = data.get("referenced_tweets")
+        if self.referenced_tweets is not None:
+            self.referenced_tweets = [
+                ReferencedTweet(referenced_tweet)
+                for referenced_tweet in self.referenced_tweets
+            ]
+
+        self.sender_id = data.get("sender_id")
+        if self.sender_id is not None:
+            self.sender_id = int(self.sender_id)
+
+        self.text = data.get("text")
+
+    def __repr__(self):
+        representation = (
+            f"<Direct Message Event id={self.id} event_type={self.event_type}"
+        )
+        if self.text is not None:
+            representation += f" text={repr(self.text)}"
+        representation += '>'
+        return representation
+
+    def __str__(self):
+        return self.text or self.__repr__()
index b93d7a86209591b8a678fee27300ccc55edcd0c7..42a189dc327dbd0f53dc3e402b5489168916934e 100644 (file)
@@ -191,13 +191,16 @@ class Tweet(HashableID, DataMapping):
 class ReferencedTweet(HashableID, DataMapping):
     """.. versionadded:: 4.0
 
+    .. versionchanged:: 4.12
+        Changed ``type`` to be optional
+
     Attributes
     ----------
     data : dict
         The JSON data representing the referenced Tweet.
     id : int
         The unique identifier of the referenced Tweet.
-    type : str
+    type : str | None
 
     References
     ----------
@@ -209,7 +212,12 @@ class ReferencedTweet(HashableID, DataMapping):
     def __init__(self, data):
         self.data = data
         self.id = int(data["id"])
-        self.type = data["type"]
+
+        self.type = data.get("type")
 
     def __repr__(self):
-        return f"<ReferencedTweet id={self.id} type={self.type}>"
+        representation = f"<ReferencedTweet id={self.id}"
+        if self.type is not None:
+            representation += f" type={self.type}"
+        representation += '>'
+        return representation