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