123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- import json
- import logging
- from datetime import datetime, timedelta
- from mastodon import Mastodon
- from config import *
- from valid import valid_accounts
- from clean import clean_accounts
- def fetch_accounts(api_function):
- accounts = api_function()
- accounts = mastodon.fetch_remaining(accounts)
- accounts = [
- acc
- for acc in accounts
- if acc["last_status_at"] is None # have no posts
- or acc["last_status_at"] < valid_date # inactive accounts
- ]
- return accounts
- if __name__ == "__main__":
- logging.basicConfig(level=logging.INFO)
- logging.info("Starting Fedi Cleaner")
- mastodon = Mastodon(
- access_token=ACCESS_TOKEN, api_base_url=API_BASE_URL, ratelimit_method="pace"
- )
- current_user = mastodon.me()
- uid = current_user["id"]
- following, followers, lists_members, blocks, mutes = [], [], [], [], []
- valid_date = datetime.now() - timedelta(days=INACTIVE_DAYS) # FIXME: UTC
- if CLEAN_FOLLOWING or not CLEAN_MUTUALS:
- logging.info("Fetching following")
- following = fetch_accounts(lambda: mastodon.account_following(uid))
- if CLEAN_FOLLOWERS or not CLEAN_MUTUALS:
- logging.info("Fetching followers")
- followers = fetch_accounts(lambda: mastodon.account_followers(uid))
- if CLEAN_LISTS: # in akkoma/pleroma list members can be unfollowed
- logging.info("Fetching lists")
- lists = mastodon.lists()
- for list_ in lists:
- list_members = fetch_accounts(lambda: mastodon.list_accounts(list_["id"]))
- lists_members.append(
- {"id": list_["id"], "title": list_["title"], "members": list_members}
- )
- if CLEAN_BLOCKS: # they can't follow us so there's no mutuals
- logging.info("Fetching blocks")
- blocks = fetch_accounts(mastodon.blocks)
- if CLEAN_MUTES:
- logging.info("Fetching mutes")
- mutes = fetch_accounts(mastodon.mutes)
- if not CLEAN_MUTUALS: # just don't want to use relationship api
- logging.info("Excluding mutuals")
- following_id = [acc["id"] for acc in following]
- followers_id = [acc["id"] for acc in followers]
- mutuals = list(set(following_id) & set(followers_id))
- # mutuals are excluded
- following = [acc for acc in following if acc["id"] not in mutuals]
- followers = [acc for acc in followers if acc["id"] not in mutuals]
- mutes = [acc for acc in mutes if acc["id"] not in mutuals]
- for list_ in lists_members:
- list_["members"] = [
- acc for acc in list_["members"] if acc["id"] not in mutuals
- ]
- accounts = {
- "following": following if CLEAN_FOLLOWING else None,
- "followers": followers if CLEAN_FOLLOWERS else None,
- "lists_members": lists_members if CLEAN_LISTS else None,
- "blocks": blocks if CLEAN_BLOCKS else None,
- "mutes": mutes if CLEAN_MUTES else None,
- }
- # if DRY_RUN:
- # with open("accounts.json", "w") as f:
- # f.write(
- # json.dumps(
- # accounts,
- # indent=4,
- # default=str,
- # )
- # )
- # now validate those accounts
- # criteria: 1. account migration 2. inactive account 3. dead instance or errors
- criteria = {
- 1: CLEAN_MIGRATED_ACCOUNTS,
- 2: CLEAN_INACTIVE_ACCOUNTS,
- 3: CLEAN_DEAD_ACCOUNTS,
- }
- operations = {}
- for k, v in accounts.items():
- logging.info(f"Validating {k}")
- if v is None:
- continue
- if k == "lists_members":
- operation = {
- list_["id"]: [
- id
- for id, state in valid_accounts(list_["members"])
- if criteria[state]
- ]
- for list_ in v
- }
- else:
- operation = [id for id, state in valid_accounts(v) if criteria[state]]
- operations[k] = operation
- # now clean those accounts
- # or only write operations to file
- if DRY_RUN:
- with open("operations.json", "w") as f:
- f.write(
- json.dumps(
- operations,
- indent=4,
- default=str,
- )
- )
- else:
- clean_accounts(mastodon, operations)
- logging.info("Done!")
|