webscout 8.2.7__py3-none-any.whl → 8.2.9__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.
Files changed (281) hide show
  1. webscout/AIauto.py +33 -15
  2. webscout/AIbase.py +96 -37
  3. webscout/AIutel.py +703 -250
  4. webscout/Bard.py +441 -323
  5. webscout/Extra/Act.md +309 -0
  6. webscout/Extra/GitToolkit/__init__.py +10 -0
  7. webscout/Extra/GitToolkit/gitapi/README.md +110 -0
  8. webscout/Extra/GitToolkit/gitapi/__init__.py +12 -0
  9. webscout/Extra/GitToolkit/gitapi/repository.py +195 -0
  10. webscout/Extra/GitToolkit/gitapi/user.py +96 -0
  11. webscout/Extra/GitToolkit/gitapi/utils.py +62 -0
  12. webscout/Extra/YTToolkit/README.md +375 -0
  13. webscout/Extra/YTToolkit/YTdownloader.py +957 -0
  14. webscout/Extra/YTToolkit/__init__.py +3 -0
  15. webscout/Extra/YTToolkit/transcriber.py +476 -0
  16. webscout/Extra/YTToolkit/ytapi/README.md +44 -0
  17. webscout/Extra/YTToolkit/ytapi/__init__.py +6 -0
  18. webscout/Extra/YTToolkit/ytapi/channel.py +307 -0
  19. webscout/Extra/YTToolkit/ytapi/errors.py +13 -0
  20. webscout/Extra/YTToolkit/ytapi/extras.py +118 -0
  21. webscout/Extra/YTToolkit/ytapi/https.py +88 -0
  22. webscout/Extra/YTToolkit/ytapi/patterns.py +61 -0
  23. webscout/Extra/YTToolkit/ytapi/playlist.py +59 -0
  24. webscout/Extra/YTToolkit/ytapi/pool.py +8 -0
  25. webscout/Extra/YTToolkit/ytapi/query.py +40 -0
  26. webscout/Extra/YTToolkit/ytapi/stream.py +63 -0
  27. webscout/Extra/YTToolkit/ytapi/utils.py +62 -0
  28. webscout/Extra/YTToolkit/ytapi/video.py +232 -0
  29. webscout/Extra/__init__.py +7 -0
  30. webscout/Extra/autocoder/__init__.py +9 -0
  31. webscout/Extra/autocoder/autocoder.py +1105 -0
  32. webscout/Extra/autocoder/autocoder_utiles.py +332 -0
  33. webscout/Extra/gguf.md +430 -0
  34. webscout/Extra/gguf.py +684 -0
  35. webscout/Extra/tempmail/README.md +488 -0
  36. webscout/Extra/tempmail/__init__.py +28 -0
  37. webscout/Extra/tempmail/async_utils.py +141 -0
  38. webscout/Extra/tempmail/base.py +161 -0
  39. webscout/Extra/tempmail/cli.py +187 -0
  40. webscout/Extra/tempmail/emailnator.py +84 -0
  41. webscout/Extra/tempmail/mail_tm.py +361 -0
  42. webscout/Extra/tempmail/temp_mail_io.py +292 -0
  43. webscout/Extra/weather.md +281 -0
  44. webscout/Extra/weather.py +194 -0
  45. webscout/Extra/weather_ascii.py +76 -0
  46. webscout/Litlogger/README.md +10 -0
  47. webscout/Litlogger/__init__.py +15 -0
  48. webscout/Litlogger/formats.py +4 -0
  49. webscout/Litlogger/handlers.py +103 -0
  50. webscout/Litlogger/levels.py +13 -0
  51. webscout/Litlogger/logger.py +92 -0
  52. webscout/Provider/AI21.py +177 -0
  53. webscout/Provider/AISEARCH/DeepFind.py +254 -0
  54. webscout/Provider/AISEARCH/Perplexity.py +333 -0
  55. webscout/Provider/AISEARCH/README.md +279 -0
  56. webscout/Provider/AISEARCH/__init__.py +9 -0
  57. webscout/Provider/AISEARCH/felo_search.py +202 -0
  58. webscout/Provider/AISEARCH/genspark_search.py +324 -0
  59. webscout/Provider/AISEARCH/hika_search.py +186 -0
  60. webscout/Provider/AISEARCH/iask_search.py +410 -0
  61. webscout/Provider/AISEARCH/monica_search.py +220 -0
  62. webscout/Provider/AISEARCH/scira_search.py +298 -0
  63. webscout/Provider/AISEARCH/webpilotai_search.py +255 -0
  64. webscout/Provider/Aitopia.py +316 -0
  65. webscout/Provider/AllenAI.py +440 -0
  66. webscout/Provider/Andi.py +228 -0
  67. webscout/Provider/Blackboxai.py +791 -0
  68. webscout/Provider/ChatGPTClone.py +237 -0
  69. webscout/Provider/ChatGPTGratis.py +194 -0
  70. webscout/Provider/ChatSandbox.py +342 -0
  71. webscout/Provider/Cloudflare.py +324 -0
  72. webscout/Provider/Cohere.py +208 -0
  73. webscout/Provider/Deepinfra.py +340 -0
  74. webscout/Provider/ExaAI.py +261 -0
  75. webscout/Provider/ExaChat.py +358 -0
  76. webscout/Provider/Flowith.py +217 -0
  77. webscout/Provider/FreeGemini.py +250 -0
  78. webscout/Provider/Gemini.py +169 -0
  79. webscout/Provider/GithubChat.py +369 -0
  80. webscout/Provider/GizAI.py +295 -0
  81. webscout/Provider/Glider.py +225 -0
  82. webscout/Provider/Groq.py +801 -0
  83. webscout/Provider/HF_space/__init__.py +0 -0
  84. webscout/Provider/HF_space/qwen_qwen2.py +206 -0
  85. webscout/Provider/HeckAI.py +375 -0
  86. webscout/Provider/HuggingFaceChat.py +469 -0
  87. webscout/Provider/Hunyuan.py +283 -0
  88. webscout/Provider/Jadve.py +291 -0
  89. webscout/Provider/Koboldai.py +384 -0
  90. webscout/Provider/LambdaChat.py +411 -0
  91. webscout/Provider/Llama3.py +259 -0
  92. webscout/Provider/MCPCore.py +315 -0
  93. webscout/Provider/Marcus.py +198 -0
  94. webscout/Provider/Nemotron.py +218 -0
  95. webscout/Provider/Netwrck.py +270 -0
  96. webscout/Provider/OLLAMA.py +396 -0
  97. webscout/Provider/OPENAI/BLACKBOXAI.py +766 -0
  98. webscout/Provider/OPENAI/Cloudflare.py +378 -0
  99. webscout/Provider/OPENAI/FreeGemini.py +283 -0
  100. webscout/Provider/OPENAI/NEMOTRON.py +232 -0
  101. webscout/Provider/OPENAI/Qwen3.py +283 -0
  102. webscout/Provider/OPENAI/README.md +952 -0
  103. webscout/Provider/OPENAI/TwoAI.py +357 -0
  104. webscout/Provider/OPENAI/__init__.py +40 -0
  105. webscout/Provider/OPENAI/ai4chat.py +293 -0
  106. webscout/Provider/OPENAI/api.py +969 -0
  107. webscout/Provider/OPENAI/base.py +249 -0
  108. webscout/Provider/OPENAI/c4ai.py +373 -0
  109. webscout/Provider/OPENAI/chatgpt.py +556 -0
  110. webscout/Provider/OPENAI/chatgptclone.py +494 -0
  111. webscout/Provider/OPENAI/chatsandbox.py +173 -0
  112. webscout/Provider/OPENAI/copilot.py +242 -0
  113. webscout/Provider/OPENAI/deepinfra.py +322 -0
  114. webscout/Provider/OPENAI/e2b.py +1414 -0
  115. webscout/Provider/OPENAI/exaai.py +417 -0
  116. webscout/Provider/OPENAI/exachat.py +444 -0
  117. webscout/Provider/OPENAI/flowith.py +162 -0
  118. webscout/Provider/OPENAI/freeaichat.py +359 -0
  119. webscout/Provider/OPENAI/glider.py +326 -0
  120. webscout/Provider/OPENAI/groq.py +364 -0
  121. webscout/Provider/OPENAI/heckai.py +308 -0
  122. webscout/Provider/OPENAI/llmchatco.py +335 -0
  123. webscout/Provider/OPENAI/mcpcore.py +389 -0
  124. webscout/Provider/OPENAI/multichat.py +376 -0
  125. webscout/Provider/OPENAI/netwrck.py +357 -0
  126. webscout/Provider/OPENAI/oivscode.py +287 -0
  127. webscout/Provider/OPENAI/opkfc.py +496 -0
  128. webscout/Provider/OPENAI/pydantic_imports.py +172 -0
  129. webscout/Provider/OPENAI/scirachat.py +477 -0
  130. webscout/Provider/OPENAI/sonus.py +304 -0
  131. webscout/Provider/OPENAI/standardinput.py +433 -0
  132. webscout/Provider/OPENAI/textpollinations.py +339 -0
  133. webscout/Provider/OPENAI/toolbaz.py +413 -0
  134. webscout/Provider/OPENAI/typefully.py +355 -0
  135. webscout/Provider/OPENAI/typegpt.py +364 -0
  136. webscout/Provider/OPENAI/uncovrAI.py +463 -0
  137. webscout/Provider/OPENAI/utils.py +318 -0
  138. webscout/Provider/OPENAI/venice.py +431 -0
  139. webscout/Provider/OPENAI/wisecat.py +387 -0
  140. webscout/Provider/OPENAI/writecream.py +163 -0
  141. webscout/Provider/OPENAI/x0gpt.py +365 -0
  142. webscout/Provider/OPENAI/yep.py +382 -0
  143. webscout/Provider/OpenGPT.py +209 -0
  144. webscout/Provider/Openai.py +496 -0
  145. webscout/Provider/PI.py +429 -0
  146. webscout/Provider/Perplexitylabs.py +415 -0
  147. webscout/Provider/QwenLM.py +254 -0
  148. webscout/Provider/Reka.py +214 -0
  149. webscout/Provider/StandardInput.py +290 -0
  150. webscout/Provider/TTI/README.md +82 -0
  151. webscout/Provider/TTI/__init__.py +7 -0
  152. webscout/Provider/TTI/aiarta.py +365 -0
  153. webscout/Provider/TTI/artbit.py +0 -0
  154. webscout/Provider/TTI/base.py +64 -0
  155. webscout/Provider/TTI/fastflux.py +200 -0
  156. webscout/Provider/TTI/magicstudio.py +201 -0
  157. webscout/Provider/TTI/piclumen.py +203 -0
  158. webscout/Provider/TTI/pixelmuse.py +225 -0
  159. webscout/Provider/TTI/pollinations.py +221 -0
  160. webscout/Provider/TTI/utils.py +11 -0
  161. webscout/Provider/TTS/README.md +192 -0
  162. webscout/Provider/TTS/__init__.py +10 -0
  163. webscout/Provider/TTS/base.py +159 -0
  164. webscout/Provider/TTS/deepgram.py +156 -0
  165. webscout/Provider/TTS/elevenlabs.py +111 -0
  166. webscout/Provider/TTS/gesserit.py +128 -0
  167. webscout/Provider/TTS/murfai.py +113 -0
  168. webscout/Provider/TTS/openai_fm.py +129 -0
  169. webscout/Provider/TTS/parler.py +111 -0
  170. webscout/Provider/TTS/speechma.py +580 -0
  171. webscout/Provider/TTS/sthir.py +94 -0
  172. webscout/Provider/TTS/streamElements.py +333 -0
  173. webscout/Provider/TTS/utils.py +280 -0
  174. webscout/Provider/TeachAnything.py +229 -0
  175. webscout/Provider/TextPollinationsAI.py +308 -0
  176. webscout/Provider/TwoAI.py +475 -0
  177. webscout/Provider/TypliAI.py +305 -0
  178. webscout/Provider/UNFINISHED/ChatHub.py +209 -0
  179. webscout/Provider/UNFINISHED/Youchat.py +330 -0
  180. webscout/Provider/UNFINISHED/liner_api_request.py +263 -0
  181. webscout/Provider/UNFINISHED/puterjs.py +635 -0
  182. webscout/Provider/UNFINISHED/test_lmarena.py +119 -0
  183. webscout/Provider/Venice.py +258 -0
  184. webscout/Provider/VercelAI.py +253 -0
  185. webscout/Provider/WiseCat.py +233 -0
  186. webscout/Provider/WrDoChat.py +370 -0
  187. webscout/Provider/Writecream.py +246 -0
  188. webscout/Provider/WritingMate.py +269 -0
  189. webscout/Provider/__init__.py +174 -0
  190. webscout/Provider/ai4chat.py +174 -0
  191. webscout/Provider/akashgpt.py +335 -0
  192. webscout/Provider/asksteve.py +220 -0
  193. webscout/Provider/cerebras.py +290 -0
  194. webscout/Provider/chatglm.py +215 -0
  195. webscout/Provider/cleeai.py +213 -0
  196. webscout/Provider/copilot.py +425 -0
  197. webscout/Provider/elmo.py +283 -0
  198. webscout/Provider/freeaichat.py +285 -0
  199. webscout/Provider/geminiapi.py +208 -0
  200. webscout/Provider/granite.py +235 -0
  201. webscout/Provider/hermes.py +266 -0
  202. webscout/Provider/julius.py +223 -0
  203. webscout/Provider/koala.py +170 -0
  204. webscout/Provider/learnfastai.py +325 -0
  205. webscout/Provider/llama3mitril.py +215 -0
  206. webscout/Provider/llmchat.py +258 -0
  207. webscout/Provider/llmchatco.py +306 -0
  208. webscout/Provider/lmarena.py +198 -0
  209. webscout/Provider/meta.py +801 -0
  210. webscout/Provider/multichat.py +364 -0
  211. webscout/Provider/oivscode.py +309 -0
  212. webscout/Provider/samurai.py +224 -0
  213. webscout/Provider/scira_chat.py +299 -0
  214. webscout/Provider/scnet.py +243 -0
  215. webscout/Provider/searchchat.py +292 -0
  216. webscout/Provider/sonus.py +258 -0
  217. webscout/Provider/talkai.py +194 -0
  218. webscout/Provider/toolbaz.py +353 -0
  219. webscout/Provider/turboseek.py +266 -0
  220. webscout/Provider/typefully.py +202 -0
  221. webscout/Provider/typegpt.py +289 -0
  222. webscout/Provider/uncovr.py +368 -0
  223. webscout/Provider/x0gpt.py +299 -0
  224. webscout/Provider/yep.py +389 -0
  225. webscout/__init__.py +4 -2
  226. webscout/cli.py +3 -28
  227. webscout/client.py +70 -0
  228. webscout/conversation.py +35 -35
  229. webscout/litagent/Readme.md +276 -0
  230. webscout/litagent/__init__.py +29 -0
  231. webscout/litagent/agent.py +455 -0
  232. webscout/litagent/constants.py +60 -0
  233. webscout/litprinter/__init__.py +59 -0
  234. webscout/optimizers.py +419 -419
  235. webscout/scout/README.md +404 -0
  236. webscout/scout/__init__.py +8 -0
  237. webscout/scout/core/__init__.py +7 -0
  238. webscout/scout/core/crawler.py +210 -0
  239. webscout/scout/core/scout.py +607 -0
  240. webscout/scout/core/search_result.py +96 -0
  241. webscout/scout/core/text_analyzer.py +63 -0
  242. webscout/scout/core/text_utils.py +277 -0
  243. webscout/scout/core/web_analyzer.py +52 -0
  244. webscout/scout/element.py +478 -0
  245. webscout/scout/parsers/__init__.py +69 -0
  246. webscout/scout/parsers/html5lib_parser.py +172 -0
  247. webscout/scout/parsers/html_parser.py +236 -0
  248. webscout/scout/parsers/lxml_parser.py +178 -0
  249. webscout/scout/utils.py +37 -0
  250. webscout/swiftcli/Readme.md +323 -0
  251. webscout/swiftcli/__init__.py +95 -0
  252. webscout/swiftcli/core/__init__.py +7 -0
  253. webscout/swiftcli/core/cli.py +297 -0
  254. webscout/swiftcli/core/context.py +104 -0
  255. webscout/swiftcli/core/group.py +241 -0
  256. webscout/swiftcli/decorators/__init__.py +28 -0
  257. webscout/swiftcli/decorators/command.py +221 -0
  258. webscout/swiftcli/decorators/options.py +220 -0
  259. webscout/swiftcli/decorators/output.py +252 -0
  260. webscout/swiftcli/exceptions.py +21 -0
  261. webscout/swiftcli/plugins/__init__.py +9 -0
  262. webscout/swiftcli/plugins/base.py +135 -0
  263. webscout/swiftcli/plugins/manager.py +269 -0
  264. webscout/swiftcli/utils/__init__.py +59 -0
  265. webscout/swiftcli/utils/formatting.py +252 -0
  266. webscout/swiftcli/utils/parsing.py +267 -0
  267. webscout/version.py +1 -1
  268. webscout/webscout_search.py +2 -182
  269. webscout/webscout_search_async.py +1 -179
  270. webscout/zeroart/README.md +89 -0
  271. webscout/zeroart/__init__.py +135 -0
  272. webscout/zeroart/base.py +66 -0
  273. webscout/zeroart/effects.py +101 -0
  274. webscout/zeroart/fonts.py +1239 -0
  275. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/METADATA +262 -83
  276. webscout-8.2.9.dist-info/RECORD +289 -0
  277. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/WHEEL +1 -1
  278. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/entry_points.txt +1 -0
  279. webscout-8.2.7.dist-info/RECORD +0 -26
  280. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/licenses/LICENSE.md +0 -0
  281. {webscout-8.2.7.dist-info → webscout-8.2.9.dist-info}/top_level.txt +0 -0
@@ -56,13 +56,7 @@ class WEBS:
56
56
  "firefox133", "firefox135",
57
57
  ) # fmt: skip
58
58
  _impersonates_os = ("android", "ios", "linux", "macos", "windows")
59
- _chat_models = {
60
- "gpt-4o-mini": "gpt-4o-mini",
61
- "llama-3.3-70b": "meta-llama/Llama-3.3-70B-Instruct-Turbo",
62
- "claude-3-haiku": "claude-3-haiku-20240307",
63
- "o3-mini": "o3-mini",
64
- "mistral-small-3": "mistralai/Mistral-Small-24B-Instruct-2501",
65
- }
59
+
66
60
 
67
61
  def __init__(
68
62
  self,
@@ -119,11 +113,6 @@ class WEBS:
119
113
  self.sleep_timestamp = 0.0
120
114
 
121
115
  self._exception_event = Event()
122
- self._chat_messages: list[dict[str, str]] = []
123
- self._chat_tokens_count = 0
124
- self._chat_vqd: str = ""
125
- self._chat_vqd_hash: str = ""
126
- self._chat_xfe: str = ""
127
116
 
128
117
  def __enter__(self) -> WEBS:
129
118
  return self
@@ -203,176 +192,7 @@ class WEBS:
203
192
  resp_content = self._get_url("GET", "https://duckduckgo.com", params={"q": keywords}).content
204
193
  return _extract_vqd(resp_content, keywords)
205
194
 
206
- def chat_yield(self, keywords: str, model: str = "gpt-4o-mini", timeout: int = 30, max_retries: int = 3) -> Iterator[str]:
207
- """Initiates a chat session with webscout AI.
208
195
 
209
- Args:
210
- keywords (str): The initial message or question to send to the AI.
211
- model (str): The model to use: "gpt-4o-mini", "llama-3.3-70b", "claude-3-haiku",
212
- "o3-mini", "mistral-small-3". Defaults to "gpt-4o-mini".
213
- timeout (int): Timeout value for the HTTP client. Defaults to 30.
214
- max_retries (int): Maximum number of retry attempts for rate limited requests. Defaults to 3.
215
-
216
- Yields:
217
- str: Chunks of the response from the AI.
218
- """
219
- # Get Cloudflare Turnstile token
220
- def get_turnstile_token():
221
- try:
222
- # Visit the DuckDuckGo chat page to get the Turnstile token
223
- resp_content = self._get_url(
224
- method="GET",
225
- url="https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1",
226
- ).content
227
-
228
- # Extract the Turnstile token if available
229
- if b'cf-turnstile-response' in resp_content:
230
- token = resp_content.split(b'cf-turnstile-response="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
231
- return token
232
- return ""
233
- except Exception:
234
- return ""
235
-
236
- # x-fe-version
237
- if not self._chat_xfe:
238
- resp_content = self._get_url(
239
- method="GET",
240
- url="https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1",
241
- ).content
242
- try:
243
- xfe1 = resp_content.split(b'__DDG_BE_VERSION__="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
244
- xfe2 = resp_content.split(b'__DDG_FE_CHAT_HASH__="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
245
- self._chat_xfe = f"{xfe1}-{xfe2}"
246
- except Exception as ex:
247
- raise WebscoutE(
248
- f"chat_yield() Error to get _chat_xfe: {type(ex).__name__}: {ex}"
249
- ) from ex
250
- # vqd
251
- if not self._chat_vqd:
252
- resp = self._get_url(
253
- method="GET", url="https://duckduckgo.com/duckchat/v1/status", headers={"x-vqd-accept": "1"}
254
- )
255
- self._chat_vqd = resp.headers.get("x-vqd-4", "")
256
- self._chat_vqd_hash = resp.headers.get("x-vqd-hash-1", "")
257
-
258
- self._chat_messages.append({"role": "user", "content": keywords})
259
- self._chat_tokens_count += max(len(keywords) // 4, 1) # approximate number of tokens
260
- if model not in self._chat_models:
261
- warnings.warn(f"{model=} is unavailable. Using 'gpt-4o-mini'", stacklevel=1)
262
- model = "gpt-4o-mini"
263
-
264
- # Get Cloudflare Turnstile token
265
- turnstile_token = get_turnstile_token()
266
-
267
- json_data = {
268
- "model": self._chat_models[model],
269
- "messages": self._chat_messages,
270
- }
271
-
272
- # Add Turnstile token if available
273
- if turnstile_token:
274
- json_data["cf-turnstile-response"] = turnstile_token
275
-
276
- # Enhanced headers to better mimic a real browser
277
- chat_headers = {
278
- "x-fe-version": self._chat_xfe,
279
- "x-vqd-4": self._chat_vqd,
280
- "x-vqd-hash-1": "",
281
- "Accept": "text/event-stream",
282
- "Accept-Language": "en-US,en;q=0.9",
283
- "Cache-Control": "no-cache",
284
- "Content-Type": "application/json",
285
- "DNT": "1",
286
- "Origin": "https://duckduckgo.com",
287
- "Referer": "https://duckduckgo.com/",
288
- "Sec-Fetch-Dest": "empty",
289
- "Sec-Fetch-Mode": "cors",
290
- "Sec-Fetch-Site": "same-origin",
291
- "User-Agent": self.client.headers.get("User-Agent", "")
292
- }
293
-
294
- # Retry logic for rate limited requests
295
- retry_count = 0
296
- while retry_count <= max_retries:
297
- try:
298
- resp = self._get_url(
299
- method="POST",
300
- url="https://duckduckgo.com/duckchat/v1/chat",
301
- headers=chat_headers,
302
- json=json_data,
303
- timeout=timeout,
304
- )
305
-
306
- self._chat_vqd = resp.headers.get("x-vqd-4", "")
307
- self._chat_vqd_hash = resp.headers.get("x-vqd-hash-1", "")
308
- chunks = []
309
-
310
- # curl_cffi uses iter_content instead of stream
311
- for chunk in resp.iter_content(chunk_size=1024):
312
- lines = chunk.split(b"data:")
313
- for line in lines:
314
- if line := line.strip():
315
- if line == b"[DONE]":
316
- break
317
- if line == b"[DONE][LIMIT_CONVERSATION]":
318
- raise ConversationLimitException("ERR_CONVERSATION_LIMIT")
319
- try:
320
- x = json_loads(line)
321
- if isinstance(x, dict):
322
- if x.get("action") == "error":
323
- err_message = x.get("type", "")
324
- if x.get("status") == 429:
325
- raise (
326
- ConversationLimitException(err_message)
327
- if err_message == "ERR_CONVERSATION_LIMIT"
328
- else RatelimitE(err_message)
329
- )
330
- raise WebscoutE(err_message)
331
- elif message := x.get("message"):
332
- chunks.append(message)
333
- yield message
334
- except Exception as e:
335
- # Skip invalid JSON data
336
- continue
337
-
338
- # If we get here, the request was successful
339
- result = "".join(chunks)
340
- self._chat_messages.append({"role": "assistant", "content": result})
341
- self._chat_tokens_count += len(result)
342
- return
343
-
344
- except RatelimitE as ex:
345
- retry_count += 1
346
- if retry_count > max_retries:
347
- raise WebscoutE(f"chat_yield() Rate limit exceeded after {max_retries} retries: {ex}") from ex
348
-
349
- # Get a fresh Turnstile token for the retry
350
- turnstile_token = get_turnstile_token()
351
- if turnstile_token:
352
- json_data["cf-turnstile-response"] = turnstile_token
353
-
354
- # Exponential backoff
355
- sleep_time = 2 ** retry_count
356
- sleep(sleep_time)
357
-
358
- except Exception as ex:
359
- raise WebscoutE(f"chat_yield() {type(ex).__name__}: {ex}") from ex
360
-
361
- def chat(self, keywords: str, model: str = "gpt-4o-mini", timeout: int = 30, max_retries: int = 3) -> str:
362
- """Initiates a chat session with webscout AI.
363
-
364
- Args:
365
- keywords (str): The initial message or question to send to the AI.
366
- model (str): The model to use: "gpt-4o-mini", "llama-3.3-70b", "claude-3-haiku",
367
- "o3-mini", "mistral-small-3". Defaults to "gpt-4o-mini".
368
- timeout (int): Timeout value for the HTTP client. Defaults to 30.
369
- max_retries (int): Maximum number of retry attempts for rate limited requests. Defaults to 3.
370
-
371
- Returns:
372
- str: The response from the AI.
373
- """
374
- answer_generator = self.chat_yield(keywords, model, timeout, max_retries)
375
- return "".join(answer_generator)
376
196
 
377
197
  def text(
378
198
  self,
@@ -1361,4 +1181,4 @@ class WEBS:
1361
1181
  "visibility_m": hour.get("visibility"),
1362
1182
  })
1363
1183
 
1364
- return formatted_data
1184
+ return formatted_data
@@ -103,11 +103,6 @@ class AsyncWEBS:
103
103
  self.sleep_timestamp = 0.0
104
104
 
105
105
  self._exception_event = asyncio.Event()
106
- self._chat_messages: List[Dict[str, str]] = []
107
- self._chat_tokens_count = 0
108
- self._chat_vqd: str = ""
109
- self._chat_vqd_hash: str = ""
110
- self._chat_xfe: str = ""
111
106
 
112
107
  async def __aenter__(self) -> AsyncWEBS:
113
108
  return self
@@ -189,179 +184,6 @@ class AsyncWEBS:
189
184
  resp_content = (await self._get_url("GET", "https://duckduckgo.com", params={"q": keywords})).content
190
185
  return _extract_vqd(resp_content, keywords)
191
186
 
192
- async def achat_yield(self, keywords: str, model: str = "gpt-4o-mini", timeout: int = 30, max_retries: int = 3) -> AsyncIterator[str]:
193
- """Initiates an async chat session with webscout AI.
194
-
195
- Args:
196
- keywords (str): The initial message or question to send to the AI.
197
- model (str): The model to use: "gpt-4o-mini", "llama-3.3-70b", "claude-3-haiku",
198
- "o3-mini", "mistral-small-3". Defaults to "gpt-4o-mini".
199
- timeout (int): Timeout value for the HTTP client. Defaults to 30.
200
- max_retries (int): Maximum number of retry attempts for rate limited requests. Defaults to 3.
201
-
202
- Yields:
203
- str: Chunks of the response from the AI.
204
- """
205
- # Get Cloudflare Turnstile token
206
- async def get_turnstile_token():
207
- try:
208
- # Visit the DuckDuckGo chat page to get the Turnstile token
209
- resp_content = (await self._get_url(
210
- method="GET",
211
- url="https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1",
212
- )).content
213
-
214
- # Extract the Turnstile token if available
215
- if b'cf-turnstile-response' in resp_content:
216
- token = resp_content.split(b'cf-turnstile-response="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
217
- return token
218
- return ""
219
- except Exception:
220
- return ""
221
-
222
- # x-fe-version
223
- if not self._chat_xfe:
224
- resp_content = (await self._get_url(
225
- method="GET",
226
- url="https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=1",
227
- )).content
228
- try:
229
- xfe1 = resp_content.split(b'__DDG_BE_VERSION__="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
230
- xfe2 = resp_content.split(b'__DDG_FE_CHAT_HASH__="', maxsplit=1)[1].split(b'"', maxsplit=1)[0].decode()
231
- self._chat_xfe = f"{xfe1}-{xfe2}"
232
- except Exception as ex:
233
- raise WebscoutE(
234
- f"achat_yield() Error to get _chat_xfe: {type(ex).__name__}: {ex}"
235
- ) from ex
236
- # vqd
237
- if not self._chat_vqd:
238
- resp = await self._get_url(
239
- method="GET", url="https://duckduckgo.com/duckchat/v1/status", headers={"x-vqd-accept": "1"}
240
- )
241
- self._chat_vqd = resp.headers.get("x-vqd-4", "")
242
- self._chat_vqd_hash = resp.headers.get("x-vqd-hash-1", "")
243
-
244
- self._chat_messages.append({"role": "user", "content": keywords})
245
- self._chat_tokens_count += max(len(keywords) // 4, 1) # approximate number of tokens
246
- if model not in self._chat_models:
247
- warnings.warn(f"{model=} is unavailable. Using 'gpt-4o-mini'", stacklevel=1)
248
- model = "gpt-4o-mini"
249
-
250
- # Get Cloudflare Turnstile token
251
- turnstile_token = await get_turnstile_token()
252
-
253
- json_data = {
254
- "model": self._chat_models[model],
255
- "messages": self._chat_messages,
256
- }
257
-
258
- # Add Turnstile token if available
259
- if turnstile_token:
260
- json_data["cf-turnstile-response"] = turnstile_token
261
-
262
- # Enhanced headers to better mimic a real browser
263
- chat_headers = {
264
- "x-fe-version": self._chat_xfe,
265
- "x-vqd-4": self._chat_vqd,
266
- "x-vqd-hash-1": "",
267
- "Accept": "text/event-stream",
268
- "Accept-Language": "en-US,en;q=0.9",
269
- "Cache-Control": "no-cache",
270
- "Content-Type": "application/json",
271
- "DNT": "1",
272
- "Origin": "https://duckduckgo.com",
273
- "Referer": "https://duckduckgo.com/",
274
- "Sec-Fetch-Dest": "empty",
275
- "Sec-Fetch-Mode": "cors",
276
- "Sec-Fetch-Site": "same-origin",
277
- "User-Agent": self.client.headers.get("User-Agent", "")
278
- }
279
-
280
- # Retry logic for rate limited requests
281
- retry_count = 0
282
- while retry_count <= max_retries:
283
- try:
284
- resp = await self._get_url(
285
- method="POST",
286
- url="https://duckduckgo.com/duckchat/v1/chat",
287
- headers=chat_headers,
288
- json=json_data,
289
- timeout=timeout,
290
- )
291
-
292
- self._chat_vqd = resp.headers.get("x-vqd-4", "")
293
- self._chat_vqd_hash = resp.headers.get("x-vqd-hash-1", "")
294
- chunks = []
295
-
296
- # curl_cffi uses aiter_content instead of aiter_bytes
297
- async for chunk in resp.aiter_content(chunk_size=1024):
298
- lines = chunk.split(b"data:")
299
- for line in lines:
300
- if line := line.strip():
301
- if line == b"[DONE]":
302
- break
303
- if line == b"[DONE][LIMIT_CONVERSATION]":
304
- raise ConversationLimitException("ERR_CONVERSATION_LIMIT")
305
- try:
306
- x = json_loads(line)
307
- if isinstance(x, dict):
308
- if x.get("action") == "error":
309
- err_message = x.get("type", "")
310
- if x.get("status") == 429:
311
- raise (
312
- ConversationLimitException(err_message)
313
- if err_message == "ERR_CONVERSATION_LIMIT"
314
- else RatelimitE(err_message)
315
- )
316
- raise WebscoutE(err_message)
317
- elif message := x.get("message"):
318
- chunks.append(message)
319
- yield message
320
- except Exception:
321
- # Skip invalid JSON data
322
- continue
323
-
324
- # If we get here, the request was successful
325
- result = "".join(chunks)
326
- self._chat_messages.append({"role": "assistant", "content": result})
327
- self._chat_tokens_count += len(result)
328
- return
329
-
330
- except RatelimitE as ex:
331
- retry_count += 1
332
- if retry_count > max_retries:
333
- raise WebscoutE(f"achat_yield() Rate limit exceeded after {max_retries} retries: {ex}") from ex
334
-
335
- # Get a fresh Turnstile token for the retry
336
- turnstile_token = await get_turnstile_token()
337
- if turnstile_token:
338
- json_data["cf-turnstile-response"] = turnstile_token
339
-
340
- # Exponential backoff
341
- sleep_time = 2 ** retry_count
342
- await asyncio.sleep(sleep_time)
343
-
344
- except Exception as ex:
345
- raise WebscoutE(f"achat_yield() {type(ex).__name__}: {ex}") from ex
346
-
347
- async def achat(self, keywords: str, model: str = "gpt-4o-mini", timeout: int = 30, max_retries: int = 3) -> str:
348
- """Initiates an async chat session with webscout AI.
349
-
350
- Args:
351
- keywords (str): The initial message or question to send to the AI.
352
- model (str): The model to use: "gpt-4o-mini", "llama-3.3-70b", "claude-3-haiku",
353
- "o3-mini", "mistral-small-3". Defaults to "gpt-4o-mini".
354
- timeout (int): Timeout value for the HTTP client. Defaults to 30.
355
- max_retries (int): Maximum number of retry attempts for rate limited requests. Defaults to 3.
356
-
357
- Returns:
358
- str: The response from the AI.
359
- """
360
- chunks = []
361
- async for chunk in self.achat_yield(keywords, model, timeout, max_retries):
362
- chunks.append(chunk)
363
- return "".join(chunks)
364
-
365
187
  async def atext(
366
188
  self,
367
189
  keywords: str,
@@ -829,4 +651,4 @@ class AsyncWEBS:
829
651
  TimeoutE: Inherits from WebscoutE, raised for API request timeouts.
830
652
  """
831
653
  # These methods are not implemented in the async version yet
832
- raise NotImplementedError("aweather method is not implemented yet")
654
+ raise NotImplementedError("aweather method is not implemented yet")
@@ -0,0 +1,89 @@
1
+ # 🎨 ZeroArt: Zero-Dependency ASCII Art Generator
2
+
3
+ ## 🚀 Overview
4
+
5
+ ZeroArt is a powerful, lightweight Python library for generating stunning ASCII art text with zero external dependencies. Transform your plain text into eye-catching, stylized art with just a few lines of code!
6
+
7
+ ## ✨ Features
8
+
9
+ - **Multiple Font Styles**
10
+ - Block Font
11
+ - Slant Font
12
+ - Neon Font
13
+ - Cyber Font
14
+
15
+ - **Zero Dependencies**
16
+ - Completely standalone library
17
+ - No external package requirements
18
+
19
+ - **Easy to Use**
20
+ - Simple, intuitive API
21
+ - Minimal setup needed
22
+
23
+ - **Text Effects**
24
+ - Rainbow coloring
25
+ - Glitch effect
26
+ - Text wrapping
27
+ - Outline generation
28
+
29
+ ## 🛠 Installation
30
+
31
+ No installation required! Just copy the `zeroart` directory into your project.
32
+
33
+ ## 💻 Usage Examples
34
+
35
+ ### Basic ASCII Art
36
+
37
+ ```python
38
+ from webscout import zeroart
39
+
40
+ # Generate ASCII art
41
+ art = zeroart.figlet_format("PYTHON", font='block')
42
+ print(art)
43
+
44
+ # Directly print ASCII art
45
+ zeroart.print_figlet("CODING", font='slant')
46
+ ```
47
+
48
+ ### Font Styles
49
+
50
+ ```python
51
+ from webscout import zeroart
52
+
53
+ # Different font styles
54
+ print(zeroart.figlet_format("AWESOME", font='block')) # Block style
55
+ print(zeroart.figlet_format("CODING", font='slant')) # Slant style
56
+ print(zeroart.figlet_format("NEON", font='neon')) # Neon style
57
+ print(zeroart.figlet_format("CYBER", font='cyber')) # Cyber style
58
+ ```
59
+
60
+ ### Text Effects
61
+
62
+ ```python
63
+ from webscout import zeroart
64
+
65
+ # Rainbow effect
66
+ print(zeroart.rainbow("COLORFUL", font='neon'))
67
+
68
+ # Glitch effect
69
+ print(zeroart.glitch("GLITCH", font='cyber'))
70
+
71
+ # Outline effect
72
+ print(zeroart.outline("BORDER", font='block'))
73
+ ```
74
+
75
+ ## 🎨 Available Fonts
76
+
77
+ 1. **Block Font**: Classic, bold block-style letters
78
+ 2. **Slant Font**: Elegant, slanted text
79
+ 3. **Neon Font**: Glowing, pixel-style art
80
+ 4. **Cyber Font**: Cyberpunk-inspired rendering
81
+
82
+ ## 🤝 Contributing
83
+
84
+ Contributions are welcome!
85
+
86
+ - Fork the repository
87
+ - Create a new font
88
+ - Add text effects
89
+ - Improve existing code
@@ -0,0 +1,135 @@
1
+ """
2
+ ZeroArt: A zero-dependency ASCII art text generator
3
+
4
+ Create awesome ASCII art text without external dependencies!
5
+ """
6
+
7
+ from typing import Dict, List, Literal, Optional, Union
8
+ from .base import ZeroArtFont
9
+ from .fonts import BlockFont, SlantFont, NeonFont, CyberFont, DottedFont, ShadowFont
10
+ from .effects import AsciiArtEffects
11
+
12
+ FontType = Literal['block', 'slant', 'neon', 'cyber', 'dotted', 'shadow']
13
+
14
+ def figlet_format(text: str, font: Union[str, ZeroArtFont] = 'block') -> str:
15
+ """
16
+ Generate ASCII art text
17
+
18
+ :param text: Text to convert
19
+ :param font: Font style (default: 'block')
20
+ :return: ASCII art representation of text
21
+ """
22
+ font_map: Dict[str, ZeroArtFont] = {
23
+ 'block': BlockFont(),
24
+ 'slant': SlantFont(),
25
+ 'neon': NeonFont(),
26
+ 'cyber': CyberFont(),
27
+ 'dotted': DottedFont(),
28
+ 'shadow': ShadowFont()
29
+ }
30
+
31
+ if isinstance(font, str):
32
+ selected_font: ZeroArtFont = font_map.get(font.lower(), BlockFont())
33
+ else:
34
+ selected_font = font
35
+ return selected_font.render(text)
36
+
37
+ def print_figlet(text: str, font: Union[str, ZeroArtFont] = 'block') -> None:
38
+ """
39
+ Print ASCII art text directly
40
+
41
+ :param text: Text to convert and print
42
+ :param font: Font style (default: 'block')
43
+ """
44
+ print(figlet_format(text, font))
45
+
46
+ # Expose additional effects with font handling
47
+ def rainbow(text: str, font: Union[str, ZeroArtFont] = 'block') -> str:
48
+ """
49
+ Apply a rainbow-like color effect to ASCII art
50
+
51
+ :param text: Text to render
52
+ :param font: Font style (default: 'block')
53
+ :return: Rainbow-styled ASCII art
54
+ """
55
+ font_map: Dict[str, ZeroArtFont] = {
56
+ 'block': BlockFont(),
57
+ 'slant': SlantFont(),
58
+ 'neon': NeonFont(),
59
+ 'cyber': CyberFont(),
60
+ 'dotted': DottedFont(),
61
+ 'shadow': ShadowFont()
62
+ }
63
+
64
+ if isinstance(font, str):
65
+ selected_font: ZeroArtFont = font_map.get(font.lower(), BlockFont())
66
+ else:
67
+ selected_font = font
68
+ return AsciiArtEffects.rainbow_effect(text, selected_font)
69
+
70
+ def glitch(text: str, font: Union[str, ZeroArtFont] = 'block', glitch_intensity: float = 0.1) -> str:
71
+ """
72
+ Apply a glitch-like distortion to ASCII art
73
+
74
+ :param text: Text to render
75
+ :param font: Font style (default: 'block')
76
+ :param glitch_intensity: Probability of character distortion
77
+ :return: Glitched ASCII art
78
+ """
79
+ font_map: Dict[str, ZeroArtFont] = {
80
+ 'block': BlockFont(),
81
+ 'slant': SlantFont(),
82
+ 'neon': NeonFont(),
83
+ 'cyber': CyberFont(),
84
+ 'dotted': DottedFont(),
85
+ 'shadow': ShadowFont()
86
+ }
87
+
88
+ if isinstance(font, str):
89
+ selected_font: ZeroArtFont = font_map.get(font.lower(), BlockFont())
90
+ else:
91
+ selected_font = font
92
+ return AsciiArtEffects.glitch_effect(text, selected_font, glitch_intensity)
93
+
94
+ wrap_text = AsciiArtEffects.wrap_text
95
+
96
+ def outline(text: str, font: Union[str, ZeroArtFont] = 'block', outline_char: str = '*') -> str:
97
+ """
98
+ Add an outline effect to ASCII art
99
+
100
+ :param text: Text to render
101
+ :param font: Font style (default: 'block')
102
+ :param outline_char: Character to use for outline
103
+ :return: ASCII art with outline
104
+ """
105
+ font_map: Dict[str, ZeroArtFont] = {
106
+ 'block': BlockFont(),
107
+ 'slant': SlantFont(),
108
+ 'neon': NeonFont(),
109
+ 'cyber': CyberFont(),
110
+ 'dotted': DottedFont(),
111
+ 'shadow': ShadowFont()
112
+ }
113
+
114
+ if isinstance(font, str):
115
+ selected_font: ZeroArtFont = font_map.get(font.lower(), BlockFont())
116
+ else:
117
+ selected_font = font
118
+ return AsciiArtEffects.outline_effect(text, selected_font, outline_char)
119
+
120
+ __all__ = [
121
+ 'figlet_format',
122
+ 'print_figlet',
123
+ 'rainbow',
124
+ 'glitch',
125
+ 'wrap_text',
126
+ 'outline',
127
+ 'BlockFont',
128
+ 'SlantFont',
129
+ 'NeonFont',
130
+ 'CyberFont',
131
+ 'DottedFont',
132
+ 'ShadowFont',
133
+ 'ZeroArtFont',
134
+ 'FontType'
135
+ ]