File size: 9,186 Bytes
1271db4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
"""
API κ΄€λ ¨ μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜ λͺ¨μŒ (ν™˜κ²½λ³€μˆ˜ 버전)
- API ν‚€ 관리 (ν™˜κ²½λ³€μˆ˜μ—μ„œ λ‘œλ“œ)
- μ‹œκ·Έλ‹ˆμ²˜ 생성
- API 헀더 생성
- Gemini API ν‚€ 랜덀 λ‘œν…Œμ΄μ…˜ μΆ”κ°€
"""

import os
import time
import hmac
import hashlib
import base64
import requests
import threading
import random
import google.generativeai as genai
import logging

logger = logging.getLogger(__name__)

# ν™˜κ²½λ³€μˆ˜μ—μ„œ API μ„€μ • λ‘œλ“œ
def get_api_configs():
    # ν™˜κ²½λ³€μˆ˜ 'API_CONFIGS'μ—μ„œ 전체 섀정을 κ°€μ Έμ˜΄
    api_configs_str = os.getenv('API_CONFIGS', '')
    
    if not api_configs_str:
        logger.error("API_CONFIGS ν™˜κ²½λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
        return [], [], [], []
    
    try:
        # ν™˜κ²½λ³€μˆ˜ 값을 exec둜 μ‹€ν–‰ν•˜μ—¬ μ„€μ • λ‘œλ“œ
        local_vars = {}
        exec(api_configs_str, {}, local_vars)
        
        return (
            local_vars.get('NAVER_API_CONFIGS', []),
            local_vars.get('NAVER_SHOPPING_CONFIGS', []),
            local_vars.get('NAVER_DATALAB_CONFIGS', []),
            local_vars.get('GEMINI_API_CONFIGS', [])
        )
    except Exception as e:
        logger.error(f"ν™˜κ²½λ³€μˆ˜ νŒŒμ‹± 였λ₯˜: {e}")
        return [], [], [], []

# API μ„€μ • λ‘œλ“œ
NAVER_API_CONFIGS, NAVER_SHOPPING_CONFIGS, NAVER_DATALAB_CONFIGS, GEMINI_API_CONFIGS = get_api_configs()

# 순차 μ‚¬μš©μ„ μœ„ν•œ μΈλ±μŠ€μ™€ 락
current_api_index = 0
current_shopping_api_index = 0
current_datalab_api_index = 0
current_gemini_api_index = 0
api_lock = threading.Lock()
shopping_lock = threading.Lock()
datalab_lock = threading.Lock()
gemini_lock = threading.Lock()

# Gemini λͺ¨λΈ μΊμ‹œ
_gemini_models = {}

# API μ„€μ • μ΄ˆκΈ°ν™” ν•¨μˆ˜ μΆ”κ°€
def initialize_api_configs():
    """API 섀정을 μ΄ˆκΈ°ν™”ν•˜κ³  λžœλ€ν•˜κ²Œ μ •λ ¬"""
    global NAVER_API_CONFIGS, NAVER_SHOPPING_CONFIGS, NAVER_DATALAB_CONFIGS, GEMINI_API_CONFIGS
    
    # API 섀정을 λ‹€μ‹œ λ‘œλ“œ
    NAVER_API_CONFIGS, NAVER_SHOPPING_CONFIGS, NAVER_DATALAB_CONFIGS, GEMINI_API_CONFIGS = get_api_configs()
    
    # API 섀정을 λžœλ€ν•˜κ²Œ μ„žκΈ°
    if NAVER_API_CONFIGS:
        random.shuffle(NAVER_API_CONFIGS)
    if NAVER_SHOPPING_CONFIGS:
        random.shuffle(NAVER_SHOPPING_CONFIGS)
    if NAVER_DATALAB_CONFIGS:
        random.shuffle(NAVER_DATALAB_CONFIGS)
    if GEMINI_API_CONFIGS:
        random.shuffle(GEMINI_API_CONFIGS)
    
    print(f"API μ„€μ • μ΄ˆκΈ°ν™” μ™„λ£Œ:")
    print(f"  - 검색광고 API: {len(NAVER_API_CONFIGS)}개")
    print(f"  - μ‡Όν•‘ API: {len(NAVER_SHOPPING_CONFIGS)}개")
    print(f"  - λ°μ΄ν„°λž© API: {len(NAVER_DATALAB_CONFIGS)}개")
    print(f"  - Gemini API: {len(GEMINI_API_CONFIGS)}개")


def generate_signature(timestamp, method, uri, secret_key):
    """μ‹œκ·Έλ‹ˆμ²˜ 생성 ν•¨μˆ˜"""
    message = f"{timestamp}.{method}.{uri}"
    digest = hmac.new(secret_key.encode("utf-8"), message.encode("utf-8"), hashlib.sha256).digest()
    return base64.b64encode(digest).decode()

def get_header(method, uri, api_key, secret_key, customer_id):
    """API 헀더 생성 ν•¨μˆ˜"""
    timestamp = str(round(time.time() * 1000))
    signature = generate_signature(timestamp, method, uri, secret_key)
    return {
        "Content-Type": "application/json; charset=UTF-8",
        "X-Timestamp": timestamp,
        "X-API-KEY": api_key,
        "X-Customer": str(customer_id),
        "X-Signature": signature
    }

def get_next_api_config():
    """순차적으둜 λ‹€μŒ API 섀정을 λ°˜ν™˜ (μŠ€λ ˆλ“œ μ•ˆμ „)"""
    global current_api_index
    
    if not NAVER_API_CONFIGS:
        logger.error("넀이버 검색광고 API 섀정이 μ—†μŠ΅λ‹ˆλ‹€.")
        return None
        
    with api_lock:
        config = NAVER_API_CONFIGS[current_api_index]
        current_api_index = (current_api_index + 1) % len(NAVER_API_CONFIGS)
    return config

def get_next_shopping_api_config():
    """순차적으둜 λ‹€μŒ μ‡Όν•‘ API 섀정을 λ°˜ν™˜ (였λ₯˜ ν‚€ κ±΄λ„ˆλ›°κΈ° μΆ”κ°€)"""
    global current_shopping_api_index
    
    if not NAVER_SHOPPING_CONFIGS:
        logger.error("넀이버 μ‡Όν•‘ API 섀정이 μ—†μŠ΅λ‹ˆλ‹€.")
        return None
        
    with shopping_lock:
        # μ΅œλŒ€ 전체 ν‚€ 수만큼 μ‹œλ„ (λ¬΄ν•œ 루프 λ°©μ§€)
        for _ in range(len(NAVER_SHOPPING_CONFIGS)):
            config = NAVER_SHOPPING_CONFIGS[current_shopping_api_index]
            current_shopping_api_index = (current_shopping_api_index + 1) % len(NAVER_SHOPPING_CONFIGS)
            
            # κΈ°λ³Έκ°’ 체크
            if config["CLIENT_ID"] and not config["CLIENT_ID"].startswith("YOUR_"):
                return config
        
        # λͺ¨λ“  ν‚€κ°€ 기본값인 경우 첫 번째 ν‚€ λ°˜ν™˜
        return NAVER_SHOPPING_CONFIGS[0] if NAVER_SHOPPING_CONFIGS else None

def get_next_datalab_api_config():
    """순차적으둜 λ‹€μŒ λ°μ΄ν„°λž© API 섀정을 λ°˜ν™˜ (μŠ€λ ˆλ“œ μ•ˆμ „)"""
    global current_datalab_api_index
    
    if not NAVER_DATALAB_CONFIGS:
        logger.error("넀이버 λ°μ΄ν„°λž© API 섀정이 μ—†μŠ΅λ‹ˆλ‹€.")
        return None
        
    with datalab_lock:
        # API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μœΌλ©΄ None λ°˜ν™˜
        if not NAVER_DATALAB_CONFIGS[0]["CLIENT_ID"] or NAVER_DATALAB_CONFIGS[0]["CLIENT_ID"].startswith("YOUR_"):
            return None
            
        config = NAVER_DATALAB_CONFIGS[current_datalab_api_index]
        current_datalab_api_index = (current_datalab_api_index + 1) % len(NAVER_DATALAB_CONFIGS)
        return config

def get_next_gemini_api_key():
    """순차적으둜 λ‹€μŒ Gemini API ν‚€λ₯Ό λ°˜ν™˜ (μŠ€λ ˆλ“œ μ•ˆμ „)"""
    global current_gemini_api_index
    
    if not GEMINI_API_CONFIGS:
        logger.warning("μ‚¬μš© κ°€λŠ₯ν•œ Gemini API ν‚€κ°€ μ—†μŠ΅λ‹ˆλ‹€.")
        return None
        
    with gemini_lock:
        # μ΅œλŒ€ 전체 ν‚€ 수만큼 μ‹œλ„ (λ¬΄ν•œ 루프 λ°©μ§€)
        for _ in range(len(GEMINI_API_CONFIGS)):
            api_key = GEMINI_API_CONFIGS[current_gemini_api_index]
            current_gemini_api_index = (current_gemini_api_index + 1) % len(GEMINI_API_CONFIGS)
            
            # 기본값이 μ•„λ‹Œ ν‚€λ§Œ λ°˜ν™˜
            if api_key and not api_key.startswith("YOUR_") and api_key.strip():
                return api_key
        
        # λͺ¨λ“  ν‚€κ°€ 기본값인 경우 None λ°˜ν™˜
        logger.warning("μ‚¬μš© κ°€λŠ₯ν•œ Gemini API ν‚€κ°€ μ—†μŠ΅λ‹ˆλ‹€.")
        return None

def get_gemini_model():
    """μΊμ‹œλœ Gemini λͺ¨λΈμ„ λ°˜ν™˜ν•˜κ±°λ‚˜ μƒˆλ‘œ 생성"""
    api_key = get_next_gemini_api_key()
    
    if not api_key:
        logger.error("Gemini API ν‚€λ₯Ό κ°€μ Έμ˜¬ 수 μ—†μŠ΅λ‹ˆλ‹€.")
        return None
    
    # μΊμ‹œμ—μ„œ λͺ¨λΈ 확인
    if api_key in _gemini_models:
        return _gemini_models[api_key]
    
    try:
        # μƒˆ λͺ¨λΈ 생성
        genai.configure(api_key=api_key)
        model = genai.GenerativeModel("gemini-2.0-flash-exp")
        
        # μΊμ‹œμ— μ €μž₯
        _gemini_models[api_key] = model
        
        logger.info(f"Gemini λͺ¨λΈ 생성 성곡: {api_key[:8]}***{api_key[-4:]}")
        return model
        
    except Exception as e:
        logger.error(f"Gemini λͺ¨λΈ 생성 μ‹€νŒ¨ ({api_key[:8]}***): {e}")
        return None

def validate_api_config(api_config):
    """API μ„€μ • μœ νš¨μ„± 검사"""
    if not api_config:
        return False, "API 섀정이 μ—†μŠ΅λ‹ˆλ‹€."
        
    API_KEY = api_config.get("API_KEY", "")
    SECRET_KEY = api_config.get("SECRET_KEY", "")
    CUSTOMER_ID_STR = api_config.get("CUSTOMER_ID", "")
    
    if not all([API_KEY, SECRET_KEY, CUSTOMER_ID_STR]):
        return False, "API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
    
    if CUSTOMER_ID_STR.startswith("YOUR_") or API_KEY.startswith("YOUR_"):
        return False, "API ν‚€κ°€ ν”Œλ ˆμ΄μŠ€ν™€λ”μž…λ‹ˆλ‹€."
    
    try:
        CUSTOMER_ID = int(CUSTOMER_ID_STR)
    except ValueError:
        return False, f"CUSTOMER_ID λ³€ν™˜ 였λ₯˜: '{CUSTOMER_ID_STR}'λŠ” μœ νš¨ν•œ μˆ«μžκ°€ μ•„λ‹™λ‹ˆλ‹€."
    
    return True, "μœ νš¨ν•œ API μ„€μ •μž…λ‹ˆλ‹€."

def validate_datalab_config(datalab_config):
    """λ°μ΄ν„°λž© API μ„€μ • μœ νš¨μ„± 검사"""
    if not datalab_config:
        return False, "λ°μ΄ν„°λž© API 섀정이 μ—†μŠ΅λ‹ˆλ‹€."
    
    CLIENT_ID = datalab_config.get("CLIENT_ID", "")
    CLIENT_SECRET = datalab_config.get("CLIENT_SECRET", "")
    
    if not all([CLIENT_ID, CLIENT_SECRET]):
        return False, "λ°μ΄ν„°λž© API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€."
    
    if CLIENT_ID.startswith("YOUR_") or CLIENT_SECRET.startswith("YOUR_"):
        return False, "λ°μ΄ν„°λž© API ν‚€κ°€ ν”Œλ ˆμ΄μŠ€ν™€λ”μž…λ‹ˆλ‹€."
    
    return True, "μœ νš¨ν•œ λ°μ΄ν„°λž© API μ„€μ •μž…λ‹ˆλ‹€."

def validate_gemini_config():
    """Gemini API μ„€μ • μœ νš¨μ„± 검사"""
    valid_keys = 0
    for api_key in GEMINI_API_CONFIGS:
        if api_key and not api_key.startswith("YOUR_") and api_key.strip():
            valid_keys += 1
    
    if valid_keys == 0:
        return False, "μ‚¬μš© κ°€λŠ₯ν•œ Gemini API ν‚€κ°€ μ—†μŠ΅λ‹ˆλ‹€."
    
    return True, f"{valid_keys}개의 μœ νš¨ν•œ Gemini API ν‚€κ°€ μ„€μ •λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€."