vnai 2.1.9__py3-none-any.whl → 2.3.7__py3-none-any.whl
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.
- vnai/__init__.py +193 -33
- vnai/beam/__init__.py +26 -3
- vnai/beam/auth.py +312 -0
- vnai/beam/fundamental.py +168 -0
- vnai/beam/patching.py +223 -0
- vnai/beam/quota.py +114 -44
- vnai/beam/sync.py +87 -0
- vnai/flow/relay.py +18 -12
- vnai/scope/__init__.py +8 -1
- vnai/scope/device.py +315 -0
- vnai/scope/lc_integration.py +351 -0
- vnai/scope/license.py +197 -0
- vnai/scope/profile.py +37 -17
- vnai/scope/promo.py +203 -107
- {vnai-2.1.9.dist-info → vnai-2.3.7.dist-info}/METADATA +3 -2
- vnai-2.3.7.dist-info/RECORD +23 -0
- {vnai-2.1.9.dist-info → vnai-2.3.7.dist-info}/WHEEL +1 -1
- vnai-2.1.9.dist-info/RECORD +0 -16
- {vnai-2.1.9.dist-info → vnai-2.3.7.dist-info}/top_level.txt +0 -0
vnai/scope/promo.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import requests
|
|
3
|
-
|
|
3
|
+
import json
|
|
4
|
+
from datetime import datetime, timedelta
|
|
4
5
|
import random
|
|
5
6
|
import threading
|
|
6
7
|
import time
|
|
7
8
|
import urllib.parse
|
|
9
|
+
from pathlib import Path
|
|
8
10
|
_vnii_check_attempted = False
|
|
9
11
|
|
|
10
12
|
class AdCategory:
|
|
@@ -19,10 +21,8 @@ class AdCategory:
|
|
|
19
21
|
SECURITY = 8
|
|
20
22
|
MAINTENANCE = 9
|
|
21
23
|
WARNING = 10
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
except ImportError:
|
|
25
|
-
lc_init = None
|
|
24
|
+
API_KEY_CHECKER_AVAILABLE = None
|
|
25
|
+
api_key_checker = None
|
|
26
26
|
logger = logging.getLogger(__name__)
|
|
27
27
|
if not logger.hasHandlers():
|
|
28
28
|
handler = logging.StreamHandler()
|
|
@@ -46,85 +46,127 @@ class ContentManager:
|
|
|
46
46
|
self.is_paid_user = None
|
|
47
47
|
self.license_checked = False
|
|
48
48
|
self._debug = debug
|
|
49
|
-
global
|
|
50
|
-
if
|
|
51
|
-
return
|
|
52
|
-
_vnii_check_attempted = True
|
|
53
|
-
import sys
|
|
54
|
-
import importlib
|
|
55
|
-
try:
|
|
56
|
-
import importlib.metadata
|
|
57
|
-
VNII_LATEST_VERSION ="0.1.1"
|
|
58
|
-
VNII_URL =f"https://github.com/vnstock-hq/licensing/releases/download/vnii-{VNII_LATEST_VERSION}/vnii-{VNII_LATEST_VERSION}.tar.gz"
|
|
59
|
-
import subprocess
|
|
49
|
+
global API_KEY_CHECKER_AVAILABLE, api_key_checker
|
|
50
|
+
if API_KEY_CHECKER_AVAILABLE is None:
|
|
60
51
|
try:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
52
|
+
from vnai.scope.lc_integration import api_key_checker as _checker
|
|
53
|
+
api_key_checker = _checker
|
|
54
|
+
API_KEY_CHECKER_AVAILABLE = True
|
|
55
|
+
except ImportError:
|
|
56
|
+
API_KEY_CHECKER_AVAILABLE = False
|
|
57
|
+
api_key_checker = None
|
|
58
|
+
self.last_display = 0
|
|
59
|
+
self.display_interval = 24 * 3600
|
|
60
|
+
self.content_base_url ="https://hq.vnstocks.com/content-delivery"
|
|
61
|
+
self.target_url ="https://vnstocks.com/lp-khoa-hoc-python-chung-khoan"
|
|
62
|
+
self.image_url = (
|
|
63
|
+
"https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg"
|
|
64
|
+
)
|
|
65
|
+
self.vnstock_dir = Path.home() /".vnstock"
|
|
66
|
+
self.message_cache_file = self.vnstock_dir /"message_cache.json"
|
|
67
|
+
self.message_cache = {}
|
|
68
|
+
self._load_message_cache()
|
|
69
|
+
self._start_periodic_display()
|
|
70
|
+
|
|
71
|
+
def _load_message_cache(self) -> None:
|
|
72
|
+
try:
|
|
73
|
+
if self.message_cache_file.exists():
|
|
74
|
+
with open(self.message_cache_file,'r') as f:
|
|
75
|
+
self.message_cache = json.load(f)
|
|
76
|
+
else:
|
|
77
|
+
self.message_cache = {}
|
|
79
78
|
except Exception as e:
|
|
80
|
-
logger.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
)
|
|
86
|
-
|
|
79
|
+
logger.debug(f"Failed to load message cache: {e}")
|
|
80
|
+
self.message_cache = {}
|
|
81
|
+
|
|
82
|
+
def _save_message_cache(self) -> None:
|
|
83
|
+
try:
|
|
84
|
+
self.vnstock_dir.mkdir(exist_ok=True)
|
|
85
|
+
with open(self.message_cache_file,'w') as f:
|
|
86
|
+
json.dump(self.message_cache, f, indent=2)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.debug(f"Failed to save message cache: {e}")
|
|
89
|
+
|
|
90
|
+
def should_show_promotional_for_rate_limit(self, tier: str) -> bool:
|
|
91
|
+
if tier !="free":
|
|
92
|
+
return False
|
|
93
|
+
last_promo = self.message_cache.get("last_promotional_message")
|
|
94
|
+
if last_promo:
|
|
87
95
|
try:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
last_time = datetime.fromisoformat(last_promo)
|
|
97
|
+
now = datetime.now()
|
|
98
|
+
if (now - last_time).total_seconds() < 86400:
|
|
99
|
+
return False
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.debug(f"Error parsing last promotional time: {e}")
|
|
102
|
+
session_start = self.message_cache.get("session_start")
|
|
103
|
+
if session_start:
|
|
104
|
+
try:
|
|
105
|
+
start_time = datetime.fromisoformat(session_start)
|
|
106
|
+
now = datetime.now()
|
|
107
|
+
if (now - start_time).total_seconds() < 300:
|
|
108
|
+
return False
|
|
109
|
+
except Exception as e:
|
|
110
|
+
logger.debug(f"Error parsing session start time: {e}")
|
|
111
|
+
else:
|
|
112
|
+
self.message_cache["session_start"] = datetime.now().isoformat()
|
|
113
|
+
self._save_message_cache()
|
|
114
|
+
return False
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
def mark_promotional_shown(self) -> None:
|
|
118
|
+
self.message_cache["last_promotional_message"] = datetime.now().isoformat()
|
|
119
|
+
self._save_message_cache()
|
|
120
|
+
|
|
121
|
+
def _check_license_status(self):
|
|
122
|
+
if self.license_checked:
|
|
92
123
|
return
|
|
93
|
-
if
|
|
124
|
+
if API_KEY_CHECKER_AVAILABLE and api_key_checker:
|
|
94
125
|
try:
|
|
95
|
-
|
|
96
|
-
status = license_info.get('status','').lower()
|
|
126
|
+
result = api_key_checker.check_license_with_vnii()
|
|
97
127
|
if self._debug:
|
|
98
|
-
logger.info(f"
|
|
99
|
-
|
|
100
|
-
|
|
128
|
+
logger.info(f"API key check result: {result}")
|
|
129
|
+
valid_statuses = [
|
|
130
|
+
'verified',
|
|
131
|
+
'cached',
|
|
132
|
+
'device_limit_exceeded'
|
|
133
|
+
]
|
|
134
|
+
if result.get('status') in valid_statuses:
|
|
135
|
+
self.is_paid_user = result.get('is_paid', False)
|
|
136
|
+
self.license_checked = True
|
|
101
137
|
if self._debug:
|
|
102
|
-
|
|
138
|
+
status_msg = (
|
|
139
|
+
"Detected paid user"
|
|
140
|
+
if self.is_paid_user
|
|
141
|
+
else"Detected free user"
|
|
142
|
+
)
|
|
143
|
+
logger.info(f"{status_msg} via API key")
|
|
103
144
|
else:
|
|
104
145
|
self.is_paid_user = False
|
|
146
|
+
self.license_checked = True
|
|
105
147
|
if self._debug:
|
|
106
|
-
logger.info(
|
|
107
|
-
|
|
148
|
+
logger.info(
|
|
149
|
+
f"No valid license: {result.get('status')}"
|
|
150
|
+
)
|
|
108
151
|
except Exception as e:
|
|
109
152
|
if self._debug:
|
|
110
|
-
logger.error(f"
|
|
111
|
-
self.is_paid_user =
|
|
153
|
+
logger.error(f"Error checking license via API key: {e}")
|
|
154
|
+
self.is_paid_user = False
|
|
155
|
+
self.license_checked = True
|
|
112
156
|
else:
|
|
113
157
|
if self._debug:
|
|
114
|
-
logger.warning(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
self.image_url = (
|
|
121
|
-
"https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg"
|
|
122
|
-
)
|
|
123
|
-
self._start_periodic_display()
|
|
158
|
+
logger.warning(
|
|
159
|
+
"API key checker not available. "
|
|
160
|
+
"Cannot determine paid user status."
|
|
161
|
+
)
|
|
162
|
+
self.is_paid_user = False
|
|
163
|
+
self.license_checked = True
|
|
124
164
|
|
|
125
165
|
def _start_periodic_display(self):
|
|
126
166
|
def periodic_display():
|
|
127
167
|
while True:
|
|
168
|
+
if not self.license_checked:
|
|
169
|
+
self._check_license_status()
|
|
128
170
|
if self.is_paid_user:
|
|
129
171
|
break
|
|
130
172
|
sleep_time = random.randint(2 * 3600, 6 * 3600)
|
|
@@ -138,16 +180,10 @@ f"Chi tiết lỗi: {e}"
|
|
|
138
180
|
thread.start()
|
|
139
181
|
|
|
140
182
|
def fetch_remote_content(self, context: str ="init", html: bool = True) -> str:
|
|
183
|
+
if not self.license_checked:
|
|
184
|
+
self._check_license_status()
|
|
141
185
|
if self.is_paid_user:
|
|
142
186
|
return""
|
|
143
|
-
"""
|
|
144
|
-
Fetch promotional content from remote service with context and format flag.
|
|
145
|
-
Args:
|
|
146
|
-
context: usage context (e.g., "init", "periodic", "loop").
|
|
147
|
-
html: if True, request HTML; otherwise plain text.
|
|
148
|
-
Returns:
|
|
149
|
-
The content string on HTTP 200, or None on failure.
|
|
150
|
-
"""
|
|
151
187
|
try:
|
|
152
188
|
params = {"context": context,"html":"true" if html else"false"}
|
|
153
189
|
url =f"{self.content_base_url}?{urllib.parse.urlencode(params)}"
|
|
@@ -161,17 +197,18 @@ f"Chi tiết lỗi: {e}"
|
|
|
161
197
|
return None
|
|
162
198
|
|
|
163
199
|
def present_content(self, context: str ="init", ad_category: int = AdCategory.FREE) -> None:
|
|
164
|
-
|
|
165
|
-
|
|
200
|
+
if not self.license_checked:
|
|
201
|
+
self._check_license_status()
|
|
202
|
+
if self.is_paid_user and ad_category == AdCategory.FREE:
|
|
166
203
|
return
|
|
167
204
|
self.last_display = time.time()
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
205
|
+
environment = None
|
|
206
|
+
try:
|
|
207
|
+
from vnai.scope.profile import inspector
|
|
208
|
+
environment = inspector.examine().get("environment","unknown")
|
|
209
|
+
except Exception as e:
|
|
210
|
+
logger.error(f"Không detect được environment: {e}")
|
|
211
|
+
environment ="unknown"
|
|
175
212
|
remote_content = self.fetch_remote_content(
|
|
176
213
|
context=context, html=(environment =="jupyter")
|
|
177
214
|
)
|
|
@@ -221,26 +258,26 @@ f"""
|
|
|
221
258
|
* Tham gia gói tài trợ [Vnstock Insider](https://vnstocks.com/insiders-program) để tăng 5X giới hạn API
|
|
222
259
|
"""
|
|
223
260
|
)
|
|
224
|
-
fallback["terminal"] = (
|
|
225
|
-
"""
|
|
226
|
-
╔═════════════════════════════════════════════════════════════════╗
|
|
227
|
-
║ ║
|
|
228
|
-
║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║
|
|
229
|
-
║ ║
|
|
230
|
-
║ ✓ Tăng ngay 500% tốc độ gọi API - Không còn lỗi RateLimit ║
|
|
231
|
-
║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║
|
|
232
|
-
║ ║
|
|
233
|
-
║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║
|
|
234
|
-
║ https://vnstocks.com/insiders-program ║
|
|
235
|
-
║ ║
|
|
236
|
-
╚═════════════════════════════════════════════════════════════════╝
|
|
237
|
-
"""
|
|
238
|
-
)
|
|
239
|
-
fallback["simple"] = (
|
|
240
|
-
"🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói "
|
|
241
|
-
"Vnstock Insider: https://vnstocks.com/insiders-program"
|
|
242
|
-
)
|
|
261
|
+
fallback["terminal"] = get_promotional_message("terminal")
|
|
262
|
+
fallback["simple"] = get_promotional_message("simple")
|
|
243
263
|
else:
|
|
264
|
+
from vnai.beam.auth import authenticator
|
|
265
|
+
tier = authenticator.get_tier()
|
|
266
|
+
period_limit_info =""
|
|
267
|
+
if tier =='guest':
|
|
268
|
+
period_limit_info = (
|
|
269
|
+
"<p style='color: #e67e22; margin-top: 10px;'>"
|
|
270
|
+
"<strong>📊 Phiên bản cộng đồng:</strong> Báo cáo tài chính được giới hạn tối đa <strong>4 kỳ</strong> để minh hoạ thuật toán. "
|
|
271
|
+
"Để truy cập đầy đủ, vui lòng <a href='https://vnstocks.com/insiders-program' style='color: #e67e22;'>tham gia gói thành viên tài trợ dự án</a>."
|
|
272
|
+
"</p>"
|
|
273
|
+
)
|
|
274
|
+
elif tier =='free':
|
|
275
|
+
period_limit_info = (
|
|
276
|
+
"<p style='color: #e67e22; margin-top: 10px;'>"
|
|
277
|
+
"<strong>📊 Phiên bản cộng đồng:</strong> Báo cáo tài chính được giới hạn tối đa <strong>8 kỳ</strong> để minh hoạ thuật toán. "
|
|
278
|
+
"Để truy cập đầy đủ, vui lòng <a href='https://vnstocks.com/insiders-program' style='color: #e67e22;'>tham gia gói thành viên tài trợ dự án</a>."
|
|
279
|
+
"</p>"
|
|
280
|
+
)
|
|
244
281
|
fallback["html"] = (
|
|
245
282
|
f"""
|
|
246
283
|
<div style="border: 1px solid #3498db; padding: 15px; border-radius: 5px; margin: 10px 0;">
|
|
@@ -251,20 +288,31 @@ f"""
|
|
|
251
288
|
<li>Cộng đồng: <a href="https://www.facebook.com/groups/vnstock.official" style="color: #3498db;">vnstocks.com/community</a></li>
|
|
252
289
|
</ul>
|
|
253
290
|
<p>Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.</p>
|
|
291
|
+
{period_limit_info}
|
|
254
292
|
</div>
|
|
255
293
|
"""
|
|
256
294
|
)
|
|
295
|
+
period_limit_markdown =""
|
|
296
|
+
if tier =='guest':
|
|
297
|
+
period_limit_markdown ="\n**📊 Phiên bản cộng đồng:** Báo cáo tài chính được giới hạn tối đa **4 kỳ** để minh hoạ thuật toán. Để truy cập đầy đủ, vui lòng [tham gia gói thành viên tài trợ dự án](https://vnstocks.com/insiders-program)."
|
|
298
|
+
elif tier =='free':
|
|
299
|
+
period_limit_markdown ="\n**📊 Phiên bản cộng đồng:** Báo cáo tài chính được giới hạn tối đa **8 kỳ** để minh hoạ thuật toán. Để truy cập đầy đủ, vui lòng [tham gia gói thành viên tài trợ dự án](https://vnstocks.com/insiders-program)."
|
|
257
300
|
fallback["markdown"] = (
|
|
258
|
-
"""
|
|
301
|
+
f"""
|
|
259
302
|
## 👋 Chào mừng bạn đến với Vnstock!
|
|
260
303
|
Cảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam
|
|
261
304
|
* Tài liệu: [Sổ tay hướng dẫn](https://vnstocks.com/docs)
|
|
262
305
|
* Cộng đồng: [Nhóm Facebook](https://facebook.com/groups/vnstock.official)
|
|
263
|
-
Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.
|
|
306
|
+
Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.{period_limit_markdown}
|
|
264
307
|
"""
|
|
265
308
|
)
|
|
309
|
+
period_limit_terminal =""
|
|
310
|
+
if tier =='guest':
|
|
311
|
+
period_limit_terminal ="\n║ 📊 Báo cáo tài chính: Giới hạn 4 kỳ (phiên bản cộng đồng) ║\n║ Nâng cấp: https://vnstocks.com/insiders-program ║"
|
|
312
|
+
elif tier =='free':
|
|
313
|
+
period_limit_terminal ="\n║ 📊 Báo cáo tài chính: Giới hạn 8 kỳ (phiên bản cộng đồng) ║\n║ Nâng cấp: https://vnstocks.com/insiders-program ║"
|
|
266
314
|
fallback["terminal"] = (
|
|
267
|
-
"""
|
|
315
|
+
f"""
|
|
268
316
|
╔════════════════════════════════════════════════════════════╗
|
|
269
317
|
║ ║
|
|
270
318
|
║ 👋 Chào mừng bạn đến với Vnstock! ║
|
|
@@ -273,7 +321,7 @@ Khám phá các tính năng mới nhất và tham gia cộng đồng để nhậ
|
|
|
273
321
|
║ chứng khoán #1 tại Việt Nam ║
|
|
274
322
|
║ ║
|
|
275
323
|
║ ✓ Tài liệu: https://vnstocks.com/docs ║
|
|
276
|
-
║ ✓ Cộng đồng: https://facebook.com/groups/vnstock.official ║
|
|
324
|
+
║ ✓ Cộng đồng: https://facebook.com/groups/vnstock.official ║{period_limit_terminal}
|
|
277
325
|
║ ║
|
|
278
326
|
║ Khám phá các tính năng mới nhất và tham gia ║
|
|
279
327
|
║ cộng đồng để nhận hỗ trợ. ║
|
|
@@ -281,13 +329,61 @@ Khám phá các tính năng mới nhất và tham gia cộng đồng để nhậ
|
|
|
281
329
|
╚════════════════════════════════════════════════════════════╝
|
|
282
330
|
"""
|
|
283
331
|
)
|
|
332
|
+
period_limit_simple =""
|
|
333
|
+
if tier =='guest':
|
|
334
|
+
period_limit_simple =" | 📊 Báo cáo tài chính: Giới hạn 4 kỳ"
|
|
335
|
+
elif tier =='free':
|
|
336
|
+
period_limit_simple =" | 📊 Báo cáo tài chính: Giới hạn 8 kỳ"
|
|
284
337
|
fallback["simple"] = (
|
|
285
338
|
"👋 Chào mừng bạn đến với Vnstock! "
|
|
286
339
|
"Tài liệu: https://vnstocks.com/onboard | "
|
|
287
340
|
"Cộng đồng: https://facebook.com/groups/vnstock.official"
|
|
341
|
+
f"{period_limit_simple}"
|
|
288
342
|
)
|
|
289
343
|
return fallback
|
|
290
344
|
manager = ContentManager()
|
|
291
345
|
|
|
292
346
|
def present(context: str ="init", ad_category: int = AdCategory.FREE) -> None:
|
|
293
|
-
manager.present_content(context=context, ad_category=ad_category)
|
|
347
|
+
manager.present_content(context=context, ad_category=ad_category)
|
|
348
|
+
|
|
349
|
+
def get_promotional_message(format_type: str ="terminal") -> str:
|
|
350
|
+
if format_type =="terminal":
|
|
351
|
+
return"""
|
|
352
|
+
╔═════════════════════════════════════════════════════════════════╗
|
|
353
|
+
║ ║
|
|
354
|
+
║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║
|
|
355
|
+
║ ║
|
|
356
|
+
║ ✓ Tăng ngay 10X tốc độ gọi API - Không còn lỗi RateLimit ║
|
|
357
|
+
║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║
|
|
358
|
+
║ ║
|
|
359
|
+
║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║
|
|
360
|
+
║ https://vnstocks.com/insiders-program ║
|
|
361
|
+
║ ║
|
|
362
|
+
╚═════════════════════════════════════════════════════════════════╝
|
|
363
|
+
"""
|
|
364
|
+
elif format_type =="simple":
|
|
365
|
+
return (
|
|
366
|
+
"🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 10X với gói "
|
|
367
|
+
"Vnstock Insider: https://vnstocks.com/insiders-program"
|
|
368
|
+
)
|
|
369
|
+
elif format_type =="html":
|
|
370
|
+
return"""
|
|
371
|
+
<div style="border: 2px solid #e74c3c; padding: 15px; border-radius: 5px; margin: 10px 0; background-color: #fadbd8;">
|
|
372
|
+
<h3 style="color: #c0392b; margin-top: 0;">🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API?</h3>
|
|
373
|
+
<p><strong>✓ Tăng ngay 10X tốc độ gọi API - Không còn lỗi RateLimit</strong></p>
|
|
374
|
+
<p><strong>✓ Tiết kiệm 85% thời gian chờ đợi giữa các request</strong></p>
|
|
375
|
+
<p style="margin-bottom: 0;">
|
|
376
|
+
<a href="https://vnstocks.com/insiders-program" style="color: #c0392b; font-weight: bold;">
|
|
377
|
+
➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK
|
|
378
|
+
</a>
|
|
379
|
+
</p>
|
|
380
|
+
</div>
|
|
381
|
+
"""
|
|
382
|
+
else:
|
|
383
|
+
return get_promotional_message("terminal")
|
|
384
|
+
|
|
385
|
+
def should_show_promotional_for_rate_limit(tier: str) -> bool:
|
|
386
|
+
return manager.should_show_promotional_for_rate_limit(tier)
|
|
387
|
+
|
|
388
|
+
def mark_promotional_shown() -> None:
|
|
389
|
+
manager.mark_promotional_shown()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vnai
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.7
|
|
4
4
|
Summary: Vnstock Analytics Interface
|
|
5
5
|
Author-email: Vnstock Analytics Team <support@vnstocks.com>
|
|
6
6
|
License: proprietary
|
|
@@ -10,10 +10,11 @@ Classifier: Operating System :: OS Independent
|
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
|
-
Requires-Python: >=3.
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
14
|
Description-Content-Type: text/markdown
|
|
15
15
|
Requires-Dist: requests>=2.25.0
|
|
16
16
|
Requires-Dist: psutil>=5.8.0
|
|
17
|
+
Requires-Dist: cffi>=1.17.1
|
|
17
18
|
Provides-Extra: dev
|
|
18
19
|
Requires-Dist: pytest>=6.0.0; extra == "dev"
|
|
19
20
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
vnai/__init__.py,sha256=LjSVIZnlr88sAYTKLTaycLGyPrAwXDtYIBxRPhziWQA,11389
|
|
2
|
+
vnai/beam/__init__.py,sha256=P9URuUI3H8PAKWo0OSAbwT5Tv3r1ANoGhv3ZQCFopv4,949
|
|
3
|
+
vnai/beam/auth.py,sha256=o22q4mHngFtt4_BLn1ytkhIeX-CcqslXqk0AZAr-ygU,11958
|
|
4
|
+
vnai/beam/fundamental.py,sha256=sqKutOsnDCRL_h7fAMZw3uAFIFEL5jDq-BN9u1OgQkg,7674
|
|
5
|
+
vnai/beam/metrics.py,sha256=2BmdJl7gB4rHwCN7G7WUMg_6rnlY3xfulBzU7usqNfE,6275
|
|
6
|
+
vnai/beam/patching.py,sha256=Kd-Q-73C97xEUUa9bnZcmCr2rWCpnof6lX5Fg_hd8jM,10322
|
|
7
|
+
vnai/beam/pulse.py,sha256=xu7cStqdkCw8be_yVwfjGtqdy-stYC511oZOkT9j6hs,2485
|
|
8
|
+
vnai/beam/quota.py,sha256=_dmsDhB0PGCA0W044sWvLGK9p9r60KePdxRvVZv6mjE,17035
|
|
9
|
+
vnai/beam/sync.py,sha256=Pfr4CziIoWwebPt0p6nTvsLlIqZuXebMcuIyTNzTk_I,3497
|
|
10
|
+
vnai/flow/__init__.py,sha256=9JepoOHS1xn0_ZrDyfft42g2cjhaka7xdG0velqmk-I,70
|
|
11
|
+
vnai/flow/queue.py,sha256=9dyMwxen1vCiFJ7wNZn-kzMY8DnB4xD575J1pPSWZLw,3223
|
|
12
|
+
vnai/flow/relay.py,sha256=I76uSJXnMOZs77x-pdnsuDy7vSdGh2lp4QBSGJ1_9gY,12791
|
|
13
|
+
vnai/scope/__init__.py,sha256=mOPU5J0y3PgmTT9IVG9geYNQASADpu8Lc-GPvA5mDQ4,376
|
|
14
|
+
vnai/scope/device.py,sha256=qnWTQ0MvUBsZDo0lYEx7lbyRmQX0XuTNAYd30AbS5J4,10825
|
|
15
|
+
vnai/scope/lc_integration.py,sha256=M2XfNIHeMT5maLACPmQFv601AF1Kx-sYw6FiOKGL9oU,12101
|
|
16
|
+
vnai/scope/license.py,sha256=Su10AeGAn6n-XYVTZoW3f2jOqp0tNAoR4Ixqr8Q12NU,6852
|
|
17
|
+
vnai/scope/profile.py,sha256=b4rruVF3m2D_vzimmgVmjNkdjTF_2ci1xeEh40WHWlk,22439
|
|
18
|
+
vnai/scope/promo.py,sha256=k7jaGLXl1U5nW3Ut-tg3qEAVJ0k_zMZ1qY4kzqPRB-0,18517
|
|
19
|
+
vnai/scope/state.py,sha256=ApzP6U0Hzp047-k3czL933soUXiZjpZSxXk7DKwf7yM,5389
|
|
20
|
+
vnai-2.3.7.dist-info/METADATA,sha256=1xTTK6p2emi3NBCLR0Jywo5KXr51RVQNWKG4_xkriDQ,691
|
|
21
|
+
vnai-2.3.7.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
|
|
22
|
+
vnai-2.3.7.dist-info/top_level.txt,sha256=4zI0qZHePCwvgSqXl4420sBcd0VzZn4MEcRsAIFae3k,5
|
|
23
|
+
vnai-2.3.7.dist-info/RECORD,,
|
vnai-2.1.9.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
vnai/__init__.py,sha256=oTfe5Hu8UUDUW3Gq84fqX4InkIfqIZTUqTv2Q28RB3c,5733
|
|
2
|
-
vnai/beam/__init__.py,sha256=h19ZEQFnWTvqed0mpTcuQf-sT9XifWhfVbaobTEs91Q,131
|
|
3
|
-
vnai/beam/metrics.py,sha256=2BmdJl7gB4rHwCN7G7WUMg_6rnlY3xfulBzU7usqNfE,6275
|
|
4
|
-
vnai/beam/pulse.py,sha256=xu7cStqdkCw8be_yVwfjGtqdy-stYC511oZOkT9j6hs,2485
|
|
5
|
-
vnai/beam/quota.py,sha256=0pgkCdARtojF-xNC0Y_VYYo4GO717F0MOexwNH2NdzY,14416
|
|
6
|
-
vnai/flow/__init__.py,sha256=9JepoOHS1xn0_ZrDyfft42g2cjhaka7xdG0velqmk-I,70
|
|
7
|
-
vnai/flow/queue.py,sha256=9dyMwxen1vCiFJ7wNZn-kzMY8DnB4xD575J1pPSWZLw,3223
|
|
8
|
-
vnai/flow/relay.py,sha256=7VUsiU1T272chBRCRR90Aodfh_PcUUAUlX3SPUtltFQ,12624
|
|
9
|
-
vnai/scope/__init__.py,sha256=gVvOMQ34V3Tm8_XmX4cRCrgKVfZ4XYQup79MWx6CWuM,197
|
|
10
|
-
vnai/scope/profile.py,sha256=RSk8w5h9rvamFozQOUrYcDSXssTF0ro70Qq-VfExQF0,21567
|
|
11
|
-
vnai/scope/promo.py,sha256=AcQDdzcIYCrTQ0Hqr0KVBCOJsBYQevp0T6emQlGlQ-E,13597
|
|
12
|
-
vnai/scope/state.py,sha256=ApzP6U0Hzp047-k3czL933soUXiZjpZSxXk7DKwf7yM,5389
|
|
13
|
-
vnai-2.1.9.dist-info/METADATA,sha256=RiB-4I-sviuL_7kRXBtEPsQyfLs15bJ_nEPHVC6qEbY,662
|
|
14
|
-
vnai-2.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
vnai-2.1.9.dist-info/top_level.txt,sha256=4zI0qZHePCwvgSqXl4420sBcd0VzZn4MEcRsAIFae3k,5
|
|
16
|
-
vnai-2.1.9.dist-info/RECORD,,
|
|
File without changes
|