vnai 2.0.6__py3-none-any.whl → 2.0.8__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 +69 -265
- vnai/beam/__init__.py +2 -5
- vnai/beam/metrics.py +62 -187
- vnai/beam/pulse.py +29 -107
- vnai/beam/quota.py +100 -482
- vnai/flow/__init__.py +2 -5
- vnai/flow/queue.py +52 -131
- vnai/flow/relay.py +158 -465
- vnai/scope/__init__.py +2 -5
- vnai/scope/profile.py +219 -762
- vnai/scope/promo.py +97 -371
- vnai/scope/state.py +71 -220
- {vnai-2.0.6.dist-info → vnai-2.0.8.dist-info}/METADATA +1 -1
- vnai-2.0.8.dist-info/RECORD +16 -0
- vnai-2.0.6.dist-info/RECORD +0 -16
- {vnai-2.0.6.dist-info → vnai-2.0.8.dist-info}/WHEEL +0 -0
- {vnai-2.0.6.dist-info → vnai-2.0.8.dist-info}/top_level.txt +0 -0
vnai/scope/promo.py
CHANGED
@@ -1,286 +1,96 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
_H='init'
|
2
|
+
_G='simple'
|
3
|
+
_F='markdown'
|
4
|
+
_E='terminal'
|
5
|
+
_D='html'
|
6
|
+
_C=True
|
7
|
+
_B=False
|
8
|
+
_A=None
|
9
|
+
import logging,requests
|
7
10
|
from datetime import datetime
|
8
|
-
import random
|
9
|
-
|
10
|
-
|
11
|
-
import
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# Enum AdCategory (tương thích với vnii)
|
16
|
-
class AdCategory:
|
17
|
-
FREE = 0
|
18
|
-
MANDATORY = 1
|
19
|
-
ANNOUNCEMENT = 2
|
20
|
-
REFERRAL = 3
|
21
|
-
FEATURE = 4
|
22
|
-
GUIDE = 5
|
23
|
-
SURVEY = 6
|
24
|
-
PROMOTION = 7
|
25
|
-
SECURITY = 8
|
26
|
-
MAINTENANCE = 9
|
27
|
-
WARNING = 10
|
28
|
-
|
29
|
-
# Thêm import kiểm tra license từ vnii
|
30
|
-
try:
|
31
|
-
from vnii import lc_init
|
32
|
-
except ImportError:
|
33
|
-
lc_init = None # Nếu không có vnii, luôn coi là free user
|
34
|
-
|
35
|
-
# Module-level logger setup
|
36
|
-
logger = logging.getLogger(__name__)
|
37
|
-
if not logger.hasHandlers():
|
38
|
-
# Add a simple stream handler that only outputs the message text
|
39
|
-
handler = logging.StreamHandler()
|
40
|
-
handler.setFormatter(logging.Formatter('%(message)s'))
|
41
|
-
logger.addHandler(handler)
|
42
|
-
logger.setLevel(logging.INFO)
|
43
|
-
|
11
|
+
import random,threading,time,urllib.parse
|
12
|
+
_vnii_check_attempted=_B
|
13
|
+
class AdCategory:FREE=0;MANDATORY=1;ANNOUNCEMENT=2;REFERRAL=3;FEATURE=4;GUIDE=5;SURVEY=6;PROMOTION=7;SECURITY=8;MAINTENANCE=9;WARNING=10
|
14
|
+
try:from vnii import lc_init
|
15
|
+
except ImportError:lc_init=_A
|
16
|
+
logger=logging.getLogger(__name__)
|
17
|
+
if not logger.hasHandlers():handler=logging.StreamHandler();handler.setFormatter(logging.Formatter('%(message)s'));logger.addHandler(handler);logger.setLevel(logging.INFO)
|
44
18
|
class ContentManager:
|
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
|
-
status = license_info.get('status', '').lower()
|
121
|
-
if 'recognized and verified' in status:
|
122
|
-
self.is_paid_user = True
|
123
|
-
logger.debug("[promo] Đã xác nhận paid user từ vnii. Sẽ không hiện quảng cáo.")
|
124
|
-
else:
|
125
|
-
logger.debug(f"[promo] Không xác nhận được paid user từ vnii. Status: {status}")
|
126
|
-
except Exception as e:
|
127
|
-
logger.warning(f"[promo] Không thể kiểm tra trạng thái sponsor: {e}. Sẽ coi là free user và hiện quảng cáo.")
|
128
|
-
else:
|
129
|
-
logger.debug("[promo] Không tìm thấy module vnii. Luôn coi là free user và hiện quảng cáo.")
|
130
|
-
|
131
|
-
# Timestamp of last content display (epoch seconds)
|
132
|
-
self.last_display = 0
|
133
|
-
# Minimum interval between displays (24 hours)
|
134
|
-
self.display_interval = 24 * 3600
|
135
|
-
|
136
|
-
# Base endpoints for fetching remote content and linking
|
137
|
-
self.content_base_url = "https://hq.vnstocks.com/static"
|
138
|
-
self.target_url = "https://vnstocks.com/lp-khoa-hoc-python-chung-khoan"
|
139
|
-
self.image_url = (
|
140
|
-
"https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg"
|
141
|
-
)
|
142
|
-
|
143
|
-
# Launch the background thread to periodically present content
|
144
|
-
logger.debug(f"[promo] is_paid_user = {self.is_paid_user}")
|
145
|
-
self._start_periodic_display()
|
146
|
-
|
147
|
-
def _start_periodic_display(self):
|
148
|
-
"""
|
149
|
-
Launch a daemon thread that sleeps a random duration between 2–6 hours,
|
150
|
-
then checks if the display interval has elapsed and calls present_content.
|
151
|
-
"""
|
152
|
-
logger.debug("[promo] Khởi tạo thread hiển thị quảng cáo định kỳ...")
|
153
|
-
def periodic_display():
|
154
|
-
logger.debug("[promo] Thread quảng cáo bắt đầu chạy.")
|
155
|
-
while True:
|
156
|
-
# Nếu là paid user thì không bao giờ hiện ads
|
157
|
-
if self.is_paid_user:
|
158
|
-
logger.debug("[promo] Đang là paid user trong thread. Không hiện quảng cáo, dừng thread.")
|
159
|
-
break
|
160
|
-
# Randomize sleep to avoid synchronized requests across instances
|
161
|
-
sleep_time = random.randint(2 * 3600, 6 * 3600)
|
162
|
-
logger.debug(f"[promo] Thread quảng cáo sẽ ngủ {sleep_time//3600} giờ...")
|
163
|
-
time.sleep(sleep_time)
|
164
|
-
|
165
|
-
# Present content if enough time has passed since last_display
|
166
|
-
current_time = time.time()
|
167
|
-
logger.debug(f"[promo] Kiểm tra điều kiện hiện quảng cáo: time since last_display = {current_time - self.last_display}s")
|
168
|
-
if current_time - self.last_display >= self.display_interval:
|
169
|
-
logger.debug("[promo] Đã đủ thời gian, sẽ gọi present_content(context='periodic')")
|
170
|
-
self.present_content(context="periodic")
|
171
|
-
else:
|
172
|
-
logger.debug("[promo] Chưa đủ thời gian, chưa hiện quảng cáo.")
|
173
|
-
|
174
|
-
thread = threading.Thread(target=periodic_display, daemon=True)
|
175
|
-
thread.start()
|
176
|
-
|
177
|
-
def fetch_remote_content(self, context: str = "init", html: bool = True) -> str:
|
178
|
-
if self.is_paid_user:
|
179
|
-
logger.debug("Paid user detected. Skip fetching remote content (ads).")
|
180
|
-
return ""
|
181
|
-
|
182
|
-
"""
|
183
|
-
Fetch promotional content from remote service with context and format flag.
|
184
|
-
|
185
|
-
Args:
|
186
|
-
context: usage context (e.g., "init", "periodic", "loop").
|
187
|
-
html: if True, request HTML; otherwise plain text.
|
188
|
-
|
189
|
-
Returns:
|
190
|
-
The content string on HTTP 200, or None on failure.
|
191
|
-
"""
|
192
|
-
try:
|
193
|
-
# Build query params and URL
|
194
|
-
params = {"context": context, "html": "true" if html else "false"}
|
195
|
-
url = f"{self.content_base_url}?{urllib.parse.urlencode(params)}"
|
196
|
-
|
197
|
-
response = requests.get(url, timeout=3)
|
198
|
-
if response.status_code == 200:
|
199
|
-
return response.text
|
200
|
-
# Log non-200 responses at debug level
|
201
|
-
logger.debug(f"Non-200 response fetching content: {response.status_code}")
|
202
|
-
return None
|
203
|
-
except Exception as e:
|
204
|
-
# Log exceptions without interrupting user code
|
205
|
-
logger.debug(f"Failed to fetch remote content: {e}")
|
206
|
-
return None
|
207
|
-
|
208
|
-
def present_content(self, context: str = "init", ad_category: int = AdCategory.FREE) -> None:
|
209
|
-
environment = None
|
210
|
-
logger.debug(f"[promo] Gọi present_content(context={context}, ad_category={ad_category}). is_paid_user = {getattr(self, 'is_paid_user', None)}")
|
211
|
-
"""
|
212
|
-
Display promotional content in the appropriate environment.
|
213
|
-
ad_category: Loại quảng cáo (FREE, ANNOUNCEMENT, ...)
|
214
|
-
"""
|
215
|
-
# Nếu là paid user và ad_category là FREE thì skip, còn lại vẫn hiện
|
216
|
-
if getattr(self, 'is_paid_user', False) and ad_category == AdCategory.FREE:
|
217
|
-
logger.debug("[promo] Đang là paid user và ad_category là FREE. Không hiện quảng cáo.")
|
218
|
-
return
|
219
|
-
|
220
|
-
# Chỉ hiện log này nếu debug mode
|
221
|
-
if logger.level <= logging.DEBUG:
|
222
|
-
logger.debug(f"[promo] Sẽ hiển thị quảng cáo với context={context}, ad_category={ad_category}")
|
223
|
-
# Update last display timestamp
|
224
|
-
self.last_display = time.time()
|
225
|
-
|
226
|
-
# Auto-detect environment if not provided
|
227
|
-
if environment is None:
|
228
|
-
try:
|
229
|
-
from vnai.scope.profile import inspector
|
230
|
-
environment = inspector.examine().get("environment", "unknown")
|
231
|
-
logger.debug(f"[promo] Đã detect environment: {environment}")
|
232
|
-
except Exception as e:
|
233
|
-
logger.debug(f"[promo] Không detect được environment: {e}")
|
234
|
-
environment = "unknown"
|
235
|
-
|
236
|
-
# Retrieve remote or HTML/text content based on environment
|
237
|
-
remote_content = self.fetch_remote_content(
|
238
|
-
context=context, html=(environment == "jupyter")
|
239
|
-
)
|
240
|
-
logger.debug(f"[promo] remote_content = {bool(remote_content)} (None -> False, có nội dung -> True)")
|
241
|
-
# Generate fallback messages if remote fetch fails
|
242
|
-
fallback = self._generate_fallback_content(context)
|
243
|
-
logger.debug(f"[promo] fallback keys: {list(fallback.keys())}")
|
244
|
-
|
245
|
-
if environment == "jupyter":
|
246
|
-
logger.debug("[promo] Đang ở môi trường Jupyter, sẽ thử display HTML/Markdown.")
|
247
|
-
try:
|
248
|
-
from IPython.display import display, HTML, Markdown
|
249
|
-
|
250
|
-
if remote_content:
|
251
|
-
logger.debug("[promo] Hiển thị quảng cáo bằng HTML từ remote_content.")
|
252
|
-
display(HTML(remote_content))
|
253
|
-
else:
|
254
|
-
logger.debug("[promo] Không có remote_content, thử display fallback Markdown/HTML.")
|
255
|
-
try:
|
256
|
-
display(Markdown(fallback["markdown"]))
|
257
|
-
except Exception as e:
|
258
|
-
logger.debug(f"[promo] Lỗi khi display Markdown: {e}, fallback HTML.")
|
259
|
-
display(HTML(fallback["html"]))
|
260
|
-
except Exception as e:
|
261
|
-
logger.debug(f"[promo] Jupyter display failed: {e}")
|
262
|
-
|
263
|
-
elif environment == "terminal":
|
264
|
-
logger.debug("[promo] Đang ở môi trường terminal, sẽ log quảng cáo ra logger.")
|
265
|
-
# Log terminal-friendly or raw content via logger
|
266
|
-
if remote_content:
|
267
|
-
logger.debug("[promo] Hiển thị quảng cáo bằng remote_content cho terminal.")
|
268
|
-
logger.debug(remote_content)
|
269
|
-
else:
|
270
|
-
logger.debug("[promo] Không có remote_content, hiển thị fallback terminal.")
|
271
|
-
logger.debug(fallback["terminal"])
|
272
|
-
|
273
|
-
else:
|
274
|
-
logger.debug(f"[promo] Môi trường khác ({environment}), hiển thị fallback simple.")
|
275
|
-
# Generic simple message for other environments
|
276
|
-
logger.debug(fallback["simple"])
|
277
|
-
|
278
|
-
def _generate_fallback_content(self, context):
|
279
|
-
fallback = {"html": "", "markdown": "", "terminal": "", "simple": ""}
|
280
|
-
|
281
|
-
if context == "loop":
|
282
|
-
fallback["html"] = (
|
283
|
-
f"""
|
19
|
+
_instance=_A;_lock=threading.Lock()
|
20
|
+
def __new__(cls):
|
21
|
+
with cls._lock:
|
22
|
+
if cls._instance is _A:cls._instance=super(ContentManager,cls).__new__(cls);cls._instance._initialize()
|
23
|
+
return cls._instance
|
24
|
+
def _initialize(self):
|
25
|
+
A='vnii';global _vnii_check_attempted
|
26
|
+
if _vnii_check_attempted:return
|
27
|
+
_vnii_check_attempted=_C;import sys,importlib
|
28
|
+
try:
|
29
|
+
import importlib.metadata
|
30
|
+
try:
|
31
|
+
old_version=importlib.metadata.version(A);VNII_LATEST_VERSION='0.0.9';VNII_URL=f"https://github.com/vnstock-hq/licensing/releases/download/vnii-{VNII_LATEST_VERSION}/vnii-{VNII_LATEST_VERSION}.tar.gz";logger.debug(f"Đã phát hiện vnii version {old_version}. Đang cập nhật lên bản mới nhất...");import subprocess;subprocess.check_call([sys.executable,'-m','pip','install',f"vnii@{VNII_URL}"]);importlib.invalidate_caches()
|
32
|
+
if A in sys.modules:importlib.reload(sys.modules[A])
|
33
|
+
else:import vnii
|
34
|
+
new_version=importlib.metadata.version(A);logger.debug(f"Đã cập nhật vnii lên version {new_version}")
|
35
|
+
except importlib.metadata.PackageNotFoundError:logger.debug('Không phát hiện vnii trên hệ thống. Luôn coi là free user, không kiểm tra license.');self.is_paid_user=_B;return
|
36
|
+
except Exception as e:
|
37
|
+
logger.warning(f"Lỗi khi kiểm tra/cài đặt vnii: {e}");user_msg=f"Không thể tự động cài đặt/cập nhật vnii. Vui lòng liên hệ admin hoặc hỗ trợ kỹ thuật của Vnstock để được trợ giúp. Chi tiết lỗi: {e}";logger.error(user_msg)
|
38
|
+
try:print(user_msg)
|
39
|
+
except Exception:pass
|
40
|
+
self.is_paid_user=_B;return
|
41
|
+
self.is_paid_user=_B;logger.debug('[promo] Bắt đầu kiểm tra trạng thái paid user với vnii...')
|
42
|
+
if lc_init is not _A:
|
43
|
+
try:
|
44
|
+
license_info=lc_init(repo_name='vnstock');logger.debug(f"[promo] license_info trả về: {license_info}");status=license_info.get('status','').lower()
|
45
|
+
if'recognized and verified'in status:self.is_paid_user=_C;logger.debug('[promo] Đã xác nhận paid user từ vnii. Sẽ không hiện quảng cáo.')
|
46
|
+
else:logger.debug(f"[promo] Không xác nhận được paid user từ vnii. Status: {status}")
|
47
|
+
except Exception as e:logger.warning(f"[promo] Không thể kiểm tra trạng thái sponsor: {e}. Sẽ coi là free user và hiện quảng cáo.")
|
48
|
+
else:logger.debug('[promo] Không tìm thấy module vnii. Luôn coi là free user và hiện quảng cáo.')
|
49
|
+
self.last_display=0;self.display_interval=86400;self.content_base_url='https://hq.vnstocks.com/static';self.target_url='https://vnstocks.com/lp-khoa-hoc-python-chung-khoan';self.image_url='https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg';logger.debug(f"[promo] is_paid_user = {self.is_paid_user}");self._start_periodic_display()
|
50
|
+
def _start_periodic_display(self):
|
51
|
+
logger.debug('[promo] Khởi tạo thread hiển thị quảng cáo định kỳ...')
|
52
|
+
def periodic_display():
|
53
|
+
logger.debug('[promo] Thread quảng cáo bắt đầu chạy.')
|
54
|
+
while _C:
|
55
|
+
if self.is_paid_user:logger.debug('[promo] Đang là paid user trong thread. Không hiện quảng cáo, dừng thread.');break
|
56
|
+
sleep_time=random.randint(7200,21600);logger.debug(f"[promo] Thread quảng cáo sẽ ngủ {sleep_time//3600} giờ...");time.sleep(sleep_time);current_time=time.time();logger.debug(f"[promo] Kiểm tra điều kiện hiện quảng cáo: time since last_display = {current_time-self.last_display}s")
|
57
|
+
if current_time-self.last_display>=self.display_interval:logger.debug("[promo] Đã đủ thời gian, sẽ gọi present_content(context='periodic')");self.present_content(context='periodic')
|
58
|
+
else:logger.debug('[promo] Chưa đủ thời gian, chưa hiện quảng cáo.')
|
59
|
+
thread=threading.Thread(target=periodic_display,daemon=_C);thread.start()
|
60
|
+
def fetch_remote_content(self,context:str=_H,html:bool=_C)->str:
|
61
|
+
if self.is_paid_user:logger.debug('Paid user detected. Skip fetching remote content (ads).');return''
|
62
|
+
try:
|
63
|
+
params={'context':context,_D:'true'if html else'false'};url=f"{self.content_base_url}?{urllib.parse.urlencode(params)}";response=requests.get(url,timeout=3)
|
64
|
+
if response.status_code==200:return response.text
|
65
|
+
logger.debug(f"Non-200 response fetching content: {response.status_code}");return
|
66
|
+
except Exception as e:logger.debug(f"Failed to fetch remote content: {e}");return
|
67
|
+
def present_content(self,context:str=_H,ad_category:int=AdCategory.FREE)->_A:
|
68
|
+
C='jupyter';B='unknown';A='is_paid_user';environment=_A;logger.debug(f"[promo] Gọi present_content(context={context}, ad_category={ad_category}). is_paid_user = {getattr(self,A,_A)}")
|
69
|
+
if getattr(self,A,_B)and ad_category==AdCategory.FREE:logger.debug('[promo] Đang là paid user và ad_category là FREE. Không hiện quảng cáo.');return
|
70
|
+
if logger.level<=logging.DEBUG:logger.debug(f"[promo] Sẽ hiển thị quảng cáo với context={context}, ad_category={ad_category}")
|
71
|
+
self.last_display=time.time()
|
72
|
+
if environment is _A:
|
73
|
+
try:from vnai.scope.profile import inspector;environment=inspector.examine().get('environment',B);logger.debug(f"[promo] Đã detect environment: {environment}")
|
74
|
+
except Exception as e:logger.debug(f"[promo] Không detect được environment: {e}");environment=B
|
75
|
+
remote_content=self.fetch_remote_content(context=context,html=environment==C);logger.debug(f"[promo] remote_content = {bool(remote_content)} (None -> False, có nội dung -> True)");fallback=self._generate_fallback_content(context);logger.debug(f"[promo] fallback keys: {list(fallback.keys())}")
|
76
|
+
if environment==C:
|
77
|
+
logger.debug('[promo] Đang ở môi trường Jupyter, sẽ thử display HTML/Markdown.')
|
78
|
+
try:
|
79
|
+
from IPython.display import display,HTML,Markdown
|
80
|
+
if remote_content:logger.debug('[promo] Hiển thị quảng cáo bằng HTML từ remote_content.');display(HTML(remote_content))
|
81
|
+
else:
|
82
|
+
logger.debug('[promo] Không có remote_content, thử display fallback Markdown/HTML.')
|
83
|
+
try:display(Markdown(fallback[_F]))
|
84
|
+
except Exception as e:logger.debug(f"[promo] Lỗi khi display Markdown: {e}, fallback HTML.");display(HTML(fallback[_D]))
|
85
|
+
except Exception as e:logger.debug(f"[promo] Jupyter display failed: {e}")
|
86
|
+
elif environment==_E:
|
87
|
+
logger.debug('[promo] Đang ở môi trường terminal, sẽ log quảng cáo ra logger.')
|
88
|
+
if remote_content:logger.debug('[promo] Hiển thị quảng cáo bằng remote_content cho terminal.');logger.debug(remote_content)
|
89
|
+
else:logger.debug('[promo] Không có remote_content, hiển thị fallback terminal.');logger.debug(fallback[_E])
|
90
|
+
else:logger.debug(f"[promo] Môi trường khác ({environment}), hiển thị fallback simple.");logger.debug(fallback[_G])
|
91
|
+
def _generate_fallback_content(self,context):
|
92
|
+
fallback={_D:'',_F:'',_E:'',_G:''}
|
93
|
+
if context=='loop':fallback[_D]=f'''
|
284
94
|
<div style="border: 1px solid #e74c3c; padding: 15px; border-radius: 5px; margin: 10px 0;">
|
285
95
|
<h3 style="color: #e74c3c;">⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests</h3>
|
286
96
|
<p>Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:</p>
|
@@ -290,40 +100,8 @@ class ContentManager:
|
|
290
100
|
<li>Tham gia gói tài trợ <a href="https://vnstocks.com/insiders-program" style="color: #3498db;">Vnstock Insider</a> để tăng 5X giới hạn API</li>
|
291
101
|
</ul>
|
292
102
|
</div>
|
293
|
-
|
294
|
-
|
295
|
-
fallback["markdown"] = (
|
296
|
-
"""
|
297
|
-
## ⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests
|
298
|
-
|
299
|
-
Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:
|
300
|
-
* Thêm thời gian chờ giữa các lần gọi API
|
301
|
-
* Sử dụng xử lý theo batch thay vì lặp liên tục
|
302
|
-
* Tham gia gói tài trợ [Vnstock Insider](https://vnstocks.com/insiders-program) để tăng 5X giới hạn API
|
303
|
-
"""
|
304
|
-
)
|
305
|
-
fallback["terminal"] = (
|
306
|
-
"""
|
307
|
-
╔═════════════════════════════════════════════════════════════════╗
|
308
|
-
║ ║
|
309
|
-
║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║
|
310
|
-
║ ║
|
311
|
-
║ ✓ Tăng ngay 500% tốc độ gọi API - Không còn lỗi RateLimit ║
|
312
|
-
║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║
|
313
|
-
║ ║
|
314
|
-
║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║
|
315
|
-
║ https://vnstocks.com/insiders-program ║
|
316
|
-
║ ║
|
317
|
-
╚═════════════════════════════════════════════════════════════════╝
|
318
|
-
"""
|
319
|
-
)
|
320
|
-
fallback["simple"] = (
|
321
|
-
"🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói "
|
322
|
-
"Vnstock Insider: https://vnstocks.com/insiders-program"
|
323
|
-
)
|
324
|
-
else:
|
325
|
-
fallback["html"] = (
|
326
|
-
f"""
|
103
|
+
''';fallback[_F]='\n## ⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests\n\nĐể tránh bị giới hạn tốc độ và tối ưu hiệu suất:\n* Thêm thời gian chờ giữa các lần gọi API\n* Sử dụng xử lý theo batch thay vì lặp liên tục\n* Tham gia gói tài trợ [Vnstock Insider](https://vnstocks.com/insiders-program) để tăng 5X giới hạn API\n ';fallback[_E]='\n╔═════════════════════════════════════════════════════════════════╗\n║ ║\n║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║\n║ ║\n║ ✓ Tăng ngay 500% tốc độ gọi API - Không còn lỗi RateLimit ║\n║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║\n║ ║\n║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║\n║ https://vnstocks.com/insiders-program ║\n║ ║\n╚═════════════════════════════════════════════════════════════════╝\n ';fallback[_G]='🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói Vnstock Insider: https://vnstocks.com/insiders-program'
|
104
|
+
else:fallback[_D]=f'''
|
327
105
|
<div style="border: 1px solid #3498db; padding: 15px; border-radius: 5px; margin: 10px 0;">
|
328
106
|
<h3 style="color: #3498db;">👋 Chào mừng bạn đến với Vnstock!</h3>
|
329
107
|
<p>Cảm ơn bạn đã sử dụng thư viện phân tích chứng khoán #1 tại Việt Nam cho Python</p>
|
@@ -333,59 +111,7 @@ class ContentManager:
|
|
333
111
|
</ul>
|
334
112
|
<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>
|
335
113
|
</div>
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
## 👋 Chào mừng bạn đến với Vnstock!
|
341
|
-
|
342
|
-
Cảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam
|
343
|
-
|
344
|
-
* Tài liệu: [Sổ tay hướng dẫn](https://vnstocks.com/docs)
|
345
|
-
* Cộng đồng: [Nhóm Facebook](https://facebook.com/groups/vnstock.official)
|
346
|
-
|
347
|
-
Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.
|
348
|
-
"""
|
349
|
-
)
|
350
|
-
fallback["terminal"] = (
|
351
|
-
"""
|
352
|
-
╔════════════════════════════════════════════════════════════╗
|
353
|
-
║ ║
|
354
|
-
║ 👋 Chào mừng bạn đến với Vnstock! ║
|
355
|
-
║ ║
|
356
|
-
║ Cảm ơn bạn đã sử dụng package phân tích ║
|
357
|
-
║ chứng khoán #1 tại Việt Nam ║
|
358
|
-
║ ║
|
359
|
-
║ ✓ Tài liệu: https://vnstocks.com/docs ║
|
360
|
-
║ ✓ Cộng đồng: https://facebook.com/groups/vnstock.official ║
|
361
|
-
║ ║
|
362
|
-
║ Khám phá các tính năng mới nhất và tham gia ║
|
363
|
-
║ cộng đồng để nhận hỗ trợ. ║
|
364
|
-
║ ║
|
365
|
-
╚════════════════════════════════════════════════════════════╝
|
366
|
-
"""
|
367
|
-
)
|
368
|
-
fallback["simple"] = (
|
369
|
-
"👋 Chào mừng bạn đến với Vnstock! "
|
370
|
-
"Tài liệu: https://vnstocks.com/onboard | "
|
371
|
-
"Cộng đồng: https://facebook.com/groups/vnstock.official"
|
372
|
-
)
|
373
|
-
return fallback
|
374
|
-
|
375
|
-
# Singleton instance for module-level use
|
376
|
-
manager = ContentManager()
|
377
|
-
|
378
|
-
# Ép buộc hiện quảng cáo ngay khi import nếu là free user
|
379
|
-
if not getattr(manager, 'is_paid_user', False):
|
380
|
-
manager.present_content(context="auto_import")
|
381
|
-
|
382
|
-
|
383
|
-
def present(context: str = "init", ad_category: int = AdCategory.FREE) -> None:
|
384
|
-
"""
|
385
|
-
Shortcut to ContentManager.present_content for external callers.
|
386
|
-
|
387
|
-
Args:
|
388
|
-
context: propagate context string to ContentManager.
|
389
|
-
ad_category: loại quảng cáo (FREE, ANNOUNCEMENT, ...)
|
390
|
-
"""
|
391
|
-
manager.present_content(context=context, ad_category=ad_category)
|
114
|
+
''';fallback[_F]='\n## 👋 Chào mừng bạn đến với Vnstock!\n\nCảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam\n\n* Tài liệu: [Sổ tay hướng dẫn](https://vnstocks.com/docs)\n* Cộng đồng: [Nhóm Facebook](https://facebook.com/groups/vnstock.official)\n\nKhám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.\n ';fallback[_E]='\n╔════════════════════════════════════════════════════════════╗\n║ ║\n║ 👋 Chào mừng bạn đến với Vnstock! ║\n║ ║\n║ Cảm ơn bạn đã sử dụng package phân tích ║\n║ chứng khoán #1 tại Việt Nam ║\n║ ║\n║ ✓ Tài liệu: https://vnstocks.com/docs ║\n║ ✓ Cộng đồng: https://facebook.com/groups/vnstock.official ║\n║ ║\n║ Khám phá các tính năng mới nhất và tham gia ║\n║ cộng đồng để nhận hỗ trợ. ║\n║ ║\n╚════════════════════════════════════════════════════════════╝\n ';fallback[_G]='👋 Chào mừng bạn đến với Vnstock! Tài liệu: https://vnstocks.com/onboard | Cộng đồng: https://facebook.com/groups/vnstock.official'
|
115
|
+
return fallback
|
116
|
+
manager=ContentManager()
|
117
|
+
def present(context:str=_H,ad_category:int=AdCategory.FREE)->_A:manager.present_content(context=context,ad_category=ad_category)
|