wuhp commited on
Commit
b72bf58
·
verified ·
1 Parent(s): 1c6176b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +183 -127
app.py CHANGED
@@ -1,140 +1,196 @@
1
  import gradio as gr
2
- import pandas as pd
3
  import requests
4
- import json
5
- import random
6
  import os
7
 
8
- # =========================
9
- # Random User-Agent Generator
10
- # =========================
11
 
12
- def generate_random_user_agent(device_type=None, browser_type=None, chrome_versions=[125, 138], firefox_versions=[120, 135]):
13
- if not device_type:
14
- device_type = random.choice(['android', 'ios', 'windows', 'ubuntu'])
15
-
16
- if not browser_type:
17
- browser_type = random.choice(['chrome', 'firefox'])
18
-
19
- if browser_type == 'chrome':
20
- major = random.randint(chrome_versions[0], chrome_versions[1])
21
- browser_version = f"{major}.{random.randint(0,9)}.{random.randint(1000,9999)}.{random.randint(0,99)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  else:
23
- browser_version = random.randint(firefox_versions[0], firefox_versions[1])
24
-
25
- if device_type == 'android':
26
- android_version = random.choice(['10.0','11.0','12.0','13.0','14.0','15.0'])
27
- device = random.choice(['Pixel 6','Pixel 7 Pro','SM-G998B','OnePlus 10 Pro'])
28
- if browser_type == 'chrome':
29
- return f"Mozilla/5.0 (Linux; Android {android_version}; {device}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{browser_version} Mobile Safari/537.36"
30
- return f"Mozilla/5.0 (Android {android_version}; Mobile; rv:{browser_version}.0) Gecko/{browser_version}.0 Firefox/{browser_version}.0"
31
-
32
- if device_type == 'ios':
33
- ios_version = random.choice(['14_0','15_0','16_0'])
34
- if browser_type == 'chrome':
35
- return f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version} like Mac OS X) AppleWebKit/537.36 (KHTML, like Gecko) CriOS/{browser_version} Mobile Safari/604.1"
36
- return f"Mozilla/5.0 (iPhone; CPU iPhone OS {ios_version} like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/{browser_version}.0 Mobile Safari/605.1.15"
37
-
38
- if device_type == 'windows':
39
- return f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{browser_version} Safari/537.36"
40
-
41
- return f"Mozilla/5.0 (X11; Ubuntu; Linux x86_64) Gecko Firefox/{browser_version}.0"
42
-
43
-
44
- # =========================
45
- # Load CSV
46
- # =========================
47
-
48
- CSV_PATH = "endpoints.csv"
49
- df = pd.read_csv(CSV_PATH)
50
-
51
- # =========================
52
- # Helpers
53
- # =========================
54
-
55
- def get_type1():
56
- return sorted(df["TYPE1"].unique())
57
-
58
- def get_type2(t1):
59
- return sorted(df[df["TYPE1"] == t1]["TYPE2"].unique())
60
-
61
- def get_rows(t1, t2):
62
- return df[(df["TYPE1"] == t1) & (df["TYPE2"] == t2)]
63
-
64
- def build_preview(rows, object_id):
65
- if rows.empty:
66
- return "No matching endpoints."
67
-
68
- preview = []
69
- for i, r in rows.iterrows():
70
- preview.append(r.URL.replace("{OBJECT_ID}", object_id))
71
- return "\n\n".join(preview)
72
 
73
- def send_single(row, object_id):
74
- url = row.URL.replace("{OBJECT_ID}", object_id)
 
75
 
 
76
  headers = {
77
- "User-Agent": generate_random_user_agent(),
78
- "Accept": "application/json",
79
  "Referer": "https://www.tiktok.com/"
80
  }
81
 
82
- try:
83
- r = requests.get(url, headers=headers, timeout=15)
84
- try:
85
- return json.dumps(r.json(), indent=2)
86
- except:
87
- return r.text
88
- except Exception as e:
89
- return str(e)
90
-
91
- # =========================
92
- # UI
93
- # =========================
94
-
95
- with gr.Blocks(title="TikTok Report Endpoint Explorer") as app:
96
- gr.Markdown("""
97
- ## TikTok Report Endpoint Explorer
98
- **Research / educational tool**
99
-
100
- **Steps**
101
- 1. Choose target type
102
- 2. Choose report category
103
- 3. Enter object ID
104
- 4. Preview endpoints
105
- 5. Send one request
106
- """)
107
-
108
- rows_state = gr.State()
109
-
110
- type1 = gr.Dropdown(label="Target Type", choices=get_type1())
111
- type2 = gr.Dropdown(label="Report Category")
112
- object_id = gr.Textbox(label="Object ID (video / user / comment)")
113
 
114
- preview = gr.Textbox(label="Preview URLs", lines=8)
115
- selection = gr.Dropdown(label="Select Endpoint")
116
-
117
- output = gr.Textbox(label="Response", lines=10)
118
-
119
- type1.change(lambda t: gr.Dropdown.update(choices=get_type2(t)), type1, type2)
120
-
121
- def on_type2(t1, t2, oid):
122
- rows = get_rows(t1, t2)
123
- options = ["ALL (preview only)"] + [str(i) for i in rows.index]
124
- return (
125
- gr.Dropdown.update(choices=options),
126
- rows,
127
- build_preview(rows, oid)
128
- )
129
-
130
- type2.change(on_type2, [type1, type2, object_id], [selection, rows_state, preview])
131
- object_id.change(on_type2, [type1, type2, object_id], [selection, rows_state, preview])
132
-
133
- def send(sel, rows, oid):
134
- if sel == "ALL (preview only)":
135
- return "Preview only. Select a single endpoint."
136
- return send_single(rows.loc[int(sel)], oid)
137
-
138
- gr.Button("Send Single Request").click(send, [selection, rows_state, object_id], output)
139
-
140
- app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import csv
3
  import requests
4
+ import time
 
5
  import os
6
 
7
+ # File path configuration
8
+ CSV_FILE = 'endpoints.csv'
 
9
 
10
+ def load_data():
11
+ """Loads the endpoints from the CSV file."""
12
+ if not os.path.exists(CSV_FILE):
13
+ print(f"Error: {CSV_FILE} not found.")
14
+ return []
15
+
16
+ data = []
17
+ try:
18
+ with open(CSV_FILE, 'r', encoding='utf-8') as f:
19
+ # Assumes headers: TYPE1, TYPE2, URL
20
+ reader = csv.DictReader(f)
21
+ for row in reader:
22
+ # specific cleanup to remove potential whitespace from CSV keys/values
23
+ cleaned_row = {k.strip(): v.strip() for k, v in row.items() if k}
24
+ data.append(cleaned_row)
25
+ return data
26
+ except Exception as e:
27
+ print(f"Error reading CSV: {e}")
28
+ return []
29
+
30
+ def get_type1_choices():
31
+ """Extracts unique TYPE1 categories (e.g., profile, video, comments)."""
32
+ data = load_data()
33
+ if not data:
34
+ return []
35
+ # Set comprehension to get unique values
36
+ return list(set([item.get('TYPE1') for item in data if item.get('TYPE1')]))
37
+
38
+ def get_type2_choices(selected_type1):
39
+ """
40
+ Extracts TYPE2 options based on selected TYPE1.
41
+ Adds an 'ALL ...' option at the beginning.
42
+ """
43
+ data = load_data()
44
+ if not data or not selected_type1:
45
+ return []
46
+
47
+ # Filter data for the selected category
48
+ choices = [item.get('TYPE2') for item in data if item.get('TYPE1') == selected_type1]
49
+ unique_choices = list(set(choices))
50
+ unique_choices.sort()
51
+
52
+ # Add the "ALL" option at the top
53
+ all_option = f"ALL {selected_type1.upper()} REPORTS"
54
+ return [all_option] + unique_choices
55
+
56
+ def execute_reports(type1, type2):
57
+ """
58
+ Main logic to send requests to TikTok endpoints.
59
+ Generator function to stream logs to the UI.
60
+ """
61
+ data = load_data()
62
+ if not data:
63
+ yield "❌ Error: endpoints.csv not found or empty."
64
+ return
65
+
66
+ targets = []
67
+
68
+ # Construct the "ALL" string to check against
69
+ all_check_str = f"ALL {type1.upper()} REPORTS"
70
+
71
+ # Check if the user selected the "ALL" option
72
+ if type2 == all_check_str:
73
+ # Filter all items matching TYPE1
74
+ targets = [item for item in data if item.get('TYPE1') == type1]
75
+ yield f"🚀 Preparing to send ALL {len(targets)} reports for category: {type1}...\n"
76
  else:
77
+ # Filter specific item matching TYPE1 and TYPE2
78
+ targets = [item for item in data if item.get('TYPE1') == type1 and item.get('TYPE2') == type2]
79
+ yield f"🚀 Preparing to send specific report: {type2}...\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
+ if not targets:
82
+ yield "❌ No matching endpoints found."
83
+ return
84
 
85
+ # Headers to mimic a real browser (helper to avoid immediate rejection)
86
  headers = {
87
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
 
88
  "Referer": "https://www.tiktok.com/"
89
  }
90
 
91
+ log_history = ""
92
+
93
+ for i, target in enumerate(targets):
94
+ url = target.get('URL')
95
+ report_name = target.get('TYPE2')
96
+
97
+ if not url:
98
+ continue
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ try:
101
+ # Send the request
102
+ # Note: TikTok endpoints usually ignore GET for actions and require POST,
103
+ # but based on your URL structure (params in URL), a GET might trigger the hit
104
+ # or the URL is intended to be loaded.
105
+ # If these are API endpoints, requests.get is standard.
106
+ response = requests.get(url, headers=headers)
107
+
108
+ timestamp = time.strftime("%H:%M:%S")
109
+
110
+ if response.status_code == 200:
111
+ log_entry = f"[{timestamp}] ✅ SENT ({i+1}/{len(targets)}): {report_name}\n"
112
+ else:
113
+ log_entry = f"[{timestamp}] ⚠️ STATUS {response.status_code} ({i+1}/{len(targets)}): {report_name}\n"
114
+
115
+ log_history += log_entry
116
+ yield log_history
117
+
118
+ # Small delay to be polite to the server
119
+ time.sleep(0.5)
120
+
121
+ except Exception as e:
122
+ log_history += f"[{time.strftime('%H:%M:%S')}] ❌ ERROR: {report_name} - {str(e)}\n"
123
+ yield log_history
124
+
125
+ yield log_history + "\n✅ DONE. All tasks completed."
126
+
127
+ # -------------------------------------------------------------------------
128
+ # UI SETUP
129
+ # -------------------------------------------------------------------------
130
+
131
+ with gr.Blocks(theme=gr.themes.Soft()) as app:
132
+ gr.Markdown("# 🤖 TikTok Report Bot Controller")
133
+ gr.Markdown("Select a category and report type. Data is loaded from `endpoints.csv`.")
134
+
135
+ with gr.Row():
136
+ with gr.Column(scale=1):
137
+ # Dropdown 1: Category (profile, video, comments)
138
+ t1_input = gr.Dropdown(
139
+ choices=get_type1_choices(),
140
+ label="Category (Type 1)",
141
+ info="Select the target category",
142
+ value=None,
143
+ interactive=True
144
+ )
145
+
146
+ # Dropdown 2: Specific Report or "ALL"
147
+ # We initialize it empty; it gets populated when t1_input changes
148
+ t2_input = gr.Dropdown(
149
+ choices=[],
150
+ label="Report Type (Type 2)",
151
+ info="Select a specific violation or 'ALL'",
152
+ interactive=True
153
+ )
154
+
155
+ send_btn = gr.Button("🚀 Send Report(s)", variant="primary")
156
+
157
+ refresh_btn = gr.Button("🔄 Reload CSV Data", variant="secondary")
158
+
159
+ with gr.Column(scale=2):
160
+ # Output Logs
161
+ logs_output = gr.Textbox(
162
+ label="Execution Logs",
163
+ placeholder="Waiting for command...",
164
+ lines=20,
165
+ max_lines=20,
166
+ autoscroll=True
167
+ )
168
+
169
+ # ---------------------------------------------------------------------
170
+ # EVENT HANDLERS
171
+ # ---------------------------------------------------------------------
172
+
173
+ # 1. When Category changes, update Report Type choices
174
+ # This fixes the AttributeError by returning a new gr.Dropdown object
175
+ def update_second_dropdown(choice):
176
+ new_choices = get_type2_choices(choice)
177
+ return gr.Dropdown(choices=new_choices, value=new_choices[0] if new_choices else None)
178
+
179
+ t1_input.change(fn=update_second_dropdown, inputs=t1_input, outputs=t2_input)
180
+
181
+ # 2. Send Button Logic
182
+ send_btn.click(
183
+ fn=execute_reports,
184
+ inputs=[t1_input, t2_input],
185
+ outputs=logs_output
186
+ )
187
+
188
+ # 3. Refresh Button Logic
189
+ def refresh_ui():
190
+ new_t1 = get_type1_choices()
191
+ return gr.Dropdown(choices=new_t1), gr.Dropdown(choices=[], value=None)
192
+
193
+ refresh_btn.click(fn=refresh_ui, outputs=[t1_input, t2_input])
194
+
195
+ if __name__ == "__main__":
196
+ app.launch()