cherry-shared2 0.1.26__tar.gz

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.
@@ -0,0 +1,91 @@
1
+ Metadata-Version: 2.4
2
+ Name: cherry_shared2
3
+ Version: 0.1.26
4
+ Summary: Cherry Bot shared utilities
5
+ Author: headria
6
+ Classifier: Development Status :: 4 - Beta
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Requires-Python: >=3.11
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: web3
14
+ Requires-Dist: pyrogram
15
+ Requires-Dist: aiohttp
16
+ Dynamic: author
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: requires-dist
21
+ Dynamic: requires-python
22
+ Dynamic: summary
23
+
24
+ # Cherry Shared
25
+
26
+ Cherry Bot shared utilities package.
27
+
28
+ ## Installation
29
+
30
+ This is a private package. Install it directly from the Git repository:
31
+
32
+ ### Using HTTPS (requires authentication)
33
+
34
+ ```bash
35
+ pip install git+https://github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
36
+ ```
37
+
38
+ For private repositories, you'll need to authenticate. You can use a personal access token:
39
+
40
+ ```bash
41
+ pip install git+https://YOUR_TOKEN@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
42
+ ```
43
+
44
+ ### Using SSH (recommended for private repos)
45
+
46
+ ```bash
47
+ pip install git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
48
+ ```
49
+
50
+ ### Installing a specific version/tag
51
+
52
+ ```bash
53
+ pip install git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@v0.1.1#egg=cherry_shared
54
+ ```
55
+
56
+ ### Installing from a requirements.txt
57
+
58
+ Add this line to your `requirements.txt`:
59
+
60
+ ```
61
+ git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
62
+ ```
63
+
64
+ Then install with:
65
+
66
+ ```bash
67
+ pip install -r requirements.txt
68
+ ```
69
+
70
+ ## Development Installation
71
+
72
+ For development, install in editable mode:
73
+
74
+ ```bash
75
+ pip install -e git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
76
+ ```
77
+
78
+ Or clone the repository and install locally:
79
+
80
+ ```bash
81
+ git clone git@github.com:YOUR_USERNAME/cherry_shared.git
82
+ cd cherry_shared
83
+ pip install -e .
84
+ ```
85
+
86
+ ## Usage
87
+
88
+ ```python
89
+ from cherry_shared import Blockchains, BotStrings, Constants, Emojis, LaunchPads
90
+ ```
91
+
@@ -0,0 +1,68 @@
1
+ # Cherry Shared
2
+
3
+ Cherry Bot shared utilities package.
4
+
5
+ ## Installation
6
+
7
+ This is a private package. Install it directly from the Git repository:
8
+
9
+ ### Using HTTPS (requires authentication)
10
+
11
+ ```bash
12
+ pip install git+https://github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
13
+ ```
14
+
15
+ For private repositories, you'll need to authenticate. You can use a personal access token:
16
+
17
+ ```bash
18
+ pip install git+https://YOUR_TOKEN@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
19
+ ```
20
+
21
+ ### Using SSH (recommended for private repos)
22
+
23
+ ```bash
24
+ pip install git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
25
+ ```
26
+
27
+ ### Installing a specific version/tag
28
+
29
+ ```bash
30
+ pip install git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@v0.1.1#egg=cherry_shared
31
+ ```
32
+
33
+ ### Installing from a requirements.txt
34
+
35
+ Add this line to your `requirements.txt`:
36
+
37
+ ```
38
+ git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
39
+ ```
40
+
41
+ Then install with:
42
+
43
+ ```bash
44
+ pip install -r requirements.txt
45
+ ```
46
+
47
+ ## Development Installation
48
+
49
+ For development, install in editable mode:
50
+
51
+ ```bash
52
+ pip install -e git+ssh://git@github.com/YOUR_USERNAME/cherry_shared.git@main#egg=cherry_shared
53
+ ```
54
+
55
+ Or clone the repository and install locally:
56
+
57
+ ```bash
58
+ git clone git@github.com:YOUR_USERNAME/cherry_shared.git
59
+ cd cherry_shared
60
+ pip install -e .
61
+ ```
62
+
63
+ ## Usage
64
+
65
+ ```python
66
+ from cherry_shared import Blockchains, BotStrings, Constants, Emojis, LaunchPads
67
+ ```
68
+
@@ -0,0 +1,369 @@
1
+ import json
2
+ import logging
3
+ from typing import Any, Dict, Literal, Optional
4
+ import aiohttp
5
+ from cherry_shared.launchpads import LaunchPad
6
+ from cherry_shared.types.dexscreener import Pair
7
+ from cherry_shared.types.launchpad import LaunchpadToken
8
+
9
+
10
+ class InfoService:
11
+
12
+ def __init__(self, info_url: str, helper_url: str, logger: logging.Logger = None):
13
+ self.info_url = info_url
14
+ self.helper_url = helper_url
15
+ if logger is None:
16
+ self._logger = logging.getLogger(__name__)
17
+ else:
18
+ self._logger = logger
19
+
20
+ async def process_request(
21
+ self,
22
+ url: str,
23
+ body: Dict[str, Any] = None,
24
+ method: str = "GET",
25
+ query: Dict[str, str] = None,
26
+ headers: Dict[str, str] = None,
27
+ timeout: int = 60,
28
+ ):
29
+ if headers is None:
30
+ headers = {"Content-Type": "application/json"}
31
+ # Define a custom timeout
32
+ custom_timeout = aiohttp.ClientTimeout(total=timeout)
33
+
34
+ async with aiohttp.ClientSession(
35
+ headers=headers, timeout=custom_timeout
36
+ ) as session:
37
+ try:
38
+ if method == "GET":
39
+ async with session.get(
40
+ url, params=query, data=json.dumps(body) if body else None
41
+ ) as resp:
42
+ if resp.status >= 400:
43
+ return False, resp.reason
44
+ if "application/json" in resp.headers.get("Content-Type", ""):
45
+ res = await resp.json()
46
+ else:
47
+ res = await resp.text()
48
+ return (
49
+ False,
50
+ f"Unexpected Content-Type: {resp.headers.get('Content-Type')}, Response: {res}",
51
+ )
52
+ elif method == "POST":
53
+ async with session.post(url, data=json.dumps(body)) as resp:
54
+ if resp.status >= 400:
55
+ return False, resp.reason
56
+ if "application/json" in resp.headers.get("Content-Type", ""):
57
+ res = await resp.json()
58
+ else:
59
+ res = await resp.text()
60
+ return (
61
+ False,
62
+ f"Unexpected Content-Type: {resp.headers.get('Content-Type')}, Response: {res}",
63
+ )
64
+
65
+ return True, res
66
+ except Exception as e:
67
+ self._logger.error(f"Error in InfoService.process_request(): {e}")
68
+ return False, "Exception: " + str(e)
69
+
70
+ async def search_address(self, address: str):
71
+ url = f"{self.info_url}/info/{address}"
72
+ success, data = await self.process_request(url)
73
+ if success:
74
+ return [Pair.from_dexsc_dict(item) for item in data["data"][:10]]
75
+
76
+ async def get_address_type(self, address: str):
77
+ url = f"{self.info_url}/address"
78
+ body = {"address": address}
79
+ success, res = await self.process_request(url, body)
80
+ if success:
81
+ return res["data"]
82
+
83
+ async def get_pair_by_pair_address(self, address: str, chainId: str):
84
+ url = f"{self.info_url}/info/pair"
85
+ success, res = await self.process_request(
86
+ url, {"address": address, "chainId": chainId}, "POST"
87
+ )
88
+
89
+ if not success:
90
+ self._logger.error(res)
91
+ return None
92
+ return Pair.from_dexsc_dict(res["data"])
93
+
94
+ async def get_chains_data(self):
95
+ url = f"{self.info_url}/chains"
96
+ success, data = await self.process_request(url)
97
+ if success:
98
+ return data
99
+
100
+ async def get_total_supply(self, chain: str, address: str):
101
+ url = self.helper_url + "/totalsupply"
102
+ body = {"chain": chain, "address": address}
103
+ success, data = await self.process_request(url, body, "POST")
104
+ if success:
105
+ return data["supply"], data["decimals"]
106
+ return None, None
107
+
108
+ async def get_trending(self):
109
+ url = self.helper_url + "/trending"
110
+ success, data = await self.process_request(url)
111
+ if success:
112
+ return data
113
+ return None
114
+
115
+ async def send_confirm_req(
116
+ self, pair_address: str, contract_address: str, chain_id: str, chat_id: int
117
+ ):
118
+ url = self.helper_url + "/confirm"
119
+ self._logger.debug(f"{pair_address}, {chain_id}, {chat_id}")
120
+ body = {
121
+ "pairAddress": pair_address,
122
+ "tokenAddress": contract_address,
123
+ "chainId": chain_id,
124
+ "chatId": chat_id,
125
+ }
126
+ self._logger.debug(f"send_confirm_req: {body}")
127
+ success, data = await self.process_request(url, body, "POST")
128
+ if not success:
129
+ self._logger.error(f"failed to send confirm req: {data}")
130
+
131
+ async def send_delete_req(self, tokenAddress: str, chat_id: int):
132
+ url = self.helper_url + "/delete"
133
+ body = {
134
+ "tokenAddress": tokenAddress,
135
+ "chatId": chat_id,
136
+ }
137
+ success, data = await self.process_request(url, body, "POST")
138
+ if not success:
139
+ self._logger.error(f"failed delete token req: {data}")
140
+
141
+ async def send_update_supply(self, tokenAddress: str, chat_id: int, supply: int):
142
+ self._logger.debug(f"{tokenAddress}, {chat_id}, {supply}")
143
+ url = self.helper_url + "/updatesupply"
144
+ body = {
145
+ "address": tokenAddress,
146
+ "chatId": chat_id,
147
+ "supply": supply,
148
+ }
149
+ success, data = await self.process_request(url, body, "POST")
150
+ if not success:
151
+ self._logger.error(f"failed update supply req: {data}")
152
+
153
+ async def generate_wallet(self, wallet_type: str):
154
+ wallet_endpoints = {
155
+ "solana": "/generatesolanawallet",
156
+ "tron": "/generatetronwallet",
157
+ "sui": "/generatesuiawallet",
158
+ }
159
+
160
+ if wallet_type not in wallet_endpoints:
161
+ self._logger.error(f"wallet endpoint not provided: {wallet_type}")
162
+ return
163
+
164
+ url = self.helper_url + wallet_endpoints[wallet_type]
165
+ success, data = await self.process_request(url)
166
+
167
+ if not success:
168
+ self._logger.error(f"Failed {wallet_type} wallet request: {data}")
169
+ return
170
+ self._logger.debug(f"generated new wallet {data['publicKey'][:10]}")
171
+ return data["publicKey"], data["privateKey"]
172
+
173
+ async def get_balance(self, wallet_address: str, chain: str):
174
+ url = self.helper_url + "/balance"
175
+ success, data = await self.process_request(
176
+ url, query={"tokenAddress": wallet_address, "chain": chain}
177
+ )
178
+ if not success:
179
+ self._logger.error(f"failed to get balance: {data}")
180
+ return None
181
+ return float(data["balance"])
182
+
183
+ async def buyNburn(
184
+ self,
185
+ raid_id: int,
186
+ chat_id: int,
187
+ amount: float,
188
+ token_address: str,
189
+ pair_address: str,
190
+ chain: str,
191
+ pk: str,
192
+ ):
193
+ url = self.helper_url + "/burn"
194
+ body = {
195
+ "raidId": raid_id,
196
+ "chatId": chat_id,
197
+ "amount": amount,
198
+ "tokenAddress": token_address,
199
+ "pairAddress": pair_address,
200
+ "chain": chain,
201
+ "privateKey": pk,
202
+ }
203
+ success, data = await self.process_request(url, body, "POST")
204
+ if not success:
205
+ self._logger.error(f"failed to send confirm req: {data}")
206
+ return False, data
207
+
208
+ status: bool = data["status"]
209
+ self._logger.debug(f"buyNburn status = {status}")
210
+ return status, None
211
+
212
+ async def check_status(self, raid_id: int):
213
+ try:
214
+ url = self.helper_url + f"/status?raidId={raid_id}"
215
+ success, data = await self.process_request(url)
216
+ if not success:
217
+ self._logger.error(f"failed to send confirm req: {data}")
218
+ return "error", data
219
+ res = data["status"], (
220
+ data["buyTx"],
221
+ data["burnTx"],
222
+ data["finalBountyAmount"],
223
+ )
224
+ return res
225
+ except Exception as e:
226
+ self._logger.error(
227
+ f"Error in InfoService.check_status(): {e}", exc_info=True
228
+ )
229
+ return "error", str(e)
230
+
231
+ async def withdraw(self, chain: str, toAddress: str, userId: int):
232
+ url = self.helper_url + "/withdraw"
233
+ query = {"chain": chain, "toAddress": toAddress, "userId": userId}
234
+ success, res = await self.process_request(url, query=query)
235
+ if not success:
236
+ self._logger.error(f"Info Withdraw: {res}")
237
+ return False, res
238
+ tx_hash = res["txHash"]
239
+ return True, tx_hash
240
+
241
+ async def validate_purchase(
242
+ self,
243
+ user_id: int,
244
+ value: float,
245
+ chain: str,
246
+ promo_code: str = None,
247
+ payout_value: float = None,
248
+ token_id: int = None,
249
+ trendingId: int = None,
250
+ volume_bot=0,
251
+ status: Literal["normal", "volume", "holder"] = "normal",
252
+ hours=None,
253
+ base_amount: float = 0.01,
254
+ **kwargs,
255
+ ):
256
+ url = self.helper_url + "/trendingslot/verify"
257
+ body = {
258
+ "chain": chain,
259
+ "value": value,
260
+ "userId": user_id,
261
+ "promoCode": promo_code,
262
+ "payoutValue": payout_value,
263
+ "tokenId": token_id,
264
+ "trendingId": trendingId,
265
+ "volumeBot": 1 if status == "holder" else volume_bot,
266
+ "status": status,
267
+ "hours": hours,
268
+ "baseAmount": base_amount,
269
+ }
270
+
271
+ # Add any additional key-value arguments to the body
272
+ body.update(kwargs)
273
+
274
+ success, res = await self.process_request(url, body, "POST")
275
+ if not success:
276
+ self._logger.error(f"Info validate_purchase: {res}")
277
+ return -1, res
278
+
279
+ code = int(res["code"])
280
+ message = res["message"] if code == 0 else res["error"]
281
+
282
+ return code, message
283
+
284
+ async def get_token_info(self, address: str, chain: str):
285
+ url = self.info_url + "/info/tokeninfo"
286
+ body = {"address": address, "chain": chain}
287
+ success, res = await self.process_request(url, body=body, method="POST")
288
+ if not success:
289
+ self._logger.error(f"get_token_info: {res}")
290
+ return None
291
+
292
+ code = int(res["code"])
293
+ if code == 0:
294
+ self._logger.debug(f"get_token_info: {res['data']}")
295
+ return res["data"]
296
+
297
+ async def get_launchpad_data(self, launchpad: LaunchPad, address: str):
298
+ url = self.info_url + f"/{launchpad.info_route}"
299
+ body = {"address": address, "chatId": 0}
300
+ success, res = await self.process_request(url, body=body, method="POST")
301
+ if not success:
302
+ self._logger.error(f"get {launchpad.name} info: {res}")
303
+ return None
304
+
305
+ code = int(res["code"])
306
+ self._logger.debug(f"get_launchpad_data: {res['data']}")
307
+ if code == 0:
308
+ return LaunchpadToken.create_token_info(res["data"])
309
+
310
+ async def get_fees(self, address: str):
311
+ try:
312
+ url = self.helper_url + f"/getFees"
313
+ body = {"address": address}
314
+ success, res = await self.process_request(url, body=body, method="POST")
315
+ if not success:
316
+ self._logger.error(f"get_fees info for address {address}: {res}")
317
+ return None
318
+ self._logger.debug(f"get_fees: {res}")
319
+ return round(float(res.get("fee", 1)), 2)
320
+ except Exception as e:
321
+ self._logger.error(f"Error in InfoService.get_fees(): {e}", exc_info=True)
322
+ return 1
323
+
324
+ async def send_message(self, chat_id: int, message: str, thread_id: str = None):
325
+ url = self.helper_url + "/sendtrendingmessage"
326
+ body = {
327
+ "chat_id": chat_id,
328
+ "text": message,
329
+ "thread_id": thread_id,
330
+ }
331
+ success, res = await self.process_request(url, body=body, method="POST")
332
+ if not success:
333
+ self._logger.error(f"send_message: {res}")
334
+ return False, res
335
+ return True, res
336
+
337
+ async def edit_message(
338
+ self, chat_id: int, message_id: int, message: str, thread_id: str = None
339
+ ):
340
+ url = self.helper_url + "/sendtrendingmessage"
341
+ body = {
342
+ "chat_id": chat_id,
343
+ "msg_id": message_id,
344
+ "text": message,
345
+ "thread_id": thread_id,
346
+ }
347
+
348
+ success, res = await self.process_request(url, body=body, method="POST")
349
+ if not success:
350
+ self._logger.error(f"edit_message: {res}")
351
+ return False, res
352
+ return True, res
353
+
354
+ async def check_rate_limit(
355
+ self,
356
+ chat_id: Optional[int] = None,
357
+ priority: Literal["high", "normal", "low"] = "normal",
358
+ ):
359
+ url = self.helper_url + "/check-rate-limit"
360
+ body = {
361
+ "chat_id": chat_id,
362
+ "priority": priority,
363
+ }
364
+ success, res = await self.process_request(url, body=body, method="POST")
365
+ if not success:
366
+ self._logger.error(f"check_rate_limit: {res}")
367
+ return 0
368
+ delay = int(res.get("delay_ms", 0)) / 1000.0
369
+ return delay
@@ -0,0 +1,5 @@
1
+ from .blockchains import Blockchains, BlockchainId, ChainType
2
+ from .bot_strings import BotStrings
3
+ from .constants import Constants
4
+ from .emojis import Emojis, state_emoji
5
+ from .launchpads import LaunchPads, LaunchPadId, LaunchPad