-
-
Save temujin9/9a9d8c57b9d95e8fbad4498af9ca909e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# find my mutuals' active mutuals | |
import atproto | |
import session.client | |
from datetime import * | |
import dateutil.parser | |
import random | |
import os | |
import tenacity | |
client = session.client.login() | |
actor_ns = atproto.xrpc_client.namespaces.sync_ns.ActorNamespace(client) | |
feed_ns = atproto.xrpc_client.namespaces.sync_ns.FeedNamespace(client) | |
graph_ns = atproto.xrpc_client.namespaces.sync_ns.GraphNamespace(client) | |
output = os.path.dirname(os.path.realpath(__file__)) + "/suggestions.html" | |
retryer = tenacity.Retrying(stop=tenacity.stop_after_attempt(3), wait=tenacity.wait_fixed(1), reraise=True) | |
def find_follows(did): | |
follows = {} | |
params = { "actor": did } | |
while True: | |
result = retryer( graph_ns.get_follows, params ) | |
follows.update( dict(map( lambda x: [x.did, x], result.follows )) ) | |
if result.cursor == None: break | |
params["cursor"] = result.cursor | |
return(follows) | |
def find_followers(did): | |
followers = {} | |
params = { "actor": did } | |
while True: | |
result = retryer( graph_ns.get_followers, params ) | |
followers.update( dict(map( lambda x: [x.did, x], result.followers )) ) | |
if result.cursor == None: break | |
params["cursor"] = result.cursor | |
return(followers) | |
mutuals = {} | |
follows = find_follows(client.me.did) | |
for did in follows: | |
if follows[did].viewer.followed_by: mutuals[did] = follows[did] | |
knockout = {client.me.did: True} | |
maybe = {} | |
found = {} | |
now = datetime.now(tz=timezone.utc) | |
mdids = list(mutuals.keys()) | |
random.shuffle(mdids) | |
print(f"found {len(mutuals)} mutuals, checking their mutuals") | |
for mdid in mdids: | |
print(f"checking {mutuals[mdid].handle}: ",end="",flush=True) | |
mfollows = find_follows(mdid) | |
mfollowers = find_followers(mdid) | |
for mfdid in mfollows: | |
if mfdid in knockout: continue | |
if mfdid in found: | |
maybe[mfdid] += 1 | |
continue | |
i_am = mfollows[mfdid].viewer | |
if i_am.muted or i_am.blocking or i_am.blocked_by or i_am.following or i_am.followed_by: | |
knockout[mfdid] = True # Known | |
continue | |
if mfdid in mfollowers: | |
maybe[mfdid] = maybe.get(mfdid,0) + 1 | |
if maybe[mfdid] >= 2: | |
full = retryer( actor_ns.get_profile, { "actor": mfdid } ) | |
if 2 * full.follows_count < full.followers_count: | |
knockout[mfdid] = True # Parasocial | |
continue | |
if full.follows_count > max(2 * full.followers_count, 5000): | |
knockout[mfdid] = True # Spam follows | |
continue | |
af = retryer( feed_ns.get_author_feed, { "actor": mfdid, "limit": 3 } ) | |
if len(af.feed) == 0: | |
knockout[mfdid] = True # Inactive | |
continue | |
last = af.feed.pop() | |
if dateutil.parser.isoparse(last.post.indexed_at) < now - timedelta(days=7): | |
knockout[mfdid] = True # Inactive | |
continue | |
found[mfdid] = mfollows[mfdid] | |
print(f"follows {len(mfollows)}, found {len(found)} of {len(maybe)} possibilities") | |
if len(found) >= 1000: break | |
with open(output, "w") as f: | |
f.write("<body bgcolor='#AAA'>") | |
for fdid in sorted(maybe, key=maybe.get, reverse=False): | |
if fdid in found: f.write(f"<a href='https://bsky.app/profile/{fdid}'>{found[fdid].handle}</a><br>\n") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment