-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdelete_tweets.py
More file actions
180 lines (156 loc) · 6.37 KB
/
delete_tweets.py
File metadata and controls
180 lines (156 loc) · 6.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
import requests
from requests_oauthlib import OAuth1
from dotenv import load_dotenv
import os
import json
import time
from datetime import datetime
from typing import List, Dict, Set, Optional, Any
from abc import ABC, abstractmethod
# Constants
MAX_DELETIONS = 17
DELETED_FILE = "deleted_tweets.json"
TWEETS_FILE = "tweets.js"
API_BASE_URL = "https://api.twitter.com/2"
# ANSI Color Codes
class Colors:
RESET = "\033[0m"
BOLD = "\033[1m"
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
BLUE = "\033[94m"
MAGENTA = "\033[95m"
CYAN = "\033[96m"
WHITE = "\033[97m"
GRAY = "\033[90m"
class BaseTwitterAPI(ABC):
def __init__(self):
self._load_environment()
self.auth = OAuth1(
self.api_key,
self.api_secret,
self.access_token,
self.access_token_secret
)
def _load_environment(self) -> None:
"""Load environment variables and validate them."""
self.log("🔄 Chargement des variables d'environnement...", Colors.CYAN)
load_dotenv()
self.api_key = os.getenv('API_KEY')
self.api_secret = os.getenv('API_SECRET')
self.access_token = os.getenv('ACCESS_TOKEN')
self.access_token_secret = os.getenv('ACCESS_TOKEN_SECRET')
if not all([self.api_key, self.api_secret, self.access_token, self.access_token_secret]):
raise ValueError("❌ Variables d'environnement manquantes")
@staticmethod
def log(message: str, color: str = Colors.WHITE) -> None:
"""Log a message with timestamp and color."""
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"{Colors.GRAY}📅 [{timestamp}]{Colors.RESET} {color}{message}{Colors.RESET}")
def _make_request(
self,
method: str,
endpoint: str,
params: Optional[Dict[str, Any]] = None,
data: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Make a request to the Twitter API."""
url = f"{API_BASE_URL}/{endpoint}"
try:
response = requests.request(
method=method,
url=url,
auth=self.auth,
params=params,
json=data
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
self.log(f"❌ Erreur API: {str(e)}", Colors.RED)
raise
def delete_tweet(self, tweet_id: str) -> bool:
"""Delete a specific tweet."""
self.log(f"🗑️ Suppression du tweet {tweet_id}", Colors.YELLOW)
try:
self._make_request("DELETE", f"tweets/{tweet_id}")
self.log(f"✅ Tweet {tweet_id} supprimé avec succès", Colors.GREEN)
return True
except Exception as e:
self.log(f"❌ Échec de la suppression du tweet {tweet_id}: {str(e)}", Colors.RED)
return False
class TwitterDeleter(BaseTwitterAPI):
def __init__(self):
super().__init__()
self.log("🚀 Initialisation du suppresseur de tweets...", Colors.MAGENTA)
def _load_deleted_ids(self) -> Set[str]:
"""Load previously deleted tweet IDs."""
if os.path.exists(DELETED_FILE):
with open(DELETED_FILE, 'r') as f:
ids = set(json.load(f))
self.log(f"📖 {len(ids)} tweets précédemment supprimés chargés", Colors.BLUE)
return ids
return set()
def _save_deleted_ids(self, ids: Set[str]) -> None:
"""Save deleted tweet IDs to file."""
with open(DELETED_FILE, 'w') as f:
json.dump(list(ids), f)
self.log(f"💾 {len(ids)} IDs de tweets supprimés sauvegardés", Colors.BLUE)
def _load_tweets_from_file(self) -> List[Dict]:
"""Load tweets from the tweets.js file."""
if not os.path.exists(TWEETS_FILE):
self.log(f"❌ Fichier {TWEETS_FILE} non trouvé", Colors.RED)
return []
try:
with open(TWEETS_FILE, 'r', encoding='utf-8') as f:
content = f.read()
# Remove the JavaScript wrapper and parse as JSON
json_str = content.replace('window.YTD.tweets.part0 = ', '')
tweets = json.loads(json_str)
self.log(f"📖 {len(tweets)} tweets chargés depuis {TWEETS_FILE}", Colors.BLUE)
return tweets
except Exception as e:
self.log(f"❌ Erreur lors de la lecture du fichier {TWEETS_FILE}: {str(e)}", Colors.RED)
return []
def delete_tweets_from_file(self) -> None:
"""Main method to delete tweets from the file."""
self.log("🚀 Démarrage du processus de suppression...", Colors.MAGENTA)
# Load previously deleted tweets
deleted_ids = self._load_deleted_ids()
self.log(f"📊 {len(deleted_ids)} tweets précédemment supprimés trouvés", Colors.BLUE)
# Load tweets from file
tweets = self._load_tweets_from_file()
if not tweets:
self.log("ℹ️ Aucun tweet à traiter", Colors.YELLOW)
return
# Process tweets
deleted = 0
skipped = 0
for tweet_data in tweets:
if deleted >= MAX_DELETIONS:
self.log(f"⏹️ Limite de suppression atteinte ({MAX_DELETIONS})", Colors.YELLOW)
break
tweet = tweet_data["tweet"]
tweet_id = tweet["id_str"]
if tweet_id in deleted_ids:
self.log(f"⏭️ Tweet {tweet_id} déjà supprimé, passage au suivant", Colors.YELLOW)
skipped += 1
continue
if self.delete_tweet(tweet_id):
deleted_ids.add(tweet_id)
deleted += 1
self.log("⏳ Attente d'une seconde avant la prochaine suppression...", Colors.CYAN)
time.sleep(1) # Rate limiting
# Save results
self._save_deleted_ids(deleted_ids)
self.log(f"🏁 Processus terminé: {deleted} nouveaux tweets supprimés, {skipped} ignorés", Colors.GREEN)
self.log(f"📊 Total des tweets supprimés: {len(deleted_ids)}", Colors.BLUE)
def main():
try:
deleter = TwitterDeleter()
deleter.delete_tweets_from_file()
except Exception as e:
TwitterDeleter.log(f"❌ Une erreur est survenue: {str(e)}", Colors.RED)
if __name__ == "__main__":
main()