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.
- webscout/AIauto.py +191 -191
- webscout/AIbase.py +122 -122
- webscout/AIutel.py +440 -440
- webscout/Bard.py +343 -161
- webscout/DWEBS.py +489 -492
- webscout/Extra/YTToolkit/YTdownloader.py +995 -995
- webscout/Extra/YTToolkit/__init__.py +2 -2
- webscout/Extra/YTToolkit/transcriber.py +476 -479
- webscout/Extra/YTToolkit/ytapi/channel.py +307 -307
- webscout/Extra/YTToolkit/ytapi/playlist.py +58 -58
- webscout/Extra/YTToolkit/ytapi/pool.py +7 -7
- webscout/Extra/YTToolkit/ytapi/utils.py +62 -62
- webscout/Extra/YTToolkit/ytapi/video.py +103 -103
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder_utiles.py +199 -199
- webscout/Extra/autocoder/rawdog.py +5 -7
- webscout/Extra/autollama.py +230 -230
- webscout/Extra/gguf.py +3 -3
- webscout/Extra/weather.py +171 -171
- webscout/LLM.py +442 -442
- webscout/Litlogger/__init__.py +67 -681
- webscout/Litlogger/core/__init__.py +6 -0
- webscout/Litlogger/core/level.py +20 -0
- webscout/Litlogger/core/logger.py +123 -0
- webscout/Litlogger/handlers/__init__.py +12 -0
- webscout/Litlogger/handlers/console.py +50 -0
- webscout/Litlogger/handlers/file.py +143 -0
- webscout/Litlogger/handlers/network.py +174 -0
- webscout/Litlogger/styles/__init__.py +7 -0
- webscout/Litlogger/styles/colors.py +231 -0
- webscout/Litlogger/styles/formats.py +377 -0
- webscout/Litlogger/styles/text.py +87 -0
- webscout/Litlogger/utils/__init__.py +6 -0
- webscout/Litlogger/utils/detectors.py +154 -0
- webscout/Litlogger/utils/formatters.py +200 -0
- webscout/Provider/AISEARCH/DeepFind.py +250 -250
- webscout/Provider/Blackboxai.py +136 -137
- webscout/Provider/ChatGPTGratis.py +226 -0
- webscout/Provider/Cloudflare.py +91 -78
- webscout/Provider/DeepSeek.py +218 -0
- webscout/Provider/Deepinfra.py +59 -35
- webscout/Provider/Free2GPT.py +131 -124
- webscout/Provider/Gemini.py +100 -115
- webscout/Provider/Glider.py +74 -59
- webscout/Provider/Groq.py +30 -18
- webscout/Provider/Jadve.py +108 -77
- webscout/Provider/Llama3.py +117 -94
- webscout/Provider/Marcus.py +191 -137
- webscout/Provider/Netwrck.py +62 -50
- webscout/Provider/PI.py +79 -124
- webscout/Provider/PizzaGPT.py +129 -83
- webscout/Provider/QwenLM.py +311 -0
- webscout/Provider/TTI/AiForce/__init__.py +22 -22
- webscout/Provider/TTI/AiForce/async_aiforce.py +257 -257
- webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -242
- webscout/Provider/TTI/Nexra/__init__.py +22 -22
- webscout/Provider/TTI/Nexra/async_nexra.py +286 -286
- webscout/Provider/TTI/Nexra/sync_nexra.py +258 -258
- webscout/Provider/TTI/PollinationsAI/__init__.py +23 -23
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -330
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -285
- webscout/Provider/TTI/artbit/__init__.py +22 -22
- webscout/Provider/TTI/artbit/async_artbit.py +184 -184
- webscout/Provider/TTI/artbit/sync_artbit.py +176 -176
- webscout/Provider/TTI/blackbox/__init__.py +4 -4
- webscout/Provider/TTI/blackbox/async_blackbox.py +212 -212
- webscout/Provider/TTI/blackbox/sync_blackbox.py +199 -199
- webscout/Provider/TTI/deepinfra/__init__.py +4 -4
- webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -227
- webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -199
- webscout/Provider/TTI/huggingface/__init__.py +22 -22
- webscout/Provider/TTI/huggingface/async_huggingface.py +199 -199
- webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -195
- webscout/Provider/TTI/imgninza/__init__.py +4 -4
- webscout/Provider/TTI/imgninza/async_ninza.py +214 -214
- webscout/Provider/TTI/imgninza/sync_ninza.py +209 -209
- webscout/Provider/TTI/talkai/__init__.py +4 -4
- webscout/Provider/TTI/talkai/async_talkai.py +229 -229
- webscout/Provider/TTI/talkai/sync_talkai.py +207 -207
- webscout/Provider/TTS/deepgram.py +182 -182
- webscout/Provider/TTS/elevenlabs.py +136 -136
- webscout/Provider/TTS/gesserit.py +150 -150
- webscout/Provider/TTS/murfai.py +138 -138
- webscout/Provider/TTS/parler.py +133 -134
- webscout/Provider/TTS/streamElements.py +360 -360
- webscout/Provider/TTS/utils.py +280 -280
- webscout/Provider/TTS/voicepod.py +116 -116
- webscout/Provider/TextPollinationsAI.py +74 -47
- webscout/Provider/WiseCat.py +193 -0
- webscout/Provider/__init__.py +144 -136
- webscout/Provider/cerebras.py +242 -227
- webscout/Provider/chatglm.py +204 -204
- webscout/Provider/dgaf.py +67 -39
- webscout/Provider/gaurish.py +105 -66
- webscout/Provider/geminiapi.py +208 -208
- webscout/Provider/granite.py +223 -0
- webscout/Provider/hermes.py +218 -218
- webscout/Provider/llama3mitril.py +179 -179
- webscout/Provider/llamatutor.py +72 -62
- webscout/Provider/llmchat.py +60 -35
- webscout/Provider/meta.py +794 -794
- webscout/Provider/multichat.py +331 -230
- webscout/Provider/typegpt.py +359 -356
- webscout/Provider/yep.py +5 -5
- webscout/__main__.py +5 -5
- webscout/cli.py +319 -319
- webscout/conversation.py +241 -242
- webscout/exceptions.py +328 -328
- webscout/litagent/__init__.py +28 -28
- webscout/litagent/agent.py +2 -3
- webscout/litprinter/__init__.py +0 -58
- webscout/scout/__init__.py +8 -8
- webscout/scout/core.py +884 -884
- webscout/scout/element.py +459 -459
- webscout/scout/parsers/__init__.py +69 -69
- webscout/scout/parsers/html5lib_parser.py +172 -172
- webscout/scout/parsers/html_parser.py +236 -236
- webscout/scout/parsers/lxml_parser.py +178 -178
- webscout/scout/utils.py +38 -38
- webscout/swiftcli/__init__.py +811 -811
- webscout/update_checker.py +2 -12
- webscout/version.py +1 -1
- webscout/webscout_search.py +1142 -1140
- webscout/webscout_search_async.py +635 -635
- webscout/zeroart/__init__.py +54 -54
- webscout/zeroart/base.py +60 -60
- webscout/zeroart/effects.py +99 -99
- webscout/zeroart/fonts.py +816 -816
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/METADATA +21 -28
- webscout-7.2.dist-info/RECORD +217 -0
- webstoken/__init__.py +30 -30
- webstoken/classifier.py +189 -189
- webstoken/keywords.py +216 -216
- webstoken/language.py +128 -128
- webstoken/ner.py +164 -164
- webstoken/normalizer.py +35 -35
- webstoken/processor.py +77 -77
- webstoken/sentiment.py +206 -206
- webstoken/stemmer.py +73 -73
- webstoken/tagger.py +60 -60
- webstoken/tokenizer.py +158 -158
- webscout/Provider/RUBIKSAI.py +0 -272
- webscout-7.0.dist-info/RECORD +0 -199
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/LICENSE.md +0 -0
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/WHEEL +0 -0
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/entry_points.txt +0 -0
- {webscout-7.0.dist-info → webscout-7.2.dist-info}/top_level.txt +0 -0
webscout/Bard.py
CHANGED
|
@@ -1,51 +1,145 @@
|
|
|
1
|
-
|
|
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
|
|
9
|
-
from
|
|
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
|
|
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
|
-
|
|
23
|
-
|
|
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
|
|
27
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
107
|
-
The
|
|
108
|
-
|
|
109
|
-
The
|
|
110
|
-
proxy:
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
334
|
+
Endpoint.GENERATE.value,
|
|
295
335
|
params=params,
|
|
296
336
|
data=data,
|
|
297
337
|
timeout=self.timeout,
|
|
298
338
|
)
|
|
299
|
-
|
|
300
|
-
|
|
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
|
-
|
|
307
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
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]")
|