]> git.mxchange.org Git - fba.git/blob - fetch_blocks.py
Continued:
[fba.git] / fetch_blocks.py
1 #!/usr/bin/python3
2 # -*- coding: utf-8 -*-
3
4 # Fedi API Block - An aggregator for fetching blocking data from fediverse nodes
5 # Copyright (C) 2023 Free Software Foundation
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published
9 # by the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16 #
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
20 import bs4
21 import itertools
22 import re
23 import reqto
24 import sys
25 import time
26 import validators
27 from fba import *
28
29 boot.acquire_lock()
30
31 fba.cursor.execute(
32     "SELECT domain, software, origin, nodeinfo_url 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() - config.get("recheck_block")]
33 )
34
35 rows = fba.cursor.fetchall()
36 print(f"INFO: Checking {len(rows)} entries ...")
37 for blocker, software, origin, nodeinfo_url in rows:
38     # DEBUG: print("DEBUG: BEFORE blocker,software,origin,nodeinfo_url:", blocker, software, origin, nodeinfo_url)
39     blockdict = []
40     blocker = fba.tidyup_domain(blocker)
41     # DEBUG: print("DEBUG: AFTER blocker,software:", blocker, software)
42
43     if blocker == "":
44         print("WARNING: blocker is now empty!")
45         continue
46     elif fba.is_blacklisted(blocker):
47         print(f"WARNING: blocker='{blocker}' is blacklisted now!")
48         continue
49
50     # DEBUG: print(f"DEBUG: blocker='{blocker}'")
51     fba.update_last_blocked(blocker)
52
53     if software == "pleroma":
54         print("INFO: blocker:", blocker)
55         try:
56             # Blocks
57             json = fba.fetch_nodeinfo(blocker, nodeinfo_url)
58             if json is None:
59                 print("WARNING: Could not fetch nodeinfo from blocker:", blocker)
60                 continue
61             elif not "metadata" in json:
62                 print(f"WARNING: json()={len(json)} does not have key 'metadata', blocker='{blocker}'")
63                 continue
64             elif not "federation" in json["metadata"]:
65                 print(f"WARNING: json()={len(json['metadata'])} does not have key 'federation', blocker='{blocker}'")
66                 continue
67
68             # DEBUG: print("DEBUG: Updating nodeinfo:", blocker)
69             fba.update_last_nodeinfo(blocker)
70
71             federation = json["metadata"]["federation"]
72
73             if "enabled" in federation:
74                 # DEBUG: print("DEBUG: Instance has no block list to analyze:", blocker)
75                 continue
76
77             if "mrf_simple" in federation:
78                 for block_level, blocks in (
79                     {**federation["mrf_simple"],
80                     **{"quarantined_instances": federation["quarantined_instances"]}}
81                 ).items():
82                     # DEBUG: print("DEBUG: block_level, blocks():", block_level, len(blocks))
83                     block_level = fba.tidyup_domain(block_level)
84                     # DEBUG: print("DEBUG: BEFORE block_level:", block_level)
85
86                     if block_level == "":
87                         print("WARNING: block_level is now empty!")
88                         continue
89
90                     # DEBUG: print(f"DEBUG: Checking {len(blocks)} entries from blocker='{blocker}',software='{software}',block_level='{block_level}' ...")
91                     for blocked in blocks:
92                         # DEBUG: print("DEBUG: BEFORE blocked:", blocked)
93                         blocked = fba.tidyup_domain(blocked)
94                         # DEBUG: print("DEBUG: AFTER blocked:", blocked)
95
96                         if blocked == "":
97                             print("WARNING: blocked is empty after fba.tidyup_domain():", blocker, block_level)
98                             continue
99                         elif fba.is_blacklisted(blocked):
100                             # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
101                             continue
102                         elif blocked.count("*") > 1:
103                             # -ACK!-oma also started obscuring domains without hash
104                             fba.cursor.execute(
105                                 "SELECT domain, nodeinfo_url FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
106                             )
107                             searchres = fba.cursor.fetchone()
108                             # DEBUG: print("DEBUG: searchres[]:", type(searchres))
109
110                             if searchres == None:
111                                 print(f"WARNING: Cannot deobsfucate blocked='{blocked}' - SKIPPED!")
112                                 continue
113
114                             blocked = searchres[0]
115                             nodeinfo_url = searchres[1]
116                             # DEBUG: print("DEBUG: Looked up domain:", blocked)
117                         elif not validators.domain(blocked):
118                             print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domai name - skipped!")
119                             continue
120
121                         # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
122                         if not validators.domain(blocked):
123                             print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domain name - skipped!")
124                             continue
125                         elif not fba.is_instance_registered(blocked):
126                             # DEBUG: print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., blocker='{blocker}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
127                             fba.add_instance(blocked, blocker, sys.argv[0], nodeinfo_url)
128
129                         if not fba.is_instance_blocked(blocker, blocked, block_level):
130                             # DEBUG: print("DEBUG: Blocking:", blocker, blocked, block_level)
131                             fba.block_instance(blocker, blocked, "unknown", block_level)
132
133                             if block_level == "reject":
134                                 # DEBUG: print("DEBUG: Adding to blockdict:", blocked)
135                                 blockdict.append(
136                                     {
137                                         "blocked": blocked,
138                                         "reason" : None
139                                     })
140                         else:
141                             print(f"DEBUG: Updating block last seen for blocker='{blocker}',blocked='{blocked}' ...")
142                             fba.update_last_seen(blocker, blocked, block_level)
143
144             # DEBUG: print("DEBUG: Committing changes ...")
145             fba.connection.commit()
146
147             # Reasons
148             if "mrf_simple_info" in federation:
149                 # DEBUG: print("DEBUG: Found mrf_simple_info:", blocker)
150                 for block_level, info in (
151                     {**federation["mrf_simple_info"],
152                     **(federation["quarantined_instances_info"]
153                     if "quarantined_instances_info" in federation
154                     else {})}
155                 ).items():
156                     # DEBUG: print("DEBUG: block_level, info.items():", block_level, len(info.items()))
157                     block_level = fba.tidyup_domain(block_level)
158                     # DEBUG: print("DEBUG: BEFORE block_level:", block_level)
159
160                     if block_level == "":
161                         print("WARNING: block_level is now empty!")
162                         continue
163
164                     # DEBUG: print(f"DEBUG: Checking {len(info.items())} entries from blocker='{blocker}',software='{software}',block_level='{block_level}' ...")
165                     for blocked, reason in info.items():
166                         # DEBUG: print("DEBUG: BEFORE blocked:", blocked)
167                         blocked = fba.tidyup_domain(blocked)
168                         # DEBUG: print("DEBUG: AFTER blocked:", blocked)
169
170                         if blocked == "":
171                             print("WARNING: blocked is empty after fba.tidyup_domain():", blocker, block_level)
172                             continue
173                         elif fba.is_blacklisted(blocked):
174                             # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
175                             continue
176                         elif blocked.count("*") > 1:
177                             # same domain guess as above, but for reasons field
178                             fba.cursor.execute(
179                                 "SELECT domain, origin, nodeinfo_url FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
180                             )
181                             searchres = fba.cursor.fetchone()
182
183                             if searchres == None:
184                                 print(f"WARNING: Cannot deobsfucate blocked='{blocked}' - SKIPPED!")
185                                 continue
186
187                             blocked = searchres[0]
188                             origin = searchres[1]
189                             nodeinfo_url = searchres[2]
190                         elif not validators.domain(blocked):
191                             print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domai name - skipped!")
192                             continue
193
194                         # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
195                         if not validators.domain(blocked):
196                             print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domain name - skipped!")
197                             continue
198                         elif not fba.is_instance_registered(blocked):
199                             # DEBUG: print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., blocker='{blocker}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
200                             fba.add_instance(blocked, blocker, sys.argv[0], nodeinfo_url)
201
202                         # DEBUG: print("DEBUG: Updating block reason:", blocker, blocked, reason["reason"])
203                         fba.update_block_reason(reason["reason"], blocker, blocked, block_level)
204
205                         for entry in blockdict:
206                             if entry["blocked"] == blocked:
207                                 # DEBUG: print("DEBUG: Updating entry reason:", blocked)
208                                 entry["reason"] = reason["reason"]
209
210             fba.connection.commit()
211         except Exception as e:
212             print(f"ERROR: blocker='{blocker}',software='{software}',exception[{type(e)}]:'{str(e)}'")
213     elif software == "mastodon":
214         print("INFO: blocker:", blocker)
215         try:
216             # json endpoint for newer mastodongs
217             try:
218                 json = {
219                     "reject"        : [],
220                     "media_removal" : [],
221                     "followers_only": [],
222                     "report_removal": []
223                 }
224
225                 # handling CSRF, I've saw at least one server requiring it to access the endpoint
226                 # DEBUG: print("DEBUG: Fetching meta:", blocker)
227                 meta = bs4.BeautifulSoup(
228                     fba.get_response(blocker, "/", fba.headers, (config.get("connection_timeout"), config.get("read_timeout"))).text,
229                     "html.parser",
230                 )
231                 try:
232                     csrf = meta.find("meta", attrs={"name": "csrf-token"})["content"]
233                     # DEBUG: print("DEBUG: Adding CSRF token:", blocker, csrf)
234                     reqheaders = {**fba.api_headers, **{"X-CSRF-Token": csrf}}
235                 except BaseException as e:
236                     # DEBUG: print("DEBUG: No CSRF token found, using normal headers:", blocker, e)
237                     reqheaders = fba.api_headers
238
239                 # DEBUG: print("DEBUG: Querying API domain_blocks:", blocker)
240                 blocks = fba.get_response(blocker, "/api/v1/instance/domain_blocks", reqheaders, (config.get("connection_timeout"), config.get("read_timeout"))).json()
241
242                 print(f"INFO: Checking {len(blocks)} entries from blocker='{blocker}',software='{software}' ...")
243                 for block in blocks:
244                     entry = {
245                         'domain': block['domain'],
246                         'hash'  : block['digest'],
247                         'reason': block['comment']
248                     }
249
250                     # DEBUG: print("DEBUG: severity,domain,hash,comment:", block['severity'], block['domain'], block['digest'], block['comment'])
251                     if block['severity'] == 'suspend':
252                         # DEBUG: print(f"DEBUG: Adding entry='{entry}' with severity='{block['severity']}' ...")
253                         json['reject'].append(entry)
254                     elif block['severity'] == 'silence':
255                         # DEBUG: print(f"DEBUG: Adding entry='{entry}' with severity='{block['severity']}' ...")
256                         json['followers_only'].append(entry)
257                     elif block['severity'] == 'reject_media':
258                         # DEBUG: print(f"DEBUG: Adding entry='{entry}' with severity='{block['severity']}' ...")
259                         json['media_removal'].append(entry)
260                     elif block['severity'] == 'reject_reports':
261                         # DEBUG: print(f"DEBUG: Adding entry='{entry}' with severity='{block['severity']}' ...")
262                         json['report_removal'].append(entry)
263                     else:
264                         print("WARNING: Unknown severity:", block['severity'], block['domain'])
265             except BaseException as e:
266                 # DEBUG: print(f"DEBUG: Failed, trying mastodon-specific fetches: blocker='{blocker}',exception[{type(e)}]={str(e)}")
267                 json = fba.get_mastodon_blocks(blocker)
268
269             print(f"INFO: Checking {len(json.items())} entries from blocker='{blocker}',software='{software}' ...")
270             for block_level, blocks in json.items():
271                 # DEBUG: print("DEBUG: blocker,block_level,blocks():", blocker, block_level, len(blocks))
272                 block_level = fba.tidyup_domain(block_level)
273                 # DEBUG: print("DEBUG: AFTER-block_level:", block_level)
274                 if block_level == "":
275                     print("WARNING: block_level is empty, blocker:", blocker)
276                     continue
277
278                 # DEBUG: print(f"DEBUG: Checking {len(blocks)} entries from blocker='{blocker}',software='{software}',block_level='{block_level}' ...")
279                 for block in blocks:
280                     blocked, blocked_hash, reason = block.values()
281                     # DEBUG: print("DEBUG: blocked,hash,reason:", blocked, blocked_hash, reason)
282                     blocked = fba.tidyup_domain(blocked)
283                     # DEBUG: print("DEBUG: AFTER-blocked:", blocked)
284
285                     if blocked == "":
286                         print("WARNING: blocked is empty:", blocker)
287                         continue
288                     elif fba.is_blacklisted(blocked):
289                         # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
290                         continue
291                     elif blocked.count("*") > 0:
292                         # Doing the hash search for instance names as well to tidy up DB
293                         fba.cursor.execute(
294                             "SELECT domain, origin, nodeinfo_url FROM instances WHERE hash = ? LIMIT 1", [blocked_hash]
295                         )
296                         searchres = fba.cursor.fetchone()
297
298                         if searchres == None:
299                             print(f"WARNING: Cannot deobsfucate blocked='{blocked}',blocked_hash='{blocked_hash}' - SKIPPED!")
300                             continue
301
302                         # DEBUG: print("DEBUG: Updating domain: ", searchres[0])
303                         blocked = searchres[0]
304                         origin = searchres[1]
305                         nodeinfo_url = searchres[2]
306
307                         # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
308                         if not validators.domain(blocked):
309                             print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domain name - skipped!")
310                             continue
311                         elif not fba.is_instance_registered(blocked):
312                             # DEBUG: print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., blocker='{blocker}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
313                             fba.add_instance(blocked, blocker, sys.argv[0], nodeinfo_url)
314                     elif not validators.domain(blocked):
315                         print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domai name - skipped!")
316                         continue
317
318                     # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
319                     if not validators.domain(blocked):
320                         print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domain name - skipped!")
321                         continue
322                     elif not fba.is_instance_registered(blocked):
323                         # DEBUG: print("DEBUG: Hash wasn't found, adding:", blocked, blocker)
324                         fba.add_instance(blocked, blocker, sys.argv[0], nodeinfo_url)
325
326                     blocking = blocked if blocked.count("*") <= 1 else blocked_hash
327                     # DEBUG: print(f"DEBUG: blocking='{blocking}',blocked='{blocked}',blocked_hash='{blocked_hash}'")
328
329                     if not fba.is_instance_blocked(blocker, blocked, block_level):
330                         # DEBUG: print("DEBUG: Blocking:", blocker, blocked, block_level)
331                         fba.block_instance(blocker, blocking, reason, block_level)
332
333                         if block_level == "reject":
334                             blockdict.append(
335                                 {
336                                     "blocked": blocked,
337                                     "reason" : reason
338                                 })
339                     else:
340                         # DEBUG: print(f"DEBUG: Updating block last seen and reason for blocker='{blocker}',blocking='{blocking}' ...")
341                         fba.update_last_seen(blocker, blocking, block_level)
342                         fba.update_block_reason(reason, blocker, blocking, block_level)
343
344             # DEBUG: print("DEBUG: Committing changes ...")
345             fba.connection.commit()
346         except Exception as e:
347             print(f"ERROR: blocker='{blocker}',software='{software}',exception[{type(e)}]:'{str(e)}'")
348     elif software == "friendica" or software == "misskey" or software == "bookwyrm" or software == "takahe":
349         print("INFO: blocker:", blocker)
350         try:
351             if software == "friendica":
352                 json = fba.get_friendica_blocks(blocker)
353             elif software == "misskey":
354                 json = fba.get_misskey_blocks(blocker)
355             elif software == "bookwyrm":
356                 print("WARNING: bookwyrm is not fully supported for fetching blacklist!", blocker)
357                 #json = fba.get_bookwyrm_blocks(blocker)
358                 continue
359             elif software == "takahe":
360                 print("WARNING: takahe is not fully supported for fetching blacklist!", blocker)
361                 #json = fba.get_takahe_blocks(blocker)
362                 continue
363
364             print(f"INFO: Checking {len(json.items())} entries from blocker='{blocker}',software='{software}' ...")
365             for block_level, blocks in json.items():
366                 # DEBUG: print("DEBUG: blocker,block_level,blocks():", blocker, block_level, len(blocks))
367                 block_level = fba.tidyup_domain(block_level)
368                 # DEBUG: print("DEBUG: AFTER-block_level:", block_level)
369                 if block_level == "":
370                     print("WARNING: block_level is empty, blocker:", blocker)
371                     continue
372
373                 # DEBUG: print(f"DEBUG: Checking {len(blocks)} entries from blocker='{blocker}',software='{software}',block_level='{block_level}' ...")
374                 for block in blocks:
375                     blocked, reason = block.values()
376                     # DEBUG: print("DEBUG: BEFORE blocked:", blocked)
377                     blocked = fba.tidyup_domain(blocked)
378                     # DEBUG: print("DEBUG: AFTER blocked:", blocked)
379
380                     if blocked == "":
381                         print("WARNING: blocked is empty:", blocker)
382                         continue
383                     elif fba.is_blacklisted(blocked):
384                         # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
385                         continue
386                     elif blocked.count("*") > 0:
387                         # Some friendica servers also obscure domains without hash
388                         fba.cursor.execute(
389                             "SELECT domain, origin, nodeinfo_url FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
390                         )
391
392                         searchres = fba.cursor.fetchone()
393
394                         if searchres == None:
395                             print(f"WARNING: Cannot deobsfucate blocked='{blocked}' - SKIPPED!")
396                             continue
397
398                         blocked = searchres[0]
399                         origin = searchres[1]
400                         nodeinfo_url = searchres[2]
401                     elif blocked.count("?") > 0:
402                         # Some obscure them with question marks, not sure if that's dependent on version or not
403                         fba.cursor.execute(
404                             "SELECT domain, origin, nodeinfo_url FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("?", "_")]
405                         )
406
407                         searchres = fba.cursor.fetchone()
408
409                         if searchres == None:
410                             print(f"WARNING: Cannot deobsfucate blocked='{blocked}' - SKIPPED!")
411                             continue
412
413                         blocked = searchres[0]
414                         origin = searchres[1]
415                         nodeinfo_url = searchres[2]
416                     elif not validators.domain(blocked):
417                         print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domai name - skipped!")
418                         continue
419
420                     # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
421                     if not validators.domain(blocked):
422                         print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domain name - skipped!")
423                         continue
424                     elif not fba.is_instance_registered(blocked):
425                         # DEBUG: print("DEBUG: Hash wasn't found, adding:", blocked, blocker)
426                         fba.add_instance(blocked, blocker, sys.argv[0], nodeinfo_url)
427
428                     if not fba.is_instance_blocked(blocker, blocked, block_level):
429                         fba.block_instance(blocker, blocked, reason, block_level)
430
431                         if block_level == "reject":
432                             blockdict.append(
433                                 {
434                                     "blocked": blocked,
435                                     "reason" : reason
436                                 })
437                     else:
438                         print(f"DEBUG: Updating block last seen and reason for blocker='{blocker}',blocked='{blocked}' ...")
439                         fba.update_last_seen(blocker, blocked, block_level)
440                         fba.update_block_reason(reason, blocker, blocked, block_level)
441
442             # DEBUG: print("DEBUG: Committing changes ...")
443             fba.connection.commit()
444         except Exception as e:
445             print(f"ERROR: blocker='{blocker}',software='{software}',exception[{type(e)}]:'{str(e)}'")
446     elif software == "gotosocial":
447         print("INFO: blocker:", blocker)
448         try:
449             # Blocks
450             federation = fba.get_response(blocker, "{fba.get_peers_url}?filter=suspended", fba.api_headers, (config.get("connection_timeout"), config.get("read_timeout"))).json()
451
452             if (federation == None):
453                 print("WARNING: No valid response:", blocker);
454             elif "error" in federation:
455                 print("WARNING: API returned error:", federation["error"])
456             else:
457                 print(f"INFO: Checking {len(federation)} entries from blocker='{blocker}',software='{software}' ...")
458                 for peer in federation:
459                     blocked = peer["domain"].lower()
460                     # DEBUG: print("DEBUG: BEFORE blocked:", blocked)
461                     blocked = fba.tidyup_domain(blocked)
462                     # DEBUG: print("DEBUG: AFTER blocked:", blocked)
463
464                     if blocked == "":
465                         print("WARNING: blocked is empty:", blocker)
466                         continue
467                     elif fba.is_blacklisted(blocked):
468                         # DEBUG: print(f"DEBUG: blocked='{blocked}' is blacklisted - skipping!")
469                         continue
470                     elif blocked.count("*") > 0:
471                         # GTS does not have hashes for obscured domains, so we have to guess it
472                         fba.cursor.execute(
473                             "SELECT domain, origin, nodeinfo_url FROM instances WHERE domain LIKE ? ORDER BY rowid LIMIT 1", [blocked.replace("*", "_")]
474                         )
475                         searchres = fba.cursor.fetchone()
476
477                         if searchres == None:
478                             print(f"WARNING: Cannot deobsfucate blocked='{blocked}' - SKIPPED!")
479                             continue
480
481                         blocked = searchres[0]
482                         origin = searchres[1]
483                         nodeinfo_url = searchres[2]
484                     elif not validators.domain(blocked):
485                         print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domai name - skipped!")
486                         continue
487
488                     # DEBUG: print("DEBUG: Looking up instance by domain:", blocked)
489                     if not validators.domain(blocked):
490                         print(f"WARNING: blocked='{blocked}',software='{software}' is not a valid domain name - skipped!")
491                         continue
492                     elif not fba.is_instance_registered(blocked):
493                         # DEBUG: print(f"DEBUG: Domain blocked='{blocked}' wasn't found, adding ..., blocker='{blocker}',origin='{origin}',nodeinfo_url='{nodeinfo_url}'")
494                         fba.add_instance(blocked, blocker, sys.argv[0], nodeinfo_url)
495
496                     if not fba.is_instance_blocked(blocker, blocked, "reject"):
497                         # DEBUG: print(f"DEBUG: blocker='{blocker}' is blocking '{blocked}' for unknown reason at this point")
498                         fba.block_instance(blocker, blocked, "unknown", "reject")
499
500                         blockdict.append(
501                             {
502                                 "blocked": blocked,
503                                 "reason" : None
504                             })
505                     else:
506                         print(f"DEBUG: Updating block last seen for blocker='{blocker}',blocked='{blocked}' ...")
507                         fba.update_last_seen(blocker, blocked, "reject")
508
509                     if "public_comment" in peer:
510                         # DEBUG: print("DEBUG: Updating block reason:", blocker, blocked, peer["public_comment"])
511                         fba.update_block_reason(peer["public_comment"], blocker, blocked, "reject")
512
513                         for entry in blockdict:
514                             if entry["blocked"] == blocked:
515                                 # DEBUG: print(f"DEBUG: Setting block reason for blocked='{blocked}':'{peer['public_comment']}'")
516                                 entry["reason"] = peer["public_comment"]
517
518                 # DEBUG: print("DEBUG: Committing changes ...")
519                 fba.connection.commit()
520         except Exception as e:
521             print(f"ERROR: blocker='{blocker}',software='{software}',exception[{type(e)}]:'{str(e)}'")
522     else:
523         print("WARNING: Unknown software:", blocker, software)
524
525     if config.get("bot_enabled") and len(blockdict) > 0:
526         send_bot_post(blocker, blockdict)
527
528     blockdict = []
529
530 boot.shutdown()