-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathtrack_outreach.py
More file actions
202 lines (164 loc) · 7.42 KB
/
Copy pathtrack_outreach.py
File metadata and controls
202 lines (164 loc) · 7.42 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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Track outreach status - who saw, replied, etc."""
import sys
import pandas as pd
from datetime import datetime
from instagram_client import InstagramClient
from database import get_db, close_db, Prospect
# Fix Windows console encoding issues
if sys.platform == 'win32':
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
def get_status_text(is_read, has_reply):
"""Convert read/reply status to text"""
if has_reply:
return "Replied"
elif is_read:
return "Seen"
else:
return "Not Seen"
def track_outreach():
"""Track all sent DMs and update status"""
# Initialize Instagram client
client = InstagramClient()
if not client.login():
print("[!] Login failed")
return
# Get all prospects from database
db = get_db()
prospects = db.query(Prospect).filter(Prospect.dm_sent == True).all()
print(f"[*] Found {len(prospects)} sent DMs to track\n")
# Fetch all threads once and create a username->thread mapping
print("[*] Fetching all DM threads...")
threads = client.get_direct_threads(amount=500)
username_to_thread = {}
for thread in threads:
if thread.users:
for user in thread.users:
if user.username:
username_to_thread[user.username] = thread
print(f"[*] Found {len(username_to_thread)} thread mappings\n")
tracking_data = []
for i, prospect in enumerate(prospects, 1):
print(f"[{i}/{len(prospects)}] Checking @{prospect.username}...", end=' ', flush=True)
try:
# Look up thread from pre-fetched mapping
thread = username_to_thread.get(prospect.username)
if not thread:
print("Thread not found")
tracking_data.append({
'username': prospect.username,
'status': 'Not Seen',
'sent_date_time': prospect.dm_sent_at.strftime('%Y-%m-%d %H:%M:%S') if prospect.dm_sent_at else '',
'last_activity_date_time': None
})
continue
# Check if there's ANY reply from the other user
has_reply = False
reply_text = None
reply_time = None
for msg in thread.messages:
# Must be from the other party (not from us)
if str(msg.user_id) != str(client.client.user_id):
has_reply = True
reply_text = msg.text if hasattr(msg, 'text') and msg.text else '[Media/Reaction]'
reply_time = msg.timestamp
break # Found a reply, use the most recent one
if has_reply:
# They replied - status is "Replied"
status_text = "Replied"
last_activity = reply_time.strftime('%Y-%m-%d %H:%M:%S') if reply_time else None
# Update database
prospect.replied = True
prospect.reply_message = reply_text
if reply_time and not prospect.replied_at:
prospect.replied_at = reply_time
prospect.read_receipt = True # If they replied, they must have read it
tracking_data.append({
'username': prospect.username,
'status': status_text,
'sent_date_time': prospect.dm_sent_at.strftime('%Y-%m-%d %H:%M:%S') if prospect.dm_sent_at else '',
'last_activity_date_time': last_activity
})
reply_preview = reply_text[:50] if reply_text else ''
print(f"{status_text}: {reply_preview}")
continue
# No reply found - check if our last message was seen
our_last_message = None
for msg in thread.messages:
if str(msg.user_id) == str(client.client.user_id):
our_last_message = msg
break
if not our_last_message:
print("No messages found")
tracking_data.append({
'username': prospect.username,
'status': 'Not Seen',
'sent_date_time': prospect.dm_sent_at.strftime('%Y-%m-%d %H:%M:%S') if prospect.dm_sent_at else '',
'last_activity_date_time': None
})
continue
# Check if our last message was seen
is_read = False
read_time = None
if hasattr(thread, 'last_seen_at') and thread.last_seen_at:
# Get the other user's ID
other_user_id = None
for user in thread.users:
if str(user.pk) != str(client.client.user_id):
other_user_id = str(user.pk)
break
if other_user_id and other_user_id in thread.last_seen_at:
our_msg_timestamp = int(our_last_message.timestamp.timestamp() * 1000000)
their_last_seen = thread.last_seen_at[other_user_id]
# Handle both dict and object formats for timestamp
if isinstance(their_last_seen, dict) and 'timestamp' in their_last_seen:
their_seen_timestamp = int(their_last_seen['timestamp'])
if their_seen_timestamp >= our_msg_timestamp:
is_read = True
read_time = datetime.fromtimestamp(their_seen_timestamp / 1000000)
elif hasattr(their_last_seen, 'timestamp'):
their_seen_timestamp = int(their_last_seen.timestamp.timestamp() * 1000000)
if their_seen_timestamp >= our_msg_timestamp:
is_read = True
read_time = their_last_seen.timestamp
status_text = "Seen" if is_read else "Not Seen"
last_activity = read_time.strftime('%Y-%m-%d %H:%M:%S') if read_time else None
# Update database
prospect.read_receipt = is_read
if is_read and not prospect.read_at:
prospect.read_at = read_time
tracking_data.append({
'username': prospect.username,
'status': status_text,
'sent_date_time': prospect.dm_sent_at.strftime('%Y-%m-%d %H:%M:%S') if prospect.dm_sent_at else '',
'last_activity_date_time': last_activity
})
print(f"{status_text}")
except Exception as e:
print(f"Error: {str(e)}")
tracking_data.append({
'username': prospect.username,
'status': 'Error',
'sent_date_time': prospect.dm_sent_at.strftime('%Y-%m-%d %H:%M:%S') if prospect.dm_sent_at else '',
'last_activity_date_time': None
})
# Save updates to database
db.commit()
close_db(db)
# Create tracking CSV
df = pd.DataFrame(tracking_data)
output_file = 'outreach_tracking.csv'
df.to_csv(output_file, index=False)
print(f"\n[+] Tracking data saved to {output_file}")
# Print summary
print("\n" + "=" * 60)
print("SUMMARY")
print("=" * 60)
status_counts = df['status'].value_counts()
for status, count in status_counts.items():
print(f"{status}: {count}")
print("=" * 60)
if __name__ == '__main__':
track_outreach()