initial follow move implemented!
This commit is contained in:
parent
413e8399d2
commit
dc256e119a
@ -7,12 +7,16 @@ from solana.transaction import Signature
|
|||||||
from solana.rpc.websocket_api import connect
|
from solana.rpc.websocket_api import connect
|
||||||
from solana.rpc.types import TokenAccountOpts, TxOpts
|
from solana.rpc.types import TokenAccountOpts, TxOpts
|
||||||
from solana.rpc.commitment import Confirmed
|
from solana.rpc.commitment import Confirmed
|
||||||
|
from base58 import b58decode
|
||||||
|
from solders.signature import Signature
|
||||||
from solders.pubkey import Pubkey
|
from solders.pubkey import Pubkey
|
||||||
from solders.keypair import Keypair
|
from solders.keypair import Keypair
|
||||||
|
from solders.transaction import VersionedTransaction
|
||||||
from solders.transaction import Transaction
|
from solders.transaction import Transaction
|
||||||
from solders.message import Message
|
from solders.message import Message
|
||||||
from solders.instruction import Instruction
|
from solders.instruction import Instruction
|
||||||
from solders.hash import Hash
|
from solders.hash import Hash
|
||||||
|
from jupiter_python_sdk.jupiter import Jupiter, Jupiter_DCA
|
||||||
from dexscreener import DexscreenerClient
|
from dexscreener import DexscreenerClient
|
||||||
from telegram import Bot
|
from telegram import Bot
|
||||||
from telegram.constants import ParseMode
|
from telegram.constants import ParseMode
|
||||||
@ -25,7 +29,7 @@ import aiohttp
|
|||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
import requests
|
import requests
|
||||||
import threading
|
import threading
|
||||||
|
import re
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
@ -245,11 +249,6 @@ async def get_token_balance(wallet_address, token_address):
|
|||||||
commitment=Confirmed
|
commitment=Confirmed
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# response = await solana_client.get_token_accounts_by_owner(
|
|
||||||
# Pubkey.from_string(wallet_address),
|
|
||||||
# {"mint": Pubkey.from_string(token_address)}
|
|
||||||
# )
|
|
||||||
if response['result']['value']:
|
if response['result']['value']:
|
||||||
balance = await solana_client.get_token_account_balance(
|
balance = await solana_client.get_token_account_balance(
|
||||||
response['result']['value'][0]['pubkey']
|
response['result']['value'][0]['pubkey']
|
||||||
@ -367,56 +366,9 @@ async def get_wallet_balances(wallet_address):
|
|||||||
|
|
||||||
return balances, token_addresses
|
return balances, token_addresses
|
||||||
|
|
||||||
async def get_converted_balances(wallet_address):
|
|
||||||
balances, token_addresses = await get_wallet_balances(wallet_address)
|
|
||||||
token_prices = await get_token_prices(token_addresses)
|
|
||||||
sol_price = await get_sol_price()
|
|
||||||
converted_balances = await convert_balances_to_currency(balances, token_prices, sol_price)
|
|
||||||
return converted_balances
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def send_initial_wallet_states(followed_wallet, your_wallet):
|
|
||||||
followed_balances = await get_converted_balances(followed_wallet)
|
|
||||||
your_balances = await get_converted_balances(your_wallet)
|
|
||||||
|
|
||||||
message = f"Initial Wallet States (Non-zero balances in {DISPLAY_CURRENCY}):\n\n"
|
|
||||||
message += f"Followed Wallet ({followed_wallet}):\n"
|
|
||||||
for token, amount in followed_balances.items():
|
|
||||||
if amount and amount > 0:
|
|
||||||
message += f"{token}: {amount:.2f}\n"
|
|
||||||
|
|
||||||
message += f"\nYour Wallet ({your_wallet}):\n"
|
|
||||||
for token, amount in your_balances.items():
|
|
||||||
if amount and amount > 0:
|
|
||||||
message += f"{token}: {amount:.2f}\n"
|
|
||||||
|
|
||||||
message += "\nMonitored Tokens:\n"
|
|
||||||
# Add monitored tokens logic here if needed
|
|
||||||
|
|
||||||
await bot.send_message(chat_id=CHAT_ID, text=message)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def get_non_zero_token_balances(wallet_address):
|
|
||||||
|
|
||||||
followed_wallet_balances, followed_token_addresses = await get_wallet_balances(FOLLOWED_WALLET)
|
|
||||||
# return non zero balances for followed wallet
|
|
||||||
non_zero_balances = {token: address for token, address in {**followed_wallet_balances}.items() if address is not None and address > 0}
|
|
||||||
logging.info(f"Getting non-zero balances for wallet: {wallet_address}")
|
|
||||||
return non_zero_balances
|
|
||||||
|
|
||||||
|
|
||||||
# non_zero_balances = {}
|
|
||||||
# logging.info(f"Getting non-zero balances for wallet: {wallet_address}")
|
|
||||||
# for token, address in TOKEN_ADDRESSES.items():
|
|
||||||
# balance = await get_token_balance_rpc(wallet_address, address)
|
|
||||||
# if balance > 0:
|
|
||||||
# non_zero_balances[token] = address
|
|
||||||
# logging.debug(f"Non-zero balance for {token}: {balance}")
|
|
||||||
# return non_zero_balances
|
|
||||||
|
|
||||||
|
|
||||||
async def list_initial_wallet_states():
|
async def list_initial_wallet_states():
|
||||||
global TOKEN_ADDRESSES, FOLLOWED_WALLET_VALUE, YOUR_WALLET_VALUE
|
global TOKEN_ADDRESSES, FOLLOWED_WALLET_VALUE, YOUR_WALLET_VALUE
|
||||||
|
|
||||||
@ -463,101 +415,9 @@ async def list_initial_wallet_states():
|
|||||||
await send_telegram_message(message)
|
await send_telegram_message(message)
|
||||||
|
|
||||||
|
|
||||||
async def follow_move(move):
|
|
||||||
followed_balances = await get_wallet_balances(FOLLOWED_WALLET)
|
|
||||||
your_balances = await get_wallet_balances(YOUR_WALLET)
|
|
||||||
|
|
||||||
if move['token'] not in followed_balances or move['token'] not in your_balances:
|
|
||||||
logging.error(f"Invalid token: {move['token']}")
|
|
||||||
return
|
|
||||||
|
|
||||||
followed_balance = followed_balances[move['token']]
|
|
||||||
your_balance = your_balances[move['token']]
|
|
||||||
|
|
||||||
proportion = your_balance / followed_balance if followed_balance > 0 else 0
|
|
||||||
amount_to_swap = move['amount'] * proportion
|
|
||||||
|
|
||||||
if your_balance >= amount_to_swap:
|
|
||||||
# Perform the swap using Jupiter API
|
|
||||||
try:
|
|
||||||
swap_result = perform_swap(move['token'], move['to_token'], amount_to_swap)
|
|
||||||
|
|
||||||
if swap_result['success']:
|
|
||||||
message = (
|
|
||||||
f"<b>Move Followed:</b>\n"
|
|
||||||
f"Swapped {amount_to_swap:.6f} {move['token']} "
|
|
||||||
f"for {swap_result['outputAmount']:.6f} {move['to_token']}"
|
|
||||||
)
|
|
||||||
logging.info(message)
|
|
||||||
else:
|
|
||||||
message = (
|
|
||||||
f"<b>Swap Failed:</b>\n"
|
|
||||||
f"Error: {swap_result['error']}"
|
|
||||||
)
|
|
||||||
logging.warning(message)
|
|
||||||
|
|
||||||
await send_telegram_message(message)
|
|
||||||
except Exception as e:
|
|
||||||
error_message = f"<b>Swap Error:</b>\n{str(e)}"
|
|
||||||
logging.error(error_message)
|
|
||||||
await send_telegram_message(error_message)
|
|
||||||
else:
|
|
||||||
message = (
|
|
||||||
f"<b>Move Failed:</b>\n"
|
|
||||||
f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}"
|
|
||||||
)
|
|
||||||
logging.warning(message)
|
|
||||||
await send_telegram_message(message)
|
|
||||||
|
|
||||||
def perform_swap(input_token, output_token, amount):
|
|
||||||
# Jupiter API endpoint
|
|
||||||
url = "https://quote-api.jup.ag/v4/quote"
|
|
||||||
|
|
||||||
# Parameters for the API request
|
|
||||||
params = {
|
|
||||||
"inputMint": input_token,
|
|
||||||
"outputMint": output_token,
|
|
||||||
"amount": int(amount * 10**9), # Convert to lamports
|
|
||||||
"slippageBps": 50, # 0.5% slippage
|
|
||||||
}
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = requests.get(url, params=params)
|
|
||||||
response.raise_for_status()
|
|
||||||
quote = response.json()
|
|
||||||
|
|
||||||
# Get the best route
|
|
||||||
route = quote['data'][0]
|
|
||||||
|
|
||||||
# Perform the swap
|
|
||||||
swap_url = "https://quote-api.jup.ag/v4/swap"
|
|
||||||
swap_data = {
|
|
||||||
"quoteResponse": route,
|
|
||||||
"userPublicKey": YOUR_WALLET,
|
|
||||||
"wrapUnwrapSOL": True
|
|
||||||
}
|
|
||||||
|
|
||||||
swap_response = requests.post(swap_url, json=swap_data)
|
|
||||||
swap_response.raise_for_status()
|
|
||||||
swap_result = swap_response.json()
|
|
||||||
|
|
||||||
# Sign and send the transaction (this part depends on your wallet setup)
|
|
||||||
# For simplicity, we'll assume the transaction is successful
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"outputAmount": float(swap_result['outputAmount']) / 10**9 # Convert from lamports
|
|
||||||
}
|
|
||||||
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": str(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
from base58 import b58decode
|
|
||||||
from solders.pubkey import Pubkey
|
|
||||||
from solders.transaction import Transaction
|
|
||||||
from solders.signature import Signature
|
|
||||||
|
|
||||||
async def get_transaction_details_rpc(tx_signature, readfromDump=False):
|
async def get_transaction_details_rpc(tx_signature, readfromDump=False):
|
||||||
url = SOLANA_HTTP_URL
|
url = SOLANA_HTTP_URL
|
||||||
@ -609,34 +469,6 @@ async def save_log(log):
|
|||||||
logging.error(f"Error saving RPC log: {e}")
|
logging.error(f"Error saving RPC log: {e}")
|
||||||
|
|
||||||
|
|
||||||
def determine_token(pubkey, watched_tokens):
|
|
||||||
# Check if the pubkey matches any watched token addresses
|
|
||||||
for token, address in watched_tokens.items():
|
|
||||||
if pubkey == address:
|
|
||||||
return token
|
|
||||||
return "Unknown"
|
|
||||||
|
|
||||||
def parse_amount_from_logs(logs):
|
|
||||||
amount_in = 0
|
|
||||||
amount_out = 0
|
|
||||||
|
|
||||||
for log in logs:
|
|
||||||
if 'SwapEvent' in log:
|
|
||||||
# Extract amounts from the log line
|
|
||||||
parts = log.split('amount_in: ')[1].split(', amount_out: ')
|
|
||||||
amount_in = int(parts[0])
|
|
||||||
amount_out = int(parts[1].split(' ')[0])
|
|
||||||
|
|
||||||
return amount_in, amount_out
|
|
||||||
|
|
||||||
def extract_swap_details(instruction, logs, watched_tokens):
|
|
||||||
# Extract source and target tokens along with amounts
|
|
||||||
from_pubkey = instruction['accounts'][0]
|
|
||||||
to_pubkey = instruction['accounts'][1]
|
|
||||||
amount_in, amount_out = parse_amount_from_logs(logs)
|
|
||||||
return from_pubkey, to_pubkey, amount_in, amount_out
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def process_log(log_result):
|
async def process_log(log_result):
|
||||||
if log_result['value']['err']:
|
if log_result['value']['err']:
|
||||||
@ -651,69 +483,18 @@ async def process_log(log_result):
|
|||||||
for log_entry in logs:
|
for log_entry in logs:
|
||||||
if any(op in log_entry for op in swap_operations):
|
if any(op in log_entry for op in swap_operations):
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
|
||||||
watched_tokens = await get_non_zero_token_balances(FOLLOWED_WALLET)
|
|
||||||
details = await parse_swap_logs(logs)
|
details = await parse_swap_logs(logs)
|
||||||
# transaction = await get_transaction_details_rpc(tx_signature_str, True)
|
|
||||||
|
|
||||||
# tokens = []
|
|
||||||
# source_token = None
|
|
||||||
# target_token = None
|
|
||||||
|
|
||||||
# # Check inner instructions for transfers and mints
|
|
||||||
# for instruction_set in transaction.get('meta', {}).get('innerInstructions', []):
|
|
||||||
# for instruction in instruction_set.get('instructions', []):
|
|
||||||
# if 'parsed' in instruction and 'info' in instruction['parsed']:
|
|
||||||
# info = instruction['parsed']['info']
|
|
||||||
# amount = None
|
|
||||||
# mint = 'Unknown'
|
|
||||||
|
|
||||||
# # Check for amount in transfer and transferChecked instructions
|
|
||||||
# if 'amount' in info:
|
|
||||||
# amount = info['amount']
|
|
||||||
# elif 'tokenAmount' in info and 'amount' in info['tokenAmount']:
|
|
||||||
# amount = info['tokenAmount']['amount']
|
|
||||||
|
|
||||||
# # Get mint if available
|
|
||||||
# if 'mint' in info:
|
|
||||||
# mint = info['mint']
|
|
||||||
|
|
||||||
# if amount is not None:
|
|
||||||
# tokens.append({'amount': amount, 'mint': mint})
|
|
||||||
|
|
||||||
# # Identify source and target tokens
|
|
||||||
# if 'source' in info:
|
|
||||||
# source_token = info['source']
|
|
||||||
# if 'destination' in info:
|
|
||||||
# target_token = info['destination']
|
|
||||||
|
|
||||||
# # Check post token balances for final token states
|
|
||||||
# for balance in transaction.get('postTokenBalances', []):
|
|
||||||
# amount = balance['uiTokenAmount']['amount']
|
|
||||||
# mint = balance['mint']
|
|
||||||
# tokens.append({'amount': amount, 'mint': mint})
|
|
||||||
|
|
||||||
# Get amount_in, amount_out, tokens, and USD value
|
|
||||||
swap_details = {
|
|
||||||
'amount_in': details['total_amount_in'],
|
|
||||||
'amount_out': details['total_amount_out'],
|
|
||||||
'tokens': tokens,
|
|
||||||
'source_token': source_token,
|
|
||||||
'target_token': target_token
|
|
||||||
}
|
|
||||||
|
|
||||||
message_text = (
|
message_text = (
|
||||||
f"Swap detected:\n"
|
f"Swap detected:\n"
|
||||||
f"Amount In: {swap_details['amount_in']}\n"
|
f"Order ID: {details['order_id']}\n"
|
||||||
f"Amount Out: {swap_details['amount_out']}\n"
|
f"Token In: {details['token_in']}\n"
|
||||||
f"Source Token: {swap_details['source_token']}\n"
|
f"Token Out: {details['token_out']}\n"
|
||||||
f"Target Token: {swap_details['target_token']}\n"
|
f"Amount In USD: {details['amount_in_USD']}\n"
|
||||||
f"Tokens: {tokens}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await send_telegram_message(message_text)
|
await send_telegram_message(message_text)
|
||||||
await follow_move(swap_details)
|
await follow_move(details)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error fetching transaction details: {e}")
|
logging.error(f"Error fetching transaction details: {e}")
|
||||||
@ -724,59 +505,123 @@ async def process_log(log_result):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# "Program log: Instruction: Swap2",
|
# "Program log: Instruction: Swap2",
|
||||||
# "Program log: order_id: 13985890735038016",
|
# "Program log: order_id: 13985890735038016",
|
||||||
# "Program log: AbrMJWfDVRZ2EWCQ1xSCpoVeVgZNpq1U2AoYG98oRXfn", source
|
# "Program log: AbrMJWfDVRZ2EWCQ1xSCpoVeVgZNpq1U2AoYG98oRXfn", source
|
||||||
# "Program log: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", target
|
# "Program log: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", target
|
||||||
|
# "Program log: before_source_balance: 58730110139, before_destination_balance: 202377778, amount_in: 58730110139, expect_amount_out: 270109505, min_return: 267408410",
|
||||||
|
# "Program log: after_source_balance: 0, after_destination_balance: 472509072",
|
||||||
|
# "Program log: source_token_change: 58730110139, destination_token_change: 270131294",
|
||||||
async def parse_swap_logs(logs):
|
async def parse_swap_logs(logs):
|
||||||
global TOKEN_ADDRESSES
|
|
||||||
|
|
||||||
token_in = None
|
token_in = None
|
||||||
token_out = None
|
token_out = None
|
||||||
amount_in = 0
|
amount_in = 0
|
||||||
amount_out_expected = 0
|
amount_out_expected = 0
|
||||||
amount_out_actual = 0
|
amount_out_actual = 0
|
||||||
|
order_id = None
|
||||||
|
|
||||||
for log in logs:
|
for log in logs:
|
||||||
|
# Check for token identifiers
|
||||||
if "Program log:" in log:
|
if "Program log:" in log:
|
||||||
if "Swap2" in log:
|
if "order_id:" in log:
|
||||||
|
order_id = log.split("order_id: ")[-1].strip()
|
||||||
|
elif "Swap2" in log:
|
||||||
token_in = None
|
token_in = None
|
||||||
token_out = None
|
token_out = None
|
||||||
elif "order_id" in log:
|
elif not token_in:
|
||||||
order_id = log.split("order_id: ")[-1]
|
token_in = log.split("Program log: ")[-1].strip()
|
||||||
else:
|
elif not token_out:
|
||||||
if not token_in:
|
token_out = log.split("Program log: ")[-1].strip()
|
||||||
token_in = log.split("Program log: ")[-1].strip()
|
|
||||||
elif not token_out:
|
|
||||||
token_out = log.split("Program log: ")[-1].strip()
|
|
||||||
|
|
||||||
elif "source_token_change:" in log:
|
# Use regex to find amount_in and amount_out patterns
|
||||||
|
if "amount_in" in log or "amount_out" in log:
|
||||||
|
amount_matches = re.findall(r"(amount_in|amount_out): (\d+)", log)
|
||||||
|
for amount_type, value in amount_matches:
|
||||||
|
if amount_type == "amount_in":
|
||||||
|
amount_in = int(value)/1e6
|
||||||
|
elif amount_type == "amount_out":
|
||||||
|
amount_out_expected = int(value)/1e6
|
||||||
|
|
||||||
|
# Check for source_token_change and destination_token_change for additional details
|
||||||
|
elif "source_token_change:" in log or "destination_token_change:" in log:
|
||||||
changes = log.split(", ")
|
changes = log.split(", ")
|
||||||
for change in changes:
|
for change in changes:
|
||||||
if "source_token_change" in change:
|
if "source_token_change" in change:
|
||||||
amount_in = int(change.split(": ")[-1])
|
amount_in = int(change.split(": ")[-1])/1e6
|
||||||
elif "destination_token_change" in change:
|
elif "destination_token_change" in change:
|
||||||
amount_out_expected = int(change.split(": ")[-1])
|
amount_out_actual = int(change.split(": ")[-1])/1e6
|
||||||
|
|
||||||
amount_out_actual = amount_out_expected # Modify if actual is derived separately
|
|
||||||
|
|
||||||
|
|
||||||
token_prices = await get_token_prices([token_in, token_out])
|
token_prices = await get_token_prices([token_in, token_out])
|
||||||
amount_in_usd = amount_in * token_prices.get(token_in, 0)
|
amount_in_usd = amount_in * token_prices.get(token_in, 0)
|
||||||
amount_out_usd = amount_out_actual * token_prices.get(token_out, 0)
|
amount_out_usd = amount_out_actual * token_prices.get(token_out, 0)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
"order_id": order_id,
|
||||||
"token_in": token_in,
|
"token_in": token_in,
|
||||||
"token_out": token_out,
|
"token_out": token_out,
|
||||||
"amount_in": amount_in,
|
"amount_in": amount_in,
|
||||||
"amount_out_expected": amount_out_expected,
|
"amount_out_expected": amount_out_expected,
|
||||||
"amount_out_actual": amount_out_actual,
|
"amount_out_actual": amount_out_actual,
|
||||||
"amount_in_USD": amount_in_usd,
|
"amount_in_USD": amount_in_usd ,
|
||||||
"amount_out_USD": amount_out_usd,
|
"amount_out_USD": amount_out_usd,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def follow_move(move):
|
||||||
|
followed_balances = await get_wallet_balances(FOLLOWED_WALLET)
|
||||||
|
your_balances = await get_wallet_balances(YOUR_WALLET)
|
||||||
|
|
||||||
|
followed_balance = followed_balances[move.token_in]
|
||||||
|
your_balance = your_balances[move.token_in]
|
||||||
|
|
||||||
|
proportion = your_balance / followed_balance if followed_balance > 0 else 0
|
||||||
|
amount_to_swap = move.amount_in * proportion
|
||||||
|
|
||||||
|
if your_balance >= amount_to_swap:
|
||||||
|
try:
|
||||||
|
# Initialize Jupiter client
|
||||||
|
private_key = Keypair.from_bytes(base58.b58decode(os.getenv("PK")))
|
||||||
|
async_client = AsyncClient(SOLANA_WS_URL)
|
||||||
|
jupiter = Jupiter(async_client, private_key)
|
||||||
|
|
||||||
|
# Perform the swap
|
||||||
|
transaction_data = await jupiter.swap(
|
||||||
|
input_mint=move.token_in,
|
||||||
|
output_mint=move.token_out,
|
||||||
|
amount=int(amount_to_swap),
|
||||||
|
slippage_bps=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
raw_transaction = VersionedTransaction.from_bytes(base64.b64decode(transaction_data))
|
||||||
|
signature = private_key.sign_message(message.to_bytes_versioned(raw_transaction.message))
|
||||||
|
signed_txn = VersionedTransaction.populate(raw_transaction.message, [signature])
|
||||||
|
opts = TxOpts(skip_preflight=False, preflight_commitment=Processed)
|
||||||
|
result = await async_client.send_raw_transaction(txn=bytes(signed_txn), opts=opts)
|
||||||
|
transaction_id = json.loads(result.to_json())['result']
|
||||||
|
|
||||||
|
message = (
|
||||||
|
f"<b>Move Followed:</b>\n"
|
||||||
|
f"Swapped {amount_to_swap:.6f} {move['token']} "
|
||||||
|
f"for {transaction_data['outputAmount']:.6f} {move['to_token']}"
|
||||||
|
)
|
||||||
|
logging.info(message)
|
||||||
|
await send_telegram_message(message)
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"<b>Swap Error:</b>\n{str(e)}"
|
||||||
|
logging.error(error_message)
|
||||||
|
await send_telegram_message(error_message)
|
||||||
|
else:
|
||||||
|
message = (
|
||||||
|
f"<b>Move Failed:</b>\n"
|
||||||
|
f"Insufficient balance to swap {amount_to_swap:.6f} {move['token']}"
|
||||||
|
)
|
||||||
|
logging.warning(message)
|
||||||
|
await send_telegram_message(message)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async def on_logs(log):
|
async def on_logs(log):
|
||||||
logging.debug(f"Received log: {log}")
|
logging.debug(f"Received log: {log}")
|
||||||
await save_log(log)
|
await save_log(log)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user