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