File: filters.py

package info (click to toggle)
python-mastodon 2.1.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 22,836 kB
  • sloc: python: 9,438; makefile: 206; sql: 98; sh: 27
file content (267 lines) | stat: -rw-r--r-- 11,612 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# filters.py - Filter-related endpoints

import re

from mastodon.errors import MastodonIllegalArgumentError
from mastodon.utility import api_version

from mastodon.internals import Mastodon as Internals
from mastodon.return_types import Filter, FilterV2, Status, Notification, FilterKeyword, FilterStatus
from mastodon.types_base import PaginatableList, NonPaginatableList, IdType

from typing import Union, Optional, List, Dict


class Mastodon(Internals):
    ###
    # Reading data: Keyword filters
    ###
    @api_version("2.4.3", "2.4.3")
    def filters(self) -> NonPaginatableList[Filter]:
        """
        Fetch all of the logged-in user's filters.
        """
        return self.__api_request('GET', '/api/v1/filters')

    @api_version("2.4.3", "2.4.3")
    def filter(self, id: Union[Filter, IdType]) -> Filter:
        """
        Fetches information about the filter with the specified `id`.
        """
        id = self.__unpack_id(id)
        return self.__api_request('GET', f'/api/v1/filters/{id}')

    @api_version("2.4.3", "2.4.3")
    def filters_apply(self, objects: Union[PaginatableList[Status], PaginatableList[Notification]], filters: Union[NonPaginatableList[Filter], NonPaginatableList[FilterV2]], context: str) -> Union[PaginatableList[Status], PaginatableList[Notification]]:
        """
        Helper function: Applies a list of filters to a list of either statuses
        or notifications and returns only those matched by none. This function will
        apply all filters that match the context provided in `context`, i.e.
        if you want to apply only notification-relevant filters, specify
        'notifications'. Valid contexts are 'home', 'notifications', 'public' and 'thread'.

        NB: This is for v1 filters. v2 filters are applied by the server, which adds the "filtered"
        attribute to filtered statuses.
        """
        # Build filter regex
        filter_strings = []
        for keyword_filter in filters:
            if not context in keyword_filter["context"]:
                continue

            filter_string = re.escape(keyword_filter["phrase"])
            if keyword_filter["whole_word"]:
                filter_string = "\\b" + filter_string + "\\b"
            filter_strings.append(filter_string)
        filter_re = re.compile("|".join(filter_strings), flags=re.IGNORECASE)

        # Apply
        filter_results = []
        for filter_object in objects:
            filter_status = filter_object
            if "status" in filter_object:
                filter_status = filter_object["status"]
            filter_text = filter_status["content"]
            filter_text = re.sub(r"<.*?>", " ", filter_text)
            filter_text = re.sub(r"\s+", " ", filter_text).strip()
            if not filter_re.search(filter_text):
                filter_results.append(filter_object)
        return filter_results

    ###
    # Writing data: Keyword filters
    ###
    @api_version("2.4.3", "2.4.3")
    def filter_create(self, phrase: str, context: str, irreversible: bool = False, whole_word: bool = True, expires_in: Optional[int] = None) -> Filter:
        """
        Creates a new keyword filter. `phrase` is the phrase that should be
        filtered out, `context` specifies from where to filter the keywords.
        Valid contexts are 'home', 'notifications', 'public' and 'thread'.

        Set `irreversible` to True if you want the filter to just delete statuses
        server side. This works only for the 'home' and 'notifications' contexts.

        Set `whole_word` to False if you want to allow filter matches to
        start or end within a word, not only at word boundaries.

        Set `expires_in` to specify for how many seconds the filter should be
        kept around.

        Returns the newly created filter.
        """
        params = self.__generate_params(locals())

        for context_val in context:
            if not context_val in ['home', 'notifications', 'public', 'thread']:
                raise MastodonIllegalArgumentError('Invalid filter context.')

        return self.__api_request('POST', '/api/v1/filters', params)

    @api_version("2.4.3", "2.4.3")
    def filter_update(self, id: Union[Filter, IdType], phrase: Optional[str] = None, context: Optional[str] = None, irreversible: Optional[bool] = None, whole_word: Optional[bool] = None, expires_in: Optional[int] = None) -> Filter:
        """
        Updates the filter with the given `id`. Parameters are the same
        as in `filter_create()`.

        Returns the updated filter.
        """
        id = self.__unpack_id(id)
        params = self.__generate_params(locals(), ['id'])
        return self.__api_request('PUT', f'/api/v1/filters/{id}', params)

    @api_version("2.4.3", "2.4.3")
    def filter_delete(self, id: Union[Filter, FilterV2, IdType]):
        """
        Deletes the filter with the given `id`.
        """
        id = self.__unpack_id(id)
        self.__api_request('DELETE', f'/api/v1/filters/{id}')

    ###
    # Filters v2 api
    ###
    @api_version("4.0.0", "4.0.0")
    def filters_v2(self) -> NonPaginatableList[FilterV2]:
        """
        Fetch all filters for the authenticated user.
        """
        return self.__api_request('GET', '/api/v2/filters')

    @api_version("4.0.0", "4.0.0")
    def filter_v2(self, filter_id: Union[Filter, IdType]) -> Filter:
        """
        Fetch a specific filter by its ID.
        """
        filter_id = self.__unpack_id(filter_id)
        return self.__api_request('GET', f'/api/v2/filters/{filter_id}')

    @api_version("4.0.0", "4.0.0")
    def create_filter_v2(
        self,
        title: str,
        context: List[str],
        filter_action: str,
        expires_in: Optional[int] = None,
        keywords_attributes: Optional[List[Dict[str, Union[str, bool]]]] = None
    ) -> FilterV2:
        """
        Create a new filter with the given parameters.

        `title` is a human readable name for the filter. 
        
        `context` is list of contexts where the filter should apply. Valid values are:
            - "home": Filter applies to the home timeline.
            - "notifications": Filter applies to notifications. Filtered notifications land in notification requests.
            - "public": Filter applies to the public timelines.
            - "thread": Filter applies to conversations.
            - "account": Filter applies to account timelines.

        `filter_action` gives the policy to be applied when the filter is matched. Valid values are:
            - "warn": The user is warned if the content matches the filter.
            - "hide": The content is completely hidden if it matches the filter.

        NB: Even if you specify "hide", the status will still be returned - it will just have the "filtered" attribute set.
        
        pass a number of seconds as `expires_in` to make the filter expire in that many seconds. Use None for no expiration.
            
        pass a list of keyword dicts to initially as `keywords_attributes`, each with the following values:
            - "keyword": The term to filter on.
            - "whole_word": Whether word boundaries should be considered.
            
        """
        params = self.__generate_params(locals(), for_json=True)
        return self.__api_request('POST', '/api/v2/filters', params, use_json=True)

    @api_version("4.0.0", "4.0.0")
    def update_filter_v2(
        self,
        filter_id: Union[FilterV2, IdType],
        title: Optional[str] = None,
        context: Optional[List[str]] = None,
        filter_action: Optional[str] = None,
        expires_in: Optional[int] = None,
        keywords_attributes: Optional[List[Dict[str, Union[str, bool, int]]]] = None
    ) -> FilterV2:
        """
        Update an existing filter with the given parameters.

        Parameters are as in `create_filter_v2()`. Only the parameters you want to update need to be provided.
        """
        filter_id = self.__unpack_id(filter_id)
        params = self.__generate_params(locals(), for_json=True)
        return self.__api_request('PUT', f'/api/v2/filters/{filter_id}', params, use_json=True)

    @api_version("4.0.0", "4.0.0")
    def delete_filter_v2(self, filter_id: Union[FilterV2, IdType]) -> None:
        """
        Delete an existing filter.
        """
        filter_id = self.__unpack_id(filter_id)
        self.__api_request('DELETE', f'/api/v2/filters/{filter_id}')

    @api_version("4.0.0", "4.0.0")
    def filter_keywords_v2(self, filter_id: Union[FilterV2, IdType]) -> NonPaginatableList[FilterKeyword]:
        """
        Fetch all keywords associated with a given filter.
        """
        filter_id = self.__unpack_id(filter_id)
        return self.__api_request('GET', f'/api/v2/filters/{filter_id}/keywords')

    @api_version("4.0.0", "4.0.0")
    def add_filter_keyword_v2(
        self,
        filter_id: Union[FilterV2, IdType],
        keyword: str,
        whole_word: bool = False
    ) -> FilterKeyword:
        """
        Add a single keyword to an existing filter.

        Parameters are as in `create_filter_v2()` `keywords_attributes`.
        """
        filter_id = self.__unpack_id(filter_id)
        params = self.__generate_params(locals())
        return self.__api_request('POST', f'/api/v2/filters/{filter_id}/keywords', params)

    @api_version("4.0.0", "4.0.0")
    def delete_filter_keyword_v2(self, keyword_id: Union[FilterKeyword, IdType]) -> None:
        """
        Delete a single keyword from any filter.
        """
        keyword_id = self.__unpack_id(keyword_id)
        self.__api_request('DELETE', f'/api/v2/filters/keywords/{keyword_id}')

    @api_version("4.0.0", "4.0.0")
    def filter_statuses_v2(self, filter_id: Union[FilterV2, IdType]) -> List[FilterStatus]:
        """
        Retrieve all status-based filters for a FilterV2.
        """
        filter_id = self.__unpack_id(filter_id)
        return self.__api_request('GET', f'/api/v2/filters/{filter_id}/statuses')

    @api_version("4.0.0", "4.0.0")
    def add_filter_status_v2(self, filter_id: Union[FilterV2, IdType], status_id: Union[Status, IdType]) -> FilterStatus:
        """
        Add a status to a filter, which will then match on that status in addition to any keywords.
        Includes reblogs, does not include replies.
        """
        filter_id = self.__unpack_id(filter_id)
        status_id = self.__unpack_id(status_id)
        params = self.__generate_params({"status_id": status_id})
        return self.__api_request('POST', f'/api/v2/filters/{filter_id}/statuses', params)

    @api_version("4.0.0", "4.0.0")
    def filter_status_v2(self, filter_status_id: Union[FilterStatus, IdType]) -> FilterStatus:
        """
        Fetch a single status-based filter by its ID.
        """
        filter_status_id = self.__unpack_id(filter_status_id)
        return self.__api_request('GET', f'/api/v2/filters/statuses/{filter_status_id}')

    @api_version("4.0.0", "4.0.0")
    def delete_filter_status_v2(self, filter_status_id: Union[FilterStatus, IdType]) -> None:
        """
        Remove a status filter from a FilterV2.
        """
        filter_status_id = self.__unpack_id(filter_status_id)
        self.__api_request('DELETE', f'/api/v2/filters/statuses/{filter_status_id}')