compiling after refactoring

This commit is contained in:
Dobromir Popov 2024-10-13 23:42:40 +03:00
parent 2c2c4ee4df
commit 921386e7fb
3 changed files with 89 additions and 199 deletions

View File

@ -55,9 +55,10 @@ from config import (
error_logger error_logger
) )
from modules.utils import (send_telegram_message, get_token_prices, get_sol_price, get_wallet_balances, convert_balances_to_currency, get_swap_transaction_details) from modules.utils import (get_token_prices, get_sol_price, get_wallet_balances, convert_balances_to_currency, get_swap_transaction_details)
from modules.SolanaAPI import SolanaAPI, solana_jsonrpc from modules.SolanaAPI import SolanaAPI, solana_jsonrpc, wallet_watch_loop
from modules.utils import telegram_utils, send_telegram_message
# # config = load_config() # # config = load_config()
# load_dotenv() # load_dotenv()
@ -139,6 +140,11 @@ except Exception as e:
logging.error(f"Error loading token info: {str(e)}") logging.error(f"Error loading token info: {str(e)}")
# # # # # # # # # # TELEGRAM # # # # # # # # # # # # # # # # # # # # TELEGRAM # # # # # # # # # #
if not telegram_utils.bot:
try:
asyncio.run(telegram_utils.initialize())
except Exception as e:
logging.error(f"Error initializing Telegram bot: {str(e)}")
# async def send_telegram_message(message): # async def send_telegram_message(message):
# try: # try:
# await bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=f"[{BOT_NAME}] {message}", parse_mode=ParseMode.HTML) # await bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=f"[{BOT_NAME}] {message}", parse_mode=ParseMode.HTML)
@ -1210,167 +1216,6 @@ async def follow_move(move):
# Helper functions # Helper functions
SOLANA_ENDPOINTS = [
"wss://api.mainnet-beta.solana.com",
# "wss://solana-api.projectserum.com",
# "wss://rpc.ankr.com/solana",
# "wss://mainnet.rpcpool.com",
]
PING_INTERVAL = 30
SUBSCRIBE_INTERVAL = 1*60 # Resubscribe every 10 minutes
# async def heartbeat(websocket):
# while True:
# try:
# await websocket.ping()
# await asyncio.sleep(PING_INTERVAL)
# except websockets.exceptions.ConnectionClosed:
# break
_first_subscription = True
_process_task = None
async def wallet_watch_loop():
global _first_subscription, _process_task
reconnect_delay = 5
max_reconnect_delay = 60
while True:
try:
try:
subscription_id = None
current_url = random.choice(SOLANA_ENDPOINTS)
async with websockets.connect(current_url, ping_interval=30, ping_timeout=20) as websocket:
logger.info(f"Connected to Solana websocket: {current_url}")
# heartbeat_task = asyncio.create_task(heartbeat(websocket))
while True:
if websocket.closed:
break
subscription_id = await subscribe(websocket)
if subscription_id is not None:
await send_telegram_message(f"Solana mainnet connected ({subscription_id})...")
if _first_subscription:
asyncio.create_task( list_initial_wallet_states())
_first_subscription = False
_process_task = asyncio.create_task(process_messages(websocket, subscription_id))
while True:
try:# drop subscription now
await process_messages(websocket, subscription_id)
# await asyncio.run(_process_task)
# await asyncio.wait_for(_process_task, timeout=SUBSCRIBE_INTERVAL)
except asyncio.TimeoutError:
# Timeout occurred, time to resubscribe
if not PROCESSING_LOG:
_process_task.cancel()
try:
await _process_task
except asyncio.CancelledError:
pass
await unsubscribe(websocket, subscription_id)
new_sub_id = await subscribe(websocket)
if new_sub_id is None: break
if new_sub_id > 1: # we sometimes get True instead of integer, so we cje
subscription_id = new_sub_id
logger.info(f"New subscription created with ID: {subscription_id}")
elif new_sub_id is True:
# Already subscribed
logger.info("Already subscribed, continuing with existing subscription")
if subscription_id:
process_task = asyncio.create_task(process_messages(websocket, subscription_id))
else:
# process_messages completed (shouldn't happen unless there's an error)
break
else:
send_telegram_message("Failed to connect. Retrying...")
# heartbeat_task.cancel()
except websockets.exceptions.WebSocketException as e:
logger.error(f"WebSocket error: {e}")
except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
await unsubscribe(websocket, subscription_id)
await send_telegram_message("reconnecting...")
logger.info(f"Attempting to reconnect in {reconnect_delay} seconds...")
websocket.close()
except Exception as e:
logger.error(f"An unexpected error occurred - breaking watch loop: {e}")
await asyncio.sleep(reconnect_delay)
reconnect_delay = min(reconnect_delay * 1.2, max_reconnect_delay)
async def subscribe(websocket):
request = {
"jsonrpc": "2.0",
"id": 1,
"method": "logsSubscribe",
"params": [
{"mentions": [FOLLOWED_WALLET]},
{"commitment": "confirmed"}
]
}
try:
await websocket.send(json.dumps(request))
logger.info("Subscription request sent")
response = await websocket.recv()
response_data = json.loads(response)
if 'result' in response_data:
subscription_id = response_data['result']
logger.info(f"Subscription successful. Subscription id: {subscription_id}")
return subscription_id
else:
logger.warning(f"Unexpected response: {response_data}")
return None
except websockets.exceptions.ConnectionClosedError as e:
logger.error(f"Connection closed unexpectedly: {e}")
await send_telegram_message("Connection to Solana network was closed. Not listening for transactions right now. Attempting to reconnect...")
await websocket.close()
return None
except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return None
async def unsubscribe(websocket, subscription_id):
if subscription_id:
request = {
"jsonrpc": "2.0",
"id": 1,
"method": "logsUnsubscribe",
"params": [subscription_id]
}
await websocket.send(json.dumps(request))
logger.info(f"Unsubscribed from subscription id: {subscription_id}")
subscription_id = None
async def process_messages(websocket, subscription_id):
try:
while True:
response = await websocket.recv()
response_data = json.loads(response)
logger.debug(f"Received response: {response_data}")
if 'params' in response_data:
log = response_data['params']['result']
logger.debug(f"Received transaction log: {log}")
asyncio.create_task(process_log(log))
else:
logger.warning(f"Unexpected response: {response_data}")
except websockets.exceptions.ConnectionClosedError as e:
logger.error(f"Connection closed unexpectedly: {e}")
await send_telegram_message("Connection to Solana network was closed. Not listening for transactions right now. Attempting to reconnect...")
pass
except json.JSONDecodeError as e:
logger.error(f"Failed to decode JSON: {e}")
except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
pk = os.getenv("PK") pk = os.getenv("PK")
async def check_PK(): async def check_PK():

View File

@ -13,8 +13,14 @@ import datetime
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
SOLANA_ENDPOINTS = ["your_endpoint_1", "your_endpoint_2"] # Add your endpoints here SOLANA_ENDPOINTS = [
SUBSCRIBE_INTERVAL = 300 # 5 minutes in seconds "wss://api.mainnet-beta.solana.com",
# "wss://solana-api.projectserum.com",
# "wss://rpc.ankr.com/solana",
# "wss://mainnet.rpcpool.com",
]
PING_INTERVAL = 30
SUBSCRIBE_INTERVAL = 1*60 # Resubscribe every 10 minutes
from config import ( from config import (
FOLLOWED_WALLET, SOLANA_HTTP_URL FOLLOWED_WALLET, SOLANA_HTTP_URL
@ -23,9 +29,9 @@ FOLLOWED_WALLET, SOLANA_HTTP_URL
class SolanaAPI: class SolanaAPI:
def __init__(self): def __init__(self):
self.websocket: Optional[websockets.WebSocketClientProtocol] = None self.websocket = None
self.subscription_id: Optional[int] = None self.subscription_id = None
self.message_queue: asyncio.Queue = asyncio.Queue() self.message_queue = asyncio.Queue()
async def connect(self): async def connect(self):
while True: while True:
@ -38,37 +44,50 @@ class SolanaAPI:
logger.error(f"Failed to connect to {current_url}: {e}") logger.error(f"Failed to connect to {current_url}: {e}")
await asyncio.sleep(5) await asyncio.sleep(5)
async def subscribe(self): async def ws_jsonrpc(self, method, params=None):
if not isinstance(params, list):
params = [params] if params is not None else []
request = { request = {
"jsonrpc": "2.0", "jsonrpc": "2.0",
"id": 1, "id": 1,
"method": "logsSubscribe", "method": method,
"params": [ "params": params
{"mentions": [FOLLOWED_WALLET]},
{"commitment": "confirmed"}
]
} }
await self.websocket.send(json.dumps(request)) await self.websocket.send(json.dumps(request))
response = await self.websocket.recv() response = await self.websocket.recv()
response_data = json.loads(response) response_data = json.loads(response)
if 'result' in response_data: if 'result' in response_data:
self.subscription_id = response_data['result'] return response_data['result']
logger.info(f"Subscription successful. Subscription id: {self.subscription_id}") elif 'error' in response_data:
logger.error(f"Error in WebSocket RPC call: {response_data['error']}")
return None
else: else:
logger.warning(f"Unexpected response: {response_data}") logger.warning(f"Unexpected response: {response_data}")
return None
async def subscribe(self):
params = [
{"mentions": [FOLLOWED_WALLET]},
{"commitment": "confirmed"}
]
result = await self.ws_jsonrpc("logsSubscribe", params)
if result is not None:
self.subscription_id = result
logger.info(f"Subscription successful. Subscription id: {self.subscription_id}")
else:
logger.error("Failed to subscribe")
async def unsubscribe(self): async def unsubscribe(self):
if self.subscription_id: if self.subscription_id:
request = { result = await self.ws_jsonrpc("logsUnsubscribe", [self.subscription_id])
"jsonrpc": "2.0", if result:
"id": 1,
"method": "logsUnsubscribe",
"params": [self.subscription_id]
}
await self.websocket.send(json.dumps(request))
logger.info(f"Unsubscribed from subscription id: {self.subscription_id}") logger.info(f"Unsubscribed from subscription id: {self.subscription_id}")
self.subscription_id = None self.subscription_id = None
else:
logger.error(f"Failed to unsubscribe from subscription id: {self.subscription_id}")
async def receive_messages(self): async def receive_messages(self):
while True: while True:
@ -83,6 +102,16 @@ class SolanaAPI:
break break
async def process_messages(self): async def process_messages(self):
while True:
message = await self.message_queue.get()
# Process the message here
# You can add your message processing logic
logger.info(f"Received message: {message}")
async def close(self):
if self.websocket:
await self.websocket.close()
logger.info("WebSocket connection closed")
while True: while True:
message = await self.message_queue.get() message = await self.message_queue.get()
try: try:

View File

@ -9,19 +9,35 @@ from telegram import Bot
from telegram.constants import ParseMode from telegram.constants import ParseMode
from config import TELEGRAM_BOT_TOKEN, DEVELOPER_CHAT_ID, BOT_NAME from config import TELEGRAM_BOT_TOKEN, DEVELOPER_CHAT_ID, BOT_NAME
# Initialize Telegram Bot class TelegramUtils:
# Create a custom connection pool def __init__(self):
conn_pool = aiohttp.TCPConnector(limit=100) # Increase the connection limit self.bot = None
timeout = aiohttp.ClientTimeout(total=30) # Set a longer timeout self.conn_pool = None
self.timeout = None
# bot = Bot(TELEGRAM_BOT_TOKEN) # , request=aiohttp.ClientSession(connector=conn_pool, timeout=timeout).request) async def initialize(self):
bot = Bot(token=TELEGRAM_BOT_TOKEN) # Create a custom connection pool
self.conn_pool = aiohttp.TCPConnector(limit=100) # Increase the connection limit
self.timeout = aiohttp.ClientTimeout(total=30) # Set a longer timeout
# Initialize Telegram Bot
self.bot = Bot(token=TELEGRAM_BOT_TOKEN)
async def send_telegram_message(self, message):
if not self.bot:
await self.initialize()
async def send_telegram_message(message):
try: try:
await bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=f"[{BOT_NAME}] {message}", parse_mode=ParseMode.HTML) await self.bot.send_message(chat_id=DEVELOPER_CHAT_ID, text=f"[{BOT_NAME}] {message}", parse_mode=ParseMode.HTML)
logging.info(f"Telegram message sent: {message}") logging.info(f"Telegram message sent: {message}")
except Exception as e: except Exception as e:
logging.error(f"Error sending Telegram message: {str(e)}") logging.error(f"Error sending Telegram message: {str(e)}")
# You can add more Telegram-related functions here if needed async def close(self):
if self.conn_pool:
await self.conn_pool.close()
# Create a global instance of TelegramUtils
telegram_utils = TelegramUtils()
# You can add more Telegram-related methods to the TelegramUtils class if needed