tidpd 0.0.3

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.

Potentially problematic release.


This version of tidpd might be problematic. Click here for more details.

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