webscout 7.0__py3-none-any.whl → 7.2__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.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (147) hide show
  1. webscout/AIauto.py +191 -191
  2. webscout/AIbase.py +122 -122
  3. webscout/AIutel.py +440 -440
  4. webscout/Bard.py +343 -161
  5. webscout/DWEBS.py +489 -492
  6. webscout/Extra/YTToolkit/YTdownloader.py +995 -995
  7. webscout/Extra/YTToolkit/__init__.py +2 -2
  8. webscout/Extra/YTToolkit/transcriber.py +476 -479
  9. webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
  10. webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
  11. webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
  12. webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
  13. webscout/Extra/YTToolkit/ytapi/video.py +103 -103
  14. webscout/Extra/autocoder/__init__.py +9 -9
  15. webscout/Extra/autocoder/autocoder_utiles.py +199 -199
  16. webscout/Extra/autocoder/rawdog.py +5 -7
  17. webscout/Extra/autollama.py +230 -230
  18. webscout/Extra/gguf.py +3 -3
  19. webscout/Extra/weather.py +171 -171
  20. webscout/LLM.py +442 -442
  21. webscout/Litlogger/__init__.py +67 -681
  22. webscout/Litlogger/core/__init__.py +6 -0
  23. webscout/Litlogger/core/level.py +20 -0
  24. webscout/Litlogger/core/logger.py +123 -0
  25. webscout/Litlogger/handlers/__init__.py +12 -0
  26. webscout/Litlogger/handlers/console.py +50 -0
  27. webscout/Litlogger/handlers/file.py +143 -0
  28. webscout/Litlogger/handlers/network.py +174 -0
  29. webscout/Litlogger/styles/__init__.py +7 -0
  30. webscout/Litlogger/styles/colors.py +231 -0
  31. webscout/Litlogger/styles/formats.py +377 -0
  32. webscout/Litlogger/styles/text.py +87 -0
  33. webscout/Litlogger/utils/__init__.py +6 -0
  34. webscout/Litlogger/utils/detectors.py +154 -0
  35. webscout/Litlogger/utils/formatters.py +200 -0
  36. webscout/Provider/AISEARCH/DeepFind.py +250 -250
  37. webscout/Provider/Blackboxai.py +136 -137
  38. webscout/Provider/ChatGPTGratis.py +226 -0
  39. webscout/Provider/Cloudflare.py +91 -78
  40. webscout/Provider/DeepSeek.py +218 -0
  41. webscout/Provider/Deepinfra.py +59 -35
  42. webscout/Provider/Free2GPT.py +131 -124
  43. webscout/Provider/Gemini.py +100 -115
  44. webscout/Provider/Glider.py +74 -59
  45. webscout/Provider/Groq.py +30 -18
  46. webscout/Provider/Jadve.py +108 -77
  47. webscout/Provider/Llama3.py +117 -94
  48. webscout/Provider/Marcus.py +191 -137
  49. webscout/Provider/Netwrck.py +62 -50
  50. webscout/Provider/PI.py +79 -124
  51. webscout/Provider/PizzaGPT.py +129 -83
  52. webscout/Provider/QwenLM.py +311 -0
  53. webscout/Provider/TTI/AiForce/__init__.py +22 -22
  54. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
  55. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
  56. webscout/Provider/TTI/Nexra/__init__.py +22 -22
  57. webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
  58. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
  59. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
  60. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
  61. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
  62. webscout/Provider/TTI/artbit/__init__.py +22 -22
  63. webscout/Provider/TTI/artbit/async_artbit.py +184 -184
  64. webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
  65. webscout/Provider/TTI/blackbox/__init__.py +4 -4
  66. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
  67. webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
  68. webscout/Provider/TTI/deepinfra/__init__.py +4 -4
  69. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
  70. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
  71. webscout/Provider/TTI/huggingface/__init__.py +22 -22
  72. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
  73. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
  74. webscout/Provider/TTI/imgninza/__init__.py +4 -4
  75. webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
  76. webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
  77. webscout/Provider/TTI/talkai/__init__.py +4 -4
  78. webscout/Provider/TTI/talkai/async_talkai.py +229 -229
  79. webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
  80. webscout/Provider/TTS/deepgram.py +182 -182
  81. webscout/Provider/TTS/elevenlabs.py +136 -136
  82. webscout/Provider/TTS/gesserit.py +150 -150
  83. webscout/Provider/TTS/murfai.py +138 -138
  84. webscout/Provider/TTS/parler.py +133 -134
  85. webscout/Provider/TTS/streamElements.py +360 -360
  86. webscout/Provider/TTS/utils.py +280 -280
  87. webscout/Provider/TTS/voicepod.py +116 -116
  88. webscout/Provider/TextPollinationsAI.py +74 -47
  89. webscout/Provider/WiseCat.py +193 -0
  90. webscout/Provider/__init__.py +144 -136
  91. webscout/Provider/cerebras.py +242 -227
  92. webscout/Provider/chatglm.py +204 -204
  93. webscout/Provider/dgaf.py +67 -39
  94. webscout/Provider/gaurish.py +105 -66
  95. webscout/Provider/geminiapi.py +208 -208
  96. webscout/Provider/granite.py +223 -0
  97. webscout/Provider/hermes.py +218 -218
  98. webscout/Provider/llama3mitril.py +179 -179
  99. webscout/Provider/llamatutor.py +72 -62
  100. webscout/Provider/llmchat.py +60 -35
  101. webscout/Provider/meta.py +794 -794
  102. webscout/Provider/multichat.py +331 -230
  103. webscout/Provider/typegpt.py +359 -356
  104. webscout/Provider/yep.py +5 -5
  105. webscout/__main__.py +5 -5
  106. webscout/cli.py +319 -319
  107. webscout/conversation.py +241 -242
  108. webscout/exceptions.py +328 -328
  109. webscout/litagent/__init__.py +28 -28
  110. webscout/litagent/agent.py +2 -3
  111. webscout/litprinter/__init__.py +0 -58
  112. webscout/scout/__init__.py +8 -8
  113. webscout/scout/core.py +884 -884
  114. webscout/scout/element.py +459 -459
  115. webscout/scout/parsers/__init__.py +69 -69
  116. webscout/scout/parsers/html5lib_parser.py +172 -172
  117. webscout/scout/parsers/html_parser.py +236 -236
  118. webscout/scout/parsers/lxml_parser.py +178 -178
  119. webscout/scout/utils.py +38 -38
  120. webscout/swiftcli/__init__.py +811 -811
  121. webscout/update_checker.py +2 -12
  122. webscout/version.py +1 -1
  123. webscout/webscout_search.py +1142 -1140
  124. webscout/webscout_search_async.py +635 -635
  125. webscout/zeroart/__init__.py +54 -54
  126. webscout/zeroart/base.py +60 -60
  127. webscout/zeroart/effects.py +99 -99
  128. webscout/zeroart/fonts.py +816 -816
  129. {webscout-7.0.dist-info → webscout-7.2.dist-info}/METADATA +21 -28
  130. webscout-7.2.dist-info/RECORD +217 -0
  131. webstoken/__init__.py +30 -30
  132. webstoken/classifier.py +189 -189
  133. webstoken/keywords.py +216 -216
  134. webstoken/language.py +128 -128
  135. webstoken/ner.py +164 -164
  136. webstoken/normalizer.py +35 -35
  137. webstoken/processor.py +77 -77
  138. webstoken/sentiment.py +206 -206
  139. webstoken/stemmer.py +73 -73
  140. webstoken/tagger.py +60 -60
  141. webstoken/tokenizer.py +158 -158
  142. webscout/Provider/RUBIKSAI.py +0 -272
  143. webscout-7.0.dist-info/RECORD +0 -199
  144. {webscout-7.0.dist-info → webscout-7.2.dist-info}/LICENSE.md +0 -0
  145. {webscout-7.0.dist-info → webscout-7.2.dist-info}/WHEEL +0 -0
  146. {webscout-7.0.dist-info → webscout-7.2.dist-info}/entry_points.txt +0 -0
  147. {webscout-7.0.dist-info → webscout-7.2.dist-info}/top_level.txt +0 -0
webscout/Bard.py CHANGED
@@ -1,51 +1,145 @@
1
- import argparse
1
+ #########################################
2
+ # Code Generated by o3-mini-high
3
+ #########################################
2
4
  import asyncio
3
5
  import json
4
6
  import os
5
7
  import random
6
8
  import re
7
9
  import string
8
- import sys
9
- from typing import Dict, List, Tuple
10
+ from enum import Enum
11
+ from pathlib import Path
12
+ from datetime import datetime
13
+ from typing import Dict, List, Tuple, Union, Optional
10
14
 
11
15
  import httpx
12
- from prompt_toolkit import prompt
13
- from prompt_toolkit import PromptSession
14
- from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
15
- from prompt_toolkit.completion import WordCompleter
16
- from prompt_toolkit.history import InMemoryHistory
17
- from prompt_toolkit.key_binding import KeyBindings
18
- from rich.console import Console
19
- from rich.markdown import Markdown
16
+ from httpx import AsyncClient, HTTPStatusError
20
17
 
18
+ # For image models using validation. Adjust based on organization internal pydantic.
19
+ from pydantic import BaseModel, field_validator
21
20
 
22
- def __create_session() -> PromptSession:
23
- return PromptSession(history=InMemoryHistory())
21
+ # Rich is retained for logging within image methods.
22
+ from rich.console import Console
23
+ from rich.markdown import Markdown
24
24
 
25
+ console = Console()
26
+
27
+ #########################################
28
+ # New Enums and functions for endpoints,
29
+ # headers, models, file upload and images.
30
+ #########################################
31
+
32
+ class Endpoint(Enum):
33
+ INIT = "https://gemini.google.com/app"
34
+ GENERATE = "https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate"
35
+ ROTATE_COOKIES = "https://accounts.google.com/RotateCookies"
36
+ UPLOAD = "https://content-push.googleapis.com/upload"
37
+
38
+ class Headers(Enum):
39
+ GEMINI = {
40
+ "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
41
+ "Host": "gemini.google.com",
42
+ "Origin": "https://gemini.google.com",
43
+ "Referer": "https://gemini.google.com/",
44
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
45
+ "X-Same-Domain": "1",
46
+ }
47
+ ROTATE_COOKIES = {
48
+ "Content-Type": "application/json",
49
+ }
50
+ UPLOAD = {
51
+ "Push-ID": "feeds/mcudyrk2a4khkz",
52
+ }
53
+
54
+ class Model(Enum):
55
+ UNSPECIFIED = ("unspecified", {}, False)
56
+ G_2_0_FLASH = (
57
+ "gemini-2.0-flash",
58
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"f299729663a2343f"]'},
59
+ False,
60
+ )
61
+ G_2_0_FLASH_EXP = (
62
+ "gemini-2.0-flash-exp",
63
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"f299729663a2343f"]'},
64
+ False,
65
+ ) # Deprecated, should be removed in the future
66
+ G_2_0_FLASH_THINKING = (
67
+ "gemini-2.0-flash-thinking",
68
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"9c17b1863f581b8a"]'},
69
+ False,
70
+ )
71
+ G_2_0_FLASH_THINKING_WITH_APPS = (
72
+ "gemini-2.0-flash-thinking-with-apps",
73
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"f8f8f5ea629f5d37"]'},
74
+ False,
75
+ )
76
+ G_2_0_EXP_ADVANCED = (
77
+ "gemini-2.0-exp-advanced",
78
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"b1e46a6037e6aa9f"]'},
79
+ True,
80
+ )
81
+ G_1_5_FLASH = (
82
+ "gemini-1.5-flash",
83
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"418ab5ea040b5c43"]'},
84
+ False,
85
+ )
86
+ G_1_5_PRO = (
87
+ "gemini-1.5-pro",
88
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"9d60dfae93c9ff1f"]'},
89
+ True,
90
+ )
91
+ G_1_5_PRO_RESEARCH = (
92
+ "gemini-1.5-pro-research",
93
+ {"x-goog-ext-525001261-jspb": '[null,null,null,null,"e5a44cb1dae2b489"]'},
94
+ True,
95
+ )
25
96
 
26
- def __create_completer(commands: list, pattern_str: str = "$") -> WordCompleter:
27
- return WordCompleter(words=commands, pattern=re.compile(pattern_str))
97
+ def __init__(self, name: str, header: dict, advanced_only: bool):
98
+ self.model_name = name
99
+ self.model_header = header
100
+ self.advanced_only = advanced_only
28
101
 
102
+ @classmethod
103
+ def from_name(cls, name: str) -> "Model":
104
+ for model in cls:
105
+ if model.model_name == name:
106
+ return model
107
+ raise ValueError(
108
+ f"Unknown model name: {name}. Available models: {', '.join([m.model_name for m in cls])}"
109
+ )
29
110
 
30
- def __get_input(
31
- prompt_sess: PromptSession = None,
32
- completer: WordCompleter = None,
33
- key_bindings: KeyBindings = None,
34
- ) -> str:
111
+ async def upload_file(file: Union[bytes, str, Path], proxy: Optional[str] = None) -> str:
35
112
  """
36
- Multiline input function.
113
+ Upload a file to Google's server and return its identifier.
114
+
115
+ Parameters:
116
+ file: bytes | str | Path
117
+ File data in bytes, or path to the file to be uploaded.
118
+ proxy: str, optional
119
+ Proxy URL.
120
+
121
+ Returns:
122
+ str: Identifier of the uploaded file.
123
+ Raises:
124
+ httpx.HTTPStatusError: If the upload request failed.
37
125
  """
38
- return (
39
- prompt_sess.prompt(
40
- completer=completer,
41
- multiline=True,
42
- auto_suggest=AutoSuggestFromHistory(),
43
- key_bindings=key_bindings,
126
+ if not isinstance(file, bytes):
127
+ with open(file, "rb") as f:
128
+ file = f.read()
129
+
130
+ async with AsyncClient(http2=True, proxies=proxy) as client:
131
+ response = await client.post(
132
+ url=Endpoint.UPLOAD.value,
133
+ headers=Headers.UPLOAD.value,
134
+ files={"file": file},
135
+ follow_redirects=True,
44
136
  )
45
- if prompt_sess
46
- else prompt(multiline=True)
47
- )
137
+ response.raise_for_status()
138
+ return response.text
48
139
 
140
+ #########################################
141
+ # Cookie loading and Chatbot classes
142
+ #########################################
49
143
 
50
144
  def load_cookies(cookie_path: str) -> Tuple[str, str]:
51
145
  """Loads cookies from the provided JSON file."""
@@ -62,56 +156,56 @@ def load_cookies(cookie_path: str) -> Tuple[str, str]:
62
156
  except StopIteration:
63
157
  raise Exception("Required cookies not found in the cookie file.")
64
158
 
65
-
66
159
  class Chatbot:
67
160
  """
68
161
  Synchronous wrapper for the AsyncChatbot class.
69
162
  """
70
-
71
163
  def __init__(
72
164
  self,
73
165
  cookie_path: str,
74
166
  proxy: dict = None,
75
167
  timeout: int = 20,
168
+ model: Model = Model.UNSPECIFIED
76
169
  ):
77
170
  self.loop = asyncio.get_event_loop()
78
171
  self.secure_1psid, self.secure_1psidts = load_cookies(cookie_path)
79
172
  self.async_chatbot = self.loop.run_until_complete(
80
- AsyncChatbot.create(self.secure_1psid, self.secure_1psidts, proxy, timeout),
173
+ AsyncChatbot.create(self.secure_1psid, self.secure_1psidts, proxy, timeout, model)
81
174
  )
82
175
 
83
176
  def save_conversation(self, file_path: str, conversation_name: str):
84
177
  return self.loop.run_until_complete(
85
- self.async_chatbot.save_conversation(file_path, conversation_name),
178
+ self.async_chatbot.save_conversation(file_path, conversation_name)
86
179
  )
87
180
 
88
181
  def load_conversations(self, file_path: str) -> List[Dict]:
89
182
  return self.loop.run_until_complete(
90
- self.async_chatbot.load_conversations(file_path),
183
+ self.async_chatbot.load_conversations(file_path)
91
184
  )
92
185
 
93
186
  def load_conversation(self, file_path: str, conversation_name: str) -> bool:
94
187
  return self.loop.run_until_complete(
95
- self.async_chatbot.load_conversation(file_path, conversation_name),
188
+ self.async_chatbot.load_conversation(file_path, conversation_name)
96
189
  )
97
190
 
98
191
  def ask(self, message: str) -> dict:
99
192
  return self.loop.run_until_complete(self.async_chatbot.ask(message))
100
193
 
101
-
102
194
  class AsyncChatbot:
103
195
  """
104
196
  A class to interact with Google Gemini.
105
- Parameters
106
- session: str
107
- The __Secure_1PSID cookie.
108
- session_ts: str
109
- The __secure_1psidts cookie.
110
- proxy: str
197
+ Parameters:
198
+ secure_1psid: str
199
+ The __Secure-1PSID cookie.
200
+ secure_1psidts: str
201
+ The __Secure-1PSIDTS cookie.
202
+ proxy: dict
203
+ Http request proxy.
111
204
  timeout: int
112
205
  Request timeout in seconds.
206
+ model: Model
207
+ Selected model for the session.
113
208
  """
114
-
115
209
  __slots__ = [
116
210
  "headers",
117
211
  "_reqid",
@@ -124,6 +218,7 @@ class AsyncChatbot:
124
218
  "secure_1psid",
125
219
  "session",
126
220
  "timeout",
221
+ "model",
127
222
  ]
128
223
 
129
224
  def __init__(
@@ -132,23 +227,11 @@ class AsyncChatbot:
132
227
  secure_1psidts: str,
133
228
  proxy: dict = None,
134
229
  timeout: int = 20,
230
+ model: Model = Model.UNSPECIFIED,
135
231
  ):
136
- """Constructor
137
-
138
- Args:
139
- secure_1psid (str): __Secure-1PSID cookie value
140
- secure_1psidts (str): __Secure-1PSIDTS cookie value
141
- proxy (dict, optional): Http request proxy. Defaults to None.
142
- timeout (int, optional): htpp request timeout. Defaults to 20.
143
- """
144
- headers = {
145
- "Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
146
- "Host": "gemini.google.com",
147
- "Origin": "https://gemini.google.com",
148
- "Referer": "https://gemini.google.com/",
149
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
150
- "X-Same-Domain": "1",
151
- }
232
+ headers = Headers.GEMINI.value.copy()
233
+ if model != Model.UNSPECIFIED:
234
+ headers.update(model.model_header)
152
235
  self._reqid = int("".join(random.choices(string.digits, k=4)))
153
236
  self.proxy = proxy
154
237
  self.conversation_id = ""
@@ -161,6 +244,7 @@ class AsyncChatbot:
161
244
  self.session.cookies.set("__Secure-1PSID", secure_1psid)
162
245
  self.session.cookies.set("__Secure-1PSIDTS", secure_1psidts)
163
246
  self.timeout = timeout
247
+ self.model = model
164
248
 
165
249
  @classmethod
166
250
  async def create(
@@ -169,25 +253,14 @@ class AsyncChatbot:
169
253
  secure_1psidts: str,
170
254
  proxy: dict = None,
171
255
  timeout: int = 20,
256
+ model: Model = Model.UNSPECIFIED,
172
257
  ) -> "AsyncChatbot":
173
- """
174
- Async constructor.
175
- """
176
- instance = cls(secure_1psid, secure_1psidts, proxy, timeout)
258
+ instance = cls(secure_1psid, secure_1psidts, proxy, timeout, model)
177
259
  instance.SNlM0e = await instance.__get_snlm0e()
178
260
  return instance
179
261
 
180
262
  async def save_conversation(self, file_path: str, conversation_name: str) -> None:
181
- """
182
- Saves conversation to the file
183
- :param file_path: file to save (json)
184
- :param conversation_name: any name of current conversation (unique one)
185
- :return: None
186
- """
187
- # Load conversations from file
188
263
  conversations = await self.load_conversations(file_path)
189
-
190
- # Update existing one
191
264
  conversation_exists = False
192
265
  for conversation in conversations:
193
266
  if conversation["conversation_name"] == conversation_name:
@@ -198,8 +271,6 @@ class AsyncChatbot:
198
271
  conversation["choice_id"] = self.choice_id
199
272
  conversation["SNlM0e"] = self.SNlM0e
200
273
  conversation_exists = True
201
-
202
- # Create conversation object
203
274
  if not conversation_exists:
204
275
  conversation = {
205
276
  "conversation_name": conversation_name,
@@ -210,25 +281,16 @@ class AsyncChatbot:
210
281
  "SNlM0e": self.SNlM0e,
211
282
  }
212
283
  conversations.append(conversation)
213
-
214
- # Save to the file
215
284
  with open(file_path, "w", encoding="utf-8") as f:
216
285
  json.dump(conversations, f, indent=4)
217
286
 
218
287
  async def load_conversations(self, file_path: str) -> List[Dict]:
219
- # Check if file exists
220
288
  if not os.path.isfile(file_path):
221
289
  return []
222
290
  with open(file_path, encoding="utf-8") as f:
223
291
  return json.load(f)
224
292
 
225
293
  async def load_conversation(self, file_path: str, conversation_name: str) -> bool:
226
- """
227
- Loads a conversation from history file. Returns whether the conversation was found
228
- :param file_path: File with conversations (json)
229
- :param conversation_name: unique conversation name
230
- :return: True if the conversation was found
231
- """
232
294
  conversations = await self.load_conversations(file_path)
233
295
  for conversation in conversations:
234
296
  if conversation["conversation_name"] == conversation_name:
@@ -241,46 +303,24 @@ class AsyncChatbot:
241
303
  return False
242
304
 
243
305
  async def __get_snlm0e(self):
244
- # Find "SNlM0e":"<ID>"
245
- if (
246
- not (self.secure_1psid and self.secure_1psidts)
247
- or self.secure_1psid[:2] != "g."
248
- ):
249
- raise Exception(
250
- "Enter correct __Secure_1PSID and __Secure_1PSIDTS value. __Secure_1PSID value must start with a g dot (g.). ",
251
- )
252
- resp = await self.session.get(
253
- "https://gemini.google.com/app",
254
- timeout=10,
255
- follow_redirects=True,
256
- )
306
+ if not (self.secure_1psid and self.secure_1psidts) or self.secure_1psid[:2] != "g.":
307
+ raise Exception("Enter correct __Secure_1PSID and __Secure_1PSIDTS value. __Secure_1PSID value must start with a g.")
308
+ resp = await self.session.get(Endpoint.INIT.value, timeout=10, follow_redirects=True)
257
309
  if resp.status_code != 200:
258
- raise Exception(
259
- f"Response code not 200. Response Status is {resp.status_code}",
260
- )
261
- SNlM0e = re.search(r'"SNlM0e":"(.*?)"', resp.text)
262
- if not SNlM0e:
263
- raise Exception(
264
- "SNlM0e value not found in response. Check __Secure_1PSID value."
265
- "\nNOTE : The cookies expire after a short period; ensure you update them as frequent as possible."
266
- f" Failed with status {resp.status_code} - {resp.reason_phrase}",
267
- )
268
- return SNlM0e.group(1)
310
+ raise Exception(f"Response code not 200. Response Status is {resp.status_code}")
311
+ snlm0e_match = re.search(r'"SNlM0e":"(.*?)"', resp.text)
312
+ if not snlm0e_match:
313
+ raise Exception("SNlM0e value not found in response. Check __Secure_1PSID value."
314
+ "\nNOTE: The cookies expire after a short period; ensure you update them frequently."
315
+ f" Failed with status {resp.status_code} - {resp.reason_phrase}")
316
+ return snlm0e_match.group(1)
269
317
 
270
318
  async def ask(self, message: str) -> dict:
271
- """
272
- Send a message to Google Gemini and return the response.
273
- :param message: The message to send to Google Gemini.
274
- :return: A dict containing the response from Google Gemini.
275
- """
276
- # url params
277
319
  params = {
278
320
  "bl": "boq_assistant-bard-web-server_20230713.13_p0",
279
321
  "_reqid": str(self._reqid),
280
322
  "rt": "c",
281
323
  }
282
-
283
- # message arr -> data["f.req"]. Message is double json stringified
284
324
  message_struct = [
285
325
  [message],
286
326
  None,
@@ -291,21 +331,24 @@ class AsyncChatbot:
291
331
  "at": self.SNlM0e,
292
332
  }
293
333
  resp = await self.session.post(
294
- "https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate",
334
+ Endpoint.GENERATE.value,
295
335
  params=params,
296
336
  data=data,
297
337
  timeout=self.timeout,
298
338
  )
299
- chat_data = json.loads(resp.content.splitlines()[3])[0][2]
300
- if not chat_data:
339
+ try:
340
+ chat_data_line = resp.content.splitlines()[3]
341
+ chat_data = json.loads(chat_data_line)[0][2]
342
+ except (IndexError, json.JSONDecodeError):
301
343
  return {"content": f"Gemini encountered an error: {resp.content}."}
344
+ if not chat_data:
345
+ return {"content": f"Gemini returned empty response: {resp.content}."}
302
346
  json_chat_data = json.loads(chat_data)
303
347
  images = []
304
348
  if len(json_chat_data) >= 3:
305
- if len(json_chat_data[4][0]) >= 4:
306
- if json_chat_data[4][0][4]:
307
- for img in json_chat_data[4][0][4]:
308
- images.append(img[0][0][0])
349
+ if len(json_chat_data[4][0]) >= 4 and json_chat_data[4][0][4]:
350
+ for img in json_chat_data[4][0][4]:
351
+ images.append(img[0][0][0])
309
352
  results = {
310
353
  "content": json_chat_data[4][0][1][0],
311
354
  "conversation_id": json_chat_data[1][0],
@@ -321,45 +364,184 @@ class AsyncChatbot:
321
364
  self._reqid += 100000
322
365
  return results
323
366
 
367
+ #########################################
368
+ # New Image classes
369
+ #########################################
324
370
 
325
- if __name__ == "__main__":
326
- import sys
327
- sys.exit(0)
328
- parser = argparse.ArgumentParser()
329
- parser.add_argument(
330
- "--session",
331
- help="__Secure-1PSID cookie",
332
- type=str,
333
- required=True,
334
- )
335
- parser.add_argument(
336
- "--session_ts",
337
- help="__secure_1psidts cookie.",
338
- type=str,
339
- required=True,
340
- )
341
- args = parser.parse_args()
371
+ class Image(BaseModel):
372
+ """
373
+ A single image object returned from Gemini.
374
+ Parameters:
375
+ url: str
376
+ URL of the image.
377
+ title: str, optional
378
+ Title of the image (default: "[Image]").
379
+ alt: str, optional
380
+ Optional description.
381
+ proxy: str, optional
382
+ Proxy used when saving the image.
383
+ """
384
+ url: str
385
+ title: str = "[Image]"
386
+ alt: str = ""
387
+ proxy: Optional[str] = None
388
+
389
+ def __str__(self):
390
+ return f"{self.title}({self.url}) - {self.alt}"
391
+
392
+ def __repr__(self):
393
+ short_url = self.url if len(self.url) <= 20 else self.url[:8] + "..." + self.url[-12:]
394
+ return f"Image(title='{self.title}', url='{short_url}', alt='{self.alt}')"
395
+
396
+ async def save(
397
+ self,
398
+ path: str = "temp",
399
+ filename: Optional[str] = None,
400
+ cookies: Optional[dict] = None,
401
+ verbose: bool = False,
402
+ skip_invalid_filename: bool = False,
403
+ ) -> Optional[str]:
404
+ """
405
+ Save the image to disk.
406
+ Parameters:
407
+ path: str, optional
408
+ Directory to save the image (default "./temp").
409
+ filename: str, optional
410
+ Filename to use; if not provided, inferred from URL.
411
+ cookies: dict, optional
412
+ Cookies used for the image request.
413
+ verbose: bool, optional
414
+ If True, outputs status messages (default False).
415
+ skip_invalid_filename: bool, optional
416
+ If True, skips saving if the filename is invalid.
417
+ Returns:
418
+ Absolute path of the saved image if successful; None if skipped.
419
+ Raises:
420
+ httpx.HTTPError if the network request fails.
421
+ """
422
+ filename = filename or self.url.split("/")[-1].split("?")[0]
423
+ try:
424
+ filename = re.search(r"^(.*\.\w+)", filename).group()
425
+ except AttributeError:
426
+ if verbose:
427
+ console.log(f"Invalid filename: {filename}")
428
+ if skip_invalid_filename:
429
+ return None
430
+ async with AsyncClient(http2=True, follow_redirects=True, cookies=cookies, proxies=self.proxy) as client:
431
+ response = await client.get(self.url)
432
+ if response.status_code == 200:
433
+ content_type = response.headers.get("content-type")
434
+ if content_type and "image" not in content_type:
435
+ console.log(f"Warning: Content type of {filename} is {content_type}, not an image.")
436
+ dest_path = Path(path)
437
+ dest_path.mkdir(parents=True, exist_ok=True)
438
+ dest = dest_path / filename
439
+ dest.write_bytes(response.content)
440
+ if verbose:
441
+ console.log(f"Image saved as {dest.resolve()}")
442
+ return str(dest.resolve())
443
+ else:
444
+ raise HTTPStatusError(
445
+ f"Error downloading image: {response.status_code} {response.reason_phrase}",
446
+ request=response.request,
447
+ response=response,
448
+ )
449
+
450
+ class WebImage(Image):
451
+ """
452
+ Image retrieved from web.
453
+ Returned when asking Gemini to "SEND an image of [something]".
454
+ """
455
+ pass
342
456
 
343
- chatbot = Chatbot(args.session, args.session_ts)
344
- prompt_session = __create_session()
345
- completions = __create_completer(["!exit", "!reset"])
457
+ class GeneratedImage(Image):
458
+ """
459
+ Image generated by ImageFX (Google's AI image generator).
460
+ Parameters:
461
+ cookies: dict[str, str]
462
+ Cookies used from the GeminiClient.
463
+ """
464
+ cookies: Dict[str, str]
465
+
466
+ @field_validator("cookies")
467
+ def validate_cookies(cls, v):
468
+ if not v:
469
+ raise ValueError("GeneratedImage requires cookies from GeminiClient.")
470
+ return v
471
+
472
+ async def save(self, **kwargs) -> Optional[str]:
473
+ """
474
+ Save the generated image to disk.
475
+ Parameters:
476
+ filename: str, optional
477
+ Filename to use; generated images are in .png format.
478
+ Additional arguments are passed to Image.save.
479
+ Returns:
480
+ Absolute path of the saved image if successful.
481
+ """
482
+ filename = kwargs.pop("filename", None) or f"{datetime.now().strftime('%Y%m%d%H%M%S')}_{self.url[-10:]}.png"
483
+ return await super().save(filename=filename, cookies=self.cookies, **kwargs)
346
484
 
485
+ #########################################
486
+ # Main usage demonstration
487
+ #########################################
488
+
489
+ if __name__ == "__main__":
490
+ """
491
+ Usage demonstration:
492
+ - Reads cookies from 'cookies.json'
493
+ - Initializes the synchronous Chatbot wrapper.
494
+ - Performs a text query.
495
+ - Performs an image generation query and downloads the generated image.
496
+ - Demonstrates saving a conversation.
497
+ """
498
+ # Define the path to cookies file
499
+ cookies_file = r"C:\Users\hp\Desktop\Webscout\cookies.json"
500
+
501
+ # Create Chatbot instance with a chosen model
502
+ try:
503
+ bot = Chatbot(cookie_path=cookies_file, model=Model.G_2_0_FLASH_THINKING_WITH_APPS)
504
+ except Exception as e:
505
+ console.log(f"[red]Error initializing Chatbot: {e}[/red]")
506
+ exit(1)
507
+
508
+ # Sample text query
509
+ text_message = "How many r's in word strawberry?"
510
+ console.log("[green]Sending text query to Gemini...[/green]")
511
+ try:
512
+ response_text = bot.ask(text_message)
513
+ console.log("[blue]Text Response:[/blue]")
514
+ console.print(Markdown(response_text.get("content", "No content received.")))
515
+ except Exception as e:
516
+ console.log(f"[red]Error sending text query: {e}[/red]")
517
+
518
+ # Image generation query
519
+ image_message = "Generate an image of a scenic view."
520
+ console.log("[green]Requesting image generation from Gemini...[/green]")
521
+ try:
522
+ response_image = bot.ask(image_message)
523
+ # Check if any image URL is returned in the response
524
+ image_urls = response_image.get("images", [])
525
+ if not image_urls:
526
+ console.log("[red]No image URLs returned in response.[/red]")
527
+ else:
528
+ image_url = image_urls[0]
529
+ console.log(f"[blue]Image URL received: {image_url}[/blue]")
530
+ # Use GeneratedImage class to download the generated image
531
+ generated_img = GeneratedImage(
532
+ url=image_url,
533
+ cookies={"__Secure-1PSID": bot.secure_1psid, "__Secure-1PSIDTS": bot.secure_1psidts}
534
+ )
535
+ saved_path = asyncio.run(generated_img.save(path="downloaded_images", verbose=True))
536
+ console.log(f"[blue]Generated image saved at: {saved_path}[/blue]")
537
+ except Exception as e:
538
+ console.log(f"[red]Error processing image generation: {e}[/red]")
539
+
540
+ # Demonstrate saving a conversation
541
+ conversation_file = "conversations.json"
542
+ conversation_name = "Sample Conversation"
347
543
  try:
348
- while True:
349
- console.print("You:")
350
- user_prompt = __get_input(prompt_sess=prompt_session, completer=completions)
351
- console.print()
352
- if user_prompt == "!exit":
353
- break
354
- elif user_prompt == "!reset":
355
- chatbot.conversation_id = ""
356
- chatbot.response_id = ""
357
- chatbot.choice_id = ""
358
- continue
359
- print("Google Gemini:")
360
- response = chatbot.ask(user_prompt)
361
- console.print(Markdown(response["content"]))
362
- console.print(response["images"] if response.get("images") else "")
363
- print()
364
- except KeyboardInterrupt:
365
- print("Exiting...")
544
+ bot.save_conversation(conversation_file, conversation_name)
545
+ console.log(f"[green]Conversation saved to {conversation_file} under the name '{conversation_name}'.[/green]")
546
+ except Exception as e:
547
+ console.log(f"[red]Error saving conversation: {e}[/red]")