Mohammed Foud commited on
Commit
48c822f
·
1 Parent(s): 0302811

Add application file

Browse files
Files changed (7) hide show
  1. Dockerfile +1 -1
  2. README_WEB.md +141 -0
  3. app.py +196 -0
  4. requirements.txt +2 -0
  5. run_flask.py +18 -0
  6. templates/index.html +300 -0
  7. tiktok.py +119 -43
Dockerfile CHANGED
@@ -25,4 +25,4 @@ RUN pip install --no-cache-dir --upgrade -r requirements.txt
25
  COPY --chown=user . /app
26
 
27
 
28
- CMD ["python3", "tiktok.py"]
 
25
  COPY --chown=user . /app
26
 
27
 
28
+ # CMD ["python3", "tiktok.py"]
README_WEB.md ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TikTok Bot Web Interface
2
+
3
+ A modern web interface for the TikTok bot with real-time progress tracking.
4
+
5
+ ## Features
6
+
7
+ - 🎨 Modern UI with Tailwind CSS
8
+ - 📊 Real-time progress bar
9
+ - 🔄 Live status updates
10
+ - 📝 Activity log
11
+ - 🎯 Easy configuration
12
+ - 🚀 One-click bot start/stop
13
+
14
+ ## Installation
15
+
16
+ 1. Install the required dependencies:
17
+ ```bash
18
+ pip install -r requirements.txt
19
+ ```
20
+
21
+ 2. Make sure you have Chrome/Chromium installed for Selenium.
22
+
23
+ ## Usage
24
+
25
+ ### Starting the Web Interface
26
+
27
+ Run the Flask application:
28
+ ```bash
29
+ python run_flask.py
30
+ ```
31
+
32
+ Or directly:
33
+ ```bash
34
+ python app.py
35
+ ```
36
+
37
+ ### Accessing the Interface
38
+
39
+ 1. Open your web browser
40
+ 2. Go to: `http://localhost:5000`
41
+ 3. You'll see the modern web interface
42
+
43
+ ### Using the Bot
44
+
45
+ 1. **Configure the Bot:**
46
+ - Enter the TikTok video URL
47
+ - Add your comment text
48
+ - Set the number of comments (1-1000)
49
+
50
+ 2. **Start the Bot:**
51
+ - Click the "Start Bot" button
52
+ - The bot will automatically log in and start posting comments
53
+
54
+ 3. **Monitor Progress:**
55
+ - Watch the real-time progress bar
56
+ - Check the statistics (successful/failed comments)
57
+ - View the activity log for detailed information
58
+
59
+ 4. **Stop the Bot:**
60
+ - Click "Stop Bot" to halt the process
61
+
62
+ ## Configuration
63
+
64
+ The bot uses the same configuration as the original script:
65
+ - Email: `foudmohammed914@gmail.com`
66
+ - Password: `009988Ppooii@@@@`
67
+
68
+ You can modify these in the `app.py` file.
69
+
70
+ ## Features
71
+
72
+ ### Real-time Progress Tracking
73
+ - Live progress bar showing completion percentage
74
+ - Success/failure counters
75
+ - Current comment display
76
+ - Activity log with timestamps
77
+
78
+ ### Modern UI
79
+ - Responsive design that works on all devices
80
+ - Dark theme with gradient backgrounds
81
+ - Smooth animations and transitions
82
+ - Intuitive user interface
83
+
84
+ ### WebSocket Communication
85
+ - Real-time updates without page refresh
86
+ - Instant status changes
87
+ - Live progress streaming
88
+
89
+ ## File Structure
90
+
91
+ ```
92
+ tik/
93
+ ├── app.py # Flask application
94
+ ├── tiktok.py # Modified TikTok bot (no tqdm)
95
+ ├── templates/
96
+ │ └── index.html # Web interface
97
+ ├── run_flask.py # Simple runner script
98
+ ├── requirements.txt # Dependencies
99
+ └── README_WEB.md # This file
100
+ ```
101
+
102
+ ## Troubleshooting
103
+
104
+ ### Common Issues
105
+
106
+ 1. **Port already in use:**
107
+ - Change the port in `app.py` or `run_flask.py`
108
+ - Kill any existing processes on port 5000
109
+
110
+ 2. **Chrome/ChromeDriver issues:**
111
+ - Make sure Chrome is installed
112
+ - The bot will automatically download ChromeDriver
113
+
114
+ 3. **Login issues:**
115
+ - Check your credentials in `app.py`
116
+ - The bot will prompt for manual login if needed
117
+
118
+ ### Logs
119
+
120
+ - Check the browser console for JavaScript errors
121
+ - View the Flask application logs in the terminal
122
+ - Check `tiktok_bot.log` for detailed bot activity
123
+
124
+ ## Security Notes
125
+
126
+ - The web interface is for local use only
127
+ - Don't expose this to the internet without proper security
128
+ - Consider adding authentication for production use
129
+
130
+ ## Development
131
+
132
+ To modify the interface:
133
+ - Edit `templates/index.html` for UI changes
134
+ - Modify `app.py` for backend logic
135
+ - Update `tiktok.py` for bot functionality
136
+
137
+ The interface uses:
138
+ - Flask for the web server
139
+ - Flask-SocketIO for real-time communication
140
+ - Tailwind CSS for styling
141
+ - Vanilla JavaScript for interactivity
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, jsonify
2
+ from flask_socketio import SocketIO, emit
3
+ import threading
4
+ import time
5
+ import os
6
+ import sys
7
+ from selenium import webdriver
8
+ from selenium.webdriver.chrome.options import Options
9
+ import logging
10
+
11
+ # Import the TikTokBot class from tiktok.py
12
+ from tiktok import TikTokBot
13
+
14
+ app = Flask(__name__)
15
+ app.config['SECRET_KEY'] = 'your-secret-key-here'
16
+ socketio = SocketIO(app, cors_allowed_origins="*")
17
+
18
+ # Global variable to track bot status
19
+ bot_status = {
20
+ 'is_running': False,
21
+ 'current_progress': 0,
22
+ 'total_comments': 0,
23
+ 'success_count': 0,
24
+ 'failed_count': 0,
25
+ 'current_comment': '',
26
+ 'status_message': 'Ready to start'
27
+ }
28
+
29
+ class ProgressCallback:
30
+ def __init__(self, socketio):
31
+ self.socketio = socketio
32
+ self.current_progress = 0
33
+ self.total_comments = 0
34
+ self.success_count = 0
35
+ self.failed_count = 0
36
+
37
+ def update_progress(self, current, total, success, failed, comment_text=""):
38
+ self.current_progress = current
39
+ self.total_comments = total
40
+ self.success_count = success
41
+ self.failed_count = failed
42
+
43
+ # Emit progress update to all connected clients
44
+ self.socketio.emit('progress_update', {
45
+ 'current': current,
46
+ 'total': total,
47
+ 'success': success,
48
+ 'failed': failed,
49
+ 'comment_text': comment_text,
50
+ 'percentage': int((current / total) * 100) if total > 0 else 0
51
+ })
52
+
53
+ @app.route('/')
54
+ def index():
55
+ return render_template('index.html')
56
+
57
+ @app.route('/start_bot', methods=['POST'])
58
+ def start_bot():
59
+ global bot_status
60
+
61
+ if bot_status['is_running']:
62
+ return jsonify({'error': 'Bot is already running'}), 400
63
+
64
+ data = request.get_json()
65
+ video_url = data.get('video_url', '')
66
+ comment_text = data.get('comment_text', '')
67
+ num_comments = int(data.get('num_comments', 500))
68
+
69
+ if not video_url or not comment_text:
70
+ return jsonify({'error': 'Video URL and comment text are required'}), 400
71
+
72
+ # Start bot in a separate thread
73
+ bot_thread = threading.Thread(
74
+ target=run_tiktok_bot,
75
+ args=(video_url, comment_text, num_comments)
76
+ )
77
+ bot_thread.daemon = True
78
+ bot_thread.start()
79
+
80
+ return jsonify({'message': 'Bot started successfully'})
81
+
82
+ @app.route('/stop_bot', methods=['POST'])
83
+ def stop_bot():
84
+ global bot_status
85
+ bot_status['is_running'] = False
86
+ bot_status['status_message'] = 'Bot stopped by user'
87
+ socketio.emit('bot_stopped', {'message': 'Bot stopped by user'})
88
+ return jsonify({'message': 'Bot stopped'})
89
+
90
+ @app.route('/status')
91
+ def get_status():
92
+ return jsonify(bot_status)
93
+
94
+ def run_tiktok_bot(video_url, comment_text, num_comments):
95
+ global bot_status
96
+
97
+ bot_status['is_running'] = True
98
+ bot_status['current_progress'] = 0
99
+ bot_status['total_comments'] = num_comments
100
+ bot_status['success_count'] = 0
101
+ bot_status['failed_count'] = 0
102
+ bot_status['status_message'] = 'Initializing bot...'
103
+
104
+ socketio.emit('bot_started', {
105
+ 'message': 'Bot started',
106
+ 'total_comments': num_comments
107
+ })
108
+
109
+ # Initialize progress callback
110
+ progress_callback = ProgressCallback(socketio)
111
+
112
+ try:
113
+ # Initialize WebDriver
114
+ bot_status['status_message'] = 'Setting up browser...'
115
+ socketio.emit('status_update', {'message': 'Setting up browser...'})
116
+
117
+ project_dir = os.path.dirname(os.path.abspath(__file__))
118
+ cache_dir = os.path.join(project_dir, "selenium_cache")
119
+
120
+ if not os.path.exists(cache_dir):
121
+ os.makedirs(cache_dir)
122
+
123
+ chrome_options = Options()
124
+ chrome_options.add_argument("--start-maximized")
125
+ chrome_options.add_argument("--headless")
126
+ chrome_options.add_argument("--disable-blink-features=AutomationControlled")
127
+ chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
128
+ chrome_options.add_experimental_option('useAutomationExtension', False)
129
+ chrome_options.add_argument(f"user-data-dir={cache_dir}")
130
+ chrome_options.add_argument("--log-level=3")
131
+ chrome_options.add_argument("--disable-logging")
132
+ chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
133
+
134
+ driver = webdriver.Chrome(options=chrome_options)
135
+
136
+ # Initialize TikTokBot with modified post_comment_on_video method
137
+ bot = TikTokBot(
138
+ driver=driver,
139
+ login_email="foudmohammed914@gmail.com",
140
+ login_password="009988Ppooii@@@@",
141
+ cookies_file='tiktok_cookies.json',
142
+ commented_file='commented_videos.json'
143
+ )
144
+
145
+ bot_status['status_message'] = 'Logging in...'
146
+ socketio.emit('status_update', {'message': 'Logging in...'})
147
+
148
+ # Login
149
+ bot.login()
150
+
151
+ bot_status['status_message'] = 'Starting to post comments...'
152
+ socketio.emit('status_update', {'message': 'Starting to post comments...'})
153
+
154
+ # Post comments with progress tracking
155
+ success = bot.post_comment_on_video_with_progress(
156
+ video_url,
157
+ comment_text,
158
+ num_comments,
159
+ progress_callback
160
+ )
161
+
162
+ if success:
163
+ bot_status['status_message'] = 'Comments posted successfully!'
164
+ socketio.emit('bot_completed', {
165
+ 'message': 'Comments posted successfully!',
166
+ 'success': True
167
+ })
168
+ else:
169
+ bot_status['status_message'] = 'Failed to post comments.'
170
+ socketio.emit('bot_completed', {
171
+ 'message': 'Failed to post comments.',
172
+ 'success': False
173
+ })
174
+
175
+ except Exception as e:
176
+ error_msg = f'Error: {str(e)}'
177
+ bot_status['status_message'] = error_msg
178
+ socketio.emit('bot_error', {'message': error_msg})
179
+ logging.error(f"Bot error: {e}")
180
+ finally:
181
+ bot_status['is_running'] = False
182
+ try:
183
+ driver.quit()
184
+ except:
185
+ pass
186
+
187
+ @socketio.on('connect')
188
+ def handle_connect():
189
+ emit('connected', {'message': 'Connected to server'})
190
+
191
+ @socketio.on('disconnect')
192
+ def handle_disconnect():
193
+ print('Client disconnected')
194
+
195
+ if __name__ == '__main__':
196
+ socketio.run(app, debug=True, host='0.0.0.0', port=5000)
requirements.txt CHANGED
@@ -7,3 +7,5 @@ uvicorn[standard]
7
  pydantic
8
  python-telegram-bot[callback-data]
9
  tqdm
 
 
 
7
  pydantic
8
  python-telegram-bot[callback-data]
9
  tqdm
10
+ flask
11
+ flask-socketio
run_flask.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple script to run the Flask TikTok Bot application
4
+ """
5
+
6
+ from app import app, socketio
7
+
8
+ if __name__ == '__main__':
9
+ print("Starting TikTok Bot Web Interface...")
10
+ print("Open your browser and go to: http://localhost:5000")
11
+ print("Press Ctrl+C to stop the server")
12
+
13
+ try:
14
+ socketio.run(app, debug=True, host='0.0.0.0', port=5000)
15
+ except KeyboardInterrupt:
16
+ print("\nShutting down server...")
17
+ except Exception as e:
18
+ print(f"Error starting server: {e}")
templates/index.html ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>TikTok Bot Controller</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.js"></script>
9
+ </head>
10
+ <body class="bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 min-h-screen">
11
+ <div class="container mx-auto px-4 py-8">
12
+ <div class="text-center mb-8">
13
+ <h1 class="text-4xl font-bold text-white mb-2">TikTok Bot Controller</h1>
14
+ <p class="text-gray-300">Automate your TikTok engagement with real-time progress tracking</p>
15
+ </div>
16
+
17
+ <div class="max-w-4xl mx-auto">
18
+ <!-- Configuration Form -->
19
+ <div class="bg-white/10 backdrop-blur-lg rounded-2xl p-6 mb-6 border border-white/20">
20
+ <h2 class="text-2xl font-semibold text-white mb-4">Bot Configuration</h2>
21
+
22
+ <form id="botForm" class="space-y-4">
23
+ <div>
24
+ <label for="videoUrl" class="block text-sm font-medium text-gray-300 mb-2">
25
+ TikTok Video URL
26
+ </label>
27
+ <input
28
+ type="url"
29
+ id="videoUrl"
30
+ name="videoUrl"
31
+ required
32
+ placeholder="https://www.tiktok.com/@username/video/..."
33
+ class="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
34
+ >
35
+ </div>
36
+
37
+ <div>
38
+ <label for="commentText" class="block text-sm font-medium text-gray-300 mb-2">
39
+ Comment Text
40
+ </label>
41
+ <textarea
42
+ id="commentText"
43
+ name="commentText"
44
+ required
45
+ rows="3"
46
+ placeholder="Enter your comment text here..."
47
+ class="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500 resize-none"
48
+ ></textarea>
49
+ </div>
50
+
51
+ <div>
52
+ <label for="numComments" class="block text-sm font-medium text-gray-300 mb-2">
53
+ Number of Comments
54
+ </label>
55
+ <input
56
+ type="number"
57
+ id="numComments"
58
+ name="numComments"
59
+ min="1"
60
+ max="1000"
61
+ value="500"
62
+ class="w-full px-4 py-3 bg-white/10 border border-white/20 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
63
+ >
64
+ </div>
65
+
66
+ <div class="flex gap-4">
67
+ <button
68
+ type="submit"
69
+ id="startBtn"
70
+ class="flex-1 bg-gradient-to-r from-green-500 to-emerald-600 hover:from-green-600 hover:to-emerald-700 text-white font-semibold py-3 px-6 rounded-lg transition-all duration-200"
71
+ >
72
+ Start Bot
73
+ </button>
74
+
75
+ <button
76
+ type="button"
77
+ id="stopBtn"
78
+ disabled
79
+ class="flex-1 bg-gradient-to-r from-red-500 to-pink-600 hover:from-red-600 hover:to-pink-700 text-white font-semibold py-3 px-6 rounded-lg transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
80
+ >
81
+ Stop Bot
82
+ </button>
83
+ </div>
84
+ </form>
85
+ </div>
86
+
87
+ <!-- Progress Section -->
88
+ <div id="progressSection" class="bg-white/10 backdrop-blur-lg rounded-2xl p-6 border border-white/20 hidden">
89
+ <h2 class="text-2xl font-semibold text-white mb-4">Progress</h2>
90
+
91
+ <!-- Status -->
92
+ <div class="mb-6">
93
+ <div class="flex items-center justify-between mb-2">
94
+ <span class="text-gray-300 font-medium">Status:</span>
95
+ <span id="statusText" class="text-white font-semibold">Ready</span>
96
+ </div>
97
+ <div id="statusIndicator" class="w-3 h-3 bg-gray-500 rounded-full"></div>
98
+ </div>
99
+
100
+ <!-- Progress Bar -->
101
+ <div class="mb-6">
102
+ <div class="flex justify-between items-center mb-2">
103
+ <span class="text-gray-300 font-medium">Progress</span>
104
+ <span id="progressText" class="text-white font-semibold">0 / 0 (0%)</span>
105
+ </div>
106
+ <div class="w-full bg-gray-700 rounded-full h-3 overflow-hidden">
107
+ <div id="progressBar" class="h-full bg-gradient-to-r from-blue-500 to-purple-600 rounded-full transition-all duration-300" style="width: 0%"></div>
108
+ </div>
109
+ </div>
110
+
111
+ <!-- Statistics -->
112
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
113
+ <div class="bg-white/5 rounded-lg p-4 text-center">
114
+ <div class="text-2xl font-bold text-green-400" id="successCount">0</div>
115
+ <div class="text-sm text-gray-300">Successful</div>
116
+ </div>
117
+ <div class="bg-white/5 rounded-lg p-4 text-center">
118
+ <div class="text-2xl font-bold text-red-400" id="failedCount">0</div>
119
+ <div class="text-sm text-gray-300">Failed</div>
120
+ </div>
121
+ <div class="bg-white/5 rounded-lg p-4 text-center">
122
+ <div class="text-2xl font-bold text-blue-400" id="currentComment">-</div>
123
+ <div class="text-sm text-gray-300">Current Comment</div>
124
+ </div>
125
+ </div>
126
+
127
+ <!-- Log -->
128
+ <div class="bg-black/20 rounded-lg p-4 max-h-40 overflow-y-auto">
129
+ <h3 class="text-white font-medium mb-2">Activity Log</h3>
130
+ <div id="logContainer" class="text-sm text-gray-300 space-y-1">
131
+ <div>System ready...</div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <script>
139
+ const socket = io();
140
+
141
+ const botForm = document.getElementById('botForm');
142
+ const startBtn = document.getElementById('startBtn');
143
+ const stopBtn = document.getElementById('stopBtn');
144
+ const progressSection = document.getElementById('progressSection');
145
+ const statusText = document.getElementById('statusText');
146
+ const statusIndicator = document.getElementById('statusIndicator');
147
+ const progressBar = document.getElementById('progressBar');
148
+ const progressText = document.getElementById('progressText');
149
+ const successCount = document.getElementById('successCount');
150
+ const failedCount = document.getElementById('failedCount');
151
+ const currentComment = document.getElementById('currentComment');
152
+ const logContainer = document.getElementById('logContainer');
153
+
154
+ let isRunning = false;
155
+
156
+ socket.on('connect', function(data) {
157
+ addLog('Connected to server', 'success');
158
+ });
159
+
160
+ socket.on('bot_started', function(data) {
161
+ isRunning = true;
162
+ updateUI();
163
+ addLog('Bot started successfully', 'success');
164
+ });
165
+
166
+ socket.on('status_update', function(data) {
167
+ statusText.textContent = data.message;
168
+ addLog(data.message, 'info');
169
+ });
170
+
171
+ socket.on('progress_update', function(data) {
172
+ const percentage = data.percentage;
173
+ progressBar.style.width = `${percentage}%`;
174
+ progressText.textContent = `${data.current} / ${data.total} (${percentage}%)`;
175
+ successCount.textContent = data.success;
176
+ failedCount.textContent = data.failed;
177
+
178
+ if (data.comment_text) {
179
+ currentComment.textContent = data.comment_text.length > 20
180
+ ? data.comment_text.substring(0, 20) + '...'
181
+ : data.comment_text;
182
+ }
183
+ });
184
+
185
+ socket.on('bot_completed', function(data) {
186
+ isRunning = false;
187
+ updateUI();
188
+ addLog(data.message, data.success ? 'success' : 'error');
189
+ });
190
+
191
+ socket.on('bot_error', function(data) {
192
+ isRunning = false;
193
+ updateUI();
194
+ addLog(data.message, 'error');
195
+ });
196
+
197
+ botForm.addEventListener('submit', async function(e) {
198
+ e.preventDefault();
199
+
200
+ const formData = new FormData(botForm);
201
+ const data = {
202
+ video_url: formData.get('videoUrl'),
203
+ comment_text: formData.get('commentText'),
204
+ num_comments: parseInt(formData.get('numComments'))
205
+ };
206
+
207
+ try {
208
+ const response = await fetch('/start_bot', {
209
+ method: 'POST',
210
+ headers: {
211
+ 'Content-Type': 'application/json',
212
+ },
213
+ body: JSON.stringify(data)
214
+ });
215
+
216
+ const result = await response.json();
217
+
218
+ if (response.ok) {
219
+ addLog('Bot request sent successfully', 'success');
220
+ } else {
221
+ addLog(`Error: ${result.error}`, 'error');
222
+ }
223
+ } catch (error) {
224
+ addLog(`Network error: ${error.message}`, 'error');
225
+ }
226
+ });
227
+
228
+ stopBtn.addEventListener('click', async function() {
229
+ try {
230
+ const response = await fetch('/stop_bot', {
231
+ method: 'POST',
232
+ headers: {
233
+ 'Content-Type': 'application/json',
234
+ }
235
+ });
236
+
237
+ const result = await response.json();
238
+ addLog(result.message, 'warning');
239
+ } catch (error) {
240
+ addLog(`Error stopping bot: ${error.message}`, 'error');
241
+ }
242
+ });
243
+
244
+ function updateUI() {
245
+ if (isRunning) {
246
+ startBtn.disabled = true;
247
+ stopBtn.disabled = false;
248
+ statusIndicator.className = 'w-3 h-3 bg-green-500 rounded-full';
249
+ progressSection.classList.remove('hidden');
250
+ } else {
251
+ startBtn.disabled = false;
252
+ stopBtn.disabled = true;
253
+ statusIndicator.className = 'w-3 h-3 bg-gray-500 rounded-full';
254
+ }
255
+ }
256
+
257
+ function addLog(message, type = 'info') {
258
+ const timestamp = new Date().toLocaleTimeString();
259
+ const logEntry = document.createElement('div');
260
+
261
+ let icon = '';
262
+ let color = 'text-gray-300';
263
+
264
+ switch(type) {
265
+ case 'success':
266
+ icon = '✅';
267
+ color = 'text-green-400';
268
+ break;
269
+ case 'error':
270
+ icon = '❌';
271
+ color = 'text-red-400';
272
+ break;
273
+ case 'warning':
274
+ icon = '⚠️';
275
+ color = 'text-yellow-400';
276
+ break;
277
+ case 'info':
278
+ icon = 'ℹ️';
279
+ color = 'text-blue-400';
280
+ break;
281
+ }
282
+
283
+ logEntry.innerHTML = `
284
+ <span class="text-xs text-gray-500">${timestamp}</span>
285
+ <span>${icon}</span>
286
+ <span class="${color}">${message}</span>
287
+ `;
288
+
289
+ logContainer.appendChild(logEntry);
290
+ logContainer.scrollTop = logContainer.scrollHeight;
291
+
292
+ while (logContainer.children.length > 50) {
293
+ logContainer.removeChild(logContainer.firstChild);
294
+ }
295
+ }
296
+
297
+ updateUI();
298
+ </script>
299
+ </body>
300
+ </html>
tiktok.py CHANGED
@@ -17,7 +17,7 @@ from selenium.common.exceptions import (
17
  TimeoutException,
18
  NoSuchElementException
19
  )
20
- from tqdm import tqdm
21
 
22
  # -------------------- Utility Functions -------------------- #
23
  def remove_emojis(text):
@@ -252,48 +252,39 @@ class TikTokBot:
252
  self.driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", comment_box)
253
  time.sleep(random.uniform(1, 2))
254
 
255
- # Use tqdm for progress bar
256
- with tqdm(total=total_attempts, desc="Posting comments", unit="comment") as pbar:
257
- for attempt in range(total_attempts):
258
- try:
259
- # Attempt to click the comment box
260
- if not self.click_element(comment_box):
261
- logging.error(f"Failed to click on comment box for {video_url} (attempt {attempt + 1}).")
262
- self.capture_screenshot(f"click_failed_attempt_{attempt + 1}")
263
- pbar.update(1)
264
- continue
265
-
266
- # Remove emojis from the comment text
267
- comment = remove_emojis(comment_text)
268
- logging.info(f"Posting comment {attempt + 1}/{total_attempts}: {comment}")
269
-
270
- # Clear the comment box first
271
- comment_box.clear()
272
- time.sleep(random.uniform(0.5, 1))
273
-
274
- # Enter comment character by character
275
- for char in comment:
276
- comment_box.send_keys(char)
277
- time.sleep(random.uniform(0.03, 0.07)) # Human-like typing speed
278
-
279
- # Submit the comment
280
- comment_box.send_keys(Keys.ENTER)
281
- success_count += 1
282
- logging.info(f"Successfully posted comment {attempt + 1}/{total_attempts} on {video_url}")
283
-
284
- # Update progress bar
285
- pbar.update(1)
286
- pbar.set_postfix({"Success": success_count, "Failed": attempt + 1 - success_count})
287
-
288
- # Random delay between comments to avoid detection
289
- time.sleep(random.uniform(3, 7))
290
-
291
- except Exception as e:
292
- logging.error(f"Failed to post comment {attempt + 1}/{total_attempts} on {video_url}: {e}")
293
- self.capture_screenshot(f"error_attempt_{attempt + 1}")
294
- pbar.update(1)
295
- pbar.set_postfix({"Success": success_count, "Failed": attempt + 1 - success_count})
296
- time.sleep(random.uniform(2, 4))
297
 
298
  # Add video to commented videos list if at least one comment was successful
299
  if success_count > 0:
@@ -320,6 +311,91 @@ class TikTokBot:
320
  """Posts multiple comments on the same video with progress tracking."""
321
  return self.post_comment_on_video(video_url, comment_text, num_comments)
322
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
  def load_commented_videos(self):
325
  """Loads the list of commented videos from a JSON file."""
 
17
  TimeoutException,
18
  NoSuchElementException
19
  )
20
+
21
 
22
  # -------------------- Utility Functions -------------------- #
23
  def remove_emojis(text):
 
252
  self.driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", comment_box)
253
  time.sleep(random.uniform(1, 2))
254
 
255
+ for attempt in range(total_attempts):
256
+ try:
257
+ # Attempt to click the comment box
258
+ if not self.click_element(comment_box):
259
+ logging.error(f"Failed to click on comment box for {video_url} (attempt {attempt + 1}).")
260
+ self.capture_screenshot(f"click_failed_attempt_{attempt + 1}")
261
+ continue
262
+
263
+ # Remove emojis from the comment text
264
+ comment = remove_emojis(comment_text)
265
+ logging.info(f"Posting comment {attempt + 1}/{total_attempts}: {comment}")
266
+
267
+ # Clear the comment box first
268
+ comment_box.clear()
269
+ time.sleep(random.uniform(0.5, 1))
270
+
271
+ # Enter comment character by character
272
+ for char in comment:
273
+ comment_box.send_keys(char)
274
+ time.sleep(random.uniform(0.03, 0.07)) # Human-like typing speed
275
+
276
+ # Submit the comment
277
+ comment_box.send_keys(Keys.ENTER)
278
+ success_count += 1
279
+ logging.info(f"Successfully posted comment {attempt + 1}/{total_attempts} on {video_url}")
280
+
281
+ # Random delay between comments to avoid detection
282
+ time.sleep(random.uniform(3, 7))
283
+
284
+ except Exception as e:
285
+ logging.error(f"Failed to post comment {attempt + 1}/{total_attempts} on {video_url}: {e}")
286
+ self.capture_screenshot(f"error_attempt_{attempt + 1}")
287
+ time.sleep(random.uniform(2, 4))
 
 
 
 
 
 
 
 
 
288
 
289
  # Add video to commented videos list if at least one comment was successful
290
  if success_count > 0:
 
311
  """Posts multiple comments on the same video with progress tracking."""
312
  return self.post_comment_on_video(video_url, comment_text, num_comments)
313
 
314
+ def post_comment_on_video_with_progress(self, video_url, comment_text, num_comments=1, progress_callback=None):
315
+ """Posts a comment on a specific video URL with the provided comment text and progress callback."""
316
+ success_count = 0
317
+ total_attempts = num_comments
318
+
319
+ try:
320
+ logging.info(f"Attempting to post {num_comments} comments on: {video_url}")
321
+ self.driver.get(video_url)
322
+ time.sleep(random.uniform(5, 7)) # Allow video page to load
323
+
324
+ self.close_popups()
325
+ self.save_cookies()
326
+
327
+ # Wait for comment box
328
+ comment_box = WebDriverWait(self.driver, 15).until(
329
+ EC.presence_of_element_located((By.XPATH, '//div[@contenteditable="true"]'))
330
+ )
331
+
332
+ # Scroll to the comment box to ensure it's in view
333
+ self.driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", comment_box)
334
+ time.sleep(random.uniform(1, 2))
335
+
336
+ for attempt in range(total_attempts):
337
+ try:
338
+ # Attempt to click the comment box
339
+ if not self.click_element(comment_box):
340
+ logging.error(f"Failed to click on comment box for {video_url} (attempt {attempt + 1}).")
341
+ self.capture_screenshot(f"click_failed_attempt_{attempt + 1}")
342
+ if progress_callback:
343
+ progress_callback.update_progress(attempt + 1, total_attempts, success_count, attempt + 1 - success_count)
344
+ continue
345
+
346
+ # Remove emojis from the comment text
347
+ comment = remove_emojis(comment_text)
348
+ logging.info(f"Posting comment {attempt + 1}/{total_attempts}: {comment}")
349
+
350
+ # Clear the comment box first
351
+ comment_box.clear()
352
+ time.sleep(random.uniform(0.5, 1))
353
+
354
+ # Enter comment character by character
355
+ for char in comment:
356
+ comment_box.send_keys(char)
357
+ time.sleep(random.uniform(0.03, 0.07)) # Human-like typing speed
358
+
359
+ # Submit the comment
360
+ comment_box.send_keys(Keys.ENTER)
361
+ success_count += 1
362
+ logging.info(f"Successfully posted comment {attempt + 1}/{total_attempts} on {video_url}")
363
+
364
+ # Update progress via callback
365
+ if progress_callback:
366
+ progress_callback.update_progress(attempt + 1, total_attempts, success_count, attempt + 1 - success_count, comment)
367
+
368
+ # Random delay between comments to avoid detection
369
+ time.sleep(random.uniform(3, 7))
370
+
371
+ except Exception as e:
372
+ logging.error(f"Failed to post comment {attempt + 1}/{total_attempts} on {video_url}: {e}")
373
+ self.capture_screenshot(f"error_attempt_{attempt + 1}")
374
+ if progress_callback:
375
+ progress_callback.update_progress(attempt + 1, total_attempts, success_count, attempt + 1 - success_count)
376
+ time.sleep(random.uniform(2, 4))
377
+
378
+ # Add video to commented videos list if at least one comment was successful
379
+ if success_count > 0:
380
+ self.commented_videos.add(video_url)
381
+ self.save_commented_videos()
382
+
383
+ logging.info(f"Completed posting comments. Success: {success_count}/{total_attempts}")
384
+ return success_count > 0
385
+
386
+ except TimeoutException:
387
+ logging.error(f"Timeout while trying to comment on {video_url}.")
388
+ self.capture_screenshot("timeout_error")
389
+ return False
390
+ except ElementClickInterceptedException:
391
+ logging.error(f"Element click intercepted while trying to comment on {video_url}.")
392
+ self.capture_screenshot("click_intercepted_error")
393
+ return False
394
+ except Exception as e:
395
+ logging.error(f"Failed to comment on {video_url}: {e}")
396
+ self.capture_screenshot("general_error")
397
+ return False
398
+
399
 
400
  def load_commented_videos(self):
401
  """Loads the list of commented videos from a JSON file."""