# along with this program. If not, see <https://www.gnu.org/licenses/>.
import json
+import logging
-from fba import blacklist
-from fba import config
-from fba import instances
-from fba import network
-
-from fba.helpers import dicts
+from fba.helpers import blacklist
+from fba.helpers import config
+from fba.helpers import dicts as dict_helper
+from fba.helpers import domain as domain_helper
from fba.helpers import tidyup
+from fba.http import csrf
+from fba.http import network
+
+from fba.models import instances
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
def fetch_peers(domain: str) -> list:
- # DEBUG: print(f"DEBUG: domain({len(domain)})={domain} - CALLED!")
- if not isinstance(domain, str):
- raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
- elif domain == "":
- raise ValueError("Parameter 'domain' is empty")
-
- # DEBUG: print(f"DEBUG: domain='{domain}' is misskey, sending API POST request ...")
- peers = list()
+ logger.debug("domain='%s' - CALLED!", domain)
+ domain_helper.raise_on(domain)
+
+ if blacklist.is_blacklisted(domain):
+ raise Exception(f"domain='{domain}' is blacklisted but function is invoked.")
+ elif not instances.is_registered(domain):
+ raise Exception(f"domain='{domain}' is not registered but function is invoked.")
+
+ logger.debug("domain='%s' is misskey, sending API POST request ...", domain)
+ peers = list()
offset = 0
- step = config.get("misskey_limit")
+ step = config.get("misskey_limit")
+
+ # No CSRF by default, you don't have to add network.api_headers by yourself here
+ headers = tuple()
+
+ try:
+ logger.debug("Checking CSRF for domain='%s'", domain)
+ headers = csrf.determine(domain, dict())
+ except network.exceptions as exception:
+ logger.warning("Exception '%s' during checking CSRF (fetch_peers,%s)", type(exception), __name__)
+ instances.set_last_error(domain, exception)
+
+ logger.debug("Returning empty list ... - EXIT!")
+ return list()
# iterating through all "suspended" (follow-only in its terminology)
# instances page-by-page, since that troonware doesn't support
# sending them all at once
while True:
- # DEBUG: print(f"DEBUG: Fetching offset='{offset}' from '{domain}' ...")
+ logger.debug("Fetching offset=%d from domain='%s' ...", offset, domain)
if offset == 0:
fetched = network.post_json_api(domain, "/api/federation/instances", json.dumps({
"sort" : "+pubAt",
"host" : None,
"limit": step
- }), {
- "Origin": domain
- })
+ }), headers)
else:
fetched = network.post_json_api(domain, "/api/federation/instances", json.dumps({
"sort" : "+pubAt",
"host" : None,
"limit" : step,
"offset": offset - 1
- }), {
- "Origin": domain
- })
-
- # DEBUG: print(f"DEBUG: fetched()={len(fetched)}")
- if len(fetched) == 0:
- # DEBUG: print(f"DEBUG: Returned zero bytes, exiting loop, domain='{domain}'")
- break
- elif len(fetched) != config.get("misskey_limit"):
- # DEBUG: print(f"DEBUG: Fetched '{len(fetched)}' row(s) but expected: '{config.get('misskey_limit')}'")
- offset = offset + (config.get("misskey_limit") - len(fetched))
- else:
- # DEBUG: print(f"DEBUG: Raising offset by step={step}")
- offset = offset + step
+ }), headers)
# Check records
- # DEBUG: print(f"DEBUG: fetched({len(fetched)})[]={type(fetched)}")
- if isinstance(fetched, dict) and "error" in fetched and "message" in fetched["error"]:
- print(f"WARNING: post_json_api() returned error: {fetched['error']['message']}")
- instances.update_last_error(domain, fetched["error"]["message"])
+ logger.debug("fetched[]='%s'", type(fetched))
+ if "error_message" in fetched:
+ logger.warning("post_json_api() for domain='%s' returned error message: '%s'", domain, fetched['error_message'])
+ instances.set_last_error(domain, fetched)
break
- elif "error_message" in fetched:
- print(f"WARNING: post_json_api() for domain='{domain}' returned error message: {fetched['error_message']}")
- instances.update_last_error(domain, fetched)
+ elif isinstance(fetched["json"], dict) and "error" in fetched["json"] and "message" in fetched["json"]["error"]:
+ logger.warning("post_json_api() returned error: '%s'", fetched["json"]["error"]["message"])
+ instances.set_last_error(domain, fetched["json"]["error"]["message"])
+ break
+
+ rows = fetched["json"]
+
+ logger.debug("rows(%d)[]='%s',step=%d", len(rows), type(rows), step)
+ if len(rows) == 0:
+ logger.debug("Returned zero bytes, domain='%s' - BREAK!", domain)
break
+ elif len(rows) != config.get("misskey_limit"):
+ logger.debug("Fetched %d row(s) but expected: %d", len(rows), config.get('misskey_limit'))
+ offset = offset + (config.get("misskey_limit") - len(rows))
+ else:
+ logger.debug("Raising offset by step=%d", step)
+ offset = offset + step
- already = 0
- for row in fetched["json"]:
- # DEBUG: print(f"DEBUG: row()={len(row)}")
- if not "host" in row:
- print(f"WARNING: row()={len(row)} does not contain key 'host': {row},domain='{domain}'")
+ added = 0
+ logger.debug("rows(%d))[]='%s'", len(rows), type(rows))
+ for row in rows:
+ logger.debug("row()=%d", len(row))
+ if "host" not in row:
+ logger.warning("row()=%d does not contain key 'host': row='%s',domain='%s' - SKIPPED!", len(row), row, domain)
continue
elif not isinstance(row["host"], str):
- print(f"WARNING: row[host][]={type(row['host'])} is not 'str'")
- continue
- elif blacklist.is_blacklisted(row["host"]):
- # DEBUG: print(f"DEBUG: row[host]='{row['host']}' is blacklisted. domain='{domain}'")
+ logger.warning("row[host][]='%s' is not of type 'str' - SKIPPED!", type(row['host']))
continue
elif row["host"] in peers:
- # DEBUG: print(f"DEBUG: Not adding row[host]='{row['host']}', already found.")
- already = already + 1
+ logger.debug("Not adding row[host]='%s', already found - SKIPPED!", row['host'])
+ continue
+ elif not domain_helper.is_wanted(row["host"]):
+ logger.debug("row[host]='%s' is not wanted - SKIPPED!", row["host"])
continue
- # DEBUG: print(f"DEBUG: Adding peer: '{row['host']}'")
+ logger.debug("Adding peer: row[host]='%s'", row['host'])
+ added = added + 1
peers.append(row["host"])
- if already == len(fetched):
- print(f"WARNING: Host returned same set of '{already}' instances, aborting loop!")
+ logger.debug("added=%d,rows()=%d", added, len(rows))
+ if added == 0:
+ logger.debug("Host returned already added (%d) peers - BREAK!", len(rows))
break
- # DEBUG: print(f"DEBUG: Adding '{len(peers)}' for domain='{domain}'")
- instances.set_data("total_peers", domain, len(peers))
+ logger.debug("peers()=%d - EXIT!", len(peers))
+ return peers
- # DEBUG: print(f"DEBUG: Updating last_instance_fetch for domain='{domain}' ...")
- instances.update_last_instance_fetch(domain)
+def fetch_blocks(domain: str) -> list:
+ logger.debug("domain='%s' - CALLED!", domain)
+ domain_helper.raise_on(domain)
- # DEBUG: print(f"DEBUG: Returning peers[]='{type(peers)}'")
- return peers
+ if blacklist.is_blacklisted(domain):
+ raise Exception(f"domain='{domain}' is blacklisted but function is invoked.")
+ elif not instances.is_registered(domain):
+ raise Exception(f"domain='{domain}' is not registered but function is invoked.")
-def fetch_blocks(domain: str) -> dict:
- # DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
- if not isinstance(domain, str):
- raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
- elif domain == "":
- raise ValueError("Parameter 'domain' is empty")
+ # No CSRF by default, you don't have to add network.api_headers by yourself here
+ headers = tuple()
- # DEBUG: print("DEBUG: Fetching misskey blocks from domain:", domain)
- blocklist = {
- "suspended": [],
- "blocked" : []
- }
+ try:
+ logger.debug("Checking CSRF for domain='%s' ...", domain)
+ headers = csrf.determine(domain, dict())
+ except network.exceptions as exception:
+ logger.warning("Exception '%s' during checking CSRF (fetch_blocks,%s)", type(exception), __name__)
+ instances.set_last_error(domain, exception)
- offset = 0
- step = config.get("misskey_limit")
+ logger.debug("Returning empty list ... - EXIT!")
+ return list()
+
+ blocklist = list()
+ offset = 0
+ step = config.get("misskey_limit")
+
+ # iterating through all "suspended" (follow-only in its terminology)
+ # instances page-by-page since it doesn't support sending them all at once
+ logger.debug("Fetching misskey blocks from domain='%s'", domain)
while True:
- # iterating through all "suspended" (follow-only in its terminology)
- # instances page-by-page, since that troonware doesn't support
- # sending them all at once
+ logger.debug("offset=%d", offset)
try:
- # DEBUG: print(f"DEBUG: Fetching offset='{offset}' from '{domain}' ...")
+ logger.debug("Fetching offset=%d from domain='%s' ...", offset, domain)
if offset == 0:
- # DEBUG: print("DEBUG: Sending JSON API request to domain,step,offset:", domain, step, offset)
+ logger.debug("Sending JSON API request to domain='%s',step=%d,offset=%d", domain, step, offset)
fetched = network.post_json_api(domain, "/api/federation/instances", json.dumps({
"sort" : "+pubAt",
"host" : None,
"suspended": True,
"limit" : step
- }), {
- "Origin": domain
- })
+ }), headers)
else:
- # DEBUG: print("DEBUG: Sending JSON API request to domain,step,offset:", domain, step, offset)
+ logger.debug("Sending JSON API request to domain='%s',step=%d,offset=%d", domain, step, offset)
fetched = network.post_json_api(domain, "/api/federation/instances", json.dumps({
"sort" : "+pubAt",
"host" : None,
"suspended": True,
"limit" : step,
"offset" : offset - 1
- }), {
- "Origin": domain
- })
+ }), headers)
+
+ logger.debug("fetched[]='%s'", type(fetched))
+ if "error_message" in fetched:
+ logger.warning("post_json_api() for domain='%s' returned error message: '%s'", domain, fetched['error_message'])
+ instances.set_last_error(domain, fetched)
+ break
+ elif isinstance(fetched["json"], dict) and "error" in fetched["json"] and "message" in fetched["json"]["error"]:
+ logger.warning("post_json_api() returned error: '%s'", fetched["json"]["error"]["message"])
+ instances.set_last_error(domain, fetched["json"]["error"]["message"])
+ break
+
+ rows = fetched["json"]
- # DEBUG: print("DEBUG: fetched():", len(fetched))
- if len(fetched) == 0:
- # DEBUG: print("DEBUG: Returned zero bytes, exiting loop:", domain)
+ logger.debug("rows(%d)[]='%s'", len(rows), type(rows))
+ if len(rows) == 0:
+ logger.debug("Returned zero bytes, domain='%s' - BREAK!", domain)
break
- elif len(fetched) != config.get("misskey_limit"):
- # DEBUG: print(f"DEBUG: Fetched '{len(fetched)}' row(s) but expected: '{config.get('misskey_limit')}'")
- offset = offset + (config.get("misskey_limit") - len(fetched))
+ elif len(rows) != config.get("misskey_limit"):
+ logger.debug("Fetched %d row(s) but expected: %d", len(rows), config.get('misskey_limit'))
+ offset = offset + (config.get("misskey_limit") - len(rows))
else:
- # DEBUG: print("DEBUG: Raising offset by step:", step)
+ logger.debug("Raising offset by step=%d", step)
offset = offset + step
count = 0
- for instance in fetched:
+ logger.debug("Checking %d row(s) of instances ...", len(rows))
+ for instance in rows:
# Is it there?
- if instance["isSuspended"] and not dicts.has_key(blocklist["suspended"], "domain", instance):
+ logger.debug("instance[]='%s'", type(instance))
+ if "host" not in instance:
+ logger.warning("instance(%d)='%s' has no key 'host' - SKIPPED!", len(instance), instance)
+ continue
+ elif instance["host"] in [None, ""]:
+ logger.debug("instance[host]='%s' is None or empty - SKIPPED!", instance["host"])
+ continue
+
+ logger.debug("instance[host]='%s' - BEFORE!", instance["host"])
+ blocked = tidyup.domain(instance["host"])
+ logger.debug("blocked[%s]='%s' - AFTER!", type(blocked), blocked)
+
+ if blocked in [None, ""]:
+ logger.warning("instance[host]='%s' is None or empty after tidyup.domain() - SKIPPED!", instance["host"])
+ continue
+ elif not domain_helper.is_wanted(blocked):
+ logger.debug("blocked='%s' is not wanted - SKIPPED!", blocked)
+ continue
+ elif "isSuspended" in instance and instance["isSuspended"] and not dict_helper.has_key(blocklist, "blocked", blocked):
count = count + 1
- blocklist["suspended"].append(
- {
- "domain": tidyup.domain(instance["host"]),
- # no reason field, nothing
- "reason": None
- }
- )
-
- # DEBUG: print(f"DEBUG: count={count}")
+ logger.debug("Appending blocker='%s',blocked='%s',block_level='suspended'", domain, blocked)
+ blocklist.append({
+ "blocker" : domain,
+ "blocked" : blocked,
+ "reason" : None,
+ "block_level": "suspended",
+ })
+
+ logger.debug("count=%d", count)
if count == 0:
- # DEBUG: print("DEBUG: API is no more returning new instances, aborting loop!")
+ logger.debug("API is no more returning new instances, aborting loop! domain='%s'", domain)
break
- except BaseException as exception:
- print("WARNING: Caught error, exiting loop:", domain, exception)
- instances.update_last_error(domain, exception)
+ except network.exceptions as exception:
+ logger.warning("Caught error, exiting loop: domain='%s',exception[%s]='%s'", domain, type(exception), str(exception))
+ instances.set_last_error(domain, exception)
offset = 0
break
while True:
- # same shit, different asshole ("blocked" aka full suspend)
+ # Fetch blocked (full suspended) instances
+ logger.debug("offset=%d", offset)
try:
if offset == 0:
- # DEBUG: print("DEBUG: Sending JSON API request to domain,step,offset:", domain, step, offset)
+ logger.debug("Sending JSON API request to domain='%s',step=%d,offset=%d", domain, step, offset)
fetched = network.post_json_api(domain, "/api/federation/instances", json.dumps({
"sort" : "+pubAt",
"host" : None,
"blocked": True,
"limit" : step
- }), {
- "Origin": domain
- })
+ }), headers)
else:
- # DEBUG: print("DEBUG: Sending JSON API request to domain,step,offset:", domain, step, offset)
+ logger.debug("Sending JSON API request to domain='%s',step=%d,offset=%d", domain, step, offset)
fetched = network.post_json_api(domain, "/api/federation/instances", json.dumps({
"sort" : "+pubAt",
"host" : None,
"blocked": True,
"limit" : step,
"offset" : offset - 1
- }), {
- "Origin": domain
- })
+ }), headers)
+
+ logger.debug("fetched[]='%s'", type(fetched))
+ if "error_message" in fetched:
+ logger.warning("post_json_api() for domain='%s' returned error message: '%s'", domain, fetched['error_message'])
+ instances.set_last_error(domain, fetched)
+ break
+ elif isinstance(fetched["json"], dict) and "error" in fetched["json"] and "message" in fetched["json"]["error"]:
+ logger.warning("post_json_api() returned error: '%s'", fetched["json"]["error"]["message"])
+ instances.set_last_error(domain, fetched["json"]["error"]["message"])
+ break
+
+ rows = fetched["json"]
- # DEBUG: print("DEBUG: fetched():", len(fetched))
- if len(fetched) == 0:
- # DEBUG: print("DEBUG: Returned zero bytes, exiting loop:", domain)
+ logger.debug("rows(%d)[]='%s'", len(rows), type(rows))
+ if len(rows) == 0:
+ logger.debug("Returned zero bytes, domain='%s' - BREAK!", domain)
break
- elif len(fetched) != config.get("misskey_limit"):
- # DEBUG: print(f"DEBUG: Fetched '{len(fetched)}' row(s) but expected: '{config.get('misskey_limit')}'")
- offset = offset + (config.get("misskey_limit") - len(fetched))
+ elif len(rows) != config.get("misskey_limit"):
+ logger.debug("Fetched %d row(s) but expected: %d'", len(rows), config.get('misskey_limit'))
+ offset = offset + (config.get("misskey_limit") - len(rows))
else:
- # DEBUG: print("DEBUG: Raising offset by step:", step)
+ logger.debug("Raising offset by step=%d", step)
offset = offset + step
count = 0
- for instance in fetched:
+ logger.debug("Checking %d row(s) of instances ...", len(rows))
+ for instance in rows:
# Is it there?
- if instance["isBlocked"] and not dicts.has_key(blocklist["blocked"], "domain", instance):
+ logger.debug("instance[]='%s'", type(instance))
+ blocked = tidyup.domain(instance["host"]) if instance["host"] != "" else None
+ logger.debug("blocked='%s' - AFTER!", blocked)
+
+ if blocked in [None, ""]:
+ logger.warning("instance[host]='%s' is None or empty after tidyup.domain() - SKIPPED!", instance["host"])
+ continue
+ elif not domain_helper.is_wanted(blocked):
+ logger.debug("blocked='%s' is not wanted - SKIPPED!", blocked)
+ continue
+ elif "isBlocked" in instance and instance["isBlocked"] and not dict_helper.has_key(blocklist, "blocked", blocked):
count = count + 1
- blocklist["blocked"].append({
- "domain": tidyup.domain(instance["host"]),
- "reason": None
+ logger.debug("Appending blocker='%s',blocked='%s',block_level='reject'", domain, blocked)
+ blocklist.append({
+ "blocker" : domain,
+ "blocked" : blocked,
+ "reason" : None,
+ "block_level": "reject",
})
- # DEBUG: print(f"DEBUG: count={count}")
+ logger.debug("count=%d", count)
if count == 0:
- # DEBUG: print("DEBUG: API is no more returning new instances, aborting loop!")
+ logger.debug("API is no more returning new instances, aborting loop!")
break
- except BaseException as exception:
- print("ERROR: Exception during POST:", domain, exception)
- instances.update_last_error(domain, exception)
+ except network.exceptions as exception:
+ logger.warning("Caught error, exiting loop: domain='%s',exception[%s]='%s'", domain, type(exception), str(exception))
+ instances.set_last_error(domain, exception)
offset = 0
break
- # DEBUG: print(f"DEBUG: Updating last_instance_fetch for domain='{domain}' ...")
- instances.update_last_instance_fetch(domain)
-
- # DEBUG: print(f"DEBUG: Returning for domain='{domain}',blocked()={len(blocklist['blocked'])},suspended()={len(blocklist['suspended'])}")
- return {
- "reject" : blocklist["blocked"],
- "followers_only": blocklist["suspended"]
- }
+ logger.debug("blocklist()=%d - EXIT!", len(blocklist))
+ return blocklist