├── price.py
├── token_function.py
├── parse_wallet.py
├── utility_functions.py
├── main.py
├── setup_function.py
├── send_purchases.py
├── database.py
├── bot_command.py
└── dm_setup.py
/price.py:
--------------------------------------------------------------------------------
1 | import requests, json
2 |
3 | def get_asset(token_address):
4 | url = "https://mainnet.helius-rpc.com/?api-key=PUT YOUR API KEY HERE"
5 | headers = {
6 | 'Content-Type': 'application/json',
7 | }
8 | data = {
9 | "jsonrpc": "2.0",
10 | "id": "my-id",
11 | "method": "getAsset",
12 | "params": {
13 | "id": token_address,
14 | "displayOptions": {
15 | "showFungible": True
16 | }
17 | },
18 | }
19 | response = requests.post(url, headers=headers, data=json.dumps(data))
20 | result = response.json()['result']
21 | price_per_token = result['token_info']['price_info']['price_per_token']
22 |
23 | return float(price_per_token)
--------------------------------------------------------------------------------
/token_function.py:
--------------------------------------------------------------------------------
1 | import requests, json
2 |
3 | def get_token_symbol(token_address):
4 | url = "https://mainnet.helius-rpc.com/?api-key=PUT YOUR API KEY HERE"
5 | headers = {
6 | 'Content-Type': 'application/json',
7 | }
8 | data = {
9 | "jsonrpc": "2.0",
10 | "id": "my-id",
11 | "method": "getAsset",
12 | "params": {
13 | "id": token_address,
14 | "displayOptions": {
15 | "showFungible": True
16 | }
17 | },
18 | }
19 | response = requests.post(url, headers=headers, data=json.dumps(data))
20 | result = response.json().get('result', {})
21 | token_symbol = result.get('token_info', {}).get('symbol', '')
22 |
23 | return token_symbol
24 |
--------------------------------------------------------------------------------
/parse_wallet.py:
--------------------------------------------------------------------------------
1 | import requests, datetime
2 | from database import transaction_exists, update_payment_details
3 |
4 | def parse_wallet(wallet_address, sender_wallet):
5 | #Put your Helius API key here
6 | api_key = ""
7 | url = f"https://api.helius.xyz/v0/addresses/{wallet_address}/transactions?api-key={api_key}"
8 |
9 | response = requests.get(url)
10 | data = response.json()
11 |
12 | for transaction in data:
13 | if transaction['type'] == 'TRANSFER' and \
14 | transaction['feePayer'] == sender_wallet and \
15 | any(tt['tokenAmount'] >= 50 and tt['mint'] == 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' for tt in transaction['tokenTransfers']):
16 | signature = transaction['signature']
17 | if not transaction_exists(signature):
18 | current_time = datetime.datetime.utcnow()
19 | cancel_time = current_time + datetime.timedelta(days=30)
20 | update_payment_details(sender_wallet, signature, current_time, cancel_time)
21 | return True
22 | return False
23 |
--------------------------------------------------------------------------------
/utility_functions.py:
--------------------------------------------------------------------------------
1 | from telegram import Update
2 | from telegram.ext import CallbackContext
3 |
4 | from dm_setup import wallet_address, handle_uuid_response
5 | from setup_function import contract_address, image_url, chosen_emoji, start_setup
6 | from bot_command import handle_new_token_address
7 |
8 | STEP_CONTRACT, STEP_IMAGE_URL, STEP_EMOJI = range(3)
9 |
10 | async def handle_user_response(update: Update, context: CallbackContext):
11 | if context.user_data.get('expecting_wallet_address'):
12 | await wallet_address(update, context)
13 | context.user_data.pop('expecting_wallet_address', None)
14 | return
15 |
16 | if context.user_data.get('awaiting_uuid'):
17 | await handle_uuid_response(update, context)
18 | context.user_data.pop('awaiting_uuid', None)
19 | return
20 |
21 | if context.user_data.get('editing_token'):
22 | await handle_new_token_address(update, context)
23 | context.user_data.pop('editing_token', None)
24 | return
25 |
26 | setup_step = context.user_data.get('setup_step')
27 |
28 | if setup_step == STEP_CONTRACT:
29 | await contract_address(update, context)
30 | elif setup_step == STEP_IMAGE_URL:
31 | await image_url(update, context)
32 | elif setup_step == STEP_EMOJI:
33 | await chosen_emoji(update, context)
34 | else:
35 | await start_setup(update, context)
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import (ApplicationBuilder, CommandHandler, MessageHandler,
2 | CallbackQueryHandler, filters, CommandHandler)
3 |
4 | import logging
5 |
6 | from utility_functions import handle_user_response
7 | from dm_setup import (start_command, handle_enter_new_wallet, handle_payment_sent, handle_use_existing_wallet, handle_yes_response, setup_buybot_command)
8 |
9 | from bot_command import (bot_command, handle_bot_selection, handle_setup, handle_edit_token,
10 | handle_edit_image, handle_edit_group, handle_edit_emoji, handle_edit_token_no, handle_edit_token_yes)
11 |
12 | from send_purchases import initialize_jobs_from_db
13 |
14 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
15 | logger = logging.getLogger(__name__)
16 |
17 | # Put your Telegram Bot Token here
18 | TOKEN = ''
19 | CONTRACT, IMAGE_URL, EMOJI, WALLET_ADDRESS = range(4)
20 |
21 | def main():
22 | application = ApplicationBuilder().token(TOKEN).build()
23 |
24 | application.add_handler(CommandHandler('start', start_command))
25 | application.add_handler(CommandHandler('bot', bot_command))
26 | application.add_handler(CommandHandler('setup_buybot', setup_buybot_command))
27 |
28 | application.add_handler(CallbackQueryHandler(handle_yes_response, pattern='^yes$'))
29 | application.add_handler(CallbackQueryHandler(handle_use_existing_wallet, pattern='^use_existing$'))
30 | application.add_handler(CallbackQueryHandler(handle_enter_new_wallet, pattern='^enter_new$'))
31 | application.add_handler(CallbackQueryHandler(handle_payment_sent, pattern='^payment_sent$'))
32 | application.add_handler(CallbackQueryHandler(handle_bot_selection, pattern='^bot_\\d+$'))
33 |
34 | application.add_handler(CallbackQueryHandler(handle_setup, pattern='^setup$'))
35 | application.add_handler(CallbackQueryHandler(handle_edit_token, pattern='^edit_token$'))
36 | application.add_handler(CallbackQueryHandler(handle_edit_image, pattern='^edit_image$'))
37 | application.add_handler(CallbackQueryHandler(handle_edit_group, pattern='^edit_group$'))
38 | application.add_handler(CallbackQueryHandler(handle_edit_emoji, pattern='^edit_emoji$'))
39 |
40 | application.add_handler(CallbackQueryHandler(handle_edit_token_no, pattern='^edit_token_no$'))
41 | application.add_handler(CallbackQueryHandler(handle_edit_token_yes, pattern='^edit_token_yes$'))
42 |
43 | application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_user_response))
44 |
45 | initialize_jobs_from_db(application)
46 |
47 | application.run_polling()
48 |
49 | if __name__ == '__main__':
50 | main()
51 |
--------------------------------------------------------------------------------
/setup_function.py:
--------------------------------------------------------------------------------
1 | from telegram import Update, ForceReply
2 | from telegram.ext import CallbackContext
3 | import logging
4 |
5 | from database import update_setup_data
6 | from token_function import get_token_symbol
7 |
8 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
9 | logger = logging.getLogger(__name__)
10 |
11 | STEP_CONTRACT, STEP_IMAGE_URL, STEP_EMOJI = range(3)
12 |
13 | async def store_partial_data_and_proceed(update, context, next_step_func):
14 | logger.info("store_partial_data_and_proceed called")
15 | chat_id = 1
16 | payment_uuid = context.user_data.get('payment_uuid')
17 | logger.info(f"UUID in store_partial_data_and_proceed: {payment_uuid}")
18 | contract_address = context.user_data.get('contract', '')
19 | token_name = context.user_data.get('token_name', '')
20 | image_url = context.user_data.get('image_url', '')
21 | chosen_emoji = context.user_data.get('chosen_emoji', '')
22 |
23 | try:
24 | update_setup_data(payment_uuid, chat_id, contract_address, token_name, image_url, chosen_emoji)
25 | await next_step_func(update, context)
26 | except Exception as e:
27 | logger.error(f"Error in store_partial_data_and_proceed: {e}")
28 |
29 | async def start_setup(query: Update, context: CallbackContext):
30 | context.user_data['setup_step'] = STEP_CONTRACT
31 | logger.info("start_setup called")
32 | await query.message.reply_text(
33 | "Please send me the contract address you want the Solana BuyBot to track.",
34 | reply_markup=ForceReply(selective=True),
35 | )
36 |
37 | async def contract_address(update: Update, context: CallbackContext):
38 | logger.info("contract_address called")
39 | if 'last_processed_text' not in context.user_data or update.message.text != context.user_data['last_processed_text']:
40 | logger.info("contract_address found context data")
41 | context.user_data['last_processed_text'] = update.message.text
42 | context.user_data['contract'] = update.message.text
43 | context.user_data['token_name'] = get_token_symbol(update.message.text)
44 | context.user_data['setup_step'] = STEP_IMAGE_URL
45 | await store_partial_data_and_proceed(update, context, image_url)
46 | else:
47 | await update.message.reply_text("Please send the contract address.")
48 |
49 | async def image_url(update: Update, context: CallbackContext):
50 | logger.info("image_url called")
51 | if update.message.text != context.user_data.get('last_processed_text'):
52 | logger.info("image_url found context data")
53 | context.user_data['last_processed_text'] = update.message.text
54 | context.user_data['setup_step'] = STEP_EMOJI
55 | context.user_data['image_url'] = update.message.text
56 | await store_partial_data_and_proceed(update, context, chosen_emoji)
57 | else:
58 | await update.message.reply_text("Now please respond with a URL to a JPG, PNG, or GIF.\n\nThis needs to be a DIRECT link to the image, or it will not work.")
59 |
60 | async def chosen_emoji(update: Update, context: CallbackContext):
61 | logger.info("chosen_emoji called")
62 | if update.message.text != context.user_data.get('last_processed_text'):
63 | logger.info("chosen_emoji found context data")
64 | context.user_data['last_processed_text'] = update.message.text
65 | context.user_data['chosen_emoji'] = update.message.text
66 | await store_partial_data_and_proceed(update, context, finalize_setup)
67 | else:
68 | await update.message.reply_text("Please send the emoji you want to use for indicating purchase amounts.")
69 |
70 | async def finalize_setup(update: Update, context: CallbackContext):
71 | logger.info("finalize_setup called")
72 | payment_uuid = context.user_data.get('payment_uuid', '')
73 | instruction_message = (
74 | f"Setup complete! Your UUID is: {payment_uuid}\n\n"
75 | "Please add the bot to your group or channel.\n"
76 | "Then, use the '/setup_buybot' command in the group or channel.\n"
77 | "When prompted, reply with this UUID.\n"
78 | )
79 | await update.message.reply_text(instruction_message)
80 |
--------------------------------------------------------------------------------
/send_purchases.py:
--------------------------------------------------------------------------------
1 | import aiohttp, urllib.parse, logging
2 | from telegram.ext import CallbackContext
3 |
4 | from database import filter_new_transactions, store_transaction, fetch_image_url, fetch_chosen_emoji, fetch_existing_data
5 | from price import get_asset
6 |
7 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
8 | logger = logging.getLogger(__name__)
9 |
10 | job_references = {}
11 |
12 | def initialize_jobs_from_db(application):
13 | existing_data = fetch_existing_data()
14 | for chat_id, contract_address, token_name in existing_data:
15 | job_context = {'chat_id': chat_id, 'contract_address': contract_address, 'token_name': token_name}
16 | job = application.job_queue.run_repeating(fetch_and_send_transactions, interval=60, first=0, data=job_context)
17 | job_references[chat_id] = job # Store the job reference
18 |
19 | def initialize_job_for_chat(application, chat_id, contract_address, token_name):
20 | job_context = {'chat_id': chat_id, 'contract_address': contract_address, 'token_name': token_name}
21 | job = application.job_queue.run_repeating(fetch_and_send_transactions, interval=60, first=0, data=job_context)
22 | job_references[chat_id] = job # Store the job reference
23 |
24 | async def fetch_and_send_transactions(context: CallbackContext):
25 | job_context = context.job.data
26 | chat_id = job_context['chat_id']
27 | token_address = job_context['contract_address']
28 | logger.info("Token name in job context: %s", job_context['token_name'])
29 | chosen_ticker = job_context['token_name']
30 | image_url = fetch_image_url(chat_id)
31 | chosen_emoji = fetch_chosen_emoji(chat_id)
32 |
33 | if not chat_id or not token_address or not chosen_ticker or not image_url or not chosen_emoji:
34 | logger.info("Missing one or more required data points, skipping operation.")
35 | return
36 |
37 | price_per_token = get_asset(token_address)
38 |
39 | url = f'https://api.helius.xyz/v0/addresses/{token_address}/transactions?api-key=PUT YOUR API KEY HERE'
40 | try:
41 | async with aiohttp.ClientSession() as session:
42 | async with session.get(url) as response:
43 | if response.status == 200:
44 | data = await response.json()
45 |
46 | swap_transactions = [t for t in data if t['type'] == 'SWAP']
47 | transaction_hashes = [t['signature'] for t in swap_transactions]
48 |
49 | new_transaction_hashes = filter_new_transactions(chat_id, transaction_hashes)
50 |
51 | for transaction in swap_transactions:
52 | transaction_hash = transaction['signature']
53 |
54 | if transaction_hash in new_transaction_hashes:
55 | description_parts = transaction['description'].split(' ')
56 |
57 | swapped_index = description_parts.index('swapped')
58 | for_index = description_parts.index('for')
59 |
60 | if swapped_index != -1 and for_index != -1:
61 | first_ticker = description_parts[swapped_index + 2]
62 | second_amount_raw = description_parts[for_index + 1].replace(',', '')
63 | second_ticker = description_parts[for_index + 2]
64 |
65 | if first_ticker != chosen_ticker and second_ticker == chosen_ticker:
66 | try:
67 | second_amount_float = float(second_amount_raw)
68 | total_amount_in_dollars = second_amount_float * price_per_token
69 |
70 | num_circles = max(1, int(total_amount_in_dollars // 10))
71 | circles = chosen_emoji * num_circles
72 |
73 | second_amount_formatted = f"${total_amount_in_dollars:,.2f}"
74 | except Exception as e:
75 | logger.error(f"Error in calculating total amount in dollars: {e}")
76 | second_amount_formatted = f"{second_amount_raw} (error in conversion)"
77 |
78 | share_text = f"${chosen_ticker} is PUMPING!\n\nIYKYK\nhttps://birdeye.so/token/{transaction_hash}?chain=solana"
79 | encoded_text = urllib.parse.quote_plus(share_text)
80 | twitter_url = f"https://twitter.com/intent/tweet?text={encoded_text}"
81 |
82 | message = (
83 | f"{circles}\n"
84 | f"{second_amount_formatted} of ${chosen_ticker} purchased.\n\n"
85 | f"📈: Birdeye\n"
86 | f"🖨: Transaction\n"
87 | f"🐦: Share Tweet"
88 | )
89 |
90 | if image_url:
91 | if image_url.lower().endswith('.gif'):
92 | await context.bot.send_animation(chat_id=chat_id, animation=image_url, caption=message, parse_mode='HTML')
93 | elif image_url.lower().endswith(('.jpg', '.jpeg', '.png')):
94 | await context.bot.send_photo(chat_id=chat_id, photo=image_url, caption=message, parse_mode='HTML')
95 | else:
96 | logger.info("Image URL does not have a recognized extension, sending message without image.")
97 | await context.bot.send_message(chat_id=chat_id, text=message, parse_mode='HTML')
98 | else:
99 | await context.bot.send_message(chat_id=chat_id, text=message, parse_mode='HTML')
100 |
101 | store_transaction(chat_id, transaction_hash)
102 |
103 | else:
104 | logger.error(f"fetch_and_send_transactions: Failed to fetch data. Status code: {response.status}")
105 | except Exception as e:
106 | logger.error(f"fetch_and_send_transactions: An error occurred: {e}")
107 |
108 | logger.info("fetch_and_send_transactions executed")
109 |
--------------------------------------------------------------------------------
/database.py:
--------------------------------------------------------------------------------
1 | import mysql.connector, time, logging
2 | from mysql.connector import Error
3 | from datetime import datetime
4 |
5 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
6 | logger = logging.getLogger(__name__)
7 |
8 | #Put your SQL database info here
9 | def execute_db_query(query, params=None, is_fetch=False):
10 | connection = None
11 | cursor = None
12 | try:
13 | connection = mysql.connector.connect(
14 | host="",
15 | port=,
16 | user="",
17 | password="",
18 | database="",
19 | charset=''
20 | )
21 | cursor = connection.cursor()
22 | cursor.execute(query, params)
23 |
24 | if is_fetch:
25 | return cursor.fetchall()
26 | else:
27 | connection.commit()
28 |
29 | except Error as e:
30 | print(f"Error: {e}")
31 | time.sleep(5)
32 |
33 | finally:
34 | if cursor:
35 | cursor.close()
36 | if connection:
37 | connection.close()
38 |
39 | def store_data(user_id, chat_id, contract_address, token_name, image_url, chosen_emoji):
40 | query = """
41 | INSERT INTO telegram_bot_data (user_id, chat_id, contract_address, token_name, image_url, chosen_emoji)
42 | VALUES (%s, %s, %s, %s, %s, %s)
43 | """
44 | execute_db_query(query, (user_id, chat_id, contract_address, token_name, image_url, chosen_emoji))
45 |
46 | def fetch_existing_data():
47 | query = "SELECT chat_id, contract_address, token_name FROM telegram_bot_data"
48 | return execute_db_query(query, is_fetch=True)
49 |
50 | def filter_new_transactions(chat_id, transaction_hashes):
51 | format_strings = ','.join(['%s'] * len(transaction_hashes))
52 | query = f"SELECT transaction_hash FROM processed_transactions WHERE chat_id = %s AND transaction_hash IN ({format_strings})"
53 | params = [chat_id, *transaction_hashes]
54 | result = execute_db_query(query, params, is_fetch=True)
55 | existing_hashes = {row[0] for row in result}
56 | new_hashes = set(transaction_hashes) - existing_hashes
57 | return list(new_hashes)
58 |
59 | def store_transaction(chat_id, transaction_hash):
60 | insert_query = "INSERT INTO processed_transactions (chat_id, transaction_hash) VALUES (%s, %s)"
61 | execute_db_query(insert_query, (chat_id, transaction_hash))
62 |
63 | delete_query = """
64 | DELETE FROM processed_transactions
65 | WHERE chat_id = %s AND transaction_hash NOT IN (
66 | SELECT transaction_hash
67 | FROM (
68 | SELECT transaction_hash
69 | FROM processed_transactions
70 | WHERE chat_id = %s
71 | ORDER BY transaction_hash DESC
72 | LIMIT 100
73 | ) AS subquery
74 | )
75 | """
76 | execute_db_query(delete_query, (chat_id, chat_id))
77 |
78 | def fetch_image_url(chat_id):
79 | query = "SELECT image_url FROM telegram_bot_data WHERE chat_id = %s"
80 | result = execute_db_query(query, (chat_id,), is_fetch=True)
81 | if result:
82 | return result[0][0]
83 | return None
84 |
85 | def fetch_chosen_emoji(chat_id):
86 | query = "SELECT chosen_emoji FROM telegram_bot_data WHERE chat_id = %s"
87 | result = execute_db_query(query, (chat_id,), is_fetch=True)
88 | if result:
89 | return result[0][0]
90 | return '🟢'
91 |
92 | def store_user_wallet(user_id, wallet_address):
93 | default_contract_address = ""
94 | default_token_name = ""
95 | default_image_url = ""
96 | default_chosen_emoji = ""
97 | chat_id = "1"
98 |
99 | query = """
100 | INSERT INTO telegram_bot_data
101 | (chat_id, user_id, wallet_address, contract_address, token_name, image_url, chosen_emoji)
102 | VALUES (%s, %s, %s, %s, %s, %s, %s)
103 | """
104 | params = (chat_id, user_id, wallet_address, default_contract_address, default_token_name, default_image_url, default_chosen_emoji)
105 | execute_db_query(query, params)
106 |
107 | def fetch_user_wallet(user_id):
108 | query = "SELECT wallet_address FROM telegram_bot_data WHERE user_id = %s"
109 | result = execute_db_query(query, (user_id,), is_fetch=True)
110 | if result:
111 | return result[0][0]
112 | else:
113 | return None
114 |
115 | def transaction_exists(signature):
116 | query = "SELECT COUNT(1) FROM telegram_bot_data WHERE payment_transaction = %s"
117 | result = execute_db_query(query, (signature,), is_fetch=True)
118 | return result[0][0] > 0
119 |
120 | def update_payment_details(user_wallet, signature, payment_date, cancel_date):
121 | query = """
122 | UPDATE telegram_bot_data
123 | SET payment_transaction = %s, payment_date = %s, cancel_date = %s
124 | WHERE wallet_address = %s
125 | """
126 | execute_db_query(query, (signature, payment_date, cancel_date, user_wallet))
127 |
128 | def fetch_active_bots(user_id):
129 | try:
130 | current_time = datetime.utcnow()
131 | query = """
132 | SELECT contract_address, token_name, payment_uuid
133 | FROM telegram_bot_data
134 | WHERE user_id = %s AND cancel_date > %s
135 | """
136 | params = (user_id, current_time)
137 | result = execute_db_query(query, params, is_fetch=True)
138 | return result if result else []
139 | except Exception as e:
140 | logger.error(f"Error in fetch_active_bots: {e}")
141 | return []
142 |
143 | def store_payment_uuid(user_id, payment_uuid):
144 | query = """
145 | UPDATE telegram_bot_data
146 | SET payment_uuid = %s
147 | WHERE user_id = %s
148 | """
149 | execute_db_query(query, (payment_uuid, user_id))
150 |
151 | def transaction_exists(payment_uuid):
152 | query = "SELECT COUNT(1) FROM telegram_bot_data WHERE payment_uuid = %s"
153 | result = execute_db_query(query, (payment_uuid,), is_fetch=True)
154 | return result[0][0] > 0
155 |
156 | def update_chat_id_for_uuid(chat_id, payment_uuid):
157 | query = """
158 | UPDATE telegram_bot_data
159 | SET chat_id = %s
160 | WHERE payment_uuid = %s
161 | """
162 | execute_db_query(query, (chat_id, payment_uuid))
163 |
164 | def update_setup_data(payment_uuid, chat_id, contract_address, token_name, image_url, chosen_emoji):
165 | logger.info("update_setup_data called")
166 | query = """
167 | UPDATE telegram_bot_data
168 | SET chat_id = %s, contract_address = %s, token_name = %s, image_url = %s, chosen_emoji = %s
169 | WHERE payment_uuid = %s
170 | """
171 | params = (chat_id, contract_address, token_name, image_url, chosen_emoji, payment_uuid)
172 | print("update_setup_data Query data:", params)
173 | try:
174 | execute_db_query(query, params)
175 | except Error as e:
176 | logger.error(f"Error in update_setup_data: {e}")
177 | raise
178 |
179 | def fetch_current_token(payment_uuid):
180 | query = "SELECT contract_address, token_name FROM telegram_bot_data WHERE payment_uuid = %s"
181 | result = execute_db_query(query, (payment_uuid,), is_fetch=True)
182 | if result:
183 | return result[0][0], result[0][1]
184 | else:
185 | return None, None
186 |
187 | def update_token_address(payment_uuid, new_token_address, token_name):
188 | query = "UPDATE telegram_bot_data SET contract_address = %s, token_name = %s WHERE payment_uuid = %s"
189 | execute_db_query(query, (new_token_address, token_name, payment_uuid))
--------------------------------------------------------------------------------
/bot_command.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CallbackContext
2 | from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
3 | import logging, re
4 | from database import fetch_active_bots, fetch_current_token, update_token_address
5 | from setup_function import start_setup
6 | from token_function import get_token_symbol
7 | from send_purchases import job_references
8 |
9 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
10 | logger = logging.getLogger(__name__)
11 |
12 | async def bot_command(update: Update, context: CallbackContext):
13 | global active_bots_mapping
14 | chat_type = update.effective_chat.type
15 | user_id = update.effective_chat.id
16 |
17 | if chat_type != "private":
18 | return
19 |
20 | active_bots = fetch_active_bots(user_id)
21 | if active_bots:
22 | bot_listing = "Select a bot to manage:\n"
23 | keyboard = []
24 | active_bots_mapping = {str(index): bot[2] for index, bot in enumerate(active_bots, start=1)}
25 | for index, bot in enumerate(active_bots, start=1):
26 | _, _, payment_uuid = bot
27 | bot_listing += f"{index}. {payment_uuid}\n"
28 | keyboard.append([InlineKeyboardButton(str(index), callback_data=f"bot_{index}")])
29 |
30 | reply_markup = InlineKeyboardMarkup(keyboard)
31 | await update.message.reply_text(bot_listing, reply_markup=reply_markup)
32 | else:
33 | await update.message.reply_text(
34 | "You do not have any active bots.\n\nPlease purchase a bot to use this command.\n\nUse the \"/start\" command to get started!")
35 |
36 | async def handle_bot_selection(update: Update, context: CallbackContext):
37 | global active_bots_mapping
38 | query = update.callback_query
39 | await query.answer()
40 |
41 | try:
42 | selected_number = query.data.replace('bot_', '')
43 | payment_uuid = active_bots_mapping.get(selected_number)
44 |
45 | if payment_uuid:
46 | context.user_data['payment_uuid'] = payment_uuid
47 | keyboard = [
48 | [InlineKeyboardButton("Setup", callback_data="setup")],
49 | [InlineKeyboardButton("Edit Token", callback_data="edit_token"),
50 | InlineKeyboardButton("Edit Image", callback_data="edit_image")],
51 | [InlineKeyboardButton("Edit TG Group", callback_data="edit_group"),
52 | InlineKeyboardButton("Edit Emoji", callback_data="edit_emoji")]
53 | ]
54 | reply_markup = InlineKeyboardMarkup(keyboard)
55 |
56 | await context.bot.send_message(
57 | chat_id=update.effective_chat.id,
58 | text=f"You chose the bot:\n{payment_uuid}.\n\nSelect an option to manage your bot:",
59 | reply_markup=reply_markup
60 | )
61 | logger.info("Bot management options sent for bot: %s", payment_uuid)
62 | else:
63 | await context.bot.send_message(
64 | chat_id=update.effective_chat.id,
65 | text="Invalid selection or bot not found."
66 | )
67 | logger.error("Invalid selection or bot not found.")
68 |
69 | except Exception as e:
70 | logger.exception("Error in handle_bot_selection: %s", str(e))
71 | await context.bot.send_message(
72 | chat_id=update.effective_chat.id,
73 | text="An error occurred while processing your request."
74 | )
75 |
76 | async def handle_setup(update: Update, context: CallbackContext):
77 | logger.info("handle_setup called")
78 | query = update.callback_query
79 | await query.answer()
80 |
81 | message_text = query.message.text
82 | logger.info(f"Message Text: {message_text}")
83 |
84 | try:
85 | match = re.search(r'\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b', message_text)
86 | if match:
87 | payment_uuid = match.group(0)
88 | context.user_data['payment_uuid'] = payment_uuid
89 | context.user_data.pop('last_processed_text', None)
90 | await start_setup(query, context)
91 | else:
92 | await context.bot.send_message(chat_id=update.effective_chat.id, text="Invalid UUID format.")
93 | except Exception as e:
94 | logger.error(f"Error in handle_setup: {e}")
95 | await context.bot.send_message(chat_id=update.effective_chat.id, text="An error occurred while processing your request.")
96 |
97 | async def handle_edit_token(update: Update, context: CallbackContext):
98 | query = update.callback_query
99 | await query.answer()
100 |
101 | payment_uuid = context.user_data.get('payment_uuid')
102 | if not payment_uuid:
103 | await context.bot.send_message(chat_id=update.effective_chat.id, text="Bot UUID not found.")
104 | return
105 |
106 | current_token_address, current_token_name = fetch_current_token(payment_uuid)
107 | context.user_data['current_token_name'] = current_token_name
108 | await context.bot.send_message(
109 | chat_id=update.effective_chat.id,
110 | text=f"Current Token:\n${current_token_name}\n{current_token_address}\n\nDo you want to change the token?",
111 | reply_markup=InlineKeyboardMarkup([
112 | [InlineKeyboardButton("Yes, edit token", callback_data="edit_token_yes")],
113 | [InlineKeyboardButton("No", callback_data="edit_token_no")]
114 | ])
115 | )
116 |
117 | async def handle_edit_token_yes(update: Update, context: CallbackContext):
118 | query = update.callback_query
119 | await query.answer()
120 |
121 | await context.bot.send_message(
122 | chat_id=update.effective_chat.id,
123 | text="Please respond with the new token address"
124 | )
125 | context.user_data['editing_token'] = True
126 |
127 | async def handle_edit_token_no(update: Update, context: CallbackContext):
128 | query = update.callback_query
129 | await query.answer()
130 |
131 | await context.bot.send_message(
132 | chat_id=update.effective_chat.id,
133 | text="No changes made to the token address."
134 | )
135 |
136 | async def handle_new_token_address(update: Update, context: CallbackContext):
137 | if context.user_data.get('editing_token'):
138 | new_token_address = update.message.text
139 | token_name = get_token_symbol(update.message.text)
140 | payment_uuid = context.user_data.get('payment_uuid')
141 | chat_id = update.effective_chat.id
142 |
143 | update_token_address(payment_uuid, new_token_address, token_name)
144 | await update.message.reply_text("Token address updated successfully.")
145 |
146 | if chat_id in job_references:
147 | job = job_references[chat_id]
148 | job.job.data['contract_address'] = new_token_address
149 | job.job.data['token_name'] = token_name
150 |
151 | context.user_data.pop('editing_token', None)
152 |
153 | async def handle_edit_image(update: Update, context: CallbackContext):
154 | query = update.callback_query
155 | await query.answer()
156 | await context.bot.send_message(chat_id=update.effective_chat.id, text="Edit Image button pressed.")
157 |
158 | async def handle_edit_group(update: Update, context: CallbackContext):
159 | query = update.callback_query
160 | await query.answer()
161 | await context.bot.send_message(chat_id=update.effective_chat.id, text="Edit TG Group button pressed.")
162 |
163 | async def handle_edit_emoji(update: Update, context: CallbackContext):
164 | query = update.callback_query
165 | await query.answer()
166 | await context.bot.send_message(chat_id=update.effective_chat.id, text="Edit Emoji button pressed.")
--------------------------------------------------------------------------------
/dm_setup.py:
--------------------------------------------------------------------------------
1 | from telegram.ext import CallbackContext
2 | from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
3 | import logging, uuid
4 |
5 | from send_purchases import initialize_job_for_chat
6 |
7 | from database import store_user_wallet, fetch_user_wallet, store_payment_uuid, execute_db_query
8 | from parse_wallet import parse_wallet
9 |
10 | logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', level=logging.INFO)
11 | logger = logging.getLogger(__name__)
12 |
13 | async def start_command(update: Update, context: CallbackContext):
14 | chat_type = update.effective_chat.type
15 | #Put your bot image here!
16 | image_url = ""
17 |
18 | if chat_type == "private":
19 | caption = (
20 | "Welcome to the premiere Solana BuyBot for Telegram groups!\n\n"
21 | "Features:\n"
22 | "- Select an SPL token to track.\n"
23 | "- Customize emojis for buybot messages.\n"
24 | "- Customize the image in messages.\n"
25 | "- Customize the text your users will tweet when they click the 'share tweet' button.\n"
26 | "- Manage multiple buy bots with memberships.\n\n"
27 | "Membership: $50 for 30 days (one token, one group).\n\n"
28 | "Only the purchaser can manage the buybot.\n\n"
29 | "Would you like to sign up to use the buybot?"
30 | )
31 |
32 | keyboard = [
33 | [InlineKeyboardButton("Yes", callback_data='yes'),
34 | InlineKeyboardButton("No", callback_data='no')]
35 | ]
36 | reply_markup = InlineKeyboardMarkup(keyboard)
37 |
38 | await update.message.reply_photo(photo=image_url, caption=caption, reply_markup=reply_markup)
39 | else:
40 | await update.message.reply_text("Please use the /start command in a DM.")
41 |
42 | async def wallet_address(update: Update, context: CallbackContext):
43 | user_id = update.effective_chat.id
44 | wallet = update.message.text
45 |
46 | store_user_wallet(user_id, wallet)
47 |
48 | await update.message.reply_text(
49 | f"Please send $50 USDC to the following address:\n"
50 | "UifrcG2hjT2p4F2e96dLUrxdmKP8VedwfVb1zNpUnuo\n"
51 | "Click the button once you've sent the payment.",
52 | reply_markup=InlineKeyboardMarkup([
53 | [InlineKeyboardButton("Sent the Payment", callback_data='payment_sent')]
54 | ])
55 | )
56 |
57 | async def handle_use_existing_wallet(update: Update, context: CallbackContext):
58 | query = update.callback_query
59 | await query.answer()
60 |
61 | user_id = query.from_user.id
62 | user_wallet = fetch_user_wallet(user_id)
63 |
64 | await query.message.reply_text(
65 | f"You are using your existing wallet: {user_wallet}\n"
66 | "Please send $50 USDC to the following address:\n"
67 | "UifrcG2hjT2p4F2e96dLUrxdmKP8VedwfVb1zNpUnuo",
68 | reply_markup=InlineKeyboardMarkup([
69 | [InlineKeyboardButton("Sent the Payment", callback_data='payment_sent')]
70 | ])
71 | )
72 |
73 | async def handle_enter_new_wallet(update: Update, context: CallbackContext):
74 | query = update.callback_query
75 | await query.answer()
76 |
77 | await query.message.reply_text("Please provide the new wallet address you will be sending the $50 USDC payment from.")
78 | context.user_data['telegram_id'] = query.from_user.id
79 |
80 | async def handle_yes_response(update: Update, context: CallbackContext):
81 | query = update.callback_query
82 | await query.answer()
83 | user_id = query.from_user.id
84 |
85 | existing_wallet = fetch_user_wallet(user_id)
86 | if existing_wallet:
87 | message = (
88 | f"You have previously used the wallet address: {existing_wallet}\n"
89 | "Would you like to use this wallet again?"
90 | )
91 | keyboard = [
92 | [InlineKeyboardButton("Use Existing Wallet", callback_data='use_existing')],
93 | [InlineKeyboardButton("Enter New Wallet", callback_data='enter_new')]
94 | ]
95 | reply_markup = InlineKeyboardMarkup(keyboard)
96 | await query.message.reply_text(message, reply_markup=reply_markup)
97 | else:
98 | await query.message.reply_text("Please provide the wallet address you will be sending the $50 USDC payment from.")
99 | context.user_data['telegram_id'] = user_id
100 | context.user_data['expecting_wallet_address'] = True
101 |
102 | async def handle_wallet_address(update: Update, context: CallbackContext):
103 | wallet_address = update.message.text
104 | user_id = context.user_data['telegram_id']
105 |
106 | store_user_wallet(user_id, wallet_address)
107 |
108 | await update.message.reply_text(
109 | f"Please send $50 USDC to the following address:\n"
110 | "UifrcG2hjT2p4F2e96dLUrxdmKP8VedwfVb1zNpUnuo\n"
111 | "Click the button once you've sent the payment.",
112 | reply_markup=InlineKeyboardMarkup([
113 | [InlineKeyboardButton("Sent the Payment", callback_data='payment_sent')]
114 | ])
115 | )
116 |
117 | async def handle_payment_sent(update: Update, context: CallbackContext):
118 | query = update.callback_query
119 | await query.answer()
120 |
121 | user_id = query.from_user.id
122 | user_wallet = fetch_user_wallet(user_id)
123 |
124 | payment_received = parse_wallet("UifrcG2hjT2p4F2e96dLUrxdmKP8VedwfVb1zNpUnuo", user_wallet)
125 | if payment_received:
126 | payment_uuid = str(uuid.uuid4())
127 | store_payment_uuid(user_id, payment_uuid)
128 |
129 | await query.message.reply_text(f"Payment received. Thank you!\n\nYour Payment UUID is: {payment_uuid}\n\nPlease use \"/bot\" command to start setting up your BuyBot!")
130 | else:
131 | await query.message.reply_text("Payment not received yet. Please check again later.")
132 |
133 | async def setup_buybot_command(update: Update, context: CallbackContext):
134 | chat_type = update.effective_chat.type
135 | if chat_type in ["group", "supergroup", "channel"]:
136 | context.user_data['awaiting_uuid'] = True
137 | await update.message.reply_text("Please reply with the UUID for the bot you want to set up.\n\nThat means, please click\"reply\" on the top right of this message, and then paste your UUID into the response.")
138 | else:
139 | await update.message.reply_text("This command can only be used in groups or channels.")
140 |
141 | async def handle_uuid_response(update: Update, context: CallbackContext):
142 | if context.user_data.get('awaiting_uuid'):
143 | uuid = update.message.text
144 | chat_id = update.effective_chat.id
145 |
146 | query = "SELECT chat_id, contract_address, token_name FROM telegram_bot_data WHERE payment_uuid = %s"
147 | result = execute_db_query(query, (uuid,), is_fetch=True)
148 |
149 | if result:
150 | retrieved_chat_id, contract_address, token_name = result[0]
151 | logger.info(f"Retrieved data from database: chat_id={retrieved_chat_id}, contract_address={contract_address}, token_name={token_name}")
152 | else:
153 | logger.info("No result found for the given UUID.")
154 |
155 | if not result:
156 | await update.message.reply_text("There's no UUID that matches that.")
157 | elif result and str(retrieved_chat_id) == str(1):
158 | update_query = "UPDATE telegram_bot_data SET chat_id = %s WHERE payment_uuid = %s"
159 | execute_db_query(update_query, (chat_id, uuid))
160 | await update.message.reply_text("Setup has been completed.\n\nThe Solana BuyBot should start momentarily.\n\nImportant note:\nAt the beginning of the process, the bot will be cataloging past purchases - so there will be many purchases coming through that might not be current. After that process it will keep all purchases up-to-date within 1-2 minutes of purchase.")
161 |
162 | initialize_job_for_chat(context.application, chat_id, contract_address, token_name)
163 |
164 | else:
165 | await update.message.reply_text("There's already an assigned chat for the bot, please go reset the chat using the /bot command in a DM to access bot management.")
166 |
167 | context.user_data.pop('awaiting_uuid', None)
--------------------------------------------------------------------------------