250 lines
7.3 KiB
Python
Executable File
250 lines
7.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
SMART SENDER
|
|
Envoi intelligent avec:
|
|
- Warmup automatique
|
|
- Volume optimization
|
|
- Sélection méthode optimale
|
|
- Rotation IPs/domaines
|
|
"""
|
|
|
|
import psycopg2
|
|
import subprocess
|
|
import sys
|
|
import json
|
|
from datetime import datetime
|
|
|
|
DB_CONFIG = {
|
|
'host': 'localhost',
|
|
'database': 'adx_system',
|
|
'user': 'admin',
|
|
'password': 'admin123'
|
|
}
|
|
|
|
# Import warmup manager functions
|
|
sys.path.append('/opt/wevads/scripts')
|
|
from importlib import import_module
|
|
|
|
def get_db():
|
|
return psycopg2.connect(**DB_CONFIG)
|
|
|
|
def get_best_method_for_target(target_isp, required_volume=100):
|
|
"""
|
|
Select best sending method based on:
|
|
- Volume capacity (can handle required_volume)
|
|
- Inbox rate
|
|
- Volume quality score
|
|
"""
|
|
conn = get_db()
|
|
cur = conn.cursor()
|
|
|
|
# Get methods with available capacity
|
|
cur.execute("""
|
|
SELECT
|
|
w.sending_method,
|
|
w.ip_address,
|
|
w.domain,
|
|
w.current_daily_limit,
|
|
w.current_inbox_rate,
|
|
w.volume_score,
|
|
w.status,
|
|
w.warmup_stage,
|
|
w.id as warmup_id
|
|
FROM admin.warmup_tracking w
|
|
WHERE w.status NOT IN ('burned', 'throttled')
|
|
AND w.current_daily_limit >= %s
|
|
ORDER BY w.volume_score DESC NULLS LAST
|
|
LIMIT 10
|
|
""", (required_volume,))
|
|
|
|
methods = cur.fetchall()
|
|
conn.close()
|
|
|
|
if not methods:
|
|
return None
|
|
|
|
# Return best option
|
|
best = methods[0]
|
|
return {
|
|
'method': best[0],
|
|
'ip': str(best[1]),
|
|
'domain': best[2],
|
|
'daily_limit': best[3],
|
|
'inbox_rate': best[4],
|
|
'volume_score': best[5],
|
|
'status': best[6],
|
|
'stage': best[7],
|
|
'warmup_id': best[8]
|
|
}
|
|
|
|
def get_available_senders(target_isp=None, min_volume=50):
|
|
"""Get all available senders sorted by score"""
|
|
conn = get_db()
|
|
cur = conn.cursor()
|
|
|
|
cur.execute("""
|
|
SELECT
|
|
w.id,
|
|
w.sending_method,
|
|
w.ip_address,
|
|
w.domain,
|
|
w.current_daily_limit - COALESCE(
|
|
(SELECT SUM(emails_sent) FROM admin.volume_history
|
|
WHERE warmup_id = w.id AND hour_timestamp >= CURRENT_DATE), 0
|
|
) as remaining_today,
|
|
w.current_inbox_rate,
|
|
w.volume_score,
|
|
w.warmup_stage,
|
|
dc.target_isps
|
|
FROM admin.warmup_tracking w
|
|
LEFT JOIN admin.discovered_combos dc ON w.combo_id = dc.id
|
|
WHERE w.status NOT IN ('burned', 'throttled')
|
|
AND w.current_daily_limit >= %s
|
|
ORDER BY w.volume_score DESC NULLS LAST
|
|
""", (min_volume,))
|
|
|
|
senders = []
|
|
for row in cur.fetchall():
|
|
wid, method, ip, domain, remaining, inbox, score, stage, targets = row
|
|
|
|
# Filter by target ISP if specified
|
|
if target_isp and targets:
|
|
if target_isp not in targets and '*' not in targets:
|
|
continue
|
|
|
|
senders.append({
|
|
'warmup_id': wid,
|
|
'method': method,
|
|
'ip': str(ip) if ip else None,
|
|
'domain': domain,
|
|
'remaining_today': remaining or 0,
|
|
'inbox_rate': inbox,
|
|
'volume_score': score,
|
|
'stage': stage,
|
|
'targets': targets
|
|
})
|
|
|
|
conn.close()
|
|
return senders
|
|
|
|
def send_with_warmup(to_emails, target_isp, subject, body, method=None):
|
|
"""
|
|
Send emails with automatic warmup management
|
|
- Selects best method if not specified
|
|
- Respects daily limits
|
|
- Records stats for warmup progression
|
|
"""
|
|
volume_needed = len(to_emails)
|
|
|
|
# Get available senders
|
|
senders = get_available_senders(target_isp, min_volume=10)
|
|
|
|
if not senders:
|
|
print("❌ No available senders")
|
|
return {'success': False, 'reason': 'No senders available'}
|
|
|
|
print(f"\n📧 Sending {volume_needed} emails to {target_isp}")
|
|
print(f" Available senders: {len(senders)}")
|
|
|
|
# Distribute across senders
|
|
emails_sent = 0
|
|
results = []
|
|
|
|
for sender in senders:
|
|
if emails_sent >= volume_needed:
|
|
break
|
|
|
|
batch_size = min(sender['remaining_today'], volume_needed - emails_sent)
|
|
if batch_size <= 0:
|
|
continue
|
|
|
|
print(f"\n Using {sender['method']} via {sender['domain'] or sender['ip']}")
|
|
print(f" Batch: {batch_size} (remaining: {sender['remaining_today']})")
|
|
|
|
# Get emails for this batch
|
|
batch_emails = to_emails[emails_sent:emails_sent + batch_size]
|
|
|
|
# Send batch (placeholder - actual send logic)
|
|
# In production, call the appropriate sender
|
|
batch_result = {
|
|
'sent': len(batch_emails),
|
|
'inbox': int(len(batch_emails) * 0.8), # Simulated
|
|
'spam': int(len(batch_emails) * 0.1),
|
|
'bounced': int(len(batch_emails) * 0.1)
|
|
}
|
|
|
|
# Record in warmup
|
|
subprocess.run([
|
|
'python3', '/opt/wevads/scripts/warmup-manager.py', 'record',
|
|
sender['ip'] or '127.0.0.1',
|
|
sender['method'],
|
|
str(batch_result['sent']),
|
|
str(batch_result['inbox']),
|
|
str(batch_result['spam'])
|
|
], capture_output=True)
|
|
|
|
emails_sent += batch_result['sent']
|
|
results.append({
|
|
'sender': sender,
|
|
'result': batch_result
|
|
})
|
|
|
|
return {
|
|
'success': True,
|
|
'total_sent': emails_sent,
|
|
'batches': results
|
|
}
|
|
|
|
def show_sender_status():
|
|
"""Show available senders and their capacity"""
|
|
print("=" * 80)
|
|
print("📧 AVAILABLE SENDERS")
|
|
print("=" * 80)
|
|
|
|
senders = get_available_senders()
|
|
|
|
print(f"\n{'Method':<12} {'Domain/IP':<25} {'Remaining':>10} {'Inbox%':>8} {'Score':>8} {'Stage'}")
|
|
print("-" * 80)
|
|
|
|
total_capacity = 0
|
|
for s in senders:
|
|
identifier = s['domain'] or s['ip'] or 'N/A'
|
|
stage_icons = {1: '🔵', 2: '🟡', 3: '🟢', 4: '🔥'}
|
|
stage_icon = stage_icons.get(s['stage'], '⚪')
|
|
|
|
print(f"{s['method']:<12} {identifier[:25]:<25} {s['remaining_today']:>10} {s['inbox_rate'] or 0:>7.1f}% {s['volume_score'] or 0:>8.1f} {stage_icon}")
|
|
total_capacity += s['remaining_today']
|
|
|
|
print("-" * 80)
|
|
print(f"Total capacity today: {total_capacity:,} emails")
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
show_sender_status()
|
|
return
|
|
|
|
cmd = sys.argv[1]
|
|
|
|
if cmd == 'status':
|
|
show_sender_status()
|
|
elif cmd == 'best':
|
|
target = sys.argv[2] if len(sys.argv) > 2 else 'hotmail'
|
|
volume = int(sys.argv[3]) if len(sys.argv) > 3 else 100
|
|
|
|
best = get_best_method_for_target(target, volume)
|
|
if best:
|
|
print(f"\n🎯 Best sender for {target} ({volume} emails):")
|
|
print(f" Method: {best['method']}")
|
|
print(f" Domain: {best['domain']}")
|
|
print(f" IP: {best['ip']}")
|
|
print(f" Daily Limit: {best['daily_limit']}")
|
|
print(f" Inbox Rate: {best['inbox_rate']}%")
|
|
print(f" Score: {best['volume_score']}")
|
|
else:
|
|
print(f"❌ No suitable sender for {volume} emails")
|
|
else:
|
|
print("Commands: status, best")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|