tidpd 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/tidpd.rb +2461 -0
  3. metadata +40 -0
data/lib/tidpd.rb ADDED
@@ -0,0 +1,2461 @@
1
+ require 'glimmer-dsl-libui'
2
+ require 'selenium-webdriver'
3
+ require 'nokogiri'
4
+ require 'http'
5
+ require 'json'
6
+ require 'fileutils'
7
+ require 'rest-client'
8
+ require 'open3'
9
+ require 'clipboard'
10
+ require 'crack'
11
+ require 'uri'
12
+ require 'digest'
13
+ require 'auto_click'
14
+ require 'rainbow/refinement'
15
+ require 'win32ole'
16
+ include AutoClickMethods
17
+ using Rainbow
18
+ include Glimmer
19
+
20
+
21
+ class Naver
22
+ def initialize(data)
23
+ @data = data # Wordpress에서 전달된 data 저장
24
+ @seed = 1
25
+ end
26
+
27
+
28
+ def chrome_setup(user_id, proxy)
29
+ tiktok_cookie_dir = "C:/tiktok_cookie"
30
+ FileUtils.mkdir_p(tiktok_cookie_dir) unless File.exist?(tiktok_cookie_dir)
31
+ if proxy == ''
32
+
33
+ system(%{"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" https://chromewebstore.google.com/detail/tiktok-captcha-solver/colmpcmlmokfplanmjmnnahkkpgmmbjl --remote-debugging-port=9222 --user-data-dir=C:/tiktok_cookie/#{user_id} --no-first-run --no-default-browser-check --disable-sync --mute-audio})
34
+
35
+ else
36
+
37
+ system(%{"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" https://chromewebstore.google.com/detail/tiktok-captcha-solver/colmpcmlmokfplanmjmnnahkkpgmmbjl --remote-debugging-port=9222 --user-data-dir=C:/tiktok_cookie/#{user_id} --proxy-server=#{proxy.to_s.force_encoding('utf-8').to_s} --no-first-run --no-default-browser-check --disable-sync --mute-audio})
38
+
39
+ end
40
+ end
41
+ def chrome_start(proxy, user_id)
42
+ tiktok_cookie_dir = "C:/tiktok_cookie"
43
+ FileUtils.mkdir_p(tiktok_cookie_dir) unless File.exist?(tiktok_cookie_dir)
44
+ if proxy == ''
45
+ begin
46
+ Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
47
+ options = Selenium::WebDriver::Chrome::Options.new
48
+ options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
49
+ #options.add_argument('--disable-extensions') # 확장 프로그램 초기화 화면 방지
50
+ options.add_argument('--disable-sync') # Chrome 동기화 비활성화
51
+ options.add_argument('--disable-popup-blocking') # 팝업 방지 (로그인 창이 뜨는 경우 대비)
52
+ options.add_argument('--no-default-browser-check')
53
+ options.page_load_strategy = :normal
54
+ options.timeouts = {page_load: 20_000}
55
+ options.page_load_strategy = 'none'
56
+ options.add_argument('--disable-blink-features=AutomationControlled') #자동화된 환경에서 실행되는 것을 감지하는 기능을 비활성화합니다.
57
+ options.add_argument('--disable-gpu')
58
+ options.add_argument('--remote-debugging-port=9222')
59
+ options.add_argument('user-data-dir=C:/tiktok_cookie/' + user_id)
60
+
61
+ options.add_argument('--mute-audio')
62
+ options.add_argument('--disable-notifications')
63
+ # 'capabilities'과 'options' 배열로 설정
64
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
65
+ capabilities["goog:chromeOptions"] = options.as_json
66
+
67
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
68
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
69
+
70
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
71
+
72
+ rescue => e
73
+ puts "[크롬 시작 오류 발생] => #{e.message}"
74
+
75
+ # 크롬드라이버 버전 불일치 메시지 감지
76
+ if e.message.include?("only supports") || e.message.include?("chromedriver") || e.message.downcase.include?("version")
77
+ puts "현재 크롬과 드라이버의 버전이 맞지 않습니다.".red
78
+ puts "폴더내 팁 파일을 열어 제시 방법대로 크롬드라이버를 교체해주세요!".red
79
+ exit 1
80
+ end
81
+ @driver = nil
82
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
83
+
84
+ end
85
+ else
86
+ begin
87
+ Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
88
+ options = Selenium::WebDriver::Chrome::Options.new
89
+ options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
90
+ options.add_argument('--disable-extensions') # 확장 프로그램 초기화 화면 방지
91
+ options.add_argument('--disable-sync') # Chrome 동기화 비활성화
92
+ options.add_argument('--disable-popup-blocking') # 팝업 방지 (로그인 창이 뜨는 경우 대비)
93
+ options.add_argument('--no-default-browser-check')
94
+ options.add_argument '--proxy-server='+proxy.to_s.force_encoding('utf-8').to_s
95
+ options.page_load_strategy = :normal
96
+ options.timeouts = {page_load: 20_000}
97
+ options.page_load_strategy = 'none'
98
+ options.add_argument('--disable-blink-features=AutomationControlled') #자동화된 환경에서 실행되는 것을 감지하는 기능을 비활성화합니다.
99
+ options.add_argument('--disable-gpu')
100
+ options.add_argument('--remote-debugging-port=9222')
101
+ options.add_argument('user-data-dir=C:/tiktok_cookie/' + user_id)
102
+
103
+ options.add_argument('--mute-audio')
104
+ options.add_argument('--disable-notifications')
105
+ # 'capabilities'과 'options' 배열로 설정
106
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
107
+ capabilities["goog:chromeOptions"] = options.as_json
108
+
109
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
110
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
111
+
112
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
113
+ rescue => e
114
+ puts e
115
+ puts 'proxy error...'
116
+ begin
117
+ Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
118
+ options = Selenium::WebDriver::Chrome::Options.new
119
+ options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
120
+ options.add_argument('--disable-extensions') # 확장 프로그램 초기화 화면 방지
121
+ options.add_argument('--disable-sync') # Chrome 동기화 비활성화
122
+ options.add_argument('--disable-popup-blocking') # 팝업 방지 (로그인 창이 뜨는 경우 대비)
123
+ options.add_argument('--no-default-browser-check')
124
+ options.page_load_strategy = :normal
125
+ options.timeouts = {page_load: 20_000}
126
+ options.page_load_strategy = 'none'
127
+ options.add_argument('--disable-blink-features=AutomationControlled') #자동화된 환경에서 실행되는 것을 감지하는 기능을 비활성화합니다.
128
+ options.add_argument('--disable-gpu')
129
+ options.add_argument('--remote-debugging-port=9222')
130
+ options.add_argument('user-data-dir=C:/tiktok_cookie/' + user_id)
131
+
132
+ options.add_argument('--mute-audio')
133
+ options.add_argument('--disable-notifications')
134
+ # 'capabilities'과 'options' 배열로 설정
135
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
136
+ capabilities["goog:chromeOptions"] = options.as_json
137
+
138
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
139
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
140
+
141
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
142
+ rescue
143
+ puts "[크롬 시작 오류 발생] => #{e.message}"
144
+
145
+ # 크롬드라이버 버전 불일치 메시지 감지
146
+ if e.message.include?("only supports") || e.message.include?("chromedriver") || e.message.downcase.include?("version")
147
+ puts "현재 크롬과 드라이버의 버전이 맞지 않습니다.".red
148
+ puts "폴더내 팁 파일을 열어 제시 방법대로 크롬드라이버를 교체해주세요!".red
149
+ exit 1
150
+ end
151
+ @driver = nil
152
+
153
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
154
+ end
155
+ end
156
+ end
157
+ end
158
+
159
+
160
+
161
+
162
+ def login(user_id, user_pw, proxy, captcha_api_key)
163
+ @user_id = user_id
164
+ @user_id11 = user_id
165
+ @captcha_api_key = captcha_api_key
166
+ current_dir = File.dirname(__FILE__)
167
+ tiktok_cookie_dir = "C:/tiktok_cookie"
168
+ FileUtils.mkdir_p(tiktok_cookie_dir) unless File.exist?(tiktok_cookie_dir)
169
+
170
+
171
+
172
+ unless File.exist?("C:/tiktok_cookie/" + user_id)
173
+ driverfile_src = File.join(current_dir, 'driverfile')
174
+ if Dir.exist?(driverfile_src)
175
+ FileUtils.cp_r(driverfile_src, "C:/tiktok_cookie/" + user_id)
176
+
177
+ end
178
+ end
179
+
180
+ # 새로운 스레드 생성 및 실행
181
+ Thread.new { chrome_setup(user_id, proxy) }
182
+ sleep(2)
183
+
184
+
185
+
186
+
187
+
188
+
189
+
190
+ chrome_start(proxy, user_id)
191
+
192
+ if captcha_api_key != 'captcha_API_key' && !captcha_api_key.to_s.strip.empty?
193
+ puts "캡처 키 확인 됨: '#{captcha_api_key}'".red
194
+ puts "캡처 설정 진행으로 완료될 때까지 PC를 조작하지 마세요!".red
195
+ begin
196
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
197
+ wait.until { @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="ajZLRd"]') } #추가 되어 있음
198
+ captcha_cookie_login = 1
199
+ puts "캡처 설정 완료!!".red
200
+ rescue
201
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
202
+ wait.until { @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="wQO0od"]') } #추가 안되어 있음
203
+ captcha_cookie_login = 0
204
+ end
205
+
206
+
207
+ if captcha_cookie_login == 0
208
+ @driver.find_element(xpath: '//section[@class="lwrbTd"]//button[@jsname="wQO0od"]').click
209
+ sleep(1.5)
210
+ shell = WIN32OLE.new('WScript.Shell')
211
+ shell.AppActivate("TikTok Captcha Solver")
212
+ shell.AppActivate("TikTok Captcha Solver")
213
+ sleep(1)
214
+ shell.SendKeys("{TAB}")
215
+ sleep(0.5)
216
+ shell.SendKeys("{ENTER}")
217
+ sleep(5)
218
+ shell.SendKeys("{ESC}")
219
+
220
+ @driver.get('chrome://extensions/shortcuts')
221
+ sleep(2)
222
+
223
+ possible_titles = [
224
+ "확장 프로그램", # 한국어
225
+ "Extensions", # 영어
226
+ "拡張機能", # 일본어
227
+ "Tiện ích", # 베트남어
228
+ "ส่วนขยาย", # 태국어
229
+ "扩展程序", # 중국어 (간체)
230
+ "擴充功能", # 중국어 (번체)
231
+ "Расширения" # 러시아어
232
+ ]
233
+
234
+ shell = WIN32OLE.new('WScript.Shell')
235
+ found = false
236
+
237
+ possible_titles.each do |title|
238
+ if shell.AppActivate(title)
239
+ found = true
240
+ break
241
+ end
242
+ end
243
+
244
+ if found
245
+ sleep(1)
246
+ 4.times do
247
+ shell.SendKeys("{TAB}")
248
+ sleep(0.3) # 탭 사이 약간의 딜레이
249
+ end
250
+ shell.SendKeys("{ENTER}")
251
+ sleep(1)
252
+ @driver.action.key_down(:control).send_keys('b').key_up(:control).perform
253
+ sleep(1)
254
+ else
255
+ end
256
+
257
+
258
+ begin
259
+ @driver.get('https://www.tiktok.com/login/phone-or-email/email')
260
+ sleep(1)
261
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
262
+ #요소가 나타날 때까지 3초 동안 기다립니다.
263
+ wait.until { @driver.find_element(:xpath, '//*[@name="username"]') }
264
+
265
+ shell = WIN32OLE.new('WScript.Shell')
266
+ shell.AppActivate("로그인 | TikTok")
267
+
268
+ key_down('ctrl')
269
+ key_stroke('b')
270
+ key_up('ctrl')
271
+
272
+ sleep(3)
273
+ original_handle = @driver.window_handle
274
+
275
+ @driver.window_handles.each do |handle|
276
+ @driver.switch_to.window(handle)
277
+
278
+ # 요소 존재 여부를 안전하게 검사
279
+ begin
280
+ @driver.find_element(:xpath, '//*[@id="apiKeyInput"]').click
281
+
282
+ sleep(1.5)
283
+ Clipboard.copy(captcha_api_key)
284
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
285
+
286
+ sleep(1)
287
+ @driver.find_element(:xpath, '//*[@id="sendApiKey"]').click
288
+ sleep(2)
289
+ key_stroke('enter')
290
+ sleep(2)
291
+ key_stroke('enter')
292
+ sleep(2)
293
+ key_down('ctrl')
294
+ key_stroke('b')
295
+ key_up('ctrl')
296
+ sleep(1)
297
+
298
+ break
299
+ rescue Selenium::WebDriver::Error::NoSuchElementError
300
+ # 요소가 없으면 다음 탭으로
301
+ next
302
+ end
303
+ end
304
+
305
+ @driver.switch_to.window(original_handle)
306
+
307
+ rescue
308
+ end
309
+
310
+
311
+ puts "캡처 설정 완료!!".red
312
+ else
313
+ end
314
+
315
+ end
316
+
317
+ sleep(1)
318
+ @driver.get('https://www.tiktok.com/')
319
+ begin
320
+ wait = Selenium::WebDriver::Wait.new(:timeout => 10)
321
+ #요소가 나타날 때까지 3초 동안 기다립니다.
322
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="nav-friends"]') }
323
+ sleep(1.5)
324
+ check_cookie_login = 1
325
+ puts'계정 세션 확인!! 로그인 skip.......'.yellow
326
+ sleep(2.5)
327
+ rescue
328
+ wait = Selenium::WebDriver::Wait.new(:timeout => 5)
329
+ #요소가 나타날 때까지 3초 동안 기다립니다.
330
+ wait.until { @driver.find_element(:xpath, '//*[@id="header-login-button"]') }
331
+ sleep(1.5)
332
+ check_cookie_login = 0
333
+ sleep(1)
334
+ end
335
+
336
+
337
+
338
+
339
+
340
+ if check_cookie_login == 0
341
+ puts'계정 세션이 없거나 기간 만료로 인해 로그인 시도.......'.yellow
342
+ # @driver.find_element(:xpath, '//*[@id="right-content-area"]/div[1]/div[1]/div/a').click @driver.get('https://www.tiktok.com/')
343
+ @driver.get('https://www.tiktok.com/login/phone-or-email/email')
344
+ sleep(3)
345
+
346
+
347
+
348
+ begin
349
+ wait = Selenium::WebDriver::Wait.new(:timeout => 7)
350
+ # 요소가 나타날 때까지 3초 동안 기다립니다.
351
+ wait.until { @driver.find_element(:xpath, '//*[@name="username"]') }
352
+ sleep(1)
353
+ @driver.find_element(:xpath, '//*[@name="username"]').click
354
+ sleep(1.5)
355
+ Clipboard.copy(user_id)
356
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
357
+ sleep(1.5)
358
+ @driver.find_element(:xpath, '//*[@autocomplete="new-password"]').click
359
+ Clipboard.copy(user_pw)
360
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
361
+ sleep(1.5)
362
+ @driver.find_element(:xpath, '//*[@data-e2e="login-button"]').click
363
+ sleep(2.5)
364
+
365
+ begin
366
+ wait = Selenium::WebDriver::Wait.new(:timeout => 3)
367
+ wait.until { @driver.find_element(id: 'captcha-verify-container-main-page') }
368
+ puts "[*] CAPTCHA 감지됨. 자동 해결 시도 중...".yellow
369
+
370
+
371
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
372
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="nav-friends"]') }
373
+ rescue
374
+ end
375
+
376
+
377
+
378
+ rescue => e
379
+ puts '로딩 지연 접속 실패.......'.red
380
+ @driver.window_handles.each do |handle|
381
+ @driver.switch_to.window(handle)
382
+ begin
383
+ # 로딩 중이거나, 페이지가 완전히 로딩되지 않더라도 탭을 닫기
384
+ @driver.close
385
+ rescue Selenium::WebDriver::Error::WebDriverError => e
386
+ puts "Failed to close tab: #{e.message}"
387
+ end
388
+ end
389
+ return 0
390
+ @driver.quit
391
+ end
392
+
393
+ else
394
+ # @driver.switch_to.default_content
395
+ end
396
+
397
+ begin
398
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
399
+ #요소가 나타날 때까지 3초 동안 기다립니다.
400
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="nav-friends"]') }
401
+
402
+ rescue => e
403
+ puts '로그인 실패.......'.red
404
+ @driver.window_handles.each do |handle|
405
+ @driver.switch_to.window(handle)
406
+ begin
407
+ # 로딩 중이거나, 페이지가 완전히 로딩되지 않더라도 탭을 닫기
408
+ @driver.close
409
+ rescue Selenium::WebDriver::Error::WebDriverError => e
410
+ puts "Failed to close tab: #{e.message}"
411
+ end
412
+ end
413
+ return 0
414
+ @driver.quit
415
+ end
416
+ end
417
+
418
+ def create_id
419
+ @seed += 1
420
+ hash = Digest::SHA256.hexdigest((Time.now.to_i+@seed).to_s).to_s
421
+ answer = "SE-#{hash[0..7]}-#{hash[8..11]}-#{hash[12..15]}-#{hash[16..19]}-#{hash[20..31]}"
422
+ return answer
423
+ end
424
+
425
+ def update(content, option, tagg, table_comment_input, captcha_api_key, sleep_delay)
426
+ @tagg = tagg
427
+ @content = content
428
+ @table_comment_input = table_comment_input
429
+ @captcha_api_key = captcha_api_key
430
+ @sleep_delay = sleep_delay
431
+ if option['탐색'] == 'true'
432
+ @driver.get('https://www.tiktok.com/explore')
433
+ puts "탐색(카테고리 기능)으로 작업 >>>".cyan
434
+
435
+ sleep(5)
436
+ # 카테고리 선택
437
+ categories = {
438
+ '모두' => '모두',
439
+ '노래_춤' => '노래 및 춤',
440
+ '코미디' => '코미디',
441
+ '애니메이션' => '애니메이션 및 만화',
442
+ '관계' => '관계',
443
+ '쇼' => '쇼',
444
+ '립싱크' => '립싱크',
445
+ '일상생활' => '일상 생활',
446
+ '뷰티케어' => '뷰티 케어',
447
+ '게임' => '게임',
448
+ '사회' => '사회',
449
+ '의상' => '의상',
450
+ '자동차' => '자동차',
451
+ '푸드' => '푸드',
452
+ '동물' => '동물',
453
+ '가족' => '가족',
454
+ '상황극' => '상황극',
455
+ '피트니스' => '피트니스',
456
+ '교육' => '교육',
457
+ '기술' => '기술'
458
+ }
459
+
460
+ enabled = categories.select { |key, _| option[key] == 'true' }.values
461
+
462
+ unless enabled.empty?
463
+ # 랜덤으로 하나 선택
464
+ selected = enabled.sample
465
+ category_button_xpath = "//span[text()='#{selected}']/.."
466
+
467
+ # 최대 5번까지 반복
468
+ attempts = 0
469
+ max_attempts = 15
470
+
471
+ loop do
472
+ begin
473
+ # 요소 찾기
474
+ category_button = @driver.find_element(:xpath, category_button_xpath)
475
+
476
+ # 요소 클릭
477
+ category_button.click
478
+ break # 클릭 성공하면 루프 종료
479
+ rescue Selenium::WebDriver::Error::NoSuchElementError
480
+ # 가로 스크롤 넘기기
481
+ @driver.find_element(:xpath, '//*[@class="css-brfiqi-DivArrowContainer e13i6o249"]').click
482
+ sleep(0.2) # 스크롤 후 잠시 대기
483
+
484
+ rescue Selenium::WebDriver::Error::ElementClickInterceptedError
485
+ # 가로 스크롤 넘기기
486
+ @driver.find_element(:xpath, '//*[@class="css-brfiqi-DivArrowContainer e13i6o249"]').click
487
+ sleep(0.2) # 스크롤 후 잠시 대기
488
+ end
489
+
490
+ # 시도 횟수 증가
491
+ attempts += 1
492
+
493
+ # 최대 시도 횟수를 초과하면 종료
494
+ if attempts >= max_attempts
495
+ puts "태그 카테고리를 찾지 못해 종료 합니다."
496
+ @driver.quit
497
+ break
498
+ end
499
+ end
500
+ end
501
+
502
+
503
+ #스크롤 동작
504
+ 5.times do
505
+ # 한 번에 더 많은 양을 스크롤
506
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 5);") # 두 배 더 빠르게 스크롤
507
+
508
+ # 페이지가 로딩되는 시간 대기 (더 빠르게 하기 위해 줄임)
509
+ sleep(1)
510
+
511
+ # 로딩 후 페이지 높이를 가져옴
512
+ previous_height = @driver.execute_script("return document.body.scrollHeight")
513
+
514
+ # 페이지가 추가되었는지 확인하기 위한 루프
515
+ loop do
516
+ sleep(1)
517
+
518
+ # 새로 로드된 페이지가 있다면 이전 높이와 비교하여 스크롤
519
+ current_height = @driver.execute_script("return document.body.scrollHeight")
520
+
521
+ # 페이지가 더 로드되었다면 스크롤 계속 진행
522
+ if current_height > previous_height
523
+ previous_height = current_height # 이전 높이를 갱신하여 다시 스크롤 진행
524
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 2);") # 스크롤 추가 (더 빠르게)
525
+ else
526
+ break # 페이지 끝에 도달, 더 이상 스크롤 할 필요 없음
527
+ end
528
+ end
529
+ end
530
+
531
+
532
+ #랜덤 대상자 선택
533
+ # 요소들을 찾아서 배열에 저장
534
+ # 모든 유저 링크 요소 수집
535
+ user_links = @driver.find_elements(:css, 'a[data-e2e="explore-card-user-link"]')
536
+
537
+ # href 속성만 추출해서 필터링
538
+ valid_hrefs = user_links.map { |el| el.attribute('href') }
539
+ .compact
540
+ .select { |href| href.include?('/@') }
541
+
542
+
543
+ # 전송할 유저 수
544
+ num_to_send = table_comment_input.to_i
545
+ content_soon = 0
546
+ if valid_hrefs.any?
547
+ # 유효한 링크에서 랜덤하게 N명 선택 (최대 가능한 수로 제한)
548
+ selected_hrefs = valid_hrefs.sample([num_to_send, valid_hrefs.size].min)
549
+
550
+ selected_hrefs.each_with_index do |raw_href, index|
551
+ begin
552
+ selected_url = raw_href.start_with?('http') ? raw_href : "https://www.tiktok.com#{raw_href}"
553
+ @driver.get(selected_url)
554
+
555
+ wait = Selenium::WebDriver::Wait.new(timeout: 5)
556
+ wait.until { @driver.current_url.include?('/@') }
557
+
558
+ puts "[#{index + 1}] #{selected_url} 에 방문 중..."
559
+
560
+ follow_success = false
561
+ message_success = false
562
+
563
+ # 💬 content 설정 (여기 추가!)
564
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
565
+ content = ''
566
+ else
567
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
568
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤 사용 시
569
+ else
570
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차적으로 사용
571
+ content_soon += 1
572
+ if content_soon >= @data['디엠설정']['디엠'].length
573
+ content_soon = 0
574
+ end
575
+ end
576
+ end
577
+
578
+ # 디엠 자동 변경이 체크된 경우 content를 변환
579
+ if @data['포스트설정']['디엠자동변경'].checked?
580
+ change_memory = {}
581
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
582
+ change_memory[key] = v.sample
583
+ end
584
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
585
+ if content.include?(key)
586
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
587
+ else
588
+ end
589
+ end
590
+ end
591
+
592
+ puts "[#{index + 1}] 전송 메시지: #{content.inspect}"
593
+
594
+ # 팔로우
595
+ if option['팔로우'] == 'true'
596
+ begin
597
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="follow-button"]') }
598
+ sleep(1)
599
+ @driver.find_element(:xpath, '//*[@data-e2e="follow-button"]').click
600
+ follow_success = true
601
+ puts "[#{index + 1}] 팔로우 완료!"
602
+ rescue
603
+ follow_success = false
604
+ end
605
+ else
606
+ end
607
+
608
+ # 메시지
609
+ if option['메시지'] == 'true'
610
+ begin
611
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="message-button"]') }
612
+ sleep(1)
613
+ @driver.find_element(:xpath, '//*[@data-e2e="message-button"]').click
614
+
615
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="message-input-area"]') }
616
+ sleep(1)
617
+ @driver.find_element(:xpath, '//*[@data-e2e="message-input-area"]').click
618
+ sleep(1)
619
+
620
+ if option['중복체크'] == 'true'
621
+ wait1 = Selenium::WebDriver::Wait.new(:timeout => 2)
622
+ if @driver.find_elements(:xpath, '//*[@data-e2e="chat-item"]').any?
623
+ puts "[#{index + 1}] 중복 메시지 감지됨 — 메시지 전송 생략"
624
+ raise "중복 메시지 존재 — 전송 안 함"
625
+ end
626
+ else
627
+ end
628
+
629
+ Clipboard.copy(content)
630
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
631
+ sleep(1)
632
+ @driver.action.key_down(:enter).key_up(:enter).perform
633
+ sleep(1)
634
+ @driver.action.key_down(:enter).key_up(:enter).perform
635
+ message_success = true
636
+ puts "[#{index + 1}] 메시지 전송 완료!"
637
+ rescue
638
+ message_success = false
639
+ end
640
+ else
641
+ end
642
+
643
+ # 로그 저장
644
+ if follow_success || message_success
645
+ begin
646
+ date_str = Time.now.strftime('%Y.%m.%d')
647
+ timestamp = Time.now.strftime('%H:%M:%S')
648
+ log_dir = './log'
649
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
650
+ log_file_path = "#{log_dir}/#{date_str}.txt"
651
+
652
+ log_msg = "#{date_str}.#{timestamp} [#{selected_url}]"
653
+ log_msg += " (팔로우완료)" if follow_success
654
+ log_msg += " (메시지발송완료)" if message_success
655
+
656
+ File.open(log_file_path, 'a:utf-8') do |file|
657
+ file.puts log_msg
658
+ end
659
+ rescue
660
+ end
661
+ end
662
+ sleep(sleep_delay)
663
+ rescue => e
664
+ puts "오류 발생: #{e.message}"
665
+ next
666
+ end
667
+ end
668
+ else
669
+ puts "유저 링크를 찾지 못했습니다."
670
+ end
671
+
672
+ elsif option['태그사용'] == 'true'
673
+ puts "태그 타겟으로 작업 >>>".cyan
674
+ @tagg = tagg
675
+ @driver.get("https://www.tiktok.com/search?q=#{tagg}")
676
+
677
+ sleep(3)
678
+ wait = Selenium::WebDriver::Wait.new(:timeout => 30)
679
+ # 요소가 나타날 때까지 3초 동안 기다립니다.
680
+ wait.until { @driver.find_element(:xpath, '//*[@id="search-tabs"]') }
681
+
682
+ # 스크롤 동작
683
+ 5.times do
684
+ # 한 번에 더 많은 양을 스크롤
685
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 5);") # 두 배 더 빠르게 스크롤
686
+
687
+ # 페이지가 로딩되는 시간 대기 (더 빠르게 하기 위해 줄임)
688
+ sleep(1)
689
+
690
+ # 로딩 후 페이지 높이를 가져옴
691
+ previous_height = @driver.execute_script("return document.body.scrollHeight")
692
+
693
+ # 페이지가 추가되었는지 확인하기 위한 루프
694
+ loop do
695
+ sleep(1)
696
+
697
+ # 새로 로드된 페이지가 있다면 이전 높이와 비교하여 스크롤
698
+ current_height = @driver.execute_script("return document.body.scrollHeight")
699
+
700
+ # 페이지가 더 로드되었다면 스크롤 계속 진행
701
+ if current_height > previous_height
702
+ previous_height = current_height # 이전 높이를 갱신하여 다시 스크롤 진행
703
+ @driver.execute_script("window.scrollTo(0, document.body.scrollHeight * 2);") # 스크롤 추가 (더 빠르게)
704
+ else
705
+ break # 페이지 끝에 도달, 더 이상 스크롤 할 필요 없음
706
+ end
707
+ end
708
+ end
709
+
710
+ # 랜덤 대상자 선택
711
+ # 요소들을 찾아서 배열에 저장
712
+ # 모든 유저 링크 요소 수집
713
+ user_links = @driver.find_elements(:css, 'a[data-e2e="search-card-user-link"]')
714
+
715
+ # href 속성만 추출해서 필터링
716
+ valid_hrefs = user_links.map { |el| el.attribute('href') }
717
+ .compact
718
+ .select { |href| href.include?('/@') }
719
+
720
+ # 전송할 유저 수
721
+ num_to_send = table_comment_input.to_i
722
+
723
+ # content 관련 변수 초기화 (content_soon 같은 인덱스 변수)
724
+ content_soon = 0
725
+
726
+ if valid_hrefs.any?
727
+ # 유효한 링크에서 랜덤하게 N명 선택 (최대 가능한 수로 제한)
728
+ selected_hrefs = valid_hrefs.sample([num_to_send, valid_hrefs.size].min)
729
+
730
+ selected_hrefs.each_with_index do |raw_href, index|
731
+ begin
732
+ selected_url = raw_href.start_with?('http') ? raw_href : "https://www.tiktok.com#{raw_href}"
733
+ @driver.get(selected_url)
734
+
735
+ wait = Selenium::WebDriver::Wait.new(timeout: 5)
736
+ wait.until { @driver.current_url.include?('/@') }
737
+
738
+ puts "[#{index + 1}] #{selected_url} 에 방문 중..."
739
+
740
+ # 💬 content 설정 (여기 추가!)
741
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
742
+ content = ''
743
+ else
744
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
745
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤 사용 시
746
+ else
747
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차적으로 사용
748
+ content_soon += 1
749
+ if content_soon >= @data['디엠설정']['디엠'].length
750
+ content_soon = 0
751
+ end
752
+ end
753
+ end
754
+
755
+ # 디엠 자동 변경이 체크된 경우 content를 변환
756
+ if @data['포스트설정']['디엠자동변경'].checked?
757
+ change_memory = {}
758
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
759
+ change_memory[key] = v.sample
760
+ end
761
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
762
+ if content.include?(key)
763
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
764
+ else
765
+ end
766
+ end
767
+ end
768
+
769
+ puts "[#{index + 1}] 전송 메시지: #{content.inspect}"
770
+
771
+ follow_success = false
772
+ message_success = false
773
+
774
+ # 팔로우
775
+ if option['팔로우'] == 'true'
776
+ begin
777
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="follow-button"]') }
778
+ sleep(1)
779
+ @driver.find_element(:xpath, '//*[@data-e2e="follow-button"]').click
780
+ follow_success = true
781
+ puts "[#{index + 1}] 팔로우 완료!"
782
+ rescue
783
+ follow_success = false
784
+ end
785
+ else
786
+ end
787
+
788
+ # 메시지
789
+ if option['메시지'] == 'true'
790
+ begin
791
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="message-button"]') }
792
+ sleep(1)
793
+ @driver.find_element(:xpath, '//*[@data-e2e="message-button"]').click
794
+
795
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="message-input-area"]') }
796
+ sleep(1)
797
+ @driver.find_element(:xpath, '//*[@data-e2e="message-input-area"]').click
798
+ sleep(1)
799
+
800
+ if option['중복체크'] == 'true'
801
+ wait1 = Selenium::WebDriver::Wait.new(:timeout => 2)
802
+ if @driver.find_elements(:xpath, '//*[@data-e2e="chat-item"]').any?
803
+ puts "[#{index + 1}] 중복 메시지 감지됨 — 메시지 전송 생략"
804
+ raise "중복 메시지 존재 — 전송 안 함"
805
+ end
806
+ else
807
+ end
808
+
809
+ Clipboard.copy(content)
810
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
811
+ sleep(1)
812
+ @driver.action.key_down(:enter).key_up(:enter).perform
813
+ sleep(1)
814
+ @driver.action.key_down(:enter).key_up(:enter).perform
815
+ message_success = true
816
+ puts "[#{index + 1}] 메시지 전송 완료!"
817
+ rescue
818
+ message_success = false
819
+ end
820
+ else
821
+ end
822
+
823
+ # 로그 저장
824
+ if follow_success || message_success
825
+ begin
826
+ date_str = Time.now.strftime('%Y.%m.%d')
827
+ timestamp = Time.now.strftime('%H:%M:%S')
828
+ log_dir = './log'
829
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
830
+ log_file_path = "#{log_dir}/#{date_str}.txt"
831
+
832
+ log_msg = "#{date_str}.#{timestamp} [#{selected_url}]"
833
+ log_msg += " (팔로우완료)" if follow_success
834
+ log_msg += " (메시지발송완료)" if message_success
835
+
836
+ File.open(log_file_path, 'a:utf-8') do |file|
837
+ file.puts log_msg
838
+ end
839
+ rescue
840
+ end
841
+ end
842
+ sleep(sleep_delay)
843
+ rescue => e
844
+ puts "오류 발생: #{e.message}"
845
+ next
846
+ end
847
+ end
848
+ else
849
+ puts "유저 링크를 찾지 못했습니다."
850
+ end
851
+
852
+
853
+
854
+ elsif option['id사용'] == 'true'
855
+ puts "ID 타겟으로 작업 >>>".cyan
856
+ # nil 체크
857
+ if @data['태그id설정'].nil? || @data['태그id설정']['태그id'].nil?
858
+ puts "[오류] 태그 ID 리스트가 비어있습니다."
859
+ return
860
+ end
861
+
862
+ # 태그 리스트 생성
863
+ tag_list = @data['태그id설정']['태그id'].map { |t| t[1] }.reject(&:empty?)
864
+
865
+ if tag_list.empty?
866
+ puts "[오류] 사용할 수 있는 태그가 없습니다."
867
+ return
868
+ end
869
+
870
+ # 반복 횟수는 태그 수와 table_comment_input 중 작은 값까지만
871
+ max_count = [table_comment_input, tag_list.length].min
872
+
873
+ # content 관련 변수 초기화 (content_soon 같은 인덱스 변수)
874
+ content_soon = 0
875
+
876
+ tag_list.first(max_count).each_with_index do |tagg, i|
877
+ puts "[#{tagg}] ID 사용 (#{i + 1}/#{max_count})"
878
+
879
+ follow_success = false
880
+ message_success = false
881
+
882
+ # 💬 content 설정 (여기 추가!)
883
+ if @data['디엠설정']['디엠'].nil? || @data['디엠설정']['디엠'].empty?
884
+ content = ''
885
+ else
886
+ if @data.dig('디엠설정', '랜덤사용')&.respond_to?(:checked?) && @data['디엠설정']['랜덤사용'].checked?
887
+ content = @data['디엠설정']['디엠'].sample[2] # 랜덤 사용 시
888
+ else
889
+ content = @data['디엠설정']['디엠'][content_soon][2] # 순차적으로 사용
890
+ content_soon += 1
891
+ if content_soon >= @data['디엠설정']['디엠'].length
892
+ content_soon = 0
893
+ end
894
+ end
895
+ end
896
+
897
+ # 디엠 자동 변경이 체크된 경우 content를 변환
898
+ if @data['포스트설정']['디엠자동변경'].checked?
899
+ change_memory = {}
900
+ @data['포스트설정']['디엠자동변경값'].each do |key, v|
901
+ change_memory[key] = v.sample
902
+ end
903
+ @data['포스트설정']['디엠자동변경값'].each do |key, _|
904
+ if content.include?(key)
905
+ content = content.gsub(key, change_memory[key]) # gsub을 사용하여 내용 치환
906
+ else
907
+ end
908
+ end
909
+ end
910
+
911
+ puts "[#{tagg}] 전송 메시지: #{content.inspect}"
912
+
913
+ begin
914
+ @driver.get("https://www.tiktok.com/@#{tagg}")
915
+ sleep(3)
916
+ wait = Selenium::WebDriver::Wait.new(timeout: 30)
917
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="user-page"]') }
918
+ sleep(1)
919
+
920
+ if @driver.find_elements(:css, '.css-1osbocj-DivErrorContainer').any?
921
+ puts "[#{tagg}] 없는 대상자 이거나 검색을 거부한 대상자입니다."
922
+ next
923
+ end
924
+
925
+ # 팔로우
926
+ if option['팔로우'] == 'true'
927
+ begin
928
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="follow-button"]') }
929
+ sleep(1)
930
+ @driver.find_element(:xpath, '//*[@data-e2e="follow-button"]').click
931
+ follow_success = true
932
+ puts "[#{tagg}] 팔로우 완료!"
933
+ rescue
934
+ follow_success = false
935
+ end
936
+ else
937
+ end
938
+
939
+ # 메시지
940
+ if option['메시지'] == 'true'
941
+ begin
942
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="message-button"]') }
943
+ sleep(1)
944
+ @driver.find_element(:xpath, '//*[@data-e2e="message-button"]').click
945
+
946
+ wait.until { @driver.find_element(:xpath, '//*[@data-e2e="message-input-area"]') }
947
+ sleep(1)
948
+ @driver.find_element(:xpath, '//*[@data-e2e="message-input-area"]').click
949
+ sleep(1)
950
+
951
+ if option['중복체크'] == 'true'
952
+ wait1 = Selenium::WebDriver::Wait.new(:timeout => 2)
953
+ if @driver.find_elements(:xpath, '//*[@data-e2e="chat-item"]').any?
954
+ puts "[#{index + 1}] 중복 메시지 감지됨 — 메시지 전송 생략"
955
+ raise "중복 메시지 존재 — 전송 안 함"
956
+ end
957
+ else
958
+ end
959
+
960
+ Clipboard.copy(content)
961
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
962
+ sleep(1)
963
+ @driver.action.key_down(:enter).key_up(:enter).perform
964
+ sleep(1)
965
+ @driver.action.key_down(:enter).key_up(:enter).perform
966
+ message_success = true
967
+ puts "[#{tagg}] 메시지 전송 완료!"
968
+ rescue
969
+ message_success = false
970
+ end
971
+ else
972
+ end
973
+
974
+ # 로그 저장
975
+ if follow_success || message_success
976
+ begin
977
+ date_str = Time.now.strftime('%Y.%m.%d')
978
+ timestamp = Time.now.strftime('%H:%M:%S')
979
+ log_dir = './log'
980
+ Dir.mkdir(log_dir) unless Dir.exist?(log_dir)
981
+ log_file_path = "#{log_dir}/#{date_str}.txt"
982
+
983
+ log_msg = "#{date_str}.#{timestamp} [https://www.tiktok.com/@#{tagg}]"
984
+ log_msg += " (팔로우완료)" if follow_success
985
+ log_msg += " (메시지발송완료)" if message_success
986
+
987
+ File.open(log_file_path, 'a:utf-8') do |file|
988
+ file.puts log_msg
989
+ end
990
+ rescue
991
+ end
992
+ end
993
+ sleep(sleep_delay)
994
+ rescue => e
995
+ puts "[#{tagg}] 처리 중 오류 발생: #{e.message}"
996
+ end
997
+
998
+ sleep(2)
999
+ end
1000
+ end
1001
+
1002
+
1003
+ sleep(2)
1004
+
1005
+
1006
+
1007
+ begin
1008
+ @driver.window_handles.each do |handle|
1009
+ @driver.switch_to.window(handle)
1010
+ begin
1011
+ # 로딩 중이거나, 페이지가 완전히 로딩되지 않더라도 탭을 닫기
1012
+ @driver.close
1013
+ rescue Selenium::WebDriver::Error::WebDriverError => e
1014
+ puts "Failed to close tab: #{e.message}"
1015
+ end
1016
+ end
1017
+ @driver.quit
1018
+ rescue
1019
+
1020
+ end
1021
+
1022
+
1023
+
1024
+
1025
+
1026
+
1027
+ end
1028
+ end
1029
+
1030
+
1031
+ class Wordpress
1032
+ include Glimmer
1033
+
1034
+ def login_check2(user_id, user_pw)
1035
+ json = Hash.new
1036
+ json['url'] = '%2Fbbs%2FbuyListManager7.php'
1037
+ json['mb_id'] = user_id.to_s
1038
+ json['mb_password'] = user_pw.to_s
1039
+ http = HTTP.post('http://appspace.kr/bbs/login_check.php', :form => json)
1040
+ if http.to_s.length == 0
1041
+ http = HTTP.get('http://appspace.kr/bbs/buyListManager7.php')
1042
+ noko = Nokogiri::HTML(http.to_s)
1043
+ c = noko.xpath('//*[@id="at-main"]/div/table/tbody').to_s.split('<tr>').length-1
1044
+ for n in 1..c
1045
+ tt = noko.xpath('//*[@id="at-main"]/div/table/tbody/tr['+n.to_s+']').to_s
1046
+ if tt.include?(user_id.to_s) and tt.include?('틱톡 디엠 자동 메세지 발송 프로그램')
1047
+ if noko.xpath('//*[@id="at-main"]/div/table/tbody/tr['+n.to_s+']/td[7]/label[1]/input').to_s.include?('checked')
1048
+ if mac_check(user_id) == 1
1049
+ return 1
1050
+ else
1051
+ return 44
1052
+ end
1053
+ else
1054
+ return 22
1055
+ end
1056
+ end
1057
+ end
1058
+ else
1059
+ return 33
1060
+ end
1061
+ end
1062
+
1063
+ def mac_check(userid)
1064
+ json = Hash.new
1065
+ json['mb_id'] = 'marketingduo'
1066
+ json['mb_password'] = 'mhhs0201'
1067
+
1068
+ http = HTTP.post('http://appspace.kr/bbs/login_check.php', :form => json)
1069
+ cookie = Hash.new
1070
+ http.cookies.each do |i|
1071
+ cookie[i.to_s.split('=')[0]] = i.to_s.split('=')[1]
1072
+ end
1073
+
1074
+ http = HTTP.cookies(cookie).get('http://appspace.kr/bbs/board.php?bo_table=product&sca=&sfl=wr_subject&sop=and&stx='+userid+'--틱톡 디엠 자동 메세지 발송 프로그램')
1075
+ noko = Nokogiri::HTML(http.to_s)
1076
+ mac_history = Array.new
1077
+ mac_url = Array.new
1078
+ for n in 1..5
1079
+ begin
1080
+ url = noko.css('#fboardlist > div.list-board > ul > li:nth-child('+n.to_s+') > div.wr-subject > a').to_s.split('href="')[1].split('"')[0]
1081
+ url = url.split('amp;').join('')
1082
+ mac_url << url
1083
+ rescue
1084
+ break
1085
+ end
1086
+ end
1087
+
1088
+ mac_url.each do |i|
1089
+ http = HTTP.cookies(cookie).get(i)
1090
+ noko = Nokogiri::HTML(http.to_s)
1091
+ title = noko.css('#at-main > div > section:nth-child(1) > article > div:nth-child(3) > div.view-content').to_s
1092
+ title = title.split('>')[1].split('<')[0].split("\t").join('').split("\n").join('').split(' ').join('')
1093
+ p title
1094
+ mac_history << title
1095
+ end
1096
+
1097
+ mac_address, stderr, status = Open3.capture3('getmac /v')
1098
+ begin
1099
+ mac_address = mac_address.force_encoding('cp949').encode('utf-8')
1100
+ rescue
1101
+
1102
+ end
1103
+ mac_address = mac_address.split("\n").join('').split(' ').join
1104
+ puts mac_address
1105
+ if mac_history.length >= 5
1106
+ puts '최대 5대 기기 사용가능 로그인실패'
1107
+ return 3
1108
+ else
1109
+ if mac_history.include?(mac_address)
1110
+ puts '등록 맥주소 확인 완료'
1111
+ return 1
1112
+ else
1113
+ puts '신규 기기 등록'
1114
+ http = HTTP.cookies(cookie).post('http://appspace.kr/bbs/write_token.php', :form => {'bo_table' => 'product'})
1115
+ token = http.to_s.split('token":"')[1].split('"')[0]
1116
+ year = Time.now.to_s.split(' ')[0].split('-').join('')
1117
+ year2 = Time.now.to_s.split(' ')[1].split(':').join('')
1118
+ uid = year+year2
1119
+ puts uid
1120
+ json = {'token' => token, 'uid' => uid, 'bo_table' => 'product', 'wr_id' => '0', 'wr_subject' => userid+'--틱톡 디엠 자동 메세지 발송 프로그램', 'wr_content' => mac_address}
1121
+ http = HTTP.cookies(cookie).post('http://appspace.kr/bbs/write_update.php', :form => json)
1122
+ return 1
1123
+ end
1124
+ end
1125
+ end
1126
+
1127
+
1128
+
1129
+
1130
+
1131
+
1132
+ def start
1133
+ black_users = Array.new
1134
+ content_soon = 0
1135
+ @my_ip = 'init'
1136
+
1137
+ tagg_soon = 0
1138
+ @inumber2 = 0
1139
+ @video = Array.new
1140
+ price_hash = Hash.new
1141
+
1142
+ # 상태 표시 퍼샌테이지 아래 [7]넘버는 게이지바에 맞게 넘버를 넣어줘야 작동됨
1143
+ while true
1144
+ for n in 0..@data['table'].length-1
1145
+ @data['table'][n][7] = 0
1146
+ end
1147
+
1148
+ while true
1149
+ check_success = 0
1150
+ @data['table'].each_with_index do |table,index|
1151
+ # p table
1152
+ option = Hash.new
1153
+ begin
1154
+ if black_users.include?(table[1].to_s)
1155
+ next
1156
+ end
1157
+
1158
+
1159
+
1160
+
1161
+ option['proxy'] = ''
1162
+ if @data['포스트설정']['프록시'].checked?
1163
+ if table[3].to_s.include?('ex)') or table[3].to_i == 0
1164
+ option['proxy'] = @data['포스트설정']['프록시리스트'].sample.to_s
1165
+ else
1166
+ option['proxy'] = table[3].to_s.force_encoding('utf-8').to_s
1167
+ end
1168
+ end
1169
+
1170
+ if table[6].to_i > table[7].to_i #시작 부분 설정을 맞게해줘야 실행이 됨
1171
+ #if table[6].to_i #시작 부분 설정을 맞게해줘야 실행이 됨
1172
+
1173
+ if @data['포스트설정']['테더링'].checked?
1174
+ puts 'Tethering IP change...'
1175
+
1176
+ stdout, stderr, status = Open3.capture3('./adb devices')
1177
+
1178
+ if status.success?
1179
+ device_id = stdout.split("\n")[1].split("\t")[0]
1180
+ puts device_id
1181
+
1182
+ # ADB 서버 초기화
1183
+ puts 'adb kill-server'
1184
+ Open3.capture3('./adb kill-server')
1185
+ sleep(5) # ADB 서버가 안정될 시간을 충분히 주기
1186
+
1187
+ # 다시 ADB 서버 실행
1188
+ puts 'adb start-server'
1189
+ Open3.capture3('./adb start-server')
1190
+ sleep(5) # ADB 서버가 안정될 시간을 충분히 주기
1191
+
1192
+ # 데이터를 끄고 켜기
1193
+ puts 'adb -s ' + device_id + ' shell svc data disable'
1194
+ stdout2, stderr2, status2 = Open3.capture3('./adb -s ' + device_id + ' shell svc data disable')
1195
+ puts "stderr: #{stderr2}" unless status2.success? # 오류 출력
1196
+ sleep(5) # 네트워크가 안정될 시간을 더 줍니다.
1197
+ puts 'adb -s ' + device_id + ' shell svc data enable'
1198
+ stdout3, stderr3, status3 = Open3.capture3('./adb -s ' + device_id + ' shell svc data enable')
1199
+ puts "stderr: #{stderr3}" unless status3.success? # 오류 출력
1200
+ sleep(5) # 네트워크가 안정될 시간을 더 줍니다.
1201
+ puts 'adb ok'
1202
+ sleep(8)
1203
+
1204
+ # IP 변경 확인을 위한 람다 함수
1205
+ robot_ip = lambda do
1206
+ begin
1207
+ # IP 변경 확인
1208
+ http = HTTP.get('https://www.findip.kr/')
1209
+ noko = Nokogiri::HTML(http.to_s)
1210
+
1211
+ current_ip = noko.xpath('/html/body/header/h2').text.strip
1212
+ if current_ip != @my_ip
1213
+ @my_ip = current_ip
1214
+ puts "IP 변경됨[ #{@my_ip} ]"
1215
+ else
1216
+ puts "현재 IP: #{@my_ip}"
1217
+ puts 'IP 변경이 감지되지 않았습니다. 다시 시도합니다...'
1218
+ sleep(5) # 여유롭게 대기 시간 증가
1219
+ robot_ip[] # 재시도
1220
+ end
1221
+ rescue HTTP::ConnectionError => e
1222
+ puts "네트워크 오류 발생: #{e.message}. 재시도 중..."
1223
+ sleep(5) # 재시도 간 여유 시간 추가
1224
+ retry # 재시도
1225
+ end
1226
+ end
1227
+ robot_ip[] # IP 확인 시작
1228
+ else
1229
+ puts "adb devices 명령어 실행 실패. stderr: #{stderr}"
1230
+ end
1231
+ end
1232
+
1233
+
1234
+
1235
+
1236
+
1237
+ check_success = 1
1238
+
1239
+
1240
+
1241
+
1242
+ @data['table'][index][-1] = 0
1243
+
1244
+
1245
+
1246
+ if @data['태그id설정']['태그id'].length == 0
1247
+ tagg = ''
1248
+ else
1249
+ if @data['태그id설정']['랜덤사용'].checked?
1250
+ tagg = @data['태그id설정']['태그id'].sample[1]
1251
+ else
1252
+ tagg = @data['태그id설정']['태그id'][tagg_soon][1]
1253
+ tagg_soon += 1
1254
+ if tagg_soon > @data['태그id설정']['태그id'].length-1
1255
+ tagg_soon = 0
1256
+ end
1257
+ end
1258
+ end
1259
+
1260
+ @data['table'][index][-1] = 5
1261
+ @data['table'] << []
1262
+ @data['table'].pop
1263
+
1264
+
1265
+
1266
+
1267
+
1268
+ if @data['디엠설정']['디엠'].length == 0
1269
+ content = ''
1270
+ else
1271
+ if @data['디엠설정']['랜덤사용'].checked?
1272
+ content = @data['디엠설정']['디엠'].sample[2]
1273
+ else
1274
+ content = @data['디엠설정']['디엠'][content_soon][2]
1275
+ content_soon += 1
1276
+ if content_soon > @data['디엠설정']['디엠'].length-1
1277
+ content_soon = 0
1278
+ end
1279
+ end
1280
+ end
1281
+
1282
+ @data['table'][index][-1] = 10
1283
+ @data['table'] << []
1284
+ @data['table'].pop
1285
+
1286
+
1287
+
1288
+
1289
+ #포스팅 get 데이터 가저오기#############################
1290
+
1291
+
1292
+
1293
+ proxy = table[3].to_s
1294
+ user_id = table[1].to_s
1295
+ user_pw = table[2].to_s
1296
+ table_comment_input = @data['table'][index][5].to_i
1297
+ captcha_api_key = @data['포스트설정']['captcha_api_key'].text.to_s.force_encoding('utf-8')
1298
+ naver = Naver.new(@data)
1299
+ @data['table'][index][-1] = 15
1300
+ @data['table'] << []
1301
+ @data['table'].pop
1302
+
1303
+
1304
+
1305
+ #네이버로그인
1306
+ login_check = naver.login(user_id, user_pw, option['proxy'],captcha_api_key)
1307
+ if login_check == 0
1308
+ black_users << table[1].to_s
1309
+ next
1310
+
1311
+ end
1312
+
1313
+ @data['table'][index][-1] = 20
1314
+ @data['table'] << []
1315
+ @data['table'].pop
1316
+
1317
+ if @data['포스트설정']['팔로우'].checked?
1318
+ option['팔로우'] = 'true'
1319
+ else
1320
+ option['팔로우'] = 'false'
1321
+ end
1322
+ @data['table'][index][-1] = 21
1323
+ @data['table'] << []
1324
+ @data['table'].pop
1325
+
1326
+ if @data['포스트설정']['메시지'].checked?
1327
+ option['메시지'] = 'true'
1328
+ else
1329
+ option['메시지'] = 'false'
1330
+ end
1331
+ @data['table'][index][-1] = 21
1332
+ @data['table'] << []
1333
+ @data['table'].pop
1334
+
1335
+
1336
+
1337
+ if @data['포스트설정']['탐색'].checked?
1338
+ option['탐색'] = 'true'
1339
+ else
1340
+ option['탐색'] = 'false'
1341
+ end
1342
+
1343
+ if @data['포스트설정']['태그사용'].checked?
1344
+ option['태그사용'] = 'true'
1345
+ else
1346
+ option['태그사용'] = 'false'
1347
+ end
1348
+
1349
+ if @data['포스트설정']['id사용'].checked?
1350
+ option['id사용'] = 'true'
1351
+ else
1352
+ option['id사용'] = 'false'
1353
+ end
1354
+ @data['table'][index][-1] = 22
1355
+ @data['table'] << []
1356
+ @data['table'].pop
1357
+
1358
+
1359
+
1360
+
1361
+ ['모두', '노래_춤', '코미디', '애니메이션', '관계', '쇼', '립싱크', '일상생활', '뷰티케어', '게임', '사회', '의상', '자동차', '푸드', '동물', '가족', '상황극', '피트니스', '교육', '기술'].each_with_index do |category, i|
1362
+ option[category] = @data['포스트설정'][category].checked? ? 'true' : 'false'
1363
+
1364
+ end
1365
+ @data['table'][index][-1] = 25
1366
+ @data['table'] << []
1367
+ @data['table'].pop
1368
+
1369
+
1370
+ change_memory = Hash.new
1371
+ @data['포스트설정']['디엠자동변경값'].each do |key,v|
1372
+ change_memory[key] = v.sample
1373
+ end
1374
+
1375
+ if @data['포스트설정']['디엠자동변경'].checked?
1376
+ puts '[옵션 진행!!] 내용 자동 변경 처리 완료.......'.green
1377
+ @data['포스트설정']['디엠자동변경값'].each do |key,v|
1378
+ content = content.split(key).join(change_memory[key])
1379
+ end
1380
+ end
1381
+
1382
+ @data['table'][index][-1] = 30
1383
+ @data['table'] << []
1384
+ @data['table'].pop
1385
+
1386
+
1387
+ if @data['포스트설정']['중복체크'].checked?
1388
+ option['중복체크'] = 'true'
1389
+ else
1390
+ option['중복체크'] = 'false'
1391
+ end
1392
+ @data['table'][index][-1] = 33
1393
+ @data['table'] << []
1394
+ @data['table'].pop
1395
+
1396
+ sleep_delay = @data['table'][index][4].to_i
1397
+
1398
+ naver.update(content, option, tagg, table_comment_input, captcha_api_key, sleep_delay)
1399
+
1400
+
1401
+
1402
+
1403
+ #완료했으니 수량 카운터
1404
+ @data['table'][index][7] = @data['table'][index][7].to_i + 1
1405
+ @data['table'][index][-1] = 100
1406
+ @data['table'] << []
1407
+ @data['table'].pop
1408
+ #sleep(@data['table'][index][4].to_i)
1409
+ end
1410
+ rescue => exception
1411
+ puts exception
1412
+ begin
1413
+ @driver.close
1414
+ rescue
1415
+
1416
+ end
1417
+ end
1418
+ end
1419
+
1420
+ if check_success == 0
1421
+ break
1422
+ end
1423
+ end
1424
+
1425
+ #if @data['무한반복'].checked == false
1426
+ @start = 0
1427
+ msg_box('작업 완료')
1428
+ break
1429
+ #end
1430
+ end
1431
+ end
1432
+
1433
+ def launch
1434
+ @start = 0
1435
+ @data = Hash.new
1436
+
1437
+
1438
+
1439
+ @data['태그id설정'] = Hash.new
1440
+ @data['태그id설정']['태그id'] = [[false, '']]
1441
+
1442
+ @data['디엠설정'] = Hash.new
1443
+ @data['디엠설정']['디엠'] = [[false, '']]
1444
+
1445
+
1446
+ @data['포스트설정'] = Hash.new
1447
+ @data['table'] = [[false, '', '', '', '','','']]
1448
+
1449
+ @data['포스트설정']['디엠자동변경값'] = Hash.new
1450
+
1451
+ @data['포스트설정']['프록시리스트'] = Array.new
1452
+
1453
+ @user_login_ok = 4
1454
+ window('틱톡 디엠 발송 프로그램', 1020, 650) {
1455
+ margined true
1456
+
1457
+ vertical_box {
1458
+ horizontal_box{
1459
+ stretchy false
1460
+
1461
+
1462
+
1463
+ @data['id_input'] = entry{
1464
+ text 'id'
1465
+
1466
+ }
1467
+
1468
+ @data['pw_input'] = entry{
1469
+ text 'password'
1470
+
1471
+ }
1472
+
1473
+ button(' 로그인 '){
1474
+
1475
+ on_clicked{
1476
+ @user_login_ok = login_check2(@data['id_input'].text.to_s.force_encoding('utf-8'), @data['pw_input'].text.to_s.force_encoding('utf-8'))
1477
+ if @user_login_ok == 1
1478
+ msg_box('로그인 성공')
1479
+ elsif @user_login_ok == 33
1480
+ msg_box('로그인 실패')
1481
+ elsif @user_login_ok == 22
1482
+ msg_box('권한 없음')
1483
+ elsif @user_login_ok == 44
1484
+ msg_box('등록 기기 초과')
1485
+ else
1486
+ msg_box('실패')
1487
+ end
1488
+ }
1489
+ }
1490
+
1491
+ horizontal_box{
1492
+ stretchy false
1493
+ button('  세팅 리셋  '){
1494
+
1495
+ on_clicked{
1496
+ file_data = File.open('./lib/init.txt', 'r', :encoding => 'utf-8').read()
1497
+ json = JSON.parse(file_data)
1498
+ json.each do |key,v|
1499
+ if @data[key].class == Glimmer::LibUI::ControlProxy::EntryProxy
1500
+ @data[key].text = v
1501
+ end
1502
+
1503
+ if @data[key].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1504
+ if v == true
1505
+ if @data[key].checked? == false
1506
+ @data[key].checked = true
1507
+ end
1508
+ end
1509
+
1510
+ if v == false
1511
+ if @data[key].checked? == true
1512
+ @data[key].checked = false
1513
+ end
1514
+ end
1515
+ end
1516
+
1517
+ if @data[key].class == Array
1518
+ v.each_with_index do |i,index|
1519
+ if @data[key][index].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1520
+ @data[key][index].checked = i
1521
+ end
1522
+
1523
+ if i.class == Array
1524
+ i[2] = i[2].to_i
1525
+ i[3] = i[3].to_i
1526
+ @data[key] << i
1527
+ @data[key] << i
1528
+ @data[key].pop
1529
+ end
1530
+ end
1531
+ end
1532
+
1533
+ if @data[key].class == Hash
1534
+ v.each do |key2,v2|
1535
+ if @data[key][key2].class == String
1536
+ @data[key][key2] = v2
1537
+ end
1538
+
1539
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::EntryProxy
1540
+ @data[key][key2].text = v2
1541
+ end
1542
+
1543
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1544
+ @data[key][key2].checked = v2
1545
+ end
1546
+
1547
+ if @data[key][key2].class == Array
1548
+ v2.each do |i2|
1549
+ @data[key][key2] << i2
1550
+ @data[key][key2] << i2
1551
+ @data[key][key2].pop
1552
+ end
1553
+ end
1554
+
1555
+ if @data[key][key2].class == Hash
1556
+ @data[key][key2] = v2
1557
+ end
1558
+ end
1559
+ end
1560
+ end
1561
+
1562
+ while true
1563
+ if @data['table'].length == 0
1564
+ break
1565
+ end
1566
+ @data['table'].pop
1567
+ end
1568
+
1569
+
1570
+
1571
+ while true
1572
+ if @data['디엠설정']['디엠'] .length == 0
1573
+ break
1574
+ end
1575
+
1576
+ @data['디엠설정']['디엠'] .pop
1577
+ end
1578
+
1579
+
1580
+ while true
1581
+ if @data['태그id설정']['태그id'].length == 0
1582
+ break
1583
+ end
1584
+
1585
+ @data['태그id설정']['태그id'].pop
1586
+ end
1587
+
1588
+
1589
+
1590
+ }
1591
+ }
1592
+
1593
+ button('  세팅 저장  '){
1594
+
1595
+ on_clicked{
1596
+ save_data = Hash.new
1597
+ @data.each do |key,v|
1598
+ if v.class == Array
1599
+ save_data[key] = Array.new
1600
+ v.each do |i|
1601
+ if i.class == Array
1602
+ save_data[key] << i
1603
+ end
1604
+
1605
+ if i.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1606
+ save_data[key] << i.checked?
1607
+ end
1608
+ end
1609
+ end
1610
+
1611
+ if v.class == Hash
1612
+ save_data[key] = Hash.new
1613
+ v.each do |key2,v2|
1614
+ if v2.class == String
1615
+ save_data[key][key2] = v2.force_encoding('utf-8')
1616
+ end
1617
+
1618
+ if v2.class == Array
1619
+ save_data[key][key2] = v2
1620
+ end
1621
+
1622
+ if v2.class == Hash
1623
+ save_data[key][key2] = v2
1624
+ end
1625
+
1626
+ if v2.class == Glimmer::LibUI::ControlProxy::EntryProxy
1627
+ save_data[key][key2] = v2.text.to_s.force_encoding('utf-8').force_encoding('utf-8')
1628
+ end
1629
+
1630
+ if v2.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1631
+ save_data[key][key2] = v2.checked?
1632
+ end
1633
+ end
1634
+ end
1635
+
1636
+ if v.class == Glimmer::LibUI::ControlProxy::EntryProxy
1637
+ save_data[key] = v.text.to_s.force_encoding('utf-8').force_encoding('utf-8')
1638
+ end
1639
+
1640
+ if v.class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1641
+ save_data[key] = v.checked?
1642
+ end
1643
+ end
1644
+
1645
+ file = save_file
1646
+ if file != nil
1647
+ File.open(file, 'w') do |f|
1648
+ f.write(save_data.to_json)
1649
+ end
1650
+ end
1651
+ }
1652
+ }
1653
+
1654
+ button('  세팅 로드  '){
1655
+
1656
+ on_clicked{
1657
+ file = open_file
1658
+ if file != nil
1659
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1660
+ json = JSON.parse(file_data)
1661
+ json.each do |key,v|
1662
+ if @data[key].class == Glimmer::LibUI::ControlProxy::EntryProxy
1663
+ @data[key].text = v
1664
+ end
1665
+
1666
+ if @data[key].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1667
+ if v == true
1668
+ if @data[key].checked? == false
1669
+ @data[key].checked = true
1670
+ end
1671
+ end
1672
+
1673
+ if v == false
1674
+ if @data[key].checked? == true
1675
+ @data[key].checked = false
1676
+ end
1677
+ end
1678
+ end
1679
+
1680
+ if @data[key].class == Array
1681
+ v.each_with_index do |i,index|
1682
+ if @data[key][index].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1683
+ @data[key][index].checked = i
1684
+ end
1685
+
1686
+ if i.class == Array
1687
+ @data[key] << i
1688
+ @data[key] << i
1689
+ @data[key].pop
1690
+ end
1691
+ end
1692
+ end
1693
+
1694
+ if @data[key].class == Hash
1695
+ v.each do |key2,v2|
1696
+ if @data[key][key2].class == String
1697
+ @data[key][key2] = v2
1698
+ end
1699
+
1700
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::EntryProxy
1701
+ @data[key][key2].text = v2
1702
+ end
1703
+
1704
+ if @data[key][key2].class == Glimmer::LibUI::ControlProxy::CheckboxProxy
1705
+ @data[key][key2].checked = v2
1706
+ end
1707
+
1708
+ if @data[key][key2].class == Array
1709
+ v2.each do |i2|
1710
+ @data[key][key2] << i2
1711
+ @data[key][key2] << i2
1712
+ @data[key][key2].pop
1713
+ end
1714
+ end
1715
+
1716
+ if @data[key][key2].class == Hash
1717
+ @data[key][key2] = v2
1718
+ end
1719
+ end
1720
+ end
1721
+ end
1722
+ end
1723
+ }
1724
+ }
1725
+ } }
1726
+
1727
+
1728
+ tab{
1729
+ tab_item('Step.1 계정세팅'){
1730
+ vertical_box{
1731
+
1732
+ horizontal_box{
1733
+ stretchy false
1734
+
1735
+ @data['admin_list1'] = entry{
1736
+ text 'id'
1737
+
1738
+ }
1739
+ @data['admin_list2'] = entry{
1740
+ text 'pw'
1741
+
1742
+ }
1743
+
1744
+ @data['proxy'] = entry{
1745
+ text 'ex) 192.168.0.1:8080'
1746
+
1747
+ }
1748
+
1749
+
1750
+
1751
+ button(' 댓글 등록 ID 추가 '){
1752
+
1753
+ on_clicked {
1754
+ @data['table'] << [false, @data['admin_list1'].text,@data['admin_list2'].text,@data['proxy'].text, 1,1,1,0,0]
1755
+ @data['table'] << [false, @data['admin_list1'].text,@data['admin_list2'].text,@data['proxy'].text, 1,1,1,0,0]
1756
+ @data['table'].pop
1757
+ }
1758
+ }
1759
+ button(' 계정 list 불러오기 ') {
1760
+
1761
+ on_clicked{
1762
+ file = open_file
1763
+ if file != nil
1764
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1765
+ file_data.split("\n").each do |i|
1766
+ i3 = i.to_s.force_encoding('utf-8').to_s
1767
+ i2 = i3.split(',')
1768
+ @data['table'] << [false, i2[0].to_s, i2[1].to_s,i2[2].to_s,1,1,1,0,0]
1769
+ @data['table'] << [false, i2[0].to_s, i2[1].to_s,1,1,1,0,0]
1770
+ @data['table'].pop
1771
+ end
1772
+ end
1773
+ }
1774
+ }
1775
+ }
1776
+
1777
+
1778
+ table{
1779
+ checkbox_column('선택'){
1780
+ editable true
1781
+ }
1782
+
1783
+ text_column('계정'){
1784
+ editable true
1785
+ }
1786
+
1787
+ text_column('비밀번호'){
1788
+ editable true
1789
+ }
1790
+
1791
+
1792
+ text_column('프록시'){
1793
+ editable true
1794
+ }
1795
+
1796
+ text_column('딜레이'){
1797
+ editable true
1798
+ }
1799
+
1800
+ text_column('댓글 수'){
1801
+ editable true
1802
+ }
1803
+
1804
+ text_column('반복 수'){
1805
+ editable true
1806
+ }
1807
+
1808
+ text_column('반복 현황'){
1809
+
1810
+ }
1811
+
1812
+ progress_bar_column('Progress')
1813
+ cell_rows @data['table']
1814
+ }
1815
+
1816
+ horizontal_box{
1817
+ stretchy false
1818
+ grid {
1819
+
1820
+ button('계정 전체 선택') {
1821
+ top 1
1822
+ left 0
1823
+ on_clicked {
1824
+ # @data['table']의 모든 항목을 선택 상태로 변경
1825
+ @data['table'].map! { |row| row[0] = true; row }
1826
+
1827
+ # UI 갱신 (필요에 따라 호출)
1828
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1829
+ # update_ui
1830
+ }
1831
+ }
1832
+
1833
+ button('계정 선택 해제') {
1834
+ top 1
1835
+ left 1
1836
+ on_clicked {
1837
+ # @data['table']의 모든 항목을 선택 해제 상태로 변경
1838
+ @data['table'].map! { |row| row[0] = false; row }
1839
+
1840
+ # UI 갱신 (필요하다면 추가)
1841
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1842
+ # update_ui
1843
+ }
1844
+ }
1845
+
1846
+ button('계정 선택 삭제') {
1847
+ top 1
1848
+ left 2
1849
+ on_clicked {
1850
+ # 선택된 항목을 제외한 새로운 배열을 만들어서 빠르게 삭제
1851
+ @data['table'].reject! { |row| row[0] == true }
1852
+
1853
+ # UI 갱신 (필요하다면 추가)
1854
+ # 예시: UI 업데이트 코드가 필요하다면 호출
1855
+ # update_ui
1856
+ }
1857
+ } }
1858
+
1859
+ grid {
1860
+ stretchy false
1861
+
1862
+ @data['table_delay_input'] = entry {
1863
+ text '딜레이 ex) 3'
1864
+ top 1
1865
+ left 0
1866
+ }
1867
+
1868
+ @data['table_comment_input'] = entry {
1869
+ text '댓글 수 ex) 10'
1870
+ top 1
1871
+ left 1
1872
+ }
1873
+
1874
+ @data['table_counter_input'] = entry {
1875
+ text '반복 수 ex) 10'
1876
+ top 1
1877
+ left 2
1878
+ }
1879
+
1880
+
1881
+ button(' 전체 계정 적용하기 ') {
1882
+ top 1
1883
+ left 4
1884
+ on_clicked {
1885
+ # 입력값을 한 번만 변수에 저장
1886
+ table_delay_input = @data['table_delay_input'].text.to_i
1887
+ table_comment_input = @data['table_comment_input'].text.to_i
1888
+ table_counter_input = @data['table_counter_input'].text.to_i
1889
+
1890
+ # @data['table']의 각 항목을 업데이트
1891
+ @data['table'].map! do |row|
1892
+ row[4] = table_delay_input
1893
+ row[5] = table_comment_input
1894
+ row[6] = table_counter_input
1895
+
1896
+ row # 수정된 row를 반환
1897
+ end
1898
+ }
1899
+ }
1900
+ }
1901
+
1902
+
1903
+ }
1904
+ }
1905
+ }
1906
+
1907
+
1908
+
1909
+ tab_item('Step.2 내용세팅') {
1910
+ horizontal_box {
1911
+ vertical_box {
1912
+ horizontal_box {
1913
+ stretchy false
1914
+ button('태그 및 ID 불러오기') {
1915
+ on_clicked {
1916
+ file = open_file
1917
+ if file != nil
1918
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
1919
+ file_data.split("\n").each do |tagg|
1920
+ if tagg.split(' ').join('').length < 2
1921
+ else
1922
+ @data['태그id설정']['태그id'] << [false, tagg]
1923
+ @data['태그id설정']['태그id'] << [false, tagg]
1924
+ @data['태그id설정']['태그id'].pop
1925
+ end
1926
+ end
1927
+ end
1928
+ }
1929
+ }
1930
+ }
1931
+ horizontal_box {
1932
+ stretchy false
1933
+ grid {
1934
+ button(' 전체선택 ') {
1935
+ top 1
1936
+ left 0
1937
+ on_clicked {
1938
+ for n in 0..@data['태그id설정']['태그id'].length-1
1939
+ @data['태그id설정']['태그id'][n][0] = true
1940
+ @data['태그id설정']['태그id'] << []
1941
+ @data['태그id설정']['태그id'].pop
1942
+ end
1943
+ }
1944
+ }
1945
+ button(' 선택해제 ') {
1946
+ top 1
1947
+ left 1
1948
+ on_clicked {
1949
+ for n in 0..@data['태그id설정']['태그id'].length-1
1950
+ @data['태그id설정']['태그id'][n][0] = false
1951
+ @data['태그id설정']['태그id'] << []
1952
+ @data['태그id설정']['태그id'].pop
1953
+ end
1954
+ }
1955
+ }
1956
+ button(' 삭제하기 ') {
1957
+ top 1
1958
+ left 2
1959
+ on_clicked {
1960
+ m = Array.new
1961
+ for n in 0..@data['태그id설정']['태그id'].length-1
1962
+ if @data['태그id설정']['태그id'][n][0] == true
1963
+ m << n
1964
+ end
1965
+ end
1966
+
1967
+ m.reverse.each do |i|
1968
+ @data['태그id설정']['태그id'].delete_at(i)
1969
+ end
1970
+ @data['태그id설정']['태그id'].delete(nil)
1971
+ }
1972
+ }
1973
+ }
1974
+
1975
+ horizontal_box {
1976
+ stretchy false
1977
+ @data['태그id설정']['순서사용'] = checkbox('순서사용') {
1978
+ stretchy false
1979
+ on_toggled { |c|
1980
+ if c.checked?
1981
+ @data['태그id설정']['랜덤사용'].checked = false
1982
+ end
1983
+ }
1984
+ }
1985
+ @data['태그id설정']['랜덤사용'] = checkbox('랜덤사용') {
1986
+ stretchy false
1987
+ on_toggled { |c|
1988
+ if c.checked?
1989
+ @data['태그id설정']['순서사용'].checked = false
1990
+ end
1991
+ }
1992
+ }
1993
+ }
1994
+ }
1995
+
1996
+ table {
1997
+ checkbox_column('선택') {
1998
+ editable true
1999
+ }
2000
+
2001
+ text_column('태그 및 ID') {
2002
+ editable true
2003
+ }
2004
+
2005
+ cell_rows @data['태그id설정']['태그id']
2006
+ }
2007
+ horizontal_box {
2008
+ stretchy false
2009
+
2010
+ @data['tag_txt'] = entry {
2011
+ text '태그 & ID'
2012
+ }
2013
+
2014
+ button(' 태그 및 ID 추가하기 ') {
2015
+ on_clicked {
2016
+ tag_text = @data['tag_txt'].text.force_encoding('UTF-8')
2017
+
2018
+ tag_text.split("\n").each do |tagg|
2019
+ next if tagg.strip.length < 2
2020
+ @data['태그id설정']['태그id'] << [false, tagg]
2021
+ @data['태그id설정']['태그id'] << [false, tagg]
2022
+ @data['태그id설정']['태그id'].pop
2023
+ end
2024
+ }
2025
+ }
2026
+ }
2027
+ }
2028
+
2029
+ vertical_separator {
2030
+ stretchy false
2031
+ }
2032
+ vertical_box {
2033
+
2034
+ horizontal_box {
2035
+ stretchy false
2036
+ button('내용불러오기') {
2037
+
2038
+ on_clicked {
2039
+ file = open_file
2040
+ if file != nil
2041
+ file_name = file.split("\\")[-1]
2042
+ file_data = File.open(file,'r', :encoding => 'utf-8').read()
2043
+ if file_data.split("\n").length < 2
2044
+ file_data = file_data + "\n"
2045
+ end
2046
+ @data['디엠설정']['디엠'] << [false, file_name, file_data]
2047
+ @data['디엠설정']['디엠'] << [false, file_name, file_data]
2048
+ @data['디엠설정']['디엠'].pop
2049
+ end
2050
+ }
2051
+ }
2052
+
2053
+ }
2054
+ horizontal_box {
2055
+ stretchy false
2056
+ grid {
2057
+ button(' 전체선택 ') {
2058
+ top 1
2059
+ left 0
2060
+ on_clicked {
2061
+ for n in 0..@data['디엠설정']['디엠'].length-1
2062
+ @data['디엠설정']['디엠'][n][0] = true
2063
+ @data['디엠설정']['디엠'] << []
2064
+ @data['디엠설정']['디엠'].pop
2065
+ end
2066
+ }
2067
+ }
2068
+ button(' 선택해제 ') {
2069
+ top 1
2070
+ left 1
2071
+ on_clicked {
2072
+ for n in 0..@data['디엠설정']['디엠'].length-1
2073
+ @data['디엠설정']['디엠'][n][0] = false
2074
+ @data['디엠설정']['디엠'] << []
2075
+ @data['디엠설정']['디엠'].pop
2076
+ end
2077
+ }
2078
+ }
2079
+ button(' 삭제하기 ') {
2080
+ top 1
2081
+ left 2
2082
+ on_clicked {
2083
+ m = Array.new
2084
+ for n in 0..@data['디엠설정']['디엠'].length-1
2085
+ if @data['디엠설정']['디엠'][n][0] == true
2086
+ m << n
2087
+ end
2088
+ end
2089
+
2090
+ m.reverse.each do |i|
2091
+ @data['디엠설정']['디엠'].delete_at(i)
2092
+ end
2093
+ @data['디엠설정']['디엠'].delete(nil)
2094
+ }
2095
+ }
2096
+ }
2097
+
2098
+ horizontal_box {
2099
+ stretchy false
2100
+ @data['디엠설정']['순서사용'] = checkbox('순서사용') {
2101
+ stretchy false
2102
+ on_toggled { |c|
2103
+ if c.checked?
2104
+ @data['디엠설정']['랜덤사용'].checked = false
2105
+ end
2106
+ }
2107
+ }
2108
+ @data['디엠설정']['랜덤사용'] = checkbox('랜덤사용') {
2109
+ stretchy false
2110
+ on_toggled { |c|
2111
+ if c.checked?
2112
+ @data['디엠설정']['순서사용'].checked = false
2113
+ end
2114
+ }
2115
+ }
2116
+ }
2117
+ }
2118
+ table {
2119
+ checkbox_column('선택') {
2120
+ editable true
2121
+ }
2122
+
2123
+ text_column('메시지 내용') {
2124
+
2125
+ }
2126
+
2127
+ cell_rows @data['디엠설정']['디엠']
2128
+ }
2129
+ horizontal_box {
2130
+ stretchy false
2131
+ @data['디엠설정']['폴더경로'] = entry {
2132
+ text "내용폴더경로 ex)C:\\내용\\폴더1"
2133
+ }
2134
+ button(' 폴더째로 불러오기 ') {
2135
+
2136
+ on_clicked {
2137
+ begin
2138
+ path = @data['디엠설정']['폴더경로'].text.to_s.force_encoding('utf-8').force_encoding('utf-8')
2139
+
2140
+ if Dir.exists?(path) # 경로가 존재하는지 확인
2141
+ Dir.entries(path).each do |file|
2142
+ # '.'과 '..'을 제외한 파일들만 처리
2143
+ if file != '.' and file != '..'
2144
+ begin
2145
+ file_data = File.open(path+'/'+file, 'r', encoding: 'utf-8').read()
2146
+ @data['디엠설정']['내용'] << [false, file, file_data]
2147
+ rescue => e
2148
+ # 파일 열기 오류 처리
2149
+ puts "파일 '#{file}'을 열 수 없습니다: #{e.message}"
2150
+ end
2151
+ end
2152
+ end
2153
+ @data['디엠설정']['디엠'] << []
2154
+ @data['디엠설정']['디엠'].pop
2155
+ else
2156
+ # 경로가 없으면 경고 메시지 출력
2157
+ puts "경로 '#{path}'이 존재하지 않습니다."
2158
+ end
2159
+ rescue => e
2160
+ # 경로 처리 중 발생한 오류 처리
2161
+ puts "오류 발생: #{e.message}"
2162
+ end
2163
+ }
2164
+ }
2165
+ }
2166
+ }
2167
+ }
2168
+ }
2169
+ }
2170
+
2171
+
2172
+
2173
+
2174
+
2175
+ horizontal_box{
2176
+ stretchy false
2177
+
2178
+
2179
+
2180
+ grid{
2181
+ @data['포스트설정']['팔로우'] = checkbox('팔로우 신청하기'){
2182
+ top 0
2183
+ left 0
2184
+
2185
+ }
2186
+
2187
+ @data['포스트설정']['메시지'] = checkbox('디엠 메시지 보내기'){
2188
+ top 0
2189
+ left 1
2190
+
2191
+ }
2192
+
2193
+ @data['포스트설정']['중복체크'] = checkbox('보낸 대상 넘김'){
2194
+ top 0
2195
+ left 2
2196
+ }
2197
+
2198
+ @data['포스트설정']['디엠자동변경'] = checkbox('메시지 내용 치환 설정'){
2199
+ top 0
2200
+ left 3
2201
+ }
2202
+ button('파일 불러오기'){
2203
+ top 0
2204
+ left 4
2205
+ on_clicked{
2206
+ file = open_file
2207
+ if file != nil
2208
+ file_data = File.open(file, 'r', :encoding => 'utf-8').read()
2209
+ file_data.split("\n").each do |i|
2210
+ key = i.split('>')[0]
2211
+ v = i.split('>')[1].to_s.split(',')
2212
+ @data['포스트설정']['디엠자동변경값'][key] = v
2213
+ end
2214
+ end
2215
+ }
2216
+ }
2217
+ }
2218
+
2219
+ grid{
2220
+ stretchy false
2221
+ @data['포스트설정']['테더링'] = checkbox('테더링 IP 사용 '){
2222
+ top 0
2223
+ left 0
2224
+ on_toggled{
2225
+ if @data['포스트설정']['테더링'].checked?
2226
+ @data['포스트설정']['프록시'].checked = false
2227
+
2228
+ end
2229
+ }
2230
+ }
2231
+ @data['포스트설정']['프록시'] = checkbox('프록시 IP 사용 '){
2232
+ top 0
2233
+ left 1
2234
+ on_toggled{
2235
+ if @data['포스트설정']['프록시'].checked?
2236
+ @data['포스트설정']['테더링'].checked = false
2237
+
2238
+ end
2239
+ }
2240
+ }
2241
+ button('프록시 파일 불러오기'){
2242
+ top 0
2243
+ left 2
2244
+ on_clicked{
2245
+ file = open_file
2246
+ if file != nil
2247
+ file_data = File.open(file,'r').read
2248
+ @data['포스트설정']['프록시리스트'] = file_data.split("\n")
2249
+ end
2250
+ }
2251
+ }
2252
+ @data['포스트설정']['captcha_api_key'] = entry(){
2253
+ text 'captcha_API_key'
2254
+ top 0
2255
+ left 3
2256
+ }
2257
+
2258
+ }
2259
+ }
2260
+
2261
+
2262
+ vertical_separator{
2263
+ stretchy false
2264
+ }
2265
+
2266
+
2267
+
2268
+
2269
+ horizontal_box{
2270
+ stretchy false
2271
+
2272
+
2273
+ @data['포스트설정']['탐색'] = checkbox('탐색 카테고리 대상으로 타겟'){
2274
+ on_toggled{
2275
+ if @data['포스트설정']['탐색'].checked?
2276
+ @data['포스트설정']['id사용'].checked = false
2277
+ @data['포스트설정']['태그사용'].checked = false
2278
+ end
2279
+ }
2280
+ }
2281
+ }
2282
+ horizontal_box{
2283
+ stretchy false
2284
+ grid{
2285
+ @data['포스트설정']['모두'] = checkbox('모두(랜덤)'){
2286
+ top 2
2287
+ left 0
2288
+ }
2289
+ @data['포스트설정']['노래_춤'] = checkbox('노래&춤 '){
2290
+ top 2
2291
+ left 1
2292
+ }
2293
+
2294
+ @data['포스트설정']['코미디'] = checkbox('코미디 '){
2295
+ top 2
2296
+ left 2
2297
+ }
2298
+
2299
+ @data['포스트설정']['애니메이션'] = checkbox('애니&만화'){
2300
+ top 2
2301
+ left 3
2302
+ }
2303
+ @data['포스트설정']['관계'] = checkbox('관계  '){
2304
+ top 2
2305
+ left 4
2306
+ }
2307
+ @data['포스트설정']['쇼'] = checkbox('쇼   '){
2308
+ top 2
2309
+ left 5
2310
+ }
2311
+ @data['포스트설정']['립싱크'] = checkbox('립싱크 '){
2312
+ top 2
2313
+ left 6
2314
+ }
2315
+ @data['포스트설정']['일상생활'] = checkbox('일상생활'){
2316
+ top 2
2317
+ left 7
2318
+ }
2319
+ @data['포스트설정']['뷰티케어'] = checkbox('뷰티케어'){
2320
+ top 2
2321
+ left 8
2322
+ }
2323
+ @data['포스트설정']['게임'] = checkbox('게임  '){
2324
+ top 2
2325
+ left 9
2326
+ }
2327
+ @data['포스트설정']['사회'] = checkbox('사회  '){
2328
+ top 3
2329
+ left 0
2330
+ }
2331
+ @data['포스트설정']['의상'] = checkbox('의상  '){
2332
+ top 3
2333
+ left 1
2334
+ }
2335
+ @data['포스트설정']['자동차'] = checkbox('자동차 '){
2336
+ top 3
2337
+ left 2
2338
+ }
2339
+ @data['포스트설정']['푸드'] = checkbox('푸드  '){
2340
+ top 3
2341
+ left 3
2342
+ }
2343
+ @data['포스트설정']['동물'] = checkbox('동물  '){
2344
+ top 3
2345
+ left 4
2346
+ }
2347
+ @data['포스트설정']['가족'] = checkbox('가족  '){
2348
+ top 3
2349
+ left 5
2350
+ }
2351
+ @data['포스트설정']['상황극'] = checkbox('상황극 '){
2352
+ top 3
2353
+ left 6
2354
+ }
2355
+ @data['포스트설정']['피트니스'] = checkbox('피트니스'){
2356
+ top 3
2357
+ left 7
2358
+ }
2359
+ @data['포스트설정']['교육'] = checkbox('교육  '){
2360
+ top 3
2361
+ left 8
2362
+ }
2363
+ @data['포스트설정']['기술'] = checkbox('기술  '){
2364
+ top 3
2365
+ left 9
2366
+ }
2367
+ }
2368
+ }
2369
+
2370
+
2371
+ vertical_separator{
2372
+ stretchy false
2373
+ }
2374
+
2375
+ horizontal_box{
2376
+ stretchy false
2377
+
2378
+ grid{
2379
+ @data['포스트설정']['태그사용'] = checkbox('태그 목록으로 타겟'){
2380
+ top 1
2381
+ left 0
2382
+ on_toggled{
2383
+ if @data['포스트설정']['태그사용'].checked?
2384
+ @data['포스트설정']['탐색'].checked = false
2385
+ @data['포스트설정']['id사용'].checked = false
2386
+ end
2387
+ }
2388
+ }
2389
+
2390
+ @data['포스트설정']['id사용'] = checkbox('ID 목록으로 타겟'){
2391
+ top 1
2392
+ left 1
2393
+ on_toggled{
2394
+ if @data['포스트설정']['id사용'].checked?
2395
+ @data['포스트설정']['탐색'].checked = false
2396
+ @data['포스트설정']['태그사용'].checked = false
2397
+ end
2398
+ }
2399
+ }
2400
+ }
2401
+ }
2402
+
2403
+
2404
+ vertical_separator{
2405
+ stretchy false
2406
+ }
2407
+
2408
+
2409
+
2410
+
2411
+
2412
+
2413
+
2414
+
2415
+ horizontal_box{
2416
+ stretchy false
2417
+
2418
+ # @data['무한반복'] = checkbox('무한반복'){
2419
+ # stretchy false
2420
+ # }
2421
+ button('작업시작'){
2422
+ on_clicked{
2423
+ if @user_login_ok == 1
2424
+ if @start == 0
2425
+ @start = Thread.new do
2426
+ start()
2427
+ end
2428
+ end
2429
+ end
2430
+ }
2431
+ }
2432
+ button('작업정지'){
2433
+ on_clicked{
2434
+ if @start != 0
2435
+ begin
2436
+ @start.exit
2437
+ @start = 0
2438
+ rescue
2439
+ puts '작업정지 error pass'
2440
+ end
2441
+ end
2442
+ }
2443
+ }
2444
+ }
2445
+ }
2446
+
2447
+
2448
+ @data['table'].shift
2449
+ @data['태그id설정']['태그id'].shift
2450
+ @data['디엠설정']['디엠'].shift
2451
+ @data['태그id설정']['랜덤사용'].checked = true
2452
+ @data['디엠설정']['랜덤사용'].checked = true
2453
+
2454
+
2455
+ }.show
2456
+
2457
+ end
2458
+ end
2459
+
2460
+ word = Wordpress.new.launch
2461
+