main.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import json
  2. import logging
  3. from datetime import datetime, timedelta
  4. from mastodon import Mastodon
  5. from config import *
  6. from valid import valid_accounts
  7. from clean import clean_accounts
  8. def fetch_accounts(api_function):
  9. accounts = api_function()
  10. accounts = mastodon.fetch_remaining(accounts)
  11. accounts = [
  12. acc
  13. for acc in accounts
  14. if acc["last_status_at"] is None # have no posts
  15. or acc["last_status_at"] < valid_date # inactive accounts
  16. ]
  17. return accounts
  18. if __name__ == "__main__":
  19. logging.basicConfig(level=logging.INFO)
  20. logging.info("Starting Fedi Cleaner")
  21. mastodon = Mastodon(
  22. access_token=ACCESS_TOKEN, api_base_url=API_BASE_URL, ratelimit_method="pace"
  23. )
  24. current_user = mastodon.me()
  25. uid = current_user["id"]
  26. following, followers, lists_members, blocks, mutes = [], [], [], [], []
  27. valid_date = datetime.now() - timedelta(days=INACTIVE_DAYS) # FIXME: UTC
  28. if CLEAN_FOLLOWING or not CLEAN_MUTUALS:
  29. logging.info("Fetching following")
  30. following = fetch_accounts(lambda: mastodon.account_following(uid))
  31. if CLEAN_FOLLOWERS or not CLEAN_MUTUALS:
  32. logging.info("Fetching followers")
  33. followers = fetch_accounts(lambda: mastodon.account_followers(uid))
  34. if CLEAN_LISTS: # in akkoma/pleroma list members can be unfollowed
  35. logging.info("Fetching lists")
  36. lists = mastodon.lists()
  37. for list_ in lists:
  38. list_members = fetch_accounts(lambda: mastodon.list_accounts(list_["id"]))
  39. lists_members.append(
  40. {"id": list_["id"], "title": list_["title"], "members": list_members}
  41. )
  42. if CLEAN_BLOCKS: # they can't follow us so there's no mutuals
  43. logging.info("Fetching blocks")
  44. blocks = fetch_accounts(mastodon.blocks)
  45. if CLEAN_MUTES:
  46. logging.info("Fetching mutes")
  47. mutes = fetch_accounts(mastodon.mutes)
  48. if not CLEAN_MUTUALS: # just don't want to use relationship api
  49. logging.info("Excluding mutuals")
  50. following_id = [acc["id"] for acc in following]
  51. followers_id = [acc["id"] for acc in followers]
  52. mutuals = list(set(following_id) & set(followers_id))
  53. # mutuals are excluded
  54. following = [acc for acc in following if acc["id"] not in mutuals]
  55. followers = [acc for acc in followers if acc["id"] not in mutuals]
  56. mutes = [acc for acc in mutes if acc["id"] not in mutuals]
  57. for list_ in lists_members:
  58. list_["members"] = [
  59. acc for acc in list_["members"] if acc["id"] not in mutuals
  60. ]
  61. accounts = {
  62. "following": following if CLEAN_FOLLOWING else None,
  63. "followers": followers if CLEAN_FOLLOWERS else None,
  64. "lists_members": lists_members if CLEAN_LISTS else None,
  65. "blocks": blocks if CLEAN_BLOCKS else None,
  66. "mutes": mutes if CLEAN_MUTES else None,
  67. }
  68. # if DRY_RUN:
  69. # with open("accounts.json", "w") as f:
  70. # f.write(
  71. # json.dumps(
  72. # accounts,
  73. # indent=4,
  74. # default=str,
  75. # )
  76. # )
  77. # now validate those accounts
  78. # criteria: 1. account migration 2. inactive account 3. dead instance or errors
  79. criteria = {
  80. 1: CLEAN_MIGRATED_ACCOUNTS,
  81. 2: CLEAN_INACTIVE_ACCOUNTS,
  82. 3: CLEAN_DEAD_ACCOUNTS,
  83. }
  84. operations = {}
  85. for k, v in accounts.items():
  86. logging.info(f"Validating {k}")
  87. if v is None:
  88. continue
  89. if k == "lists_members":
  90. operation = {
  91. list_["id"]: [
  92. id
  93. for id, state in valid_accounts(list_["members"])
  94. if criteria[state]
  95. ]
  96. for list_ in v
  97. }
  98. else:
  99. operation = [id for id, state in valid_accounts(v) if criteria[state]]
  100. operations[k] = operation
  101. # now clean those accounts
  102. # or only write operations to file
  103. if DRY_RUN:
  104. with open("operations.json", "w") as f:
  105. f.write(
  106. json.dumps(
  107. operations,
  108. indent=4,
  109. default=str,
  110. )
  111. )
  112. else:
  113. clean_accounts(mastodon, operations)
  114. logging.info("Done!")