]> git.mxchange.org Git - fba.git/blob - fetch_blocks.py
Continued:
[fba.git] / fetch_blocks.py
1 import reqto
2 import time
3 import bs4
4 import itertools
5 import re
6 import fba
7
8 fba.cursor.execute(
9     "SELECT domain, software FROM instances WHERE software IN ('pleroma', 'mastodon', 'friendica', 'misskey', 'gotosocial', 'bookwyrm', 'takahe') AND (last_blocked IS NULL OR last_blocked < ?) ORDER BY rowid DESC", [time.time() - fba.config["recheck_block"]]
10 )
11
12 for blocker, software in fba.cursor.fetchall():
13     # NOISY-DEBUG: print("DEBUG: BEFORE blocker,software:", blocker, software)
14     blockdict = []
15     blocker = fba.tidyup(blocker)
16     # NOISY-DEBUG: print("DEBUG: AFTER blocker,software:", blocker, software)
17
18     if blocker == "":
19         print("WARNING: blocker is now empty!")
20         continue
21     elif fba.is_blacklisted(blocker):
22         print(f"WARNING: blocker='{blocker}' is blacklisted now!")
23         continue
24
25     # NOISY-DEBUG: print(f"DEBUG: blocker='{blocker}'")
26     fba.update_last_blocked(blocker)
27
28     if software == "pleroma":
29         print("INFO: blocker:", blocker)
30         try:
31             # Blocks
32             json = fba.fetch_nodeinfo(blocker)
33             if json is None:
34                 print("WARNING: Could not fetch nodeinfo from blocker:", blocker)
35                 continue
36
37             print("DEBUG: Updating nodeinfo:", blocker)
38             fba.update_last_nodeinfo(blocker)
39
40             federation = json["metadata"]["federation"]
41
42             if "enabled" in federation:
43                 # NOISY-DEBUG: print("DEBUG: Instance has no block list to analyze:", blocker)
44                 continue
45
46             if "mrf_simple" in federation:
47                 for block_level, blocks in (
48                     {**federation["mrf_simple"],
49                     **{"quarantined_instances": federation["quarantined_instances"]}}
50                 ).items():
51                     # NOISY-DEBUG: print("DEBUG: block_level, blocks():", block_level, len(blocks))
52                     block_level = fba.tidyup(block_level)
53                     # NOISY-DEBUG: print("DEBUG: BEFORE block_level:", block_level)
54
55                     if block_level == "":
56                         print("WARNING: block_level is now empty!")
57                         continue
58
59                     for blocked in blocks:
60                         # NOISY-DEBUG: print("DEBUG: BEFORE blocked:", blocked)
61                         blocked = fba.tidyup(blocked)
62                         # NOISY-DEBUG: print("DEBUG: AFTER blocked:", blocked)
63
64                         if blocked == "":
65                             print("WARNING: blocked is empty after fba.tidyup():", blocker, block_level)
66                             continue
67
68                         if blocked.count("*") > 1:
69                             # -ACK!-oma also started obscuring domains without hash
70                             fba.cursor.execute(
71                                 "SELECT domain FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
72                             )
73                             searchres = fba.cursor.fetchone()
74                             # NOISY-DEBUG: print("DEBUG: searchres[]:", type(searchres))
75                             if searchres != None:
76                                 blocked = searchres[0]
77                                 # NOISY-DEBUG: print("DEBUG: Looked up domain:", blocked)
78
79                         # NOISY-DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
80                         fba.cursor.execute(
81                             "SELECT domain FROM instances WHERE domain = ? LIMIT 1", [blocked]
82                         )
83
84                         if fba.cursor.fetchone() == None:
85                             # NOISY-DEBUG: print("DEBUG: Domain wasn't found, adding:", blocked, blocker)
86                             fba.add_instance(blocked, blocker, argv[0])
87
88                         fba.cursor.execute(
89                             "SELECT * FROM blocks WHERE blocker = ? AND blocked = ? AND block_level = ?",
90                             (
91                                blocker,
92                                blocked,
93                                block_level
94                            ),
95                         )
96
97                         if fba.cursor.fetchone() == None:
98                             # NOISY-DEBUG: print("DEBUG: Blocking:", blocker, blocked, block_level)
99                             fba.block_instance(blocker, blocked, "unknown", block_level)
100
101                             if block_level == "reject":
102                                 # NOISY-DEBUG: print("DEBUG: Adding to blockdict:", blocked)
103                                 blockdict.append(
104                                     {
105                                         "blocked": blocked,
106                                         "reason": None
107                                     })
108                         else:
109                             # NOISY-DEBUG: print("DEBUG: Updating last_seen:", blocker, blocked, block_level)
110                             fba.update_last_seen(blocker, blocked, block_level)
111
112             fba.connection.commit()
113
114             # Reasons
115             if "mrf_simple_info" in federation:
116                 # NOISY-DEBUG: print("DEBUG: Found mrf_simple_info:", blocker)
117                 for block_level, info in (
118                     {**federation["mrf_simple_info"],
119                     **(federation["quarantined_instances_info"]
120                     if "quarantined_instances_info" in federation
121                     else {})}
122                 ).items():
123                     # NOISY-DEBUG: print("DEBUG: block_level, info.items():", block_level, len(info.items()))
124                     block_level = fba.tidyup(block_level)
125                     # NOISY-DEBUG: print("DEBUG: BEFORE block_level:", block_level)
126
127                     if block_level == "":
128                         print("WARNING: block_level is now empty!")
129                         continue
130
131                     for blocked, reason in info.items():
132                         # NOISY-DEBUG: print("DEBUG: BEFORE blocked:", blocked)
133                         blocked = fba.tidyup(blocked)
134                         # NOISY-DEBUG: print("DEBUG: AFTER blocked:", blocked)
135
136                         if blocked == "":
137                             print("WARNING: blocked is empty after fba.tidyup():", blocker, block_level)
138                             continue
139                         elif blocked.count("*") > 1:
140                             # same domain guess as above, but for reasons field
141                             fba.cursor.execute(
142                                 "SELECT domain FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
143                             )
144                             searchres = fba.cursor.fetchone()
145
146                             if searchres != None:
147                                 blocked = searchres[0]
148
149                         # NOISY-DEBUG: print("DEBUG: Updating block reason:", blocker, blocked, reason["reason"])
150                         fba.update_block_reason(reason["reason"], blocker, blocked, block_level)
151
152                         for entry in blockdict:
153                             if entry["blocked"] == blocked:
154                                 # NOISY-DEBUG: print("DEBUG: Updating entry reason:", blocked)
155                                 entry["reason"] = reason["reason"]
156
157             fba.connection.commit()
158         except Exception as e:
159             print("error:", e, blocker, software)
160     elif software == "mastodon":
161         print("INFO: blocker:", blocker)
162         try:
163             # json endpoint for newer mastodongs
164             try:
165                 json = {
166                     "reject": [],
167                     "media_removal": [],
168                     "followers_only": [],
169                     "report_removal": []
170                 }
171
172                 # handling CSRF, I've saw at least one server requiring it to access the endpoint
173                 # NOISY-DEBUG: print("DEBUG: Fetching meta:", blocker)
174                 meta = bs4.BeautifulSoup(
175                     reqto.get(f"https://{blocker}/about", headers=fba.headers, timeout=(fba.config["connection_timeout"], config["read_timeout"])).text,
176                     "html.parser",
177                 )
178                 try:
179                     csrf = meta.find("meta", attrs={"name": "csrf-token"})["content"]
180                     # NOISY-DEBUG: print("DEBUG: Adding CSRF token:", blocker, csrf)
181                     reqheaders = {**fba.headers, **{"x-csrf-token": csrf}}
182                 except:
183                     # NOISY-DEBUG: print("DEBUG: No CSRF token found, using normal headers:", blocker)
184                     reqheaders = fba.headers
185
186                 # NOISY-DEBUG: print("DEBUG: Quering API domain_blocks:", blocker)
187                 blocks = reqto.get(f"https://{blocker}/api/v1/instance/domain_blocks", headers=reqheaders, timeout=(fba.config["connection_timeout"], config["read_timeout"])).json()
188
189                 # NOISY-DEBUG: print("DEBUG: blocks():", len(blocks))
190                 for block in blocks:
191                     entry = {
192                         'domain': block['domain'],
193                         'hash': block['digest'],
194                         'reason': block['comment']
195                     }
196
197                     # NOISY-DEBUG: print("DEBUG: severity,domain,hash,comment:", block['severity'], block['domain'], block['digest'], block['comment'])
198                     if block['severity'] == 'suspend':
199                         json['reject'].append(entry)
200                     elif block['severity'] == 'silence':
201                         json['followers_only'].append(entry)
202                     elif block['severity'] == 'reject_media':
203                         json['media_removal'].append(entry)
204                     elif block['severity'] == 'reject_reports':
205                         json['report_removal'].append(entry)
206                     else:
207                         print("WARNING: Unknown severity:", block['severity'], block['domain'])
208             except:
209                 # NOISY-DEBUG: print("DEBUG: Failed, Trying mastodon-specific fetches:", blocker)
210                 json = fba.get_mastodon_blocks(blocker)
211
212             # NOISY-DEBUG: print("DEBUG: json.items():", blocker, len(json.items()))
213             for block_level, blocks in json.items():
214                 # NOISY-DEBUG: print("DEBUG: blocker,block_level,blocks():", blocker, block_level, len(blocks))
215                 block_level = fba.tidyup(block_level)
216                 # NOISY-DEBUG: print("DEBUG: AFTER-block_level:", block_level)
217                 if block_level == "":
218                     print("WARNING: block_level is empty, blocker:", blocker)
219                     continue
220
221                 for instance in blocks:
222                     blocked, blocked_hash, reason = instance.values()
223                     # NOISY-DEBUG: print("DEBUG: blocked,hash,reason:", blocked, blocked_hash, reason)
224                     blocked = fba.tidyup(blocked)
225                     # NOISY-DEBUG: print("DEBUG: AFTER-blocked:", blocked)
226
227                     if blocked == "":
228                         print("WARNING: blocked is empty:", blocker)
229                         continue
230                     elif blocked.count("*") < 1:
231                         # No obsfucation for this instance
232                         fba.cursor.execute(
233                             "SELECT hash FROM instances WHERE domain = ? LIMIT 1", [blocked]
234                         )
235
236                         if fba.cursor.fetchone() == None:
237                             # NOISY-DEBUG: print("DEBUG: Hash wasn't found, adding:", blocked, blocker)
238                             fba.add_instance(blocked, blocker, argv[0])
239                     else:
240                         # Doing the hash search for instance names as well to tidy up DB
241                         fba.cursor.execute(
242                             "SELECT domain FROM instances WHERE hash = ? LIMIT 1", [blocked_hash]
243                         )
244                         searchres = fba.cursor.fetchone()
245
246                         if searchres != None:
247                             # NOISY-DEBUG: print("DEBUG: Updating domain: ", searchres[0])
248                             blocked = searchres[0]
249
250                     fba.cursor.execute(
251                         "SELECT * FROM blocks WHERE blocker = ? AND blocked = ? AND block_level = ?",
252                         (
253                             blocker,
254                             blocked if blocked.count("*") <= 1 else blocked_hash,
255                             block_level
256                         ),
257                     )
258
259                     if fba.cursor.fetchone() == None:
260                         fba.block_instance(blocker, blocked if blocked.count("*") <= 1 else blocked_hash, reason, block_level)
261
262                         if block_level == "reject":
263                             blockdict.append(
264                                 {
265                                     "blocked": blocked,
266                                     "reason": reason
267                                 })
268                     else:
269                         fba.update_last_seen(blocker, blocked if blocked.count("*") <= 1 else blocked_hash, block_level)
270
271                     if reason != '':
272                         # NOISY-DEBUG: print("DEBUG: Updating block reason:", blocker, blocked, reason)
273                         fba.update_block_reason(reason, blocker, blocked if blocked.count("*") <= 1 else blocked_hash, block_level)
274
275             fba.connection.commit()
276         except Exception as e:
277             print("error:", e, blocker, software)
278     elif software == "friendica" or software == "misskey" or software == "bookwyrm" or software == "takahe":
279         print("INFO: blocker:", blocker)
280         try:
281             if software == "friendica":
282                 json = fba.get_friendica_blocks(blocker)
283             elif software == "misskey":
284                 json = fba.get_misskey_blocks(blocker)
285             elif software == "bookwyrm":
286                 print("WARNING: bookwyrm is not fully supported for fetching blacklist!", blocker)
287                 #json = fba.get_bookwyrm_blocks(blocker)
288             elif software == "takahe":
289                 print("WARNING: takahe is not fully supported for fetching blacklist!", blocker)
290                 #json = fba.get_takahe_blocks(blocker)
291
292             for block_level, blocks in json.items():
293                 # NOISY-DEBUG: print("DEBUG: blocker,block_level,blocks():", blocker, block_level, len(blocks))
294                 block_level = fba.tidyup(block_level)
295                 # NOISY-DEBUG: print("DEBUG: AFTER-block_level:", block_level)
296                 if block_level == "":
297                     print("WARNING: block_level is empty, blocker:", blocker)
298                     continue
299
300                 for instance in blocks:
301                     blocked, reason = instance.values()
302                     # NOISY-DEBUG: print("DEBUG: BEFORE blocked:", blocked)
303                     blocked = fba.tidyup(blocked)
304                     # NOISY-DEBUG: print("DEBUG: AFTER blocked:", blocked)
305
306                     if blocked == "":
307                         print("WARNING: blocked is empty:", blocker)
308                         continue
309                     if blocked.count("*") > 0:
310                         # Some friendica servers also obscure domains without hash
311                         fba.cursor.execute(
312                             "SELECT domain FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
313                         )
314                         searchres = fba.cursor.fetchone()
315                         if searchres != None:
316                             blocked = searchres[0]
317
318                     if blocked.count("?") > 0:
319                         # Some obscure them with question marks, not sure if that's dependent on version or not
320                         fba.cursor.execute(
321                             "SELECT domain FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("?", "_")]
322                         )
323                         searchres = fba.cursor.fetchone()
324                         if searchres != None:
325                             blocked = searchres[0]
326
327                     # NOISY-DEBUG: print("DEBUG: AFTER-blocked:", blocked)
328                     fba.cursor.execute(
329                         "SELECT domain FROM instances WHERE domain = ? LIMIT 1", [blocked]
330                     )
331
332                     if fba.cursor.fetchone() == None:
333                         # NOISY-DEBUG: print("DEBUG: Hash wasn't found, adding:", blocked, blocker)
334                         fba.add_instance(blocked, blocker)
335
336                     fba.cursor.execute(
337                         "SELECT * FROM blocks WHERE blocker = ? AND blocked = ?",
338                         (blocker, blocked),
339                     )
340                     if fba.cursor.fetchone() == None:
341                         fba.block_instance(blocker, blocked, reason, block_level)
342
343                         if block_level == "reject":
344                             blockdict.append(
345                                 {
346                                     "blocked": blocked,
347                                     "reason": reason
348                                 })
349                     else:
350                         fba.update_last_seen(blocker, blocked, block_level)
351
352                     if reason != '':
353                         # NOISY-DEBUG: print("DEBUG: Updating block reason:", blocker, blocked, reason)
354                         fba.update_block_reason(reason, blocker, blocked, block_level)
355
356             fba.connection.commit()
357         except Exception as e:
358             print("error:", e, blocker, software)
359     elif software == "gotosocial":
360         print("INFO: blocker:", blocker)
361         try:
362             # Blocks
363             federation = reqto.get(f"https://{blocker}{get_peers_url}?filter=suspended", headers=fba.headers, timeout=(fba.config["connection_timeout"], config["read_timeout"])).json()
364
365             if (federation == None):
366                 print("WARNING: No valid response:", blocker);
367             elif "error" in federation:
368                 print("WARNING: API returned error:", federation["error"])
369             else:
370                 # NOISY-DEBUG: print("DEBUG: Checking fenderation():", len(federation))
371                 for peer in federation:
372                     blocked = peer["domain"].lower()
373                     # NOISY-DEBUG: print("DEBUG: BEFORE blocked:", blocked)
374                     blocked = fba.tidyup(blocked)
375                     # NOISY-DEBUG: print("DEBUG: AFTER blocked:", blocked)
376
377                     if blocked == "":
378                         print("WARNING: blocked is empty:", blocker)
379                         continue
380                     elif blocked.count("*") > 0:
381                         # GTS does not have hashes for obscured domains, so we have to guess it
382                         fba.cursor.execute(
383                             "SELECT domain FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
384                         )
385                         searchres = fba.cursor.fetchone()
386
387                         if searchres != None:
388                             blocked = searchres[0]
389
390                     fba.cursor.execute(
391                         "SELECT domain FROM instances WHERE domain = ? LIMIT 1", [blocked]
392                     )
393
394                     if fba.cursor.fetchone() == None:
395                         # NOISY-DEBUG: print("DEBUG: Updating block reason:", blocker, blocked, reason)
396                         fba.update_block_reason(reason, blocker, blocked, block_level)
397
398                     fba.cursor.execute(
399                         "SELECT * FROM blocks WHERE blocker = ? AND blocked = ? AND block_level = ?",
400                         (
401                             blocker,
402                             blocked,
403                             "reject"
404                         ),
405                     )
406
407                     if fba.cursor.fetchone() == None:
408                         fba.block_instance(blocker, blocked, "unknown", "reject")
409
410                         blockdict.append(
411                             {
412                                 "blocked": blocked,
413                                 "reason": None
414                             })
415                     else:
416                         fba.update_last_seen(blocker, blocked, "reject")
417
418                     if "public_comment" in peer:
419                         # NOISY-DEBUG: print("DEBUG: Updating block reason:", blocker, blocked, peer["public_comment"])
420                         fba.update_block_reason(peer["public_comment"], blocker, blocked, "reject")
421
422                         for entry in blockdict:
423                             if entry["blocked"] == blocked:
424                                 entry["reason"] = peer["public_comment"]
425
426                 fba.connection.commit()
427         except Exception as e:
428             print("error:", e, blocker, software)
429     else:
430         print("WARNING: Unknown software:", blocker, software)
431
432     if fba.config["bot_enabled"] and len(blockdict) > 0:
433         send_bot_post(blocker, blockdict)
434
435     blockdict = []
436
437 fba.connection.close()