tblog_duopack 0.0.37 → 0.0.50

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 +4 -4
  2. data/lib/tblog_duopack.rb +461 -264
  3. metadata +3 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a1b0ef75c64d091e8224a4f7617c78c90df8a17c0a008942b8ce99d99fd0c89
4
- data.tar.gz: 858eb135367f64e0cd028f3883ee873b4bb443b67f762485e6af749508a07ed0
3
+ metadata.gz: bb08b4c28392011498f8a6683f49c393d6a5fe70d83839f5185719143c1954d1
4
+ data.tar.gz: ab184f85d87dbea4323e2c4c8a2ac558ddd6d3dce2cf22fd476e737c276a43cc
5
5
  SHA512:
6
- metadata.gz: 787f8a9f2f72c86dfccb4a721916ec4881fb16f8b8ef431aff25e97513bd028b1b6d6c898b018c0ac7cdc1167d2fbb9facc893d09aff559b2fd652c7f277b47d
7
- data.tar.gz: 587816d392ee1c14cd4ec2c6acbd6d7543598b8ab29c9a28d02513cd3883c1bcf5bdd232111395101e6af7467b197232d761201f465ed4298005a22509f2d282
6
+ metadata.gz: 86af3d2f53bb272ae1be71a518aa8df37b62fd9ba9b344c1f308d3b64aa5fe0257e09cc7cf1836e5d95e03edf7ad79830467c65b2c0495a46814cb6378e09397
7
+ data.tar.gz: 3469d71fd2b555ec39f895870e3c68c1f245c274058e86b957a9845b7a91cdf702b2db96cf1133c81ccd09f34eb58102b4a0a792521e828fc123ffe76dda25f2
data/lib/tblog_duopack.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'glimmer-dsl-libui'
2
2
  require 'selenium-webdriver'
3
- # require 'webdrivers'
4
3
  require 'iconv'
5
4
  require 'nokogiri'
6
5
  require 'http'
@@ -14,9 +13,9 @@ require 'clipboard'
14
13
  require 'crack'
15
14
  require 'uri'
16
15
  require 'cgi'
17
- require 'digest'
18
16
  require 'auto_click'
19
17
  require 'rainbow/refinement'
18
+ require 'httpclient'
20
19
  include AutoClickMethods
21
20
  using Rainbow
22
21
  include Glimmer
@@ -248,115 +247,186 @@ class Naver
248
247
  end
249
248
 
250
249
  def chrome_start(proxy)
251
- # 공통 옵션 설정
252
- Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
253
- options = Selenium::WebDriver::Chrome::Options.new
254
- options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
255
- options.add_extension('./crx/app.crx') # 확장 프로그램을 첫 번째 탭에 추가
256
- options.add_argument('--disable-blink-features=AutomationControlled')
257
- options.add_argument('--disable-popup-blocking')
258
- options.add_argument('--dns-prefetch-disable')
259
- options.add_argument('--disable-dev-shm-usage')
260
- options.add_argument('--disable-software-rasterizer')
261
- options.add_argument('--ignore-certificate-errors')
262
- options.add_argument('--disable-gpu') # GPU 가속 끄기
263
- options.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36') # user-agent 위조
264
- options.add_argument('--disable-web-security')
265
- options.add_argument('--allow-running-insecure-content')
266
- options.add_argument('--allow-insecure-localhost')
267
- options.add_argument('--no-sandbox')
268
- options.add_argument('--disable-translate')
269
- options.add_argument('--disable-extensions-file-access-check')
270
- options.add_argument('--disable-impl-side-painting')
250
+ # 공통 옵션 설정
251
+ begin
252
+ Selenium::WebDriver::Chrome::Service.driver_path = './chromedriver.exe'
253
+ rescue => e
254
+ puts "chromedriver 버전 불일치!!"
255
+ puts "아래 지침을 따라주세요."
256
+ puts "1.프로그램 종료!"
257
+ puts "2.크롬 업데이트!"
258
+ puts "3.프로그램 폴더 내부에 ★tip★-시작시-크롬창이....파일 실행"
259
+ puts "4.안내된 방식으로 크롬 드라이버 교체"
260
+ puts "5.재 시작"
261
+ exit 1
262
+ end
263
+ options = Selenium::WebDriver::Chrome::Options.new
264
+ options.add_argument('--no-first-run') # 자동 실행 시 나타나는 "첫 실행" 화면 방지
265
+ options.add_extension('./crx/app.crx') # 확장 프로그램을 첫 번째 탭에 추가
266
+ options.add_argument('--disable-blink-features=AutomationControlled')
267
+ options.add_argument('--disable-popup-blocking')
268
+ options.add_argument('--dns-prefetch-disable')
269
+ options.add_argument('--disable-dev-shm-usage')
270
+ options.add_argument('--disable-software-rasterizer')
271
+ options.add_argument('--ignore-certificate-errors')
272
+ options.add_argument('--disable-gpu') # GPU 가속 끄기
273
+ options.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36') # user-agent 위조
274
+ options.add_argument('--disable-web-security')
275
+ options.add_argument('--allow-running-insecure-content')
276
+ options.add_argument('--allow-insecure-localhost')
277
+ options.add_argument('--no-sandbox')
278
+ options.add_argument('--disable-translate')
279
+ options.add_argument('--disable-extensions-file-access-check')
280
+ options.add_argument('--disable-impl-side-painting')
281
+ options.add_argument('--log-level=3')
282
+ # 자동화된 테스트 제거
283
+ options.exclude_switches = ['enable-automation']
271
284
 
272
- # 자동화된 테스트 제거
273
- options.exclude_switches = ['enable-automation']
274
-
275
- options.add_preference("profile.password_manager_enabled", false) # 비밀번호 관리자 비활성화
276
- options.add_preference("credentials_enable_service", false) # 비밀번호 저장 기능 비활성화
277
- #options.add_preference("profile.managed_default_content_settings.cookies", 2) # 쿠키 관련 팝업 차단
278
- options.add_preference("profile.default_content_setting_values.notifications", 2) # 알림 차단
279
- options.add_argument("--disable-save-password-bubble") # 비밀번호 저장 팝업 차단
285
+ options.add_preference("profile.password_manager_enabled", false) # 비밀번호 관리자 비활성화
286
+ options.add_preference("credentials_enable_service", false) # 비밀번호 저장 기능 비활성화
287
+ #options.add_preference("profile.managed_default_content_settings.cookies", 2) # 쿠키 관련 팝업 차단
288
+ options.add_preference("profile.default_content_setting_values.notifications", 2) # 알림 차단
289
+ options.add_argument("--disable-save-password-bubble") # 비밀번호 저장 팝업 차단
290
+
280
291
 
292
+ # Proxy 설정
293
+ if proxy != ''
294
+ options.add_argument('--proxy-server=' + proxy.to_s.force_encoding('utf-8'))
295
+ end
296
+
297
+ # 브라우저 실행
298
+ begin
299
+ # 'capabilities'과 'options' 배열로 설정
300
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
301
+ capabilities["goog:chromeOptions"] = options.as_json
281
302
 
282
- # Proxy 설정
283
- if proxy != ''
284
- options.add_argument('--proxy-server=' + proxy.to_s.force_encoding('utf-8'))
285
- end
303
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
304
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
286
305
 
287
- # 브라우저 실행
288
- begin
289
- # 'capabilities'과 'options' 배열로 설정
290
- capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
291
- capabilities["goog:chromeOptions"] = options.as_json
306
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
307
+
308
+ sleep(1)
309
+ # 번째 탭에서 로그인 페이지 열기
310
+ @driver.get('https://www.tistory.com/auth/login')
311
+ sleep(1)
292
312
 
293
- # Selenium 4에서는 'capabilities'만 사용하는 방식
294
- @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
313
+ # 현재 핸들 저장
314
+ main_tab = @driver.window_handle
295
315
 
296
- @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
297
-
298
- sleep(1)
299
- # 두 번째 탭에서 로그인 페이지 열기
300
- @driver.get('https://www.tistory.com/auth/login')
301
- sleep(1)
302
-
303
- rescue => e
304
-
305
- puts "Error: #{e.message}"
306
- puts 'Using default Chrome driver without proxy'
307
- # 'capabilities'과 'options' 배열로 설정
308
- capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
309
- capabilities["goog:chromeOptions"] = options.as_json
316
+ # 모든 순회
317
+ @driver.window_handles.each do |handle|
318
+ next if handle == main_tab # 현재 탭은 남긴다
310
319
 
311
- # Selenium 4에서는 'capabilities'만 사용하는 방식
312
- @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
320
+ @driver.switch_to.window(handle)
321
+ @driver.close # 다른 닫기
322
+ end
313
323
 
314
- @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
315
-
316
- # 첫 번째 탭에서 확장 프로그램을 로드
317
- #@driver.get("chrome-extension://ifibfemgeogfhoebkmokieepdoobkbpo/options/options.html")
318
- sleep(1)
319
- # 번째 탭에서 로그인 페이지 열기
320
- @driver.get('https://www.tistory.com/auth/login')
321
- sleep(1)
322
- end
323
- end
324
-
325
- def login(user_id, user_pw, proxy, captcha_api_key)
326
- chrome_start(proxy)
327
- @captcha_api_key = captcha_api_key
328
- @user_id = user_id
324
+ # 다시 로그인 탭으로 전환
325
+ @driver.switch_to.window(main_tab)
326
+ sleep(1)
327
+ rescue => e
328
+
329
+ puts "Error: #{e.message}"
330
+ puts 'Using default Chrome driver without proxy'
331
+ # 'capabilities'과 'options' 배열로 설정
332
+ capabilities = Selenium::WebDriver::Remote::Capabilities.chrome
333
+ capabilities["goog:chromeOptions"] = options.as_json
329
334
 
330
- user_cookie_file = []
331
- begin
332
- Dir.entries('./cookie').each do |i|
333
- if i != '.' && i != '..'
334
- user_cookie_file << i
335
- end
336
- end
337
- rescue
338
- end
335
+ # Selenium 4에서는 'capabilities'만 사용하는 방식
336
+ @driver = Selenium::WebDriver.for(:chrome, capabilities: [capabilities, options])
339
337
 
340
- @cookie4 = {}
341
- if user_cookie_file.include?(user_id+'.txt')
342
- f = File.open('./cookie/'+user_id+'.txt', 'r')
343
- @cookie4 = JSON.parse(f.read)
344
- f.close
345
- end
338
+ @driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: function(){ return false; }});") # 셀레니움 감지 방지
346
339
 
347
- # 기존 쿠키가 있으면 쿠키를 추가
348
- begin
349
- @cookie4.each do |i|
350
- @driver.manage.add_cookie(name: i['name'], value: i['value'], same_site: i['same_site'], domain: i['domain'], path: i['path'])
340
+ # 번째 탭에서 확장 프로그램을 로드
341
+ #@driver.get("chrome-extension://ifibfemgeogfhoebkmokieepdoobkbpo/options/options.html")
342
+ sleep(1)
343
+ # 번째 탭에서 로그인 페이지 열기
344
+ @driver.get('https://www.tistory.com/auth/login')
345
+ sleep(1)
346
+
347
+ # 현재 탭 핸들 저장
348
+ main_tab = @driver.window_handle
349
+
350
+ # 모든 탭 순회
351
+ @driver.window_handles.each do |handle|
352
+ next if handle == main_tab # 현재 탭은 남긴다
353
+
354
+ @driver.switch_to.window(handle)
355
+ @driver.close # 다른 탭 닫기
356
+ end
357
+
358
+ # 다시 로그인 탭으로 전환
359
+ @driver.switch_to.window(main_tab)
360
+ sleep(1)
351
361
  end
352
- rescue
353
362
  end
354
363
 
355
- @driver.switch_to.window(@driver.window_handles.last)
356
- sleep(1.5)
357
- @driver.get('https://www.tistory.com/auth/login')
364
+ def login(user_id, user_pw, proxy, captcha_api_key)
365
+ chrome_start(proxy)
366
+ @captcha_api_key = captcha_api_key
367
+ @user_id = user_id
358
368
 
369
+ if captcha_api_key != 'captcha_api_key 입력' && !captcha_api_key.to_s.strip.empty?
370
+ puts "캡처 키 확인 됨: '#{captcha_api_key}'".red
371
+ @driver.switch_to.new_window(:tab)
372
+ sleep(1.5)
373
+ @driver.switch_to.window(@driver.window_handles[1])
374
+ @driver.navigate.to("chrome-extension://ifibfemgeogfhoebkmokieepdoobkbpo/options/options.html")
375
+ sleep(3)
376
+ @driver.find_element(:xpath, '//*[@name="apiKey"]').click
377
+ Clipboard.copy(captcha_api_key)
378
+ sleep(0.5)
379
+ @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
380
+ sleep(0.5)
381
+ @driver.find_element(:xpath, '//*[@data-lang="login"]').click
382
+
383
+ begin
384
+ sleep(2)
385
+ @driver.switch_to.alert.dismiss
386
+ sleep(1)
387
+ rescue
388
+ end
389
+ @driver.close
390
+ sleep(1)
391
+ @driver.switch_to.window(@driver.window_handles[0])
392
+ sleep(2)
393
+ else
394
+ end
395
+
396
+ user_cookie_file = []
397
+ begin
398
+ Dir.entries('./cookie').each do |i|
399
+ if i != '.' && i != '..'
400
+ user_cookie_file << i
401
+ end
402
+ end
403
+ rescue
404
+ end
405
+
406
+ @cookie4 = {}
407
+ if user_cookie_file.include?(user_id+'.txt')
408
+ f = File.open('./cookie/'+user_id+'.txt', 'r')
409
+ @cookie4 = JSON.parse(f.read)
410
+ f.close
411
+ end
412
+
413
+
414
+
415
+ # 기존 쿠키가 있으면 쿠키를 추가
416
+ begin
417
+ @cookie4.each do |i|
418
+ @driver.manage.add_cookie(name: i['name'], value: i['value'], same_site: i['same_site'], domain: i['domain'], path: i['path'])
419
+ end
420
+ rescue
421
+ end
422
+
423
+
424
+ sleep(1.5)
425
+ @driver.get('https://www.tistory.com/auth/login')
359
426
  sleep(1)
427
+ @driver.switch_to.window(@driver.window_handles[0])
428
+
429
+
360
430
  begin
361
431
  wait = Selenium::WebDriver::Wait.new(:timeout => 3)
362
432
  wait.until { @driver.find_element(:xpath, '//*[@id="cMain"]/div/div/div/div/a[2]/span[2]') }
@@ -394,25 +464,7 @@ class Naver
394
464
  wait.until { @driver.find_element(:xpath, '//*[@id="captchaContainer"]') }
395
465
  puts '-[√] 로그인 중 캡챠 요구 발생 처리 진행.......'.yellow
396
466
  sleep(1)
397
- @driver.switch_to.window(@driver.window_handles[0])
398
- sleep(1)
399
- @driver.find_element(:xpath, '//*[@name="apiKey"]').click
400
- Clipboard.copy(captcha_api_key)
401
- sleep(0.5)
402
- @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
403
- sleep(0.5)
404
- @driver.find_element(:xpath, '//*[@data-lang="login"]').click
405
-
406
- begin
407
- sleep(2)
408
- @driver.switch_to.alert.dismiss
409
- sleep(1)
410
- rescue
411
-
412
- end
413
-
414
- # 두 번째 탭으로 전환
415
- @driver.switch_to.window(@driver.window_handles[1])
467
+
416
468
 
417
469
  begin
418
470
  wait = Selenium::WebDriver::Wait.new(:timeout => 7)
@@ -518,6 +570,7 @@ class Naver
518
570
 
519
571
 
520
572
 
573
+
521
574
  def update(title, content, option, url, keyword, captcha_api_key)#dd_time
522
575
  puts 'start...'.yellow
523
576
  puts(url)
@@ -540,20 +593,6 @@ class Naver
540
593
  end
541
594
 
542
595
 
543
-
544
-
545
-
546
-
547
-
548
-
549
-
550
-
551
-
552
-
553
-
554
-
555
-
556
-
557
596
  #@driver.manage.window.maximize
558
597
  #창 크기 최대화
559
598
  category2 = option['category'].to_s
@@ -578,12 +617,6 @@ class Naver
578
617
  end
579
618
 
580
619
 
581
-
582
-
583
-
584
-
585
-
586
-
587
620
  sleep(1)
588
621
 
589
622
  begin
@@ -596,10 +629,6 @@ class Naver
596
629
  puts e
597
630
  end
598
631
 
599
-
600
-
601
-
602
-
603
632
 
604
633
 
605
634
  sleep(1)
@@ -1239,29 +1268,7 @@ class Naver
1239
1268
  wait.until { @driver.find_element(:xpath, '//*[@id="captchaContainer"]') }
1240
1269
  puts '-[√] 로그인 중 캡챠 요구 발생 처리 진행.......'.yellow
1241
1270
  sleep(1)
1242
- @driver.switch_to.window(@driver.window_handles[0])
1243
- sleep(1)
1244
- @driver.find_element(:xpath, '//*[@name="apiKey"]').click
1245
- sleep(0.5)
1246
- @driver.find_element(:xpath, '//*[@name="apiKey"]').clear
1247
- sleep(0.5)
1248
- Clipboard.copy(captcha_api_key)
1249
- sleep(0.5)
1250
- @driver.action.key_down(:control).send_keys('v').key_up(:control).perform
1251
- sleep(0.5)
1252
- @driver.find_element(:xpath, '//*[@data-lang="login"]').click
1253
1271
 
1254
- begin
1255
- sleep(2)
1256
- @driver.switch_to.alert.dismiss
1257
- sleep(1)
1258
- rescue
1259
-
1260
- end
1261
-
1262
- # 두 번째 탭으로 전환
1263
- @driver.switch_to.window(@driver.window_handles[1])
1264
-
1265
1272
  begin
1266
1273
  wait = Selenium::WebDriver::Wait.new(:timeout => 7)
1267
1274
  wait.until { @driver.find_element(:xpath, '//*[@data-state="ready"]') }
@@ -1390,7 +1397,7 @@ class Naver
1390
1397
  @driver.find_element(:xpath, '//*[@id="publish-btn"]').click #등록완료버튼
1391
1398
  rescue
1392
1399
  # 타임아웃을 100초로 설정
1393
- wait = Selenium::WebDriver::Wait.new(:timeout => 100)
1400
+ wait = Selenium::WebDriver::Wait.new(:timeout => 120)
1394
1401
  # 요소가 나타날 때까지 100초 동안 기다립니다.
1395
1402
  wait.until { @driver.find_element(:xpath, '//*[@data-state="solved"]') }
1396
1403
  sleep(2)
@@ -1436,14 +1443,7 @@ class Naver
1436
1443
  rescue
1437
1444
  end
1438
1445
  end
1439
-
1440
-
1441
-
1442
-
1443
-
1444
-
1445
-
1446
-
1446
+
1447
1447
 
1448
1448
  end
1449
1449
  end
@@ -1637,21 +1637,81 @@ class Wordpress
1637
1637
 
1638
1638
 
1639
1639
 
1640
- def auto_image
1640
+ def auto_image(keyword = nil)
1641
+ keyword ||= @keyword
1642
+ puts "키워드: #{keyword}"
1643
+
1644
+ client = HTTPClient.new
1645
+ client.default_header = {
1646
+ 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '\
1647
+ '(KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36',
1648
+ 'Accept' => 'application/json, text/javascript, */*; q=0.01',
1649
+ 'Accept-Language' => 'en-US,en;q=0.9',
1650
+ 'Referer' => "https://unsplash.com/s/photos/#{URI.encode_www_form_component(keyword)}",
1651
+ 'X-Requested-With' => 'XMLHttpRequest'
1652
+ }
1653
+
1654
+ retry_count = 0
1655
+ max_retries = 10
1656
+ results = []
1657
+
1641
1658
  begin
1642
1659
  page = rand(1..15)
1643
- http = HTTP.get('https://unsplash.com/napi/photos?per_page=12&page='+page.to_s)
1644
- json = JSON.parse(http.to_s)
1645
- mm = Array.new
1646
- json.each do |i|
1647
- mm << i['urls']['full']
1660
+ url = "https://unsplash.com/napi/search/photos?query=#{URI.encode_www_form_component(keyword)}&page=#{page}&per_page=20"
1661
+ puts "Request URL: #{url}"
1662
+ res = client.get(url)
1663
+
1664
+ unless res.status == 200
1665
+ puts "HTTP Error: #{res.status}"
1666
+ raise "HTTP Error"
1648
1667
  end
1649
- url = mm.sample
1650
- Down.download(url, destination: "./image/memory.png")
1651
- rescue
1652
- puts 'auto_image 일시적 error 5초후 제시도...'
1653
- sleep(5)
1668
+
1669
+ json = JSON.parse(res.body)
1670
+ results = json['results']
1671
+ mm = []
1672
+
1673
+ results.each do |photo|
1674
+ full_url = photo.dig('urls', 'full').to_s
1675
+ regular_url = photo.dig('urls', 'regular').to_s
1676
+
1677
+ if full_url.start_with?("https://images.unsplash.com/photo-") &&
1678
+ regular_url.include?("1080")
1679
+ mm << full_url
1680
+ end
1681
+ end
1682
+
1683
+ if mm.empty?
1684
+ raise "No matching image"
1685
+ end
1686
+
1687
+ selected_url = mm.sample
1688
+ Down.download(selected_url, destination: "./image/memory.png")
1689
+ puts "이미지 다운로드 완료: #{selected_url}"
1690
+
1691
+ rescue => e
1692
+ retry_count += 1
1693
+ puts "auto_image 에러: #{e.message} (재시도 #{retry_count}/#{max_retries})"
1694
+ sleep(3)
1695
+ if retry_count < max_retries
1654
1696
  retry
1697
+ else
1698
+ puts "최대 재시도 초과. 조건 무시하고 랜덤 이미지 다운로드 시도..."
1699
+
1700
+ if results && !results.empty?
1701
+ random_photo = results.sample
1702
+ fallback_url = random_photo.dig('urls', 'full')
1703
+ if fallback_url
1704
+ Down.download(fallback_url, destination: "./image/memory.png")
1705
+ puts "랜덤 이미지 다운로드 완료: #{fallback_url}"
1706
+ else
1707
+ puts "랜덤 이미지 URL을 찾을 수 없습니다. 단색 배경 이미지 생성합니다."
1708
+ color_image
1709
+ end
1710
+ else
1711
+ puts "이미지 결과가 없어 다운로드할 수 없습니다. 단색 배경 이미지 생성합니다."
1712
+ color_image
1713
+ end
1714
+ end
1655
1715
  end
1656
1716
  end
1657
1717
 
@@ -1663,35 +1723,52 @@ class Wordpress
1663
1723
 
1664
1724
  def save_image
1665
1725
  if @data['이미지설정']['이미지'].length == 0
1666
-
1726
+ return
1727
+ end
1728
+
1729
+ if @data['이미지설정']['순서사용'].checked?
1730
+ image_path = @data['이미지설정']['이미지'][@image_counter][2]
1731
+ @image_counter += 1
1732
+ if @image_counter > @data['이미지설정']['이미지'].length - 1
1733
+ @image_counter = 0
1734
+ end
1667
1735
  else
1668
- if @data['이미지설정']['순서사용'].checked?
1669
- image_path = @data['이미지설정']['이미지'][@image_counter][2]
1670
- @image_counter += 1
1671
- if @image_counter > @data['이미지설정']['이미지'].length-1
1672
- @image_counter = 0
1673
- end
1674
- else
1675
- image_path = @data['이미지설정']['이미지'].sample[2]
1736
+ # 초기화가 안됐거나 다 썼으면 새롭게 섞는다
1737
+ @shuffled_images ||= []
1738
+ if @shuffled_images.empty?
1739
+ @shuffled_images = @data['이미지설정']['이미지'].shuffle
1676
1740
  end
1677
- img = Magick::Image.read(image_path).first
1678
- img.write('./image/memory.png')
1741
+
1742
+ image_path = @shuffled_images.shift[2]
1679
1743
  end
1744
+
1745
+ img = Magick::Image.read(image_path).first
1746
+ img.write('./image/memory.png')
1680
1747
  end
1681
1748
 
1682
1749
  def change_image_size(w)
1683
1750
  img = Magick::Image.read('./image/memory.png').first
1684
1751
  width = img.columns
1685
1752
  height = img.rows
1686
- begin
1687
- if @data['image_type'][0].checked? or @data['image_type'][2].checked?
1688
- img.resize!(w, w*(height.to_f/width.to_f))
1689
- else
1690
- img.resize!(w, w)
1753
+
1754
+ # '원본' 선택된 경우, 리사이징을 하지 않고 원본 이미지를 그대로 반환
1755
+ if w == 'original'
1756
+ return img # 원본 이미지 그대로 반환
1757
+ else
1758
+ begin
1759
+ if @data['image_type'][0].checked? or @data['image_type'][2].checked?
1760
+ # 비율을 맞추어 리사이징
1761
+ img.resize!(w, w * (height.to_f / width.to_f))
1762
+ else
1763
+ # 정사각형으로 리사이징
1764
+ img.resize!(w, w)
1765
+ end
1766
+ rescue
1767
+ img.resize!(w, w) # 예외 처리 시에도 리사이징
1691
1768
  end
1692
- rescue
1693
- img.resize!(w, w)
1694
1769
  end
1770
+
1771
+ # 리사이징된 이미지 저장
1695
1772
  img.write('./image/memory.png')
1696
1773
  end
1697
1774
 
@@ -1763,22 +1840,28 @@ class Wordpress
1763
1840
  auto_image()
1764
1841
  end
1765
1842
 
1766
- image_size = [480,740,650,550,480]
1843
+ # '원본'을 포함한 이미지 크기 옵션 추가
1844
+ image_size = [480, 740, 650, 550, 480, 'original']
1767
1845
  size = 0
1768
- for n in 0..4
1846
+
1847
+ for n in 0..5 # 0부터 5까지 반복, '원본' 옵션까지 포함
1769
1848
  if @data['image_size'][n].checked?
1770
- if n == 0
1771
- size = image_size.sample
1849
+ if n == 5 # '원본'이 선택되었을 경우
1850
+ size = 'original'
1851
+ elsif n == 0
1852
+ size = image_size.sample # 랜덤 선택
1772
1853
  else
1773
1854
  size = image_size[n]
1774
1855
  end
1775
1856
  end
1776
1857
  end
1858
+
1859
+ # '원본'이 선택되지 않았다면 기본 값 설정
1777
1860
  if size == 0
1778
1861
  size = 480
1779
1862
  end
1780
-
1781
- change_image_size(size)
1863
+
1864
+ change_image_size(size) # 크기 변경 함수 호출
1782
1865
 
1783
1866
  if @data['이미지설정']['필터사용'].checked?
1784
1867
  image_filter()
@@ -2173,6 +2256,22 @@ class Wordpress
2173
2256
  @data['table'].pop
2174
2257
  #제목끝
2175
2258
  # content = " #{content} "
2259
+ if @data['포스트설정']['gpt키워드'].checked?
2260
+ gpt_keyword_prompt = @data['포스트설정']['gpt키워드_프롬프트'].text.to_s.force_encoding('utf-8')
2261
+ gpt_keyword_prompt_sample = gpt_keyword_prompt.strip.empty? ? "프롬프트: 관련된 글을 1500자에서 2500자 사이로 만들어줘" : gpt_keyword_prompt
2262
+ chat = Chat.new(@data['포스트설정']['api_key'].text.to_s.force_encoding('utf-8'), gpt_keyword_prompt)
2263
+ gpt_text = chat.message(keyword)
2264
+ #content = content.to_s + "\n(자동생성글)\n" + gpt_text.to_s
2265
+ content = content.to_s + "(자동생성글)" + gpt_text.to_s
2266
+ elsif @data['포스트설정']['내용을자동생성'].checked?
2267
+ content = auto_text
2268
+ elsif @data['포스트설정']['내용과자동생성'].checked?
2269
+ #content = content + "\n(자동생성글)\n" + auto_text
2270
+ content = content + "(자동생성글)" + auto_text
2271
+ end
2272
+ @data['table'][index][-1] = 45
2273
+ @data['table'] << []
2274
+ @data['table'].pop
2176
2275
 
2177
2276
  if @data['포스트설정']['특정단어굵기'].checked?
2178
2277
  content2 = ''
@@ -2212,7 +2311,7 @@ class Wordpress
2212
2311
  content = content2
2213
2312
  end
2214
2313
  end
2215
- @data['table'][index][-1] = 35
2314
+ @data['table'][index][-1] = 50
2216
2315
  @data['table'] << []
2217
2316
  @data['table'].pop
2218
2317
  if @data['포스트설정']['단어크기변경'].checked?
@@ -2233,19 +2332,7 @@ class Wordpress
2233
2332
  @data['table'][index][-1] = 50
2234
2333
  @data['table'] << []
2235
2334
  @data['table'].pop
2236
- if @data['포스트설정']['gpt키워드'].checked?
2237
- gpt_keyword_prompt = @data['포스트설정']['gpt키워드_프롬프트'].text.to_s.force_encoding('utf-8')
2238
- gpt_keyword_prompt_sample = gpt_keyword_prompt.strip.empty? ? "프롬프트: 관련된 글을 1500자에서 2500자 사이로 만들어줘" : gpt_keyword_prompt
2239
- chat = Chat.new(@data['포스트설정']['api_key'].text.to_s.force_encoding('utf-8'), gpt_keyword_prompt)
2240
- gpt_text = chat.message(keyword)
2241
- #content = content.to_s + "\n(자동생성글)\n" + gpt_text.to_s
2242
- content = content.to_s + "(자동생성글)" + gpt_text.to_s
2243
- elsif @data['포스트설정']['내용을자동생성'].checked?
2244
- content = auto_text
2245
- elsif @data['포스트설정']['내용과자동생성'].checked?
2246
- #content = content + "\n(자동생성글)\n" + auto_text
2247
- content = content + "(자동생성글)" + auto_text
2248
- end
2335
+
2249
2336
 
2250
2337
  if @data['포스트설정']['내용키워드삽입'].checked?
2251
2338
  puts '내용키워드삽입...'
@@ -2427,7 +2514,6 @@ class Wordpress
2427
2514
 
2428
2515
  if @data['포스트설정']['내용사진자동삽입'].checked?
2429
2516
  puts '내용사진자동삽입...'
2430
-
2431
2517
  sn = @data['포스트설정']['내용사진자동삽입시작숫자'].text.to_s.force_encoding('utf-8').to_i
2432
2518
  en = @data['포스트설정']['내용사진자동삽입끝숫자'].text.to_s.force_encoding('utf-8').to_i
2433
2519
 
@@ -2439,54 +2525,71 @@ class Wordpress
2439
2525
  end
2440
2526
 
2441
2527
  if cn != 0
2442
- # 내용 분할 (자동 생성 글과 텍스트)
2443
- content5 = content.split("(자동생성글)")[0].to_s.split("\n")
2444
- content55 = content.split("(자동생성글)")[1].to_s if @data['포스트설정']['내용과자동생성'].checked? || @data['포스트설정']['gpt키워드'].checked?
2445
-
2446
- # 줄 찾기
2447
- empty_positions = content5.each_with_index.select { |line, index| line.strip.empty? }.map { |line, index| index }
2448
-
2449
- # 줄이 부족하면 텍스트 끝에 빈 줄 추가
2450
- if empty_positions.length < cn
2451
- # 부족한 빈 줄의 수
2452
- missing_empty_lines = cn - empty_positions.length
2453
- missing_empty_lines.times do
2454
- content5 << "" # 텍스트 마지막에 빈 줄 추가
2528
+ # content5 구성 위치 배열 생성
2529
+ if @data['포스트설정']['내용과자동생성'].checked?
2530
+ if @data['포스트설정']['자동글 수식에 입력'].checked?
2531
+ content5 = content.split("(자동생성글)")[0].to_s.split("\n")
2532
+ content55 = content.split("(자동생성글)")[1].to_s
2533
+ position = content5.length.times.to_a.sample(cn).sort.reverse
2534
+ else
2535
+ content5 = content.split("(자동생성글)")[0].to_s.split("\n")
2536
+ content55 = content.split("(자동생성글)")[1].to_s
2537
+ position = content5.length.times.to_a.sample(cn).sort.reverse
2455
2538
  end
2456
- empty_positions = content5.each_with_index.select { |line, index| line.strip.empty? }.map { |line, index| index } # 다시 빈 줄 위치 계산
2539
+ elsif @data['포스트설정']['gpt키워드'].checked?
2540
+ content5 = content.split("(자동생성글)")[1].to_s.split("\n")
2541
+ content_prefix = content.split("(자동생성글)")[0].to_s
2542
+ content55 = ''
2543
+ position = content5.length.times.to_a.sample(cn).sort.reverse
2544
+ else
2545
+ content5 = content.split("\n")
2546
+ content55 = ''
2547
+ position = content5.length.times.to_a.sample(cn).sort.reverse
2457
2548
  end
2458
2549
 
2459
- # 이미지 삽입할 위치를 지정 (빈 줄 위치에 삽입)
2460
- position = empty_positions[0..cn-1]
2550
+ # 중복 필터링 로직
2551
+ while true
2552
+ check11 = 0
2553
+ position.each_with_index do |pos, idx|
2554
+ if content5[pos].to_s.include?('style') || content5[pos].to_s.include?('<') || content5[pos].to_s.include?('>')
2555
+ check11 = 1
2556
+ position[idx] += 4
2557
+ end
2558
+ end
2559
+ break if check11 == 0
2560
+ end
2461
2561
 
2462
- # 이미지 URL 가져오기
2463
2562
  if @data['포스트설정']['내용과자동생성'].checked? || @data['포스트설정']['gpt키워드'].checked?
2464
- image_url22 = get_image_file().force_encoding('utf-8')
2563
+ sleep(2)
2564
+ puts '이미지 자동 세탁 중 · · · '
2465
2565
  end
2466
-
2467
- # 각 위치에 이미지를 하나씩만 삽입
2566
+
2567
+ p content5
2568
+ puts content55
2569
+ p position
2570
+
2571
+ sleep(2)
2468
2572
  position.each do |i|
2469
- image_url = get_image_file().force_encoding('utf-8')
2470
- puts '사진넣는위치 => ' + i.to_s
2471
-
2472
- # 링크가 있을 경우 링크 포함
2573
+ image_url22 = get_image_file().force_encoding('utf-8')
2473
2574
  if @data['포스트설정']['내용사진링크'].checked?
2474
- image_memory << "<a href='" + @data['포스트설정']['내용사진링크값'].text.to_s.force_encoding('utf-8') + "'><img src='" + image_url + "' alt='" + keyword.force_encoding('utf-8') + "'></a>"
2475
- content5[i] = '**image**' # 줄에 이미지를 삽입
2575
+ image_tag = '<a href="' + @data['포스트설정']['내용사진링크값'].text.to_s.force_encoding('utf-8') +
2576
+ '"><img src="' + image_url22 + '" alt="' + keyword.force_encoding('utf-8') + '" class="aligncenter size-full"></a>'
2476
2577
  else
2477
- image_memory << "<img src='" + image_url + "' alt='" + keyword + "' class='aligncenter size-full'>"
2478
- content5[i] = '**image**' # 빈 줄에 이미지를 삽입
2578
+ image_tag = '<img src="' + image_url22 + '" alt="' + keyword + '" class="aligncenter size-full">'
2479
2579
  end
2580
+ content5.insert(i, image_tag)
2480
2581
  end
2481
2582
 
2482
- # 자동 생성된 내용과 합치기
2483
- if @data['포스트설정']['내용과자동생성'].checked? || @data['포스트설정']['gpt키워드'].checked?
2583
+ sleep(2)
2584
+ puts '이미지 자동 세탁 완료 · · · '
2585
+
2586
+ if @data['포스트설정']['내용과자동생성'].checked?
2484
2587
  content = content5.join("\n") + '(자동생성글)' + content55
2588
+ elsif @data['포스트설정']['gpt키워드'].checked?
2589
+ content = content_prefix + "(자동생성글)" + content5.join("\n")
2485
2590
  else
2486
2591
  content = content5.join("\n")
2487
2592
  end
2488
-
2489
- puts content
2490
2593
  end
2491
2594
  end
2492
2595
 
@@ -2498,12 +2601,22 @@ class Wordpress
2498
2601
  content = content_memory[0]
2499
2602
  content_end = content_memory[1].to_s
2500
2603
 
2604
+ if @data['포스트설정']['gpt키워드'].checked?
2605
+ if @data['포스트설정']['gpt상단'].checked?
2606
+ content = "(자동생성글)\n" + content_end + "\n" + content
2607
+ else
2608
+ content = content + "\n(자동생성글)\n" + content_end
2609
+ end
2610
+ else
2611
+ content = content + "\n(자동생성글)\n" + content_end
2612
+ end
2613
+
2501
2614
  if @data['포스트설정']['특정단어키워드로변경'].checked?
2502
2615
  @data['포스트설정']['특정단어키워드로변경값'].text.to_s.force_encoding('utf-8').split(',').each do |i|
2503
2616
  content = content.split(i.force_encoding('utf-8')).join(keyword)
2504
2617
  end
2505
2618
  end
2506
-
2619
+
2507
2620
  @data['table'][index][-1] = 75
2508
2621
  @data['table'] << []
2509
2622
  @data['table'].pop
@@ -2619,11 +2732,76 @@ class Wordpress
2619
2732
  # end
2620
2733
  #end
2621
2734
 
2622
- if @data['포스트설정']['지도로변경'].checked?
2623
- @data['포스트설정']['지도로변경단어'].text.to_s.force_encoding('utf-8').split(',').each do |i|
2624
- content = content.split(i.force_encoding('utf-8')).join(" ""<koreamap>"+@data['포스트설정']['지도주소'].text.to_s.force_encoding('utf-8').force_encoding('utf-8')+"</koreamap>")
2735
+ if @data['포스트설정']['지도로변경'].checked?
2736
+ map_address = @data['포스트설정']['지도주소'].text.to_s.force_encoding('utf-8')
2737
+ change_words = @data['포스트설정']['지도로변경단어'].text.to_s.force_encoding('utf-8').split(',')
2738
+
2739
+ content_lines = content.split("\n")
2740
+ new_lines = []
2741
+
2742
+ content_lines.each do |line|
2743
+ # 이미 처리된 줄은 건너뜀
2744
+ if line.include?("<koreamap>")
2745
+ new_lines << line
2746
+ next
2747
+ end
2748
+
2749
+ processed = false
2750
+
2751
+ change_words.each do |change_word|
2752
+ # 1. [단어][주소] 패턴 처리: 분리 + 출력
2753
+ if line =~ /#{Regexp.escape(change_word)}\[(.*?)\]/
2754
+ match_data = line.match(/(.*)#{Regexp.escape(change_word)}\[(.*?)\](.*)/)
2755
+ if match_data
2756
+ before = match_data[1]
2757
+ address = match_data[2]
2758
+ after = match_data[3]
2759
+
2760
+ # 태그 추출
2761
+ open_tag = before[/<p[^>]*?>/i] || ""
2762
+ close_tag = after[/<\/p>/i] || ""
2763
+
2764
+ # 텍스트 정리
2765
+ before_text = before.sub(open_tag, '')
2766
+ after_text = after.sub(close_tag, '')
2767
+
2768
+ # 줄 분리
2769
+ new_lines << "#{open_tag}#{before_text}</p>" unless before_text.strip.empty?
2770
+ new_lines << "<koreamap>#{address}</koreamap>"
2771
+ new_lines << "#{open_tag}#{after_text}#{close_tag}" unless after_text.strip.empty?
2772
+
2773
+ processed = true
2774
+ break
2775
+ end
2776
+ end
2777
+
2778
+ # 2. 일반 단어 처리 (한 줄에만 등장하는 경우)
2779
+ if !processed && line.include?(change_word)
2780
+ parts = line.split(change_word, 2)
2781
+ prefix = parts[0]
2782
+ suffix = parts[1]
2783
+
2784
+ open_tag = prefix[/<p[^>]*?>/i] || ""
2785
+ close_tag = suffix[/<\/p>/i] || ""
2786
+
2787
+ prefix_text = prefix.sub(open_tag, '')
2788
+ suffix_text = suffix.sub(close_tag, '')
2789
+
2790
+ new_lines << "#{open_tag}#{prefix_text}</p>" unless prefix_text.strip.empty?
2791
+ new_lines << "<koreamap>#{map_address}</koreamap>"
2792
+ new_lines << "#{open_tag}#{suffix_text}#{close_tag}" unless suffix_text.strip.empty?
2793
+
2794
+ processed = true
2795
+ break
2796
+ end
2797
+ end
2798
+
2799
+ # 변경이 없었으면 원래 줄 추가
2800
+ new_lines << line unless processed
2625
2801
  end
2626
- end
2802
+
2803
+ content = new_lines.join("\n")
2804
+ end
2627
2805
 
2628
2806
 
2629
2807
 
@@ -2637,15 +2815,18 @@ class Wordpress
2637
2815
  # content = content
2638
2816
  # soosick_1 = content_end
2639
2817
  #else
2640
- if @data['포스트설정']['gpt키워드'].checked?
2641
- if @data['포스트설정']['gpt상단'].checked?
2642
- content = content_end+"\n"+content+"\n"
2643
- else
2644
- content = content+"\n"+content_end+"\n"
2645
- end
2818
+ parts = content.split('(자동생성글)', 2)
2819
+ content_main = parts[0].strip
2820
+ content_end = parts[1].to_s.strip
2821
+
2822
+ if @data['포스트설정']['gpt키워드'].checked?
2823
+ if @data['포스트설정']['gpt상단'].checked?
2824
+ content = content_end + "\n" + content_main + "\n"
2646
2825
  else
2647
- content = content+"\n"+content_end+"\n"
2648
-
2826
+ content = content_main + "\n" + content_end + "\n"
2827
+ end
2828
+ else
2829
+ content = content_main + "\n" + content_end + "\n"
2649
2830
  end
2650
2831
 
2651
2832
 
@@ -3934,6 +4115,7 @@ class Wordpress
3934
4115
  @data['image_size'][2].checked = false
3935
4116
  @data['image_size'][3].checked = false
3936
4117
  @data['image_size'][4].checked = false
4118
+ @data['image_size'][5].checked = false
3937
4119
  end
3938
4120
  }
3939
4121
  }
@@ -3944,6 +4126,7 @@ class Wordpress
3944
4126
  @data['image_size'][2].checked = false
3945
4127
  @data['image_size'][3].checked = false
3946
4128
  @data['image_size'][4].checked = false
4129
+ @data['image_size'][5].checked = false
3947
4130
  end
3948
4131
  }
3949
4132
  }
@@ -3954,6 +4137,7 @@ class Wordpress
3954
4137
  @data['image_size'][0].checked = false
3955
4138
  @data['image_size'][3].checked = false
3956
4139
  @data['image_size'][4].checked = false
4140
+ @data['image_size'][5].checked = false
3957
4141
  end
3958
4142
  }
3959
4143
  }
@@ -3964,6 +4148,7 @@ class Wordpress
3964
4148
  @data['image_size'][2].checked = false
3965
4149
  @data['image_size'][0].checked = false
3966
4150
  @data['image_size'][4].checked = false
4151
+ @data['image_size'][5].checked = false
3967
4152
  end
3968
4153
  }
3969
4154
  }
@@ -3974,6 +4159,18 @@ class Wordpress
3974
4159
  @data['image_size'][2].checked = false
3975
4160
  @data['image_size'][3].checked = false
3976
4161
  @data['image_size'][0].checked = false
4162
+ @data['image_size'][5].checked = false
4163
+ end
4164
+ }
4165
+ }
4166
+ @data['image_size'][5] = checkbox('원본 px'){
4167
+ on_toggled{
4168
+ if @data['image_size'][5].checked?
4169
+ @data['image_size'][1].checked = false
4170
+ @data['image_size'][2].checked = false
4171
+ @data['image_size'][3].checked = false
4172
+ @data['image_size'][0].checked = false
4173
+ @data['image_size'][4].checked = false
3977
4174
  end
3978
4175
  }
3979
4176
  }
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tblog_duopack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.37
4
+ version: 0.0.50
5
5
  platform: ruby
6
6
  authors:
7
7
  - zon
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-02-13 00:00:00.000000000 Z
10
+ date: 2025-05-23 00:00:00.000000000 Z
12
11
  dependencies: []
13
12
  description: File to Clipboard gem
14
13
  email: mymin26@naver.com
@@ -21,7 +20,6 @@ homepage: ''
21
20
  licenses:
22
21
  - zon
23
22
  metadata: {}
24
- post_install_message:
25
23
  rdoc_options: []
26
24
  require_paths:
27
25
  - lib
@@ -36,8 +34,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
36
34
  - !ruby/object:Gem::Version
37
35
  version: '0'
38
36
  requirements: []
39
- rubygems_version: 3.3.7
40
- signing_key:
37
+ rubygems_version: 3.6.7
41
38
  specification_version: 4
42
39
  summary: file to clipboard
43
40
  test_files: []