Skip to content

Commit

Permalink
changed back to 10 sec sleep time before following new user
Browse files Browse the repository at this point in the history
  • Loading branch information
OfficialCodeVoyage committed Sep 15, 2024
1 parent f5e2569 commit 55555bd
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 61 deletions.
2 changes: 1 addition & 1 deletion last_line.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
15
130579
82 changes: 23 additions & 59 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import time
import os
import logging
from typing import List, Tuple
from typing import List
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Configure logging
logging.basicConfig(
level=logging.INFO,
level=logging.INFO, # Set to INFO for general logs; use DEBUG for more verbosity
format='%(asctime)s [%(levelname)s] %(message)s',
handlers=[
logging.FileHandler("follow_users.log"),
Expand All @@ -28,8 +28,8 @@
logging.error("GitHub token not found. Please set GITHUB_TOKEN in the environment variables.")
exit(1)

# Semaphore to limit concurrent requests
SEM = asyncio.Semaphore(5) # Adjust the number based on your needs and testing
# Semaphore to limit concurrent requests (set to 1 for sequential processing)
SEM = asyncio.Semaphore(1)

# Function to read usernames from a file
def read_usernames(file_path: str) -> List[str]:
Expand Down Expand Up @@ -72,47 +72,25 @@ def write_last_line(file_path: str, line_number: int) -> None:
logging.exception(f"An error occurred while writing to '{file_path}': {e}")

# Asynchronous function to follow a user on GitHub
async def follow_user(session: aiohttp.ClientSession, username: str) -> Tuple[int, str]:
async def follow_user(session: aiohttp.ClientSession, username: str, line_number: int) -> None:
url = f'https://api.github.com/user/following/{username}'
async with SEM: # Limit concurrency
async with SEM: # Ensure sequential processing
try:
async with session.put(url) as response:
status = response.status
text = await response.text()

# Log rate limit headers
rate_limit_remaining = response.headers.get('X-RateLimit-Remaining')
rate_limit_reset = response.headers.get('X-RateLimit-Reset')
if rate_limit_remaining and rate_limit_reset:
logging.debug(f"Rate Limit Remaining: {rate_limit_remaining}")
logging.debug(f"Rate Limit Reset Time: {rate_limit_reset}")
if status == 204:
logging.info(f"Line {line_number + 1}: Successfully followed '{username}'.")
elif status == 404:
logging.warning(f"Line {line_number + 1}: User '{username}' not found.")
elif status == 403 or status == 429:
logging.error(f"Line {line_number + 1}: Rate limit exceeded or forbidden access.")
else:
logging.error(f"Line {line_number + 1}: Failed to follow '{username}': {status}, {text}")

return status, text
except Exception as e:
logging.exception(f"Error following user '{username}': {e}")
return 0, str(e)

# Function to handle rate limiting based on GitHub's response headers
async def handle_rate_limit(headers: dict):
rate_limit_remaining = headers.get('X-RateLimit-Remaining')
rate_limit_reset = headers.get('X-RateLimit-Reset')

if rate_limit_remaining is not None and rate_limit_reset is not None:
rate_limit_remaining = int(rate_limit_remaining)
rate_limit_reset = int(rate_limit_reset)

if rate_limit_remaining == 0:
current_time = int(time.time())
sleep_duration = rate_limit_reset - current_time + 5 # Add a buffer of 5 seconds
if sleep_duration > 0:
reset_time_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(rate_limit_reset))
logging.warning(f"Rate limit reached. Sleeping until {reset_time_str} ({sleep_duration} seconds).")
await asyncio.sleep(sleep_duration)

# Function to check and handle rate limits after each request
async def check_rate_limit_after_request(response: aiohttp.ClientResponse):
headers = response.headers
await handle_rate_limit(headers)
logging.exception(f"Line {line_number + 1}: Error following user '{username}': {e}")

# Main asynchronous function
async def main():
Expand All @@ -129,27 +107,15 @@ async def main():

async with aiohttp.ClientSession(headers=headers) as session:
for i, username in enumerate(usernames[last_line:], start=last_line):
status_code, response_text = await follow_user(session, username)

if status_code == 204:
logging.info(f"Line {i + 1}: Successfully followed '{username}'.")
write_last_line(LAST_LINE_FILE, i + 1)
elif status_code == 404:
logging.warning(f"Line {i + 1}: User '{username}' not found.")
write_last_line(LAST_LINE_FILE, i + 1)
elif status_code == 403:
if 'rate limit' in response_text.lower():
logging.error(f"Line {i + 1}: Rate limit exceeded.")
# Extract headers from the last response
await handle_rate_limit(session._connector._session.headers)
else:
logging.error(f"Line {i + 1}: Forbidden access when trying to follow '{username}'.")
else:
logging.error(f"Line {i + 1}: Failed to follow '{username}': {status_code}, {response_text}")
await follow_user(session, username, i)

# Optional: Dynamic sleep based on remaining rate limit
# This example uses a fixed sleep; you can adjust it based on rate limits
await asyncio.sleep(1) # Adjust as needed
# Wait for 10 seconds before processing the next user
if i < total_usernames - 1:
#logging.info("Waiting for 10 seconds before following the next user...")
await asyncio.sleep(10)

# Update the last processed line
write_last_line(LAST_LINE_FILE, i + 1)

logging.info("Finished processing all usernames.")

Expand All @@ -160,5 +126,3 @@ async def main():
logging.info("Script interrupted by user.")
except Exception as e:
logging.exception(f"An unexpected error occurred: {e}")

###test!
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
requests~=2.31.0
beautifulsoup4~=4.12.3
aiohttp
python-dotenv
python-dotenv
aiolimiter

0 comments on commit 55555bd

Please sign in to comment.