notification filtering

pull/397/head
halcy 2025-02-15 20:08:22 +02:00
rodzic 442c9c3164
commit 7e2937148b
11 zmienionych plików z 1818 dodań i 9 usunięć

Wyświetl plik

@ -41,6 +41,7 @@ v2.0.0 (IN PROGRESS)
* Add trend admin functions (`admin_trending_tags`, `admin_trending_statuses`, `admin_trending_links`, `admin_approve_trending_link`, `admin_reject_trending_link`, `admin_approve_trending_status`, `admin_reject_trending_status`, `admin_approve_trending_tag`, `admin_reject_trending_tag`)
* Add admin IP block functions (`admin_ip_blocks`, `admin_ip_block`, `admin_ip_block_create`, `admin_ip_block_delete`)
* Add `instance_domain_blocks`
* Add notification policy and requests (`notifications_policy`, `update_notifications_policy`, `notifications_requests`, `notification_request`, `accept_notification_request`, `reject_notification_request`, `notifications_merged`, `accept_multiple_notification_requests`, `dismiss_multiple_notification_requests`)
v1.8.1
------

Wyświetl plik

@ -11,7 +11,7 @@ Reading
~~~~~~~
.. automethod:: Mastodon.notifications
.. automethod:: Mastodon.notifications_unread_count
Writing
~~~~~~~
.. automethod:: Mastodon.notifications_clear
@ -19,6 +19,22 @@ Writing
.. automethod:: Mastodon.conversations_read
Source filtering for notifications
----------------------------------
These functions allow you to get information about source filters as well as to create and update filters, and
to accept or reject notification requests for filtered notifications.
.. authomedod:: Mastodon.notifications_policy
.. automethod:: Mastodon.update_notifications_policy
.. automethod:: Mastodon.notification_requests
.. automethod:: Mastodon.notification_request
.. automethod:: Mastodon.accept_notification_request
.. automethod:: Mastodon.dismiss_notification_request
.. automethod:: Mastodon.accept_multiple_notification_requests
.. automethod:: Mastodon.dismiss_multiple_notification_requests
.. automethod:: Mastodon.notifications_merged
Keyword filters
---------------
These functions allow you to get information about keyword filters as well as to create and update filters.
@ -26,6 +42,8 @@ These functions allow you to get information about keyword filters as well as to
**Very Important Note: The filtering system was revised in 4.0.0. This means that these functions will now not work anymore if an instance is on Mastodon 4.0.0 or above.
When updating Mastodon.py for 4.0.0, we'll make an effort to emulate old behaviour, but this will not always be possible. Consider these methods deprecated, for now.**
The filters are applied to everything - notifications, timeline, ....
Reading
~~~~~~~
.. automethod:: Mastodon.filters
@ -38,6 +56,7 @@ Writing
.. automethod:: Mastodon.filter_update
.. automethod:: Mastodon.filter_delete
Push notifications
------------------
Mastodon supports the delivery of notifications via webpush.

Wyświetl plik

@ -6,3 +6,8 @@ echo ".. py:class: Mastodon" >> 15_everything.rst
echo "" >> 15_everything.rst
cat 01_general.rst 02_return_values.rst 03_errors.rst 04_auth.rst 05_statuses.rst 06_accounts.rst 07_timelines.rst 08_instances.rst 09_notifications.rst 10_streaming.rst 11_misc.rst 12_utilities.rst 13_admin.rst | grep -E "automethod|autoclass" >> 15_everything.rst
echo "" >> 15_everything.rst
# Now go through 15_everything.rst and add :no-index: to all the automethods and autoclasses
# This is because we don't want to index them, as they are already indexed in their own pages
sed -i 's/automethod::/automethod:: :no-index:/g' 15_everything.rst
sed -i 's/autoclass::/autoclass:: :no-index:/g' 15_everything.rst

Wyświetl plik

@ -343,9 +343,10 @@ class Mastodon():
Internal streaming API helper.
Returns the correct URL for the streaming API.
Caches the URL.
"""
if self.__streaming_base is not None:
return self.__streaming_base
# Try to support implementations that have no v1 endpoint (Sharkey does this)
streaming_api_url = None
try:

Wyświetl plik

@ -3,7 +3,7 @@ from mastodon.errors import MastodonIllegalArgumentError
from mastodon.utility import api_version
from mastodon.internals import Mastodon as Internals
from mastodon.return_types import Notification, IdType, PaginatableList, Account, UnreadNotificationsCount
from mastodon.return_types import Notification, IdType, PaginatableList, Account, UnreadNotificationsCount, NotificationPolicy, NotificationRequest
from typing import Union, Optional, List
class Mastodon(Internals):
@ -89,3 +89,97 @@ class Mastodon(Internals):
else:
params = self.__generate_params(locals())
self.__api_request('POST', '/api/v1/notifications/dismiss', params)
##
# Notification policies
##
@api_version("4.3.0", "4.3.0")
def notifications_policy(self) -> NotificationPolicy:
"""
Fetch the user's notification filtering policy. Requires scope `read:notifications`.
"""
return self.__api_request('GET', '/api/v2/notifications/policy')
@api_version("4.3.0", "4.3.0")
def update_notifications_policy(self, for_not_following: Optional[str] = None, for_not_followers: Optional[str] = None,
for_new_accounts: Optional[str] = None, for_private_mentions: Optional[str] = None,
for_limited_accounts: Optional[str] = None) -> NotificationPolicy:
"""
Update the user's notification filtering policy. Requires scope `write:notifications`.
- `for_not_following`: "accept", "filter", or "drop" notifications from non-followed accounts.
- `for_not_followers`: "accept", "filter", or "drop" notifications from non-followers.
- `for_new_accounts`: "accept", "filter", or "drop" notifications from accounts created in the past 30 days.
- `for_private_mentions`: "accept", "filter", or "drop" notifications from private mentions.
- `for_limited_accounts`: "accept", "filter", or "drop" notifications from accounts limited by moderators.
"""
params = self.__generate_params(locals())
return self.__api_request('PATCH', '/api/v2/notifications/policy', params)
##
# Notification requests
##
@api_version("4.3.0", "4.3.0")
def notification_requests(self, max_id: Optional[IdType] = None, since_id: Optional[IdType] = None,
min_id: Optional[IdType] = None, limit: Optional[int] = None) -> PaginatableList[NotificationRequest]:
"""
Fetch notification requests filtered by the user's policy. Requires scope `read:notifications`.
NB: Notification requests are what happens when the user has set their policy to filter notifications from some source.
"""
params = self.__generate_params(locals())
return self.__api_request('GET', '/api/v1/notifications/requests', params)
@api_version("4.3.0", "4.3.0")
def notification_request(self, id: Union[NotificationRequest, IdType]) -> NotificationRequest:
"""
Fetch a single notification request by ID. Requires scope `read:notifications`.
"""
id = self.__unpack_id(id)
return self.__api_request('GET', f'/api/v1/notifications/requests/{id}')
@api_version("4.3.0", "4.3.0")
def accept_notification_request(self, id: Union[NotificationRequest, IdType]) -> None:
"""
Accept a notification request. This moves filtered notifications from a user back into the main notifications feed
and allows future notifications from them. Requires scope `write:notifications`.
"""
id = self.__unpack_id(id)
self.__api_request('POST', f'/api/v1/notifications/requests/{id}/accept')
@api_version("4.3.0", "4.3.0")
def dismiss_notification_request(self, id: Union[NotificationRequest, IdType]) -> None:
"""
Dismiss a notification request, removing it from pending requests. Requires scope `write:notifications`.
"""
id = self.__unpack_id(id)
self.__api_request('POST', f'/api/v1/notifications/requests/{id}/dismiss')
@api_version("4.3.0", "4.3.0")
def accept_multiple_notification_requests(self, ids: List[Union[NotificationRequest, IdType]]) -> None:
"""
Accept multiple notification requests at once. This moves filtered notifications from those users back into
the main notifications feed and allows future notifications from them. Requires scope `write:notifications`.
"""
params = self.__generate_params({"id[]": [self.__unpack_id(i) for i in ids]})
self.__api_request('POST', '/api/v1/notifications/requests/accept', params)
@api_version("4.3.0", "4.3.0")
def dismiss_multiple_notification_requests(self, ids: List[Union[NotificationRequest, IdType]]) -> None:
"""
Dismiss multiple notification requests, removing them from pending requests. Requires scope `write:notifications`.
"""
params = self.__generate_params({"id[]": [self.__unpack_id(i) for i in ids]})
self.__api_request('POST', '/api/v1/notifications/requests/dismiss', params)
@api_version("4.3.0", "4.3.0")
def notifications_merged(self) -> bool:
"""
Check whether accepted notification requests have been merged into the main notification feed.
Accepting a notification request schedules a background job that merges the filtered notifications.
Clients can poll this endpoint to check if the merge has completed. Requires scope `read:notifications`.
"""
result = self.__api_request('GET', '/api/v1/notifications/requests/merged', override_type = dict)
return result["merged"]

Wyświetl plik

@ -1301,6 +1301,38 @@ class Tag(AttribAccessDict):
* 4.0.0: added
"""
id: "Optional[str]"
"""
The ID of the Tag in the database. Only present for data returned from admin endpoints. (optional)
Version history:
* 3.5.0: added
"""
trendable: "Optional[bool]"
"""
Whether the hashtag has been approved to trend. Only present for data returned from admin endpoints. (optional)
Version history:
* 3.5.0: added
"""
usable: "Optional[bool]"
"""
Whether the hashtag has not been disabled from auto-linking. Only present for data returned from admin endpoints. (optional)
Version history:
* 3.5.0: added
"""
requires_review: "Optional[bool]"
"""
Whether the hashtag has not been reviewed yet to approve or deny its trending. Only present for data returned from admin endpoints. (optional)
Version history:
* 3.5.0: added
"""
_version = "4.0.0"
class TagHistory(AttribAccessDict):
@ -1796,6 +1828,7 @@ class Notification(AttribAccessDict):
* 3.3.0: added `status`
* 3.5.0: added `update` and `admin.sign_up`
* 4.0.0: added `admin.report`
* 4.3.0: added `severed_relationships` and `moderation_warning`
"""
created_at: "datetime"
@ -1831,6 +1864,30 @@ class Notification(AttribAccessDict):
* 4.3.0: added
"""
report: "Optional[Report]"
"""
Report that was the object of the notification. (optional)
Version history:
* 4.0.0: added
"""
event: "Optional[RelationshipSeveranceEvent]"
"""
Summary of the event that caused follow relationships to be severed. (optional)
Version history:
* 4.3.0: added
"""
moderation_warning: "Optional[AccountWarning]"
"""
Moderation warning that caused the notification. (optional)
Version history:
* 4.3.0: added
"""
_version = "4.3.0"
class Context(AttribAccessDict):
@ -5539,7 +5596,7 @@ class AdminIpBlock(AttribAccessDict):
.. code-block:: python
# Returns a AdminIpBlock object
TODO_TO_BE_IMPLEMENTED
mastodon.admin_ip_blocks()[0]
See also (Mastodon API documentation): https://docs.joinmastodon.org/entities/Admin_IpBlock
"""
@ -5603,7 +5660,7 @@ class DomainBlock(AttribAccessDict):
.. code-block:: python
# Returns a DomainBlock object
TODO_TO_BE_IMPLEMENTED
mastodon.instance_domain_blocks()[0]
See also (Mastodon API documentation): https://docs.joinmastodon.org/entities/DomainBlock
"""
@ -6589,6 +6646,70 @@ class Appeal(AttribAccessDict):
_version = "4.3.0"
class NotificationRequest(AttribAccessDict):
"""
Represents a group of filtered notifications from a specific user.
Example:
.. code-block:: python
# Returns a NotificationRequest object
mastodon.notification_requests()[0]
See also (Mastodon API documentation): https://docs.joinmastodon.org/entities/NotificationRequest
"""
id: "str"
"""
The ID of the notification request in the database.
Version history:
* 4.3.0: added
"""
created_at: "datetime"
"""
When the first filtered notification from that user was created.
Version history:
* 4.3.0: added
"""
updated_at: "datetime"
"""
When the notification request was last updated.
Version history:
* 4.3.0: added
"""
account: "Account"
"""
The account that performed the action that generated the filtered notifications.
Version history:
* 4.3.0: added
"""
notifications_count: "str"
"""
How many of this accounts notifications were filtered.
Version history:
* 4.3.0: added
"""
last_status: "Optional[Status]"
"""
Most recent status associated with a filtered notification from that account. (optional)
Version history:
* 4.3.0: added
"""
_version = "4.3.0"
ENTITY_NAME_MAP = {
"Account": Account,
"AccountField": AccountField,
@ -6699,6 +6820,7 @@ ENTITY_NAME_MAP = {
"AccountWarning": AccountWarning,
"UnreadNotificationsCount": UnreadNotificationsCount,
"Appeal": Appeal,
"NotificationRequest": NotificationRequest,
}
__all__ = [
"Account",
@ -6810,5 +6932,6 @@ __all__ = [
"AccountWarning",
"UnreadNotificationsCount",
"Appeal",
"NotificationRequest",
]

Wyświetl plik

@ -163,7 +163,7 @@ if sys.version_info < (3, 9):
DomainBlock, ExtendedDescription, FilterKeyword, FilterStatus, IdentityProof, StatusSource, \
Suggestion, Translation, AccountCreationError, AccountCreationErrorDetails, AccountCreationErrorDetailsField, NotificationPolicy, \
NotificationPolicySummary, RelationshipSeveranceEvent, GroupedNotificationsResults, PartialAccountWithAvatar, NotificationGroup, AccountWarning, \
UnreadNotificationsCount, Appeal, TrendingLinkHistory
UnreadNotificationsCount, Appeal, TrendingLinkHistory, NotificationRequest
if isinstance(t, ForwardRef):
try:
t = t._evaluate(globals(), locals(), frozenset())
@ -520,6 +520,10 @@ class AttribAccessDict(OrderedStrDict, Entity):
While the subclasses implement specific fields with proper typing, parsing and documentation,
they all inherit from this class, and parsing is extremely permissive to allow for forward and
backward compatibility as well as compatibility with other implementations of the Mastodon API.
This class can ALSO have pagination information attached, for paginating lists *inside* the object,
because that's what Mastodon 4.3.0 does for groupee notifications. This is special cased in the class
definition, though.
"""
def __init__(self, **kwargs):
"""

Wyświetl plik

@ -2895,6 +2895,10 @@
[
"4.0.0",
"added `admin.report`"
],
[
"4.3.0",
"added `severed_relationships` and `moderation_warning`"
]
],
"enum": {
@ -2971,6 +2975,36 @@
]
],
"enum": null
},
"report": {
"description": "Report that was the object of the notification.",
"enum": null,
"version_history": [["4.0.0", "added"]],
"field_type": "Report",
"field_subtype": null,
"field_structuretype": null,
"is_optional": true,
"is_nullable": false
},
"event": {
"description": "Summary of the event that caused follow relationships to be severed.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "RelationshipSeveranceEvent",
"field_subtype": null,
"field_structuretype": null,
"is_optional": true,
"is_nullable": false
},
"moderation_warning": {
"description": "Moderation warning that caused the notification.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "AccountWarning",
"field_subtype": null,
"field_structuretype": null,
"is_optional": true,
"is_nullable": false
}
}
},
@ -9179,7 +9213,7 @@
{
"name": "Notification Policy",
"python_name": "NotificationPolicy",
"func_call": "TODO_TO_BE_IMPLEMENTED",
"func_call": "mastodon.notification_policy()",
"func_call_real": null,
"func_call_additional": null,
"func_alternate_acc": null,
@ -9271,7 +9305,7 @@
{
"name": "Notification Policy Summary",
"python_name": "NotificationPolicySummary",
"func_call": "TODO_TO_BE_IMPLEMENTED",
"func_call": "mastodon.notification_policy().summary",
"func_call_real": null,
"func_call_additional": null,
"func_alternate_acc": null,
@ -9818,5 +9852,79 @@
"is_nullable": false
}
}
},
{
"name": "Notification Request",
"python_name": "NotificationRequest",
"func_call": "mastodon.notification_requests()[0]",
"func_call_real": null,
"func_call_additional": null,
"func_alternate_acc": null,
"manual_update": true,
"masto_doc_link": "https://docs.joinmastodon.org/entities/NotificationRequest",
"description": "Represents a group of filtered notifications from a specific user.",
"fields": {
"id": {
"description": "The ID of the notification request in the database.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "str",
"field_subtype": null,
"field_structuretype": null,
"is_optional": false,
"is_nullable": false
},
"created_at": {
"description": "When the first filtered notification from that user was created.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "datetime",
"field_subtype": null,
"field_structuretype": null,
"is_optional": false,
"is_nullable": false
},
"updated_at": {
"description": "When the notification request was last updated.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "datetime",
"field_subtype": null,
"field_structuretype": null,
"is_optional": false,
"is_nullable": false
},
"account": {
"description": "The account that performed the action that generated the filtered notifications.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "Account",
"field_subtype": null,
"field_structuretype": null,
"is_optional": false,
"is_nullable": false
},
"notifications_count": {
"description": "How many of this accounts notifications were filtered.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "str",
"field_subtype": null,
"field_structuretype": null,
"is_optional": false,
"is_nullable": false
},
"last_status": {
"description": "Most recent status associated with a filtered notification from that account.",
"enum": null,
"version_history": [["4.3.0", "added"]],
"field_type": "Status",
"field_subtype": null,
"field_structuretype": null,
"is_optional": true,
"is_nullable": false
}
}
}
]

Wyświetl plik

@ -0,0 +1,187 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Authorization:
- Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2
Connection:
- keep-alive
User-Agent:
- tests/v311
method: GET
uri: http://localhost:3000/api/v2/notifications/policy
response:
body:
string: '{"for_not_following":"filter","for_not_followers":"accept","for_new_accounts":"accept","for_private_mentions":"filter","for_limited_accounts":"filter","summary":{"pending_requests_count":0,"pending_notifications_count":0}}'
headers:
Cache-Control:
- private, no-store
Content-Length:
- '222'
Content-Security-Policy:
- default-src 'none'; frame-ancestors 'none'; form-action 'none'
Content-Type:
- application/json; charset=utf-8
ETag:
- W/"017d15acc00d01e7395f157390a5994c"
Referrer-Policy:
- strict-origin-when-cross-origin
Server-Timing:
- cache_read.active_support;dur=0.03, sql.active_record;dur=15.61, cache_generate.active_support;dur=2.11,
cache_write.active_support;dur=0.21, instantiation.active_record;dur=23.59,
start_processing.action_controller;dur=0.01, render.active_model_serializers;dur=0.42,
process_action.action_controller;dur=70.04
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Permitted-Cross-Domain-Policies:
- none
X-RateLimit-Limit:
- '300'
X-RateLimit-Remaining:
- '299'
X-RateLimit-Reset:
- '2025-02-15T17:15:00.275621Z'
X-Request-Id:
- ca548381-9b3f-4fe5-b62a-ff679ec7960a
X-Runtime:
- '0.128868'
X-XSS-Protection:
- '0'
vary:
- Authorization, Origin
status:
code: 200
message: OK
- request:
body: for_not_following=filter&for_not_followers=accept
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Authorization:
- Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2
Connection:
- keep-alive
Content-Length:
- '49'
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- tests/v311
method: PATCH
uri: http://localhost:3000/api/v2/notifications/policy
response:
body:
string: '{"for_not_following":"filter","for_not_followers":"accept","for_new_accounts":"accept","for_private_mentions":"filter","for_limited_accounts":"filter","summary":{"pending_requests_count":0,"pending_notifications_count":0}}'
headers:
Cache-Control:
- private, no-store
Content-Length:
- '222'
Content-Security-Policy:
- default-src 'none'; frame-ancestors 'none'; form-action 'none'
Content-Type:
- application/json; charset=utf-8
ETag:
- W/"017d15acc00d01e7395f157390a5994c"
Referrer-Policy:
- strict-origin-when-cross-origin
Server-Timing:
- cache_read.active_support;dur=0.02, sql.active_record;dur=1.37, cache_generate.active_support;dur=1.27,
cache_write.active_support;dur=0.12, instantiation.active_record;dur=0.34,
start_processing.action_controller;dur=0.00, render.active_model_serializers;dur=0.18,
process_action.action_controller;dur=27.26
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Permitted-Cross-Domain-Policies:
- none
X-RateLimit-Limit:
- '300'
X-RateLimit-Remaining:
- '299'
X-RateLimit-Reset:
- '2025-02-15T17:15:00.373556Z'
X-Request-Id:
- f09de8b0-6938-48c6-b66d-d29eb0a777f5
X-Runtime:
- '0.051088'
X-XSS-Protection:
- '0'
vary:
- Authorization, Origin
status:
code: 200
message: OK
- request:
body: for_not_following=accept&for_not_followers=accept&for_new_accounts=accept&for_private_mentions=accept&for_limited_accounts=accept
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Authorization:
- Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2
Connection:
- keep-alive
Content-Length:
- '129'
Content-Type:
- application/x-www-form-urlencoded
User-Agent:
- tests/v311
method: PATCH
uri: http://localhost:3000/api/v2/notifications/policy
response:
body:
string: '{"for_not_following":"accept","for_not_followers":"accept","for_new_accounts":"accept","for_private_mentions":"accept","for_limited_accounts":"accept","summary":{"pending_requests_count":0,"pending_notifications_count":0}}'
headers:
Cache-Control:
- private, no-store
Content-Length:
- '222'
Content-Security-Policy:
- default-src 'none'; frame-ancestors 'none'; form-action 'none'
Content-Type:
- application/json; charset=utf-8
ETag:
- W/"5b34317397a0dd74b93479cc531b50b9"
Referrer-Policy:
- strict-origin-when-cross-origin
Server-Timing:
- cache_read.active_support;dur=0.02, sql.active_record;dur=5.47, cache_generate.active_support;dur=1.23,
cache_write.active_support;dur=0.13, instantiation.active_record;dur=0.52,
start_processing.action_controller;dur=0.00, transaction.active_record;dur=4.72,
render.active_model_serializers;dur=0.29, process_action.action_controller;dur=35.37
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Permitted-Cross-Domain-Policies:
- none
X-RateLimit-Limit:
- '300'
X-RateLimit-Remaining:
- '299'
X-RateLimit-Reset:
- '2025-02-15T17:15:00.497607Z'
X-Request-Id:
- a5ea3462-c2bc-481e-83eb-fd338c8638cf
X-Runtime:
- '0.124773'
X-XSS-Protection:
- '0'
vary:
- Authorization, Origin
status:
code: 200
message: OK
version: 1

Wyświetl plik

@ -80,3 +80,66 @@ def test_notifications_dismiss_pre_2_9_2(api, api2):
def test_notifications_clear(api):
api.notifications_clear()
@pytest.mark.vcr(match_on=['path'])
def test_notifications_policy(api2):
"""Test fetching and updating the notifications policy."""
# Fetch current policy
policy = api2.notifications_policy()
assert policy is not None
# Update policy
updated_policy = api2.update_notifications_policy(for_not_following="filter", for_not_followers="accept")
assert updated_policy.for_not_following == "filter"
assert updated_policy.for_not_followers == "accept"
# Finally, change it to everything being accepted
updated_policy = api2.update_notifications_policy(for_not_following="accept", for_not_followers="accept", for_new_accounts="accept", for_limited_accounts="accept", for_private_mentions="accept")
@pytest.mark.vcr()
def test_notification_requests_accept(api, api2):
"""Test fetching, accepting, and dismissing notification requests."""
temp = api
api = api2
api2 = temp
# Generate some request
posted = []
api2.update_notifications_policy(for_not_following="filter", for_not_followers="filter", for_new_accounts="filter", for_limited_accounts="filter", for_private_mentions="filter")
time.sleep(1)
while not api2.notifications_merged():
time.sleep(1)
print("Waiting for notifications to merge...")
time.sleep(1)
try:
reply_name = api2.account_verify_credentials().username
for i in range(5):
posted.append(api.status_post(f"@{reply_name} please follow me - {i+200}!", visibility="public"))
time.sleep(3)
# Fetch notification requests
requests = api2.notification_requests()
assert requests is not None
assert len(requests) > 0
request_id = requests[0].id
# Fetch a single request
single_request = api2.notification_request(request_id)
assert single_request.id == request_id
# Accept the request
api2.accept_notification_request(request_id)
time.sleep(5)
# Check if notifications have been merged
merged_status = api2.notifications_merged()
assert isinstance(merged_status, bool)
assert merged_status == True
finally:
for status in posted:
api.status_delete(status)
api2.update_notifications_policy(for_not_following="accept", for_not_followers="accept", for_new_accounts="accept", for_limited_accounts="accept", for_private_mentions="accept")