├── .gitignore ├── README.md ├── backup └── __init__.py ├── ban.py ├── bot_states.py ├── command_handlers.py ├── config.py ├── decorators.py ├── error_handlers.py ├── excel_generator.py ├── flask_app.py ├── input_handlers.py ├── job_callbacks.py ├── keyboards.py ├── lang.py ├── migrate.py ├── models.py ├── mq_bot.py ├── requirements.txt ├── static └── css │ └── admin.css ├── tariffs.py └── templates ├── base.html ├── lost_top_ups.html ├── statistics.html ├── user_deposit.html ├── user_lookup.html └── withdrawals.html /.gitignore: -------------------------------------------------------------------------------- 1 | \.DS_Store 2 | __pycache__/ 3 | env 4 | .idea 5 | partners/ 6 | ascension\.db 7 | excel/transactions/ 8 | docs/transactions/ 9 | backup/conversations 10 | config\.json 11 | migrations -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ICO DAY 2 | 3 | [**Telegram Hyip Bot**](https://t.me/ico_day_bot) 4 | 5 | [**Marketing Page**](https://telegra.ph/Dobro-pozhalovat-v-ICO-DAY-05-10) 6 | 7 |  8 | 9 | Financial pyramid developed by me, for which I was not paid. 10 | 11 | **Python 3.6 required** 12 | 13 | **Main features** 14 | 15 | - Everyday payments 16 | - Intuitive interface 17 | - Refferals excel export 18 | - Detailed transaction history 19 | - Automated flood detecting and ban system 20 | - Web admin page 21 | - Realtime new refferals, daily income and account top up notifications 22 | - Payments from unknown wallets can be managed in admin section 23 | - Blockcypher integration for payments accepting 24 | - Conversation authsave on app shutdown 25 | - Hi-load 26 | 27 | **Main stack** 28 | - [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) 29 | - [flask](https://github.com/pallets/flask) 30 | - [peewee](https://github.com/coleifer/peewee) 31 | 32 | The motivation behind it is to keep things small and not redundant. 33 | 34 | **Notes** 35 | 36 | This project has been developed with weak python knowledge, so some parts of my code may look ugly. If I've been developing it now I would: 37 | 38 | - Used Decimals over floats 39 | - Created two separate apps (web app preferably Django + REST) and use bot as a GUI 40 | - Used multilanguage strings 41 | 42 | **Setup** 43 | 44 | Before running the app you need to tweak two configs. 45 | 46 | - config.py stores some info which is accessed from different app parts. 47 | 48 | - config.json stores private information. 49 | 50 | ``` 51 | { 52 | "token": "YOUR TELEGRAM TOKEN", 53 | "admin": { 54 | "username": "name", 55 | "password": "pass" 56 | }, 57 | "blockcypher_key": "YOUR BLOCKCYPHER TOKEN" 58 | } 59 | ``` 60 | 61 | If you wanna use webhook instead of websockets you should place it like this. 62 | 63 | ``` 64 | ... 65 | |___telegram-hyip 66 | |___keys 67 | ``` 68 | 69 | Keys folder must be if the same place with the telegram-hyip project. 70 | 71 | **To run** the app just run `python flask_app.py`. 72 | 73 | **To shutdown** the app you can use `CTRL + C`, but make sure that it saved all conversation data before pressing `CTRL + C` again. -------------------------------------------------------------------------------- /backup/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BugDeveloper/telegram-hyip/6ce6f30f59c640eacff8b3635cdb1bff8babe692/backup/__init__.py -------------------------------------------------------------------------------- /ban.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | 4 | class Ban: 5 | 6 | BAN_MULTIPLIER = 3 7 | 8 | def __init__(self): 9 | self.banned_until = datetime.datetime.now() 10 | self.ban_hours = 1 11 | 12 | def set_banned(self): 13 | self.banned_until = datetime.datetime.now() + datetime.timedelta(hours=self.ban_hours) 14 | ban_hours = self.ban_hours 15 | self.ban_hours = self.ban_hours * self.BAN_MULTIPLIER 16 | return ban_hours 17 | 18 | def banned(self): 19 | return self.banned_until > datetime.datetime.now() 20 | -------------------------------------------------------------------------------- /bot_states.py: -------------------------------------------------------------------------------- 1 | MAIN, WALLET_CHANGE, CREATE_WITHDRAWAL, TRANSFER_BALANCE_TO_DEPOSIT, TRANSFER_BALANCE_TO_USER, DEMO_TOP_UP = range(6) 2 | -------------------------------------------------------------------------------- /command_handlers.py: -------------------------------------------------------------------------------- 1 | from peewee import DoesNotExist 2 | from telegram.ext import CommandHandler, run_async 3 | import bot_states 4 | import keyboards 5 | import lang 6 | from decorators import user_auth 7 | from models import User 8 | 9 | 10 | @run_async 11 | def _start_command(bot, update, args): 12 | chat_id = update.message.chat_id 13 | first_name = update.message.chat.first_name 14 | username = update.message.from_user.username 15 | 16 | try: 17 | user = User.get(chat_id=chat_id) 18 | text = f'{first_name}, вы уже зарегистрированны в системе. Добро пожаловать домой!' 19 | except DoesNotExist: 20 | referral = None 21 | try: 22 | if len(args) > 0: 23 | referred_by = args[0] 24 | referral = User.get(chat_id=referred_by) 25 | except DoesNotExist: 26 | pass 27 | 28 | user = User.create( 29 | chat_id=chat_id, 30 | username=username, 31 | first_name=update.message.from_user.first_name, 32 | last_name=update.message.from_user.last_name, 33 | referral=referral 34 | ) 35 | 36 | text = f'{first_name}, вы были успешно зарегистрированны в системе!' 37 | 38 | bot.send_message(chat_id=user.chat_id, text=text, reply_markup=keyboards.main_keyboard()) 39 | return bot_states.MAIN 40 | 41 | 42 | @run_async 43 | @user_auth 44 | def _transfer_balance_to_deposit_command(bot, update): 45 | bot.send_message( 46 | chat_id=bot.chat_id, 47 | text=lang.transfer_balance_to_deposit(bot.user.balance), 48 | reply_markup=keyboards.back_keyboard() 49 | ) 50 | return bot_states.TRANSFER_BALANCE_TO_DEPOSIT 51 | 52 | 53 | @run_async 54 | @user_auth 55 | def _wallet_change_command(bot, update): 56 | bot.send_message( 57 | chat_id=bot.chat_id, 58 | text=lang.enter_new_wallet(), 59 | reply_markup=keyboards.back_keyboard() 60 | ) 61 | return bot_states.WALLET_CHANGE 62 | 63 | 64 | @run_async 65 | @user_auth 66 | def _transfer_balance_to_user(bot, update): 67 | bot.send_message( 68 | chat_id=bot.chat_id, 69 | text=lang.transfer_balance_to_user(bot.user.balance), 70 | reply_markup=keyboards.back_keyboard() 71 | ) 72 | return bot_states.TRANSFER_BALANCE_TO_USER 73 | 74 | 75 | @run_async 76 | @user_auth 77 | def _withdrawal_command(bot, update): 78 | bot.send_message( 79 | chat_id=bot.chat_id, 80 | text=lang.create_withdrawal(bot.user.balance), 81 | reply_markup=keyboards.back_keyboard() 82 | ) 83 | return bot_states.CREATE_WITHDRAWAL 84 | 85 | 86 | @run_async 87 | @user_auth 88 | def _demo_top_up(bot, update): 89 | bot.send_message( 90 | chat_id=bot.chat_id, 91 | text='Сколько?', 92 | reply_markup=keyboards.back_keyboard() 93 | ) 94 | 95 | return bot_states.DEMO_TOP_UP 96 | 97 | 98 | def transfer_balance_to_deposit(): 99 | handler = CommandHandler('transfer_deposit', _transfer_balance_to_deposit_command) 100 | return handler 101 | 102 | 103 | def change_wallet_initiation_handler(): 104 | handler = CommandHandler('wallet', _wallet_change_command) 105 | return handler 106 | 107 | 108 | def start_command_handler(): 109 | handler = CommandHandler('start', _start_command, pass_args=True) 110 | return handler 111 | 112 | 113 | def transfer_balance_to_user(): 114 | handler = CommandHandler('transfer_user', _transfer_balance_to_user) 115 | return handler 116 | 117 | 118 | def withdraw_command_handler(): 119 | handler = CommandHandler('withdraw', _withdrawal_command) 120 | return handler 121 | 122 | 123 | def demo_top_up(): 124 | handler = CommandHandler('demo_top_up', _demo_top_up) 125 | return handler 126 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | _ETH_ADDRESS = '(тут адрес эфира)' 2 | _DB_NAME = 'ascension.db' 3 | _SUPPORT_ACCOUNT = '@ico_day_support' 4 | DEBUG = True 5 | 6 | 7 | def get_support_account(): 8 | return _SUPPORT_ACCOUNT 9 | 10 | 11 | def db_name(): 12 | return _DB_NAME 13 | 14 | 15 | def project_eth_address(): 16 | return _ETH_ADDRESS.lower() 17 | -------------------------------------------------------------------------------- /decorators.py: -------------------------------------------------------------------------------- 1 | from peewee import DoesNotExist 2 | import bot_states 3 | import lang 4 | from models import User 5 | 6 | 7 | def user_auth(func): 8 | def inner(bot, update, *args, **kwargs): 9 | chat_id = update.message.chat_id 10 | bot.chat_id = chat_id 11 | try: 12 | bot.user = User.get(chat_id=chat_id) 13 | except DoesNotExist: 14 | bot.send_message(chat_id, lang.not_registered()) 15 | return bot_states.MAIN 16 | return func(bot, update, *args, **kwargs) 17 | 18 | return inner 19 | 20 | 21 | def back_button(func): 22 | def inner(bot, update, *args, **kwargs): 23 | import keyboards 24 | text = update.message.text 25 | chat_id = update.message.chat_id 26 | 27 | if text == keyboards.MAIN_BUTTONS['back']: 28 | bot.send_message( 29 | chat_id=chat_id, 30 | text=lang.back_to_main_menu(), 31 | reply_markup=keyboards.main_keyboard() 32 | ) 33 | return bot_states.MAIN 34 | return func(bot, update, *args, **kwargs) 35 | return inner 36 | -------------------------------------------------------------------------------- /error_handlers.py: -------------------------------------------------------------------------------- 1 | from telegram.error import Unauthorized, BadRequest, TimedOut, NetworkError, ChatMigrated, TelegramError 2 | from telegram.ext import run_async 3 | import bot_states 4 | 5 | 6 | @run_async 7 | def timed_out_handler(bot, update): 8 | user_id = update.message.chat_id 9 | bot.send_message( 10 | chat_id=user_id, 11 | text='Во время обработки прошлого запроса произошла ошибка.\n' 12 | 'Пожалуйста сообщите в тех поддержку.' 13 | ) 14 | return bot_states.MAIN 15 | 16 | 17 | def error_callback(bot, update, error): 18 | try: 19 | raise error 20 | except Unauthorized: 21 | print('remove update.message.chat_id from conversation list') 22 | except BadRequest: 23 | print('handle malformed requests - read more below!') 24 | except TimedOut: 25 | print('handle slow connection problems') 26 | except NetworkError: 27 | print('handle other connection problems') 28 | except ChatMigrated as e: 29 | print('the chat_id of a group has changed, use e.new_chat_id instead') 30 | except TelegramError: 31 | print('handle all other telegram related errors') 32 | -------------------------------------------------------------------------------- /excel_generator.py: -------------------------------------------------------------------------------- 1 | import xlsxwriter 2 | from telegram.ext import run_async 3 | import tariffs 4 | 5 | _EXCEL_DOCS_FOLDER = 'docs' 6 | 7 | 8 | @run_async 9 | def transactions_excel(bot, user): 10 | 11 | transfer_from = { 12 | 'Сумма': 'amount', 13 | 'Дата': 'created_at', 14 | 'От пользователя': 'from_user', 15 | } 16 | 17 | transfer_to = { 18 | 'Сумма': 'amount', 19 | 'Дата': 'created_at', 20 | 'Пользователю': 'to_user', 21 | } 22 | 23 | cols = { 24 | 'Сумма': 'amount', 25 | 'Дата': 'created_at', 26 | } 27 | 28 | withdrawals = user.withdrawals 29 | top_ups = user.top_ups 30 | deposit_transfers = user.deposit_transfers 31 | transfers_from = user.transfers_from 32 | transfers_to = user.transfers_to 33 | 34 | filename = f'{_EXCEL_DOCS_FOLDER}/transactions/{user}_transactions.xlsx' 35 | 36 | workbook = xlsxwriter.Workbook(filename) 37 | worksheet = workbook.add_worksheet() 38 | bold = workbook.add_format({'bold': True}) 39 | header = workbook.add_format() 40 | header.set_font_size(15) 41 | 42 | row = 0 43 | worksheet.write(row, 0, 'Выводы', header) 44 | row += 1 45 | 46 | row = _write_models_to_excel(withdrawals, cols, worksheet, bold, row) 47 | row += 1 48 | worksheet.write(row, 0, 'Пополнения', header) 49 | row += 1 50 | row = _write_models_to_excel(top_ups, cols, worksheet, bold, row) 51 | row += 1 52 | worksheet.write(row, 0, 'Переводы в депозит', header) 53 | row += 1 54 | row = _write_models_to_excel(deposit_transfers, cols, worksheet, bold, row) 55 | row += 1 56 | worksheet.write(row, 0, 'Переводы другим пользователям', header) 57 | row += 1 58 | row = _write_models_to_excel(transfers_from, transfer_to, worksheet, bold, row) 59 | row += 1 60 | worksheet.write(row, 0, 'Переводы вам от других пользователей', header) 61 | row += 1 62 | row = _write_models_to_excel(transfers_to, transfer_from, worksheet, bold, row) 63 | 64 | workbook.close() 65 | bot.send_document(chat_id=user.chat_id, document=open(filename, 'rb')) 66 | 67 | 68 | @run_async 69 | def partners_excel(bot, user): 70 | cols = { 71 | 'Телеграм username': 'username', 72 | 'Имя': 'first_name', 73 | 'Фамилия': 'last_name', 74 | 'Депозит': 'deposit', 75 | 'Дата регистрации': 'created_at' 76 | } 77 | 78 | partners_list = user.partners_per_levels 79 | filename = f'{_EXCEL_DOCS_FOLDER}/partners/{user}_partners.xlsx' 80 | 81 | workbook = xlsxwriter.Workbook(filename) 82 | worksheet = workbook.add_worksheet() 83 | bold = workbook.add_format({'bold': True}) 84 | row = 0 85 | level_number = 0 86 | total_partners_reward = 0 87 | for level in partners_list: 88 | if not row == 0: 89 | row += 2 90 | worksheet.write(row, 0, f'{level_number + 1} реферальный уровень', bold) 91 | if level_number == 0: 92 | worksheet.write(row, 2, user.first_level_partners_deposit, bold) 93 | elif level_number == 1: 94 | worksheet.write(row, 2, user.second_level_partners_deposit, bold) 95 | elif level_number == 2: 96 | worksheet.write(row, 2, user.third_level_partners_deposit, bold) 97 | row += 1 98 | col = 0 99 | for field in cols.keys(): 100 | worksheet.write(row, col, field, bold) 101 | col += 1 102 | row += 1 103 | col = 0 104 | for partner in level: 105 | for prop_name in cols.values(): 106 | if prop_name == 'created_at': 107 | worksheet.write(row, col, partner.created_at.strftime("%d/%m/%y")) 108 | else: 109 | worksheet.write(row, col, getattr(partner, prop_name)) 110 | col += 1 111 | row += 1 112 | col = 0 113 | level_number += 1 114 | row += 1 115 | col = 0 116 | workbook.close() 117 | 118 | bot.send_document(chat_id=user.chat_id, document=open(filename, 'rb')) 119 | 120 | 121 | def _write_models_to_excel(models, cols, worksheet, bold, row_start_with): 122 | row = row_start_with 123 | col = 0 124 | 125 | for field in cols.keys(): 126 | worksheet.write(row, col, field, bold) 127 | col += 1 128 | row += 1 129 | col = 0 130 | for model in models: 131 | for prop_name in cols.values(): 132 | try: 133 | if prop_name == 'created_at': 134 | worksheet.write(row, col, model.created_at.strftime("%d/%m/%y")) 135 | elif prop_name == 'to_user' or prop_name == 'from_user': 136 | worksheet.write(row, col, f'@{getattr(model, prop_name).username}') 137 | else: 138 | worksheet.write(row, col, getattr(model, prop_name)) 139 | except AttributeError: 140 | continue 141 | col += 1 142 | row += 1 143 | col = 0 144 | return row 145 | -------------------------------------------------------------------------------- /flask_app.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import logging 3 | import os 4 | import pickle 5 | import requests 6 | from telegram.ext import messagequeue as mq 7 | import sys 8 | from pathlib import Path 9 | import telegram 10 | from flask import Flask, request, render_template, Response 11 | from peewee import DoesNotExist, fn 12 | from telegram.ext import ConversationHandler, MessageHandler, Filters 13 | from telegram.utils.promise import Promise 14 | from telegram.utils.request import Request as TelegramRequest 15 | import bot_states 16 | import command_handlers 17 | import config 18 | import error_handlers 19 | import input_handlers 20 | import mq_bot 21 | from job_callbacks import reward_users, notify_inactive_users 22 | from models import User, TopUp, Withdrawal 23 | from flask_basicauth import BasicAuth 24 | import json 25 | 26 | app = Flask(__name__) 27 | basic_auth = BasicAuth(app) 28 | 29 | _ETH_WEI = 1000000000000000000 30 | _DNSOMATIC = 'http://myip.dnsomatic.com' 31 | 32 | 33 | def load_data(): 34 | try: 35 | f = open('backup/conversations', 'rb') 36 | conv_handler.conversations = pickle.load(f) 37 | f.close() 38 | except FileNotFoundError: 39 | logging.error("Data file not found") 40 | except Exception: 41 | logging.error(sys.exc_info()[0]) 42 | 43 | 44 | def save_data(): 45 | resolved = dict() 46 | for k, v in conv_handler.conversations.items(): 47 | if isinstance(v, tuple) and len(v) is 2 and isinstance(v[1], Promise): 48 | try: 49 | new_state = v[1].result() 50 | except: 51 | new_state = v[0] 52 | resolved[k] = new_state 53 | else: 54 | resolved[k] = v 55 | try: 56 | f = open('backup/conversations', 'wb+') 57 | pickle.dump(resolved, f) 58 | f.close() 59 | except: 60 | logging.error(sys.exc_info()[0]) 61 | print('=======================') 62 | print('CONVERSATION DATA SAVED') 63 | 64 | 65 | def stop_updater(): 66 | print('STOPPING BOT UPDATES') 67 | updater.stop() 68 | print('BOT UPDATES STOPPED') 69 | 70 | 71 | dirname = os.path.dirname(__file__) 72 | docs = os.path.join(dirname, 'docs') 73 | partners = os.path.join(dirname, 'docs/partners') 74 | transactions = os.path.join(dirname, 'docs/transactions') 75 | 76 | 77 | def create_folder(path): 78 | if not os.path.exists(path): 79 | os.makedirs(path) 80 | 81 | 82 | create_folder(docs) 83 | create_folder(partners) 84 | create_folder(transactions) 85 | 86 | with open('config.json') as config_file: 87 | config_json = json.load(config_file) 88 | token = config_json['token'] 89 | app.config['BASIC_AUTH_USERNAME'] = config_json['admin']['username'] 90 | app.config['BASIC_AUTH_PASSWORD'] = config_json['admin']['password'] 91 | try: 92 | blockcypher_key = config_json['blockcypher_key'] 93 | except KeyError: 94 | blockcypher_key = '' 95 | 96 | HOOKS_API = f'https://api.blockcypher.com/v1/eth/main/hooks?token={blockcypher_key}' 97 | 98 | 99 | q = mq.MessageQueue(all_burst_limit=25, all_time_limit_ms=1017) 100 | tel_request = TelegramRequest(con_pool_size=8) 101 | mq_bot = mq_bot.MQBot(token=token, request=tel_request, mqueue=q) 102 | 103 | updater = telegram.ext.updater.Updater( 104 | bot=mq_bot, 105 | request_kwargs={'read_timeout': 6, 'connect_timeout': 7}, 106 | ) 107 | dispatcher = updater.dispatcher 108 | 109 | change_wallet_command_handler = command_handlers.change_wallet_initiation_handler() 110 | withdraw_command_handler = command_handlers.withdraw_command_handler() 111 | start_command_handler = command_handlers.start_command_handler() 112 | transfer_balance_to_deposit_command_handler = command_handlers.transfer_balance_to_deposit() 113 | transfer_balance_to_user_command_handler = command_handlers.transfer_balance_to_user() 114 | demo_top_up_command_handler = command_handlers.demo_top_up() 115 | 116 | main_handler = input_handlers.main_menu_input_handler() 117 | change_wallet_handler = input_handlers.change_wallet_input_handler() 118 | create_withdraw_input_handler = input_handlers.withdrawal_input_handler() 119 | transfer_balance_to_deposit_input_handler = input_handlers.transfer_balance_to_deposit_input_handler() 120 | transfer_balance_to_user_input_handler = input_handlers.transfer_balance_to_user_input_handler() 121 | callback_query_handler = input_handlers.callback_query_handler() 122 | demo_top_up_input_handler = input_handlers.demo_top_up() 123 | 124 | 125 | conv_handler = ConversationHandler( 126 | entry_points=[ 127 | start_command_handler, 128 | main_handler 129 | ], 130 | states={ 131 | bot_states.MAIN: [ 132 | start_command_handler, 133 | main_handler, 134 | change_wallet_command_handler, 135 | withdraw_command_handler, 136 | transfer_balance_to_deposit_command_handler, 137 | transfer_balance_to_user_command_handler, 138 | demo_top_up_command_handler 139 | ], 140 | bot_states.WALLET_CHANGE: [ 141 | change_wallet_handler, 142 | ], 143 | bot_states.CREATE_WITHDRAWAL: [ 144 | create_withdraw_input_handler, 145 | ], 146 | bot_states.TRANSFER_BALANCE_TO_DEPOSIT: [ 147 | transfer_balance_to_deposit_input_handler, 148 | ], 149 | bot_states.TRANSFER_BALANCE_TO_USER: [ 150 | transfer_balance_to_user_input_handler, 151 | ], 152 | bot_states.DEMO_TOP_UP: [ 153 | demo_top_up_input_handler 154 | ] 155 | }, 156 | fallbacks=[ 157 | ], 158 | timed_out_behavior=[ 159 | MessageHandler( 160 | Filters.text, 161 | error_handlers.timed_out_handler 162 | ) 163 | ], 164 | run_async_timeout=1.0 165 | ) 166 | 167 | dispatcher.add_handler(conv_handler) 168 | dispatcher.add_handler(callback_query_handler) 169 | dispatcher.add_error_handler(error_handlers.error_callback) 170 | 171 | j = updater.job_queue 172 | j.run_daily(reward_users, time=datetime.time(hour=3)) 173 | j.run_daily(notify_inactive_users, days=(5,), time=datetime.time(hour=14)) 174 | 175 | logging.basicConfig( 176 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', 177 | level=logging.INFO 178 | ) 179 | 180 | PRIVATE_SSH = '../keys/private.key' 181 | CERT_SSH = '../keys/cert.pem' 182 | 183 | if Path(PRIVATE_SSH).is_file() and Path(CERT_SSH).is_file(): 184 | updater.start_webhook( 185 | listen='0.0.0.0', 186 | port=8443, 187 | url_path=token, 188 | key=PRIVATE_SSH, 189 | cert=CERT_SSH, 190 | webhook_url=f'https://167.99.218.143:8443/{token}' 191 | ) 192 | print('Webhook updater started') 193 | else: 194 | updater.start_polling() 195 | print('Polling updater started') 196 | 197 | load_data() 198 | 199 | 200 | @app.route('/confirmed_transaction', methods=['POST']) 201 | def top_up_balance(): 202 | data = request.get_json() 203 | 204 | if data['outputs'][0]['addresses'][0].lower() != config.project_eth_address()[2:]: 205 | return Response( 206 | response='Success', 207 | status=200, 208 | mimetype='application/json' 209 | ) 210 | 211 | amount = data['total'] / _ETH_WEI 212 | if not amount: 213 | return Response( 214 | response='Success', 215 | status=200, 216 | mimetype='application/json' 217 | ) 218 | wallet = data['inputs'][0]['addresses'][0].lower() 219 | wallet = f'0x{wallet}' 220 | 221 | try: 222 | user = User.get(wallet=wallet) 223 | top_up = TopUp.create( 224 | user=user, 225 | amount=amount, 226 | from_wallet=wallet 227 | ) 228 | except DoesNotExist: 229 | top_up = TopUp.create( 230 | amount=amount, 231 | received=False, 232 | from_wallet=wallet 233 | ) 234 | 235 | return Response( 236 | response='Success', 237 | status=200, 238 | mimetype='application/json' 239 | ) 240 | 241 | 242 | class ValidationError(Exception): 243 | pass 244 | 245 | 246 | @app.route('/user_deposit') 247 | @basic_auth.required 248 | def user_deposit(): 249 | return render_template( 250 | 'user_deposit.html' 251 | ) 252 | 253 | 254 | @app.route('/increase_user_deposit', methods=['POST']) 255 | @basic_auth.required 256 | def increase_user_deposit(): 257 | json = request.get_json(silent=True) 258 | user_id = json['user_id'] 259 | amount = json['amount'] 260 | try: 261 | user_id = int(user_id) 262 | except TypeError: 263 | return Response( 264 | response='Неправильный id пользователя', 265 | status=400, 266 | mimetype='application/json' 267 | ) 268 | try: 269 | user = User.get(User.chat_id == user_id) 270 | except DoesNotExist as e: 271 | return Response( 272 | response='Нет такого юзера', 273 | status=400, 274 | mimetype='application/json' 275 | ) 276 | 277 | try: 278 | amount = float(amount) 279 | except ValueError: 280 | return Response( 281 | response='Не похоже на дробное число', 282 | status=400, 283 | mimetype='application/json' 284 | ) 285 | 286 | TopUp.create( 287 | amount=amount, 288 | user=user 289 | ) 290 | return Response( 291 | response='Успешно', 292 | status=200, 293 | mimetype='application/json' 294 | ) 295 | 296 | 297 | @app.route('/approve_withdrawal', methods=['POST']) 298 | @basic_auth.required 299 | def approve_withdrawal(): 300 | id = request.get_json(silent=True)['id'] 301 | withdrawal = Withdrawal.get(id=id) 302 | if withdrawal.approved: 303 | return Response( 304 | response='Вывод уже был подтвержден', 305 | status=400, 306 | mimetype='application/json' 307 | ) 308 | withdrawal.approved = True 309 | withdrawal.save() 310 | return Response( 311 | response='Успешно', 312 | status=200, 313 | mimetype='application/json' 314 | ) 315 | 316 | 317 | @app.route('/withdrawals') 318 | @basic_auth.required 319 | def withdrawals(): 320 | withdrawals = Withdrawal.select(Withdrawal, User) \ 321 | .where(Withdrawal.approved == False).order_by(Withdrawal.created_at).join(User) 322 | 323 | total_sum = Withdrawal.select(fn.COALESCE(fn.SUM(Withdrawal.amount), 0).alias('total_sum')) \ 324 | .where(Withdrawal.approved == False).execute() 325 | 326 | return render_template( 327 | 'withdrawals.html', 328 | total_sum=total_sum[0].total_sum, 329 | withdrawals=withdrawals, 330 | ) 331 | 332 | 333 | @app.route('/lost_top_ups') 334 | @basic_auth.required 335 | def lost_top_ups(): 336 | lost_top_ups = TopUp.select().where(TopUp.received == False) 337 | 338 | return render_template( 339 | 'lost_top_ups.html', 340 | lost_top_ups=lost_top_ups 341 | ) 342 | 343 | 344 | @app.route('/delete_top_up', methods=['DELETE']) 345 | @basic_auth.required 346 | def top_up_delete(): 347 | json_request = request.get_json(silent=True) 348 | id = json_request['id'] 349 | top_up = TopUp.get(id=id) 350 | top_up.delete_instance() 351 | 352 | return Response( 353 | response='Успешно', 354 | status=200, 355 | mimetype='application/json' 356 | ) 357 | 358 | 359 | @app.route('/user_lookup') 360 | @basic_auth.required 361 | def user_lookup(): 362 | ITEMS_PER_PAGE = 15 363 | page = request.args.get('page') 364 | id = request.args.get('id') 365 | username = request.args.get('username') 366 | try: 367 | page = int(page) 368 | except (ValueError, TypeError): 369 | page = 1 370 | 371 | users = User.select() 372 | users_count = users.count() 373 | if id: 374 | try: 375 | id = int(id) 376 | users = users.where(User.chat_id == id) 377 | except ValueError: 378 | pass 379 | if username: 380 | users = users.where(User.username.contains(username)) 381 | 382 | users = users.order_by(-User.created_at).paginate(page, ITEMS_PER_PAGE) 383 | 384 | if (page + 1) * ITEMS_PER_PAGE - users_count > ITEMS_PER_PAGE: 385 | next_link = None 386 | else: 387 | next_link = f'/user_lookup?page={page + 1}' 388 | 389 | if page - 1 <= 0: 390 | prev_link = None 391 | else: 392 | prev_link = f'/user_lookup?page={page - 1}' 393 | 394 | return render_template( 395 | 'user_lookup.html', 396 | users=users, 397 | users_count=users_count, 398 | next_link=next_link, 399 | prev_link=prev_link 400 | ) 401 | 402 | 403 | @app.route('/received_top_up', methods=['POST']) 404 | @basic_auth.required 405 | def top_up_received(): 406 | json_request = request.get_json(silent=True) 407 | id = json_request['id'] 408 | user_id = json_request['user_id'] 409 | try: 410 | user_id = int(user_id) 411 | except TypeError: 412 | return Response( 413 | response='Неверный id пользователя', 414 | status=400, 415 | mimetype='application/json' 416 | ) 417 | top_up = TopUp.get(id=id) 418 | if top_up.received: 419 | return Response( 420 | response='Пополнение уже зачислено', 421 | status=400, 422 | mimetype='application/json' 423 | ) 424 | try: 425 | user = User.get(User.chat_id == user_id) 426 | except DoesNotExist as e: 427 | return Response( 428 | response='Нет такого пользователя', 429 | status=400, 430 | mimetype='application/json' 431 | ) 432 | 433 | top_up.user = user 434 | top_up.received = True 435 | top_up.save() 436 | 437 | return Response( 438 | response='Успешно', 439 | status=200, 440 | mimetype='application/json' 441 | ) 442 | 443 | 444 | @app.route('/') 445 | @basic_auth.required 446 | def statistics(): 447 | def get_chart_data_for_transactions(transactions, columns): 448 | statistics_data = {} 449 | for transaction in transactions: 450 | date = transaction.created_at.strftime("%d %B") 451 | if date not in statistics_data: 452 | statistics_data[date] = 0 453 | statistics_data[date] += float(transaction.amount) 454 | 455 | chart_data = [columns] 456 | for day in statistics_data.keys(): 457 | chart_data.append([day, statistics_data[day]]) 458 | 459 | return chart_data 460 | 461 | now = datetime.datetime.now() 462 | month_ago = now - datetime.timedelta(days=30) 463 | withdrawals = Withdrawal.select() \ 464 | .where(Withdrawal.created_at < now) \ 465 | .where(Withdrawal.created_at > month_ago).order_by(Withdrawal.created_at) 466 | 467 | top_ups = TopUp.select() \ 468 | .where(TopUp.created_at < now) \ 469 | .where(TopUp.created_at > month_ago).order_by(TopUp.created_at) 470 | 471 | withdrawal_data = get_chart_data_for_transactions( 472 | withdrawals, 473 | [ 474 | 'Day', 475 | 'Withdrawals' 476 | ] 477 | ) 478 | 479 | top_up_data = get_chart_data_for_transactions( 480 | top_ups, 481 | [ 482 | 'Day', 483 | 'TopUps' 484 | ] 485 | ) 486 | 487 | registrations = User.select().where(User.created_at < now) \ 488 | .where(User.created_at > month_ago).order_by(User.created_at) 489 | 490 | registration_temp = {} 491 | for registration in registrations: 492 | date = registration.created_at.strftime("%d %B") 493 | if date not in registration_temp: 494 | registration_temp[date] = 0 495 | registration_temp[date] += 1 496 | 497 | registration_data = [ 498 | [ 499 | 'Day', 500 | 'Registrations' 501 | ] 502 | ] 503 | 504 | for day in registration_temp.keys(): 505 | registration_data.append([day, registration_temp[day]]) 506 | 507 | return render_template( 508 | 'statistics.html', 509 | withdrawal_data=withdrawal_data, 510 | top_up_data=top_up_data, 511 | registration_data=registration_data 512 | ) 513 | 514 | 515 | def transaction_hook_exists(json_data): 516 | for hook in json_data: 517 | if hook['address'] == config.project_eth_address()[2:]: 518 | print('Webhook exists') 519 | return True 520 | return False 521 | 522 | 523 | def blockcypher_webhook(): 524 | if not blockcypher_key: 525 | print('Running without blockcypher webhook. Transactions will not be recorded!') 526 | return True 527 | hooks = requests.get(HOOKS_API).json() 528 | if transaction_hook_exists(hooks): 529 | return True 530 | 531 | print('Webhook doesn\'t exists') 532 | 533 | f = requests.request('GET', _DNSOMATIC) 534 | server_ip = f.text 535 | 536 | response = requests.post( 537 | HOOKS_API, 538 | json={ 539 | "event": "confirmed-tx", 540 | "address": config.project_eth_address()[2:], 541 | "url": f"http://{server_ip}:8000/confirmed_transaction" 542 | } 543 | ) 544 | 545 | if 'id' in response.json().keys(): 546 | print('Hook created successfully') 547 | return True 548 | else: 549 | print('Hook error') 550 | return False 551 | 552 | 553 | if __name__ == "__main__": 554 | if blockcypher_webhook(): 555 | app.run(threaded=True, host='0.0.0.0', port=8000) 556 | stop_updater() 557 | save_data() 558 | sys.exit(0) 559 | else: 560 | print('Terminating the app') 561 | -------------------------------------------------------------------------------- /input_handlers.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import telegram 3 | from peewee import DoesNotExist, fn 4 | from telegram import InlineKeyboardButton, InlineKeyboardMarkup 5 | from telegram.ext import run_async, RegexHandler, MessageHandler, Filters, CallbackQueryHandler 6 | import bot_states 7 | import keyboards 8 | import lang 9 | import excel_generator 10 | from models import User, TopUp, Withdrawal, UserTransfer, DepositTransfer 11 | from eth_utils import is_address as is_eth_address 12 | from ban import Ban 13 | import tariffs 14 | from decorators import user_auth, back_button 15 | 16 | _partners_excel_query_time = {} 17 | _transactions_excel_query_time = {} 18 | _commands_spam_filter = {} 19 | _bans = {} 20 | 21 | _POSITIVE_FLOAT_REGEX = f'^(?![0.]+$)\d+(\.\d{1,2})|{keyboards.MAIN_BUTTONS["back"]}?$' 22 | 23 | 24 | class UserFloodRestrictions: 25 | ALLOWED_COMMAND = 30 26 | ALLOWED_TIME = 20 27 | 28 | 29 | def user_is_spamming(chat_id): 30 | now = datetime.datetime.now() 31 | if chat_id not in _commands_spam_filter: 32 | _commands_spam_filter[chat_id] = [] 33 | _commands_spam_filter[chat_id].append(now) 34 | 35 | updated_filter = [ 36 | query_time for query_time in _commands_spam_filter[chat_id] 37 | if (now - query_time).seconds < UserFloodRestrictions.ALLOWED_TIME 38 | ] 39 | 40 | _commands_spam_filter[chat_id] = updated_filter 41 | 42 | if len(updated_filter) > UserFloodRestrictions.ALLOWED_COMMAND: 43 | return True 44 | return False 45 | 46 | 47 | @run_async 48 | def notify_ban(bot, user_id, ban_hours): 49 | text = f'Поздравляем! Вы были забанены за флуд на кол-во часов: {ban_hours}.' 50 | bot.send_message(chat_id=user_id, text=text) 51 | 52 | 53 | @user_auth 54 | def _main_menu(bot, update, user_data): 55 | user_id = update.message.chat_id 56 | 57 | if not _bans.get(user_id, None): 58 | _bans[user_id] = Ban() 59 | 60 | ban = _bans.get(user_id) 61 | if ban.banned(): 62 | return bot_states.MAIN 63 | 64 | if user_is_spamming(user_id): 65 | ban_hours = ban.set_banned() 66 | notify_ban(bot, user_id, ban_hours) 67 | return bot_states.MAIN 68 | 69 | user = bot.user 70 | 71 | username = update.message.from_user.username 72 | if user.username != username: 73 | user.username = username 74 | user.save() 75 | 76 | text = update.message.text 77 | 78 | if text == keyboards.MAIN_BUTTONS['bank']: 79 | return MainMenu.bank(bot, user) 80 | elif text == keyboards.MAIN_BUTTONS['top_up']: 81 | return MainMenu.top_up(bot, user) 82 | elif text == keyboards.MAIN_BUTTONS['withdraw']: 83 | return MainMenu.withdraw(bot, user) 84 | elif text == keyboards.MAIN_BUTTONS['partners']: 85 | return MainMenu.partners(bot, user) 86 | elif text == keyboards.MAIN_BUTTONS['transactions']: 87 | return MainMenu.transactions(bot, user) 88 | elif text == keyboards.MAIN_BUTTONS['help']: 89 | return MainMenu.help(bot, user) 90 | 91 | 92 | def user_request_excel_too_often(user_id, query_time): 93 | if user_id in query_time: 94 | last_query = query_time[user_id] 95 | now = datetime.datetime.now() 96 | seconds_passed = (now - last_query).seconds 97 | if seconds_passed < 60: 98 | return True 99 | query_time[user_id] = datetime.datetime.now() 100 | return False 101 | 102 | 103 | @user_auth 104 | def _callback(bot, update): 105 | query = update.callback_query 106 | user = bot.user 107 | user_id = user.chat_id 108 | 109 | if query.data == 'partners_excel': 110 | if user_request_excel_too_often(user_id, _partners_excel_query_time): 111 | text = 'Нельзя запрашивать excel партнёров чаще, чем раз в минуту.' 112 | bot.send_message(chat_id=user_id, text=text) 113 | return 114 | excel_generator.partners_excel(bot, user) 115 | elif query.data == 'transactions_excel': 116 | if user_request_excel_too_often(user_id, _transactions_excel_query_time): 117 | text = 'Нельзя запрашивать excel транзакций чаще, чем раз в минуту.' 118 | bot.send_message(chat_id=user_id, text=text) 119 | return 120 | excel_generator.transactions_excel(bot, user) 121 | 122 | 123 | class MainMenu: 124 | 125 | @staticmethod 126 | @run_async 127 | def transactions(bot, user): 128 | count_of_last_trx = 3 129 | top_ups = user.top_ups.order_by(TopUp.id.desc()).limit(count_of_last_trx) 130 | withdrawals = user.withdrawals.order_by(Withdrawal.id.desc()).limit(count_of_last_trx) 131 | keyboard = [ 132 | [ 133 | InlineKeyboardButton("Скачать excel таблицу транзакций", callback_data='transactions_excel'), 134 | ], 135 | ] 136 | reply_markup = InlineKeyboardMarkup(keyboard) 137 | bot.send_message(chat_id=user.chat_id, text=lang.top_ups(top_ups)) 138 | bot.send_message( 139 | chat_id=user.chat_id, 140 | text=lang.withdrawals(withdrawals), 141 | reply_markup=reply_markup 142 | ) 143 | return bot_states.MAIN 144 | 145 | @staticmethod 146 | @run_async 147 | def partners(bot, user): 148 | keyboard = [ 149 | [ 150 | InlineKeyboardButton("Скачать excel таблицу партнёров", callback_data='partners_excel'), 151 | ], 152 | ] 153 | reply_markup = InlineKeyboardMarkup(keyboard) 154 | text = lang.partners(user, bot.username, user.referral) 155 | bot.send_message( 156 | chat_id=user.chat_id, 157 | text=text, 158 | reply_markup=reply_markup 159 | ) 160 | return bot_states.MAIN 161 | 162 | @staticmethod 163 | @run_async 164 | def withdraw(bot, user): 165 | if user.wallet: 166 | text = lang.withdraw(user.wallet) 167 | bot.send_message(chat_id=user.chat_id, text=text) 168 | return bot_states.MAIN 169 | else: 170 | text = f'{lang.wallet_not_set()}\n{lang.enter_new_wallet()}' 171 | 172 | bot.send_message( 173 | chat_id=user.chat_id, 174 | text=text, 175 | reply_markup=keyboards.back_keyboard() 176 | ) 177 | return bot_states.WALLET_CHANGE 178 | 179 | @staticmethod 180 | @run_async 181 | def top_up(bot, user): 182 | text = '' 183 | import config 184 | if config.DEBUG: 185 | text = '/demo_top_up - пополнить счет в демо режиме\n' 186 | 187 | if user.wallet: 188 | text += lang.top_up(user.wallet) 189 | bot.send_message( 190 | chat_id=user.chat_id, 191 | text=text, 192 | ) 193 | 194 | bot.send_message( 195 | chat_id=user.chat_id, 196 | text=lang.top_up_invest_wallet(), 197 | parse_mode=telegram.ParseMode.MARKDOWN 198 | ) 199 | 200 | return bot_states.MAIN 201 | else: 202 | text = lang.wallet_not_set() + '\n' + lang.enter_new_wallet() 203 | bot.send_message( 204 | chat_id=user.chat_id, 205 | text=text, 206 | reply_markup=keyboards.back_keyboard(), 207 | ) 208 | return bot_states.WALLET_CHANGE 209 | 210 | @staticmethod 211 | @run_async 212 | def bank(bot, user): 213 | text = lang.deposit(user.deposit, user.balance, user.deposit_reward, user.sum_deposit_reward) 214 | bot.send_message( 215 | chat_id=user.chat_id, 216 | text=text 217 | ) 218 | return bot_states.MAIN 219 | 220 | @staticmethod 221 | @run_async 222 | def help(bot, user): 223 | bot.send_message( 224 | chat_id=user.chat_id, 225 | text=lang.help(), 226 | parse_mode=telegram.ParseMode.MARKDOWN 227 | ) 228 | return bot_states.MAIN 229 | 230 | 231 | class LessThanMinimalWithdraw(Exception): 232 | pass 233 | 234 | 235 | class NotEnoughBalance(Exception): 236 | pass 237 | 238 | 239 | def _validate_transaction(user, text, demo=False): 240 | amount = float(text) 241 | if demo: 242 | return amount 243 | if amount < tariffs.minimal_eth_withdraw(): 244 | raise LessThanMinimalWithdraw() 245 | if amount > user.balance: 246 | raise NotEnoughBalance() 247 | return amount 248 | 249 | 250 | @user_auth 251 | @back_button 252 | def _transfer_balance_to_user(bot, update): 253 | text = update.message.text 254 | 255 | transfer_data = text.split(' ') 256 | 257 | if len(transfer_data) != 2: 258 | bot.send_message(chat_id=bot.chat_id, text=lang.invalid_input()) 259 | return bot_states.TRANSFER_BALANCE_TO_USER 260 | 261 | username = transfer_data[0].lower() 262 | 263 | try: 264 | user_to_transfer = User.get(fn.Lower(User.username) == username) 265 | amount = _validate_transaction(bot.user, transfer_data[1]) 266 | except ValueError: 267 | bot.send_message(chat_id=bot.chat_id, text=lang.invalid_input()) 268 | return bot_states.TRANSFER_BALANCE_TO_USER 269 | except LessThanMinimalWithdraw: 270 | bot.send_message(chat_id=bot.chat_id, text=lang.minimal_withdraw_amount()) 271 | return bot_states.TRANSFER_BALANCE_TO_USER 272 | except NotEnoughBalance: 273 | bot.send_message(chat_id=bot.chat_id, text=lang.not_enough_eth()) 274 | return bot_states.TRANSFER_BALANCE_TO_USER 275 | except DoesNotExist: 276 | bot.send_message(chat_id=bot.chat_id, text=lang.user_not_registered()) 277 | return bot_states.TRANSFER_BALANCE_TO_USER 278 | 279 | UserTransfer.create( 280 | from_user=bot.user, 281 | to_user=user_to_transfer, 282 | amount=amount, 283 | ) 284 | 285 | bot.send_message( 286 | chat_id=bot.chat_id, 287 | text=lang.balance_transferred_to_user(amount, user_to_transfer.username), 288 | reply_markup=keyboards.main_keyboard() 289 | ) 290 | 291 | return bot_states.MAIN 292 | 293 | 294 | @user_auth 295 | @back_button 296 | def _transfer_balance_to_deposit(bot, update): 297 | text = update.message.text 298 | chat_id = bot.chat_id 299 | 300 | try: 301 | amount = _validate_transaction(bot.user, text) 302 | except ValueError: 303 | bot.send_message(chat_id=chat_id, text=lang.invalid_input()) 304 | return bot_states.TRANSFER_BALANCE_TO_DEPOSIT 305 | except LessThanMinimalWithdraw: 306 | bot.send_message(chat_id=chat_id, text=lang.minimal_withdraw_amount()) 307 | return bot_states.TRANSFER_BALANCE_TO_DEPOSIT 308 | except NotEnoughBalance: 309 | bot.send_message(chat_id=chat_id, text=lang.not_enough_eth()) 310 | return bot_states.TRANSFER_BALANCE_TO_DEPOSIT 311 | 312 | DepositTransfer.create( 313 | user=bot.user, 314 | amount=amount 315 | ) 316 | 317 | bot.send_message( 318 | chat_id=chat_id, 319 | text=lang.balance_transferred_to_deposit(amount), 320 | reply_markup=keyboards.main_keyboard() 321 | ) 322 | return bot_states.MAIN 323 | 324 | 325 | @user_auth 326 | @back_button 327 | def _create_withdrawal(bot, update): 328 | text = update.message.text 329 | 330 | try: 331 | amount = _validate_transaction(bot.user, text) 332 | except ValueError: 333 | bot.send_message(chat_id=bot.chat_id, text=lang.invalid_input()) 334 | return bot_states.CREATE_WITHDRAWAL 335 | except LessThanMinimalWithdraw: 336 | bot.send_message(chat_id=bot.chat_id, text=lang.minimal_withdraw_amount()) 337 | return bot_states.CREATE_WITHDRAWAL 338 | except NotEnoughBalance: 339 | bot.send_message(chat_id=bot.chat_id, text=lang.not_enough_eth()) 340 | return bot_states.CREATE_WITHDRAWAL 341 | 342 | try: 343 | not_approved_withdrawal = Withdrawal.get(approved=False) 344 | bot.send_message( 345 | chat_id=bot.chat_id, 346 | text=lang.not_approved_previous(not_approved_withdrawal.amount), 347 | reply_markup=keyboards.main_keyboard() 348 | ) 349 | return bot_states.MAIN 350 | except DoesNotExist: 351 | pass 352 | 353 | Withdrawal.create( 354 | user=bot.user, 355 | amount=amount 356 | ) 357 | 358 | bot.send_message( 359 | chat_id=bot.chat_id, 360 | text=lang.withdrawal_created(bot.user.wallet), 361 | reply_markup=keyboards.main_keyboard() 362 | ) 363 | 364 | return bot_states.MAIN 365 | 366 | 367 | @run_async 368 | @user_auth 369 | @back_button 370 | def _change_wallet(bot, update): 371 | wallet = update.message.text.lower() 372 | if wallet[0:2] != '0x': 373 | wallet = f'0x{wallet}' 374 | 375 | if not is_eth_address(wallet): 376 | bot.send_message(chat_id=bot.chat_id, text=lang.invalid_input()) 377 | return bot_states.WALLET_CHANGE 378 | try: 379 | User.get(wallet=wallet) 380 | bot.send_message(chat_id=bot.chat_id, text=lang.eth_address_taken()) 381 | return bot_states.WALLET_CHANGE 382 | except DoesNotExist: 383 | pass 384 | 385 | user = bot.user 386 | user.wallet = wallet.lower() 387 | user.save() 388 | bot.send_message( 389 | chat_id=bot.chat_id, 390 | text=lang.wallet_successfully_set(wallet), 391 | reply_markup=keyboards.main_keyboard() 392 | ) 393 | return bot_states.MAIN 394 | 395 | 396 | def callback_query_handler(): 397 | callback_handler = CallbackQueryHandler(_callback) 398 | return callback_handler 399 | 400 | @run_async 401 | @user_auth 402 | @back_button 403 | def _demo_top_up(bot, update): 404 | from models import TopUp 405 | chat_id = bot.chat_id 406 | text = update.message.text 407 | user = bot.user 408 | try: 409 | amount = _validate_transaction(user, text, demo=True) 410 | except ValueError: 411 | bot.send_message(chat_id=chat_id, text='Инпут ерор)))))00') 412 | return bot_states.DEMO_TOP_UP 413 | 414 | top_up = TopUp.create( 415 | user=user, 416 | amount=amount, 417 | from_wallet=user.wallet 418 | ) 419 | bot.send_message( 420 | chat_id=chat_id, 421 | text=lang.back_to_main_menu(), 422 | reply_markup=keyboards.main_keyboard() 423 | ) 424 | return bot_states.MAIN 425 | 426 | 427 | def main_menu_input_handler(): 428 | regex = f'^({keyboards.MAIN_BUTTONS["bank"]}|' \ 429 | f'{keyboards.MAIN_BUTTONS["transactions"]}|' \ 430 | f'{keyboards.MAIN_BUTTONS["top_up"]}|' \ 431 | f'{keyboards.MAIN_BUTTONS["withdraw"]}|' \ 432 | f'{keyboards.MAIN_BUTTONS["partners"]}|' \ 433 | f'{keyboards.MAIN_BUTTONS["help"]}' \ 434 | f')$' 435 | main_handler = RegexHandler( 436 | regex, 437 | _main_menu, 438 | pass_user_data=True 439 | ) 440 | return main_handler 441 | 442 | 443 | def withdrawal_input_handler(): 444 | handler = MessageHandler( 445 | Filters.text, 446 | _create_withdrawal 447 | ) 448 | return handler 449 | 450 | 451 | def transfer_balance_to_deposit_input_handler(): 452 | handler = MessageHandler( 453 | Filters.text, 454 | _transfer_balance_to_deposit 455 | ) 456 | return handler 457 | 458 | 459 | def transfer_balance_to_user_input_handler(): 460 | handler = MessageHandler( 461 | Filters.text, 462 | _transfer_balance_to_user 463 | ) 464 | return handler 465 | 466 | 467 | def demo_top_up(): 468 | handler = MessageHandler( 469 | Filters.text, 470 | _demo_top_up 471 | ) 472 | return handler 473 | 474 | 475 | def change_wallet_input_handler(): 476 | wallet_handler = MessageHandler( 477 | Filters.text, 478 | _change_wallet 479 | ) 480 | return wallet_handler 481 | -------------------------------------------------------------------------------- /job_callbacks.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import config 3 | import tariffs 4 | from models import User 5 | 6 | 7 | def reward_users(bot, job): 8 | levels_percentage = tariffs.get_referral_levels_percentage() 9 | deposit_reward = User.deposit * User.deposit_reward 10 | query = User.update( 11 | balance=( 12 | User.balance 13 | + User.deposit * User.deposit_reward 14 | + User.first_level_partners_deposit * User.deposit_reward * levels_percentage[0] 15 | + User.second_level_partners_deposit * User.deposit_reward * levels_percentage[1] 16 | + User.third_level_partners_deposit * User.deposit_reward * levels_percentage[2] 17 | ), 18 | sum_deposit_reward=User.sum_deposit_reward + deposit_reward 19 | ).where(User.deposit >= tariffs.eth_minimal_deposit()) 20 | query.execute() 21 | 22 | users = User.select().where(User.deposit >= tariffs.eth_minimal_deposit()) 23 | 24 | for user in users: 25 | deposit_reward = user.deposit * user.deposit_reward 26 | first_level_reward = user.first_level_partners_deposit * user.deposit_reward * levels_percentage[0] 27 | second_level_reward = user.second_level_partners_deposit * user.deposit_reward * levels_percentage[1] 28 | third_level_reward = user.third_level_partners_deposit * user.deposit_reward * levels_percentage[2] 29 | 30 | bot.send_message( 31 | chat_id=user.chat_id, 32 | text=f'Вы получили начисления:\n' 33 | f'Депозит: {deposit_reward:.7f} ETH\n' 34 | f'1 уровень: {first_level_reward:.7f} ETH\n' 35 | f'2 уровень: {second_level_reward:.7f} ETH\n' 36 | f'3 уровень: {third_level_reward:.7f} ETH' 37 | ) 38 | 39 | 40 | def notify_inactive_users(bot, job): 41 | four_days_ago = datetime.datetime.now() - datetime.timedelta(days=4) 42 | inactive_users = User.select(User.chat_id).where( 43 | (User.created_at < four_days_ago) & (User.deposit < tariffs.eth_minimal_deposit()) 44 | ) 45 | 46 | text = f'Мы видим, что вы ещё не начали зарабатывать с нами.\n' \ 47 | f'Если у вас есть вопросы - напишите нам в техподдержку и мы поможем:\n' \ 48 | f'{config.get_support_account()}' 49 | for user in inactive_users: 50 | bot.send_message( 51 | chat_id=user.chat_id, 52 | text=text 53 | ) 54 | -------------------------------------------------------------------------------- /keyboards.py: -------------------------------------------------------------------------------- 1 | from telegram import ReplyKeyboardMarkup 2 | 3 | MAIN_BUTTONS = { 4 | 'bank': '💰 Мой счет', 5 | 'top_up': '💼 Вложить', 6 | 'withdraw': '🤑 Вывести', 7 | 'transactions': '⏳ История', 8 | 'partners': '👥 Партнеры', 9 | 'help': '❓ О нас', 10 | 'back': '⬅️ Назад' 11 | } 12 | 13 | _MAIN_KEYBOARD = [ 14 | [ 15 | MAIN_BUTTONS['bank'], 16 | MAIN_BUTTONS['transactions'], 17 | ], 18 | [ 19 | MAIN_BUTTONS['top_up'], 20 | MAIN_BUTTONS['withdraw'] 21 | ], 22 | [ 23 | MAIN_BUTTONS['partners'], 24 | MAIN_BUTTONS['help'], 25 | ] 26 | ] 27 | 28 | _BACK_KEYBOARD = [ 29 | [ 30 | MAIN_BUTTONS['back'] 31 | ] 32 | ] 33 | 34 | 35 | def main_keyboard(): 36 | return ReplyKeyboardMarkup(_MAIN_KEYBOARD) 37 | 38 | 39 | def back_keyboard(): 40 | return ReplyKeyboardMarkup(_BACK_KEYBOARD) 41 | -------------------------------------------------------------------------------- /lang.py: -------------------------------------------------------------------------------- 1 | import tariffs 2 | import config 3 | 4 | 5 | def eth_address_taken(): 6 | return 'Данный eth адрес уже занят.' 7 | 8 | 9 | def not_registered(): 10 | return 'Вы не зарегистрированны в системе. Для начала введите команду /start.' 11 | 12 | 13 | def withdrawals(withdrawals_list): 14 | if not len(withdrawals_list): 15 | return 'У вас пока нет выводов.' 16 | withdrawals = 'Ваши последние выводы:\n' 17 | withdrawals += '\n'.join([f'{withdrawal.amount:.7f} ETH - {withdrawal.created_at}' for withdrawal in withdrawals_list]) 18 | return withdrawals 19 | 20 | 21 | def top_ups(top_ups_list): 22 | if not len(top_ups_list): 23 | return 'У вас пока нет пополнений.' 24 | top_ups = 'Ваши последние пополнения:\n' 25 | top_ups += '\n'.join([f'{top_up.amount:.7f} ETH - {top_up.created_at}' for top_up in top_ups_list]) 26 | return top_ups 27 | 28 | 29 | def back_to_main_menu(): 30 | return 'Возврат в главное меню.' 31 | 32 | 33 | def balance_transferred_to_user(amount, to_user): 34 | return f'Сумма в {amount:.7f} ETH успешно переведена в пользователю {to_user}.' 35 | 36 | 37 | def balance_transferred_from_user(amount, from_user): 38 | return f'Пользователь {from_user} ETH перевёл вам на баланс {amount} ETH.' 39 | 40 | 41 | def balance_transferred_to_deposit(amount): 42 | return f'Сумма в {amount:.7f} ETH успешно переведена в депозит.' 43 | 44 | 45 | def not_approved_previous(amount): 46 | return f'Ваш прошлый вывод на сумму {amount:.7f} ETH ещё не был утверждён.' \ 47 | f' Вы сможете создать новый запрос на вывод после утверджения предыдущего.' 48 | 49 | 50 | def user_not_registered(): 51 | return 'Такой пользователь не зарегистрирован.' 52 | 53 | 54 | def not_enough_eth(): 55 | return 'У вас недостаточно средств. Введите другую сумму.' 56 | 57 | 58 | def wrong_command(): 59 | return 'Введите валидную команду.' 60 | 61 | 62 | def withdrawal_created(wallet): 63 | return f'Средства будут перечислены на адрес {wallet} в ближайшую среду или воскресенье.' 64 | 65 | 66 | def minimal_withdraw_amount(): 67 | return f'Сумма перевода должна превышать {tariffs.minimal_eth_withdraw()} ETH.' 68 | 69 | 70 | def partners(user, bot_username, user_invited_by=None): 71 | partners_info = '' 72 | if user_invited_by: 73 | partners_info = f'Вы были приглашены пользователем: {user_invited_by}\n' 74 | referral_link = f'https://telegram.me/{bot_username}?start={str(user.chat_id)}' 75 | partners_info += f'Ваша реферальная ссылка: {referral_link}\n' 76 | 77 | level_percentage = tariffs.get_referral_levels_percentage() 78 | 79 | for idx, percentage in enumerate(level_percentage): 80 | partners_info += f'Уровень {idx + 1} - {percentage * 100 * user.deposit_reward}% в день\n' 81 | 82 | return partners_info 83 | 84 | 85 | def invalid_input(): 86 | return 'Введите валидное значение.' 87 | 88 | 89 | def wallet_successfully_set(wallet): 90 | return f'ETH кошелёк {wallet} успешно привязан к вашему аккаунту.' 91 | 92 | 93 | def deposit(user_deposit, user_balance, user_reward, sum_deposit_reward): 94 | text = f'Депозит: {user_deposit:.7f} ETH. \n' 95 | if user_deposit >= tariffs.eth_minimal_deposit(): 96 | text += f'Баланс: {user_balance:.7f} ETH. \n' \ 97 | f'Процентная ставка: {user_reward * 100}% в день.\n' 98 | else: 99 | text += f'Начальный депозит: {tariffs.eth_minimal_deposit()} ETH\n' 100 | text += 'Перевод из баланса в депозит: /transfer_deposit\n' \ 101 | 'Перевод баланса пользователю: /transfer_user' 102 | return text 103 | 104 | 105 | def top_up(wallet): 106 | return f'Ваш кошелёк: {wallet}\n' \ 107 | 'Изменить адрес ETH кошелька: /wallet\n' \ 108 | f'ETH адрес для пополнения депозита: ' 109 | 110 | 111 | def top_up_invest_wallet(): 112 | return f'*{config.project_eth_address()}*' 113 | 114 | 115 | def withdraw(wallet): 116 | return f'Ваш кошелёк: {wallet}.\n' \ 117 | 'Изменить адрес ETH кошелька: /wallet\n' \ 118 | 'Вывод средств: /withdraw' 119 | 120 | 121 | def wallet_not_set(): 122 | return 'Ваш адрес для вывода не установлен.' 123 | 124 | 125 | def enter_new_wallet(): 126 | return 'Введите адрес вашего ETH кошелька:' 127 | 128 | 129 | def transfer_balance_to_deposit(balance): 130 | return f'Ваш баланс: {balance:.7f} ETH.\n' \ 131 | 'Введите сумму, которую хотите перевести в депозит:' 132 | 133 | 134 | def transfer_balance_to_user(balance): 135 | return f'Ваш баланс: {balance:.7f} ETH.\n' \ 136 | 'Введите имя пользователя (алиас) Telegram и сумму которую хотите перевести через пробел.\n' \ 137 | 'Например: "ivan 1.03"' 138 | 139 | 140 | def create_withdrawal(balance): 141 | return f'Ваш баланс: {balance:.7f} ETH.\n' \ 142 | 'Введите сумму, которую хотите вывести:' 143 | 144 | 145 | def help(): 146 | return 'Разработал[ ](https://telegra.ph/Dobro-pozhalovat-v-ICO-DAY-05-10)' \ 147 | '[BugDeveloper](https://github.com/BugDeveloper)' 148 | -------------------------------------------------------------------------------- /migrate.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sqlite3 3 | from peewee import SqliteDatabase 4 | from models import User, TopUp, Withdrawal, DepositTransfer, UserTransfer 5 | 6 | _DB_NAME = 'ascension.db' 7 | 8 | print('Delete current database? (Y/n)') 9 | delete_db = input().lower() 10 | 11 | if delete_db == 'y' or delete_db == '': 12 | if os.path.isfile(_DB_NAME): 13 | os.remove(_DB_NAME) 14 | elif delete_db == 'n': 15 | pass 16 | else: 17 | print('Incorrect input, script stopped') 18 | exit(1) 19 | 20 | db = SqliteDatabase(_DB_NAME) 21 | db.create_tables([User, TopUp, Withdrawal, DepositTransfer, UserTransfer]) 22 | sqlite = sqlite3.connect(_DB_NAME) 23 | sqlite.close() 24 | -------------------------------------------------------------------------------- /models.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import peewee 3 | import playhouse 4 | from peewee import * 5 | from playhouse.hybrid import hybrid_property 6 | from playhouse.signals import post_save, pre_save 7 | from telegram.ext import Dispatcher 8 | import lang 9 | import tariffs 10 | 11 | db = SqliteDatabase('ascension.db') 12 | 13 | 14 | class Payments: 15 | @staticmethod 16 | def update_levels_deposit(user, amount): 17 | first_level_upper = user.referral 18 | if not first_level_upper: 19 | return 20 | first_level_upper.first_level_partners_deposit += amount 21 | first_level_upper.save() 22 | 23 | second_level_upper = first_level_upper.referral 24 | if not second_level_upper: 25 | return 26 | second_level_upper.second_level_partners_deposit += amount 27 | second_level_upper.save() 28 | 29 | third_level_upper = second_level_upper.referral 30 | if not third_level_upper: 31 | return 32 | third_level_upper.third_level_partners_deposit += amount 33 | third_level_upper.save() 34 | 35 | 36 | class AscensionModel(playhouse.signals.Model): 37 | class Meta: 38 | database = db 39 | 40 | 41 | class User(AscensionModel): 42 | chat_id = peewee.IntegerField(primary_key=True) 43 | referral = peewee.ForeignKeyField('self', backref='partners', null=True, on_delete='SET NULL') 44 | deposit = peewee.FloatField(default=0) 45 | balance = peewee.FloatField(default=0) 46 | sum_deposit_reward = peewee.FloatField(default=0) 47 | first_level_partners_deposit = peewee.FloatField(default=0) 48 | second_level_partners_deposit = peewee.FloatField(default=0) 49 | third_level_partners_deposit = peewee.FloatField(default=0) 50 | wallet = peewee.CharField(max_length=40, null=True, unique=True) 51 | username = peewee.CharField(max_length=40, null=True) 52 | first_name = peewee.CharField(max_length=40) 53 | last_name = peewee.CharField(max_length=40, null=True) 54 | created_at = DateTimeField(default=datetime.datetime.now()) 55 | 56 | def __str__(self): 57 | text = f'{self.first_name}' 58 | if self.last_name: 59 | text = f'{text} {self.last_name}' 60 | if self.username: 61 | text = f'@{self.username}' 62 | return text 63 | 64 | @hybrid_property 65 | def deposit_reward(self): 66 | gold_tariff_comp = self.deposit >= tariffs.tariff_deposit(tariffs.GOLD_TARIFF_INDEX) 67 | silver_tariff_comp = self.deposit >= tariffs.tariff_deposit(tariffs.SILVER_TARIFF_INDEX) 68 | bronze_tariff_comp = self.deposit >= tariffs.tariff_deposit(tariffs.BRONZE_TARIFF_INDEX) 69 | 70 | if gold_tariff_comp: 71 | return tariffs.tariff_reward(tariffs.GOLD_TARIFF_INDEX) 72 | elif silver_tariff_comp: 73 | return tariffs.tariff_reward(tariffs.SILVER_TARIFF_INDEX) 74 | elif bronze_tariff_comp: 75 | return tariffs.tariff_reward(tariffs.BRONZE_TARIFF_INDEX) 76 | else: 77 | return tariffs.tariff_reward(tariffs.NO_TARIFF_INDEX) 78 | 79 | @hybrid_property 80 | def first_level_deposit(self): 81 | return 0 82 | 83 | @first_level_deposit.expression 84 | def first_level_deposit(cls): 85 | return User.select(fn.SUM(User.deposit)).where(User.referral == cls) 86 | 87 | @deposit_reward.expression 88 | def deposit_reward(cls): 89 | return Case( 90 | None, 91 | expression_tuples=[ 92 | ( 93 | cls.deposit.__ge__( 94 | tariffs.tariff_deposit(tariffs.GOLD_TARIFF_INDEX) 95 | ), 96 | tariffs.tariff_reward(tariffs.GOLD_TARIFF_INDEX) 97 | ), 98 | ( 99 | cls.deposit.between( 100 | tariffs.tariff_deposit(tariffs.SILVER_TARIFF_INDEX), 101 | tariffs.tariff_deposit(tariffs.GOLD_TARIFF_INDEX) 102 | ), 103 | tariffs.tariff_reward(tariffs.SILVER_TARIFF_INDEX) 104 | ), 105 | ( 106 | cls.deposit.between( 107 | tariffs.tariff_deposit(tariffs.BRONZE_TARIFF_INDEX), 108 | tariffs.tariff_deposit(tariffs.SILVER_TARIFF_INDEX) 109 | ), 110 | tariffs.tariff_reward(tariffs.BRONZE_TARIFF_INDEX) 111 | ), 112 | ], 113 | default=tariffs.tariff_reward(tariffs.NO_TARIFF_INDEX) 114 | ) 115 | 116 | @property 117 | def partners_per_levels(self): 118 | partners_list = [] 119 | first_level_query = User.select().where(User.referral == self) 120 | 121 | first_level_partners = first_level_query.execute() 122 | partners_list.append(first_level_partners) 123 | 124 | first_level_ids = [] 125 | for partner in first_level_partners: 126 | first_level_ids.append(partner.chat_id) 127 | 128 | second_level_query = ( 129 | User.select() 130 | .where(User.referral << first_level_ids) 131 | ) 132 | second_level_partners = second_level_query.execute() 133 | partners_list.append(second_level_partners) 134 | 135 | second_level_ids = [] 136 | for partner in second_level_partners: 137 | second_level_ids.append(partner.chat_id) 138 | 139 | third_level_query = ( 140 | User.select() 141 | .where(User.referral << second_level_ids) 142 | ) 143 | third_level_partners = third_level_query.execute() 144 | partners_list.append(third_level_partners) 145 | 146 | return partners_list 147 | 148 | 149 | @post_save(sender=User) 150 | def on_save_handler(model_class, instance, created): 151 | if created: 152 | if instance.referral: 153 | Dispatcher.get_instance().bot.send_message( 154 | chat_id=instance.referral.chat_id, 155 | text=f'По вашей ссылке зарегистрировался новый партнёр: {instance}' 156 | ) 157 | 158 | 159 | class DepositTransfer(AscensionModel): 160 | user = ForeignKeyField(User, on_delete='CASCADE', related_name='deposit_transfers', null=True) 161 | amount = peewee.FloatField() 162 | created_at = DateTimeField(default=datetime.datetime.now()) 163 | 164 | 165 | @pre_save(sender=DepositTransfer) 166 | def on_save_handler(model_class, instance, created): 167 | user = instance.user 168 | amount = instance.amount 169 | 170 | if user.balance < amount: 171 | raise PermissionError() 172 | 173 | 174 | @post_save(sender=DepositTransfer) 175 | def on_save_handler(model_class, instance, created): 176 | user = instance.user 177 | amount = instance.amount 178 | user.balance -= amount 179 | user.deposit += amount 180 | user.save() 181 | Payments.update_levels_deposit(user, instance.amount) 182 | 183 | 184 | class UserTransfer(AscensionModel): 185 | from_user = ForeignKeyField(User, on_delete='CASCADE', related_name='transfers_from', null=True) 186 | to_user = ForeignKeyField(User, on_delete='CASCADE', related_name='transfers_to', null=True) 187 | amount = peewee.FloatField() 188 | created_at = DateTimeField(default=datetime.datetime.now()) 189 | 190 | 191 | @pre_save(sender=UserTransfer) 192 | def on_save_handler(model_class, instance, created): 193 | amount = instance.amount 194 | from_user = instance.from_user 195 | 196 | if from_user.balance < amount: 197 | raise PermissionError() 198 | 199 | 200 | @post_save(sender=UserTransfer) 201 | def on_save_handler(model_class, instance, created): 202 | from_user = instance.from_user 203 | to_user = instance.to_user 204 | amount = instance.amount 205 | 206 | from_user.balance -= amount 207 | from_user.save() 208 | to_user.balance += amount 209 | to_user.save() 210 | 211 | Dispatcher.get_instance().bot.send_message( 212 | chat_id=to_user.chat_id, 213 | text=lang.balance_transferred_from_user(amount, from_user), 214 | ) 215 | 216 | 217 | class TopUp(AscensionModel): 218 | user = ForeignKeyField(User, on_delete='CASCADE', related_name='top_ups', null=True) 219 | amount = peewee.FloatField() 220 | from_wallet = peewee.CharField(max_length=40, null=True) 221 | received = peewee.BooleanField(default=True) 222 | created_at = DateTimeField(default=datetime.datetime.now()) 223 | 224 | 225 | @post_save(sender=TopUp) 226 | def on_save_handler(model_class, instance, created): 227 | if instance.user: 228 | user = instance.user 229 | user.deposit += instance.amount 230 | user.save() 231 | Payments.update_levels_deposit(user, instance.amount) 232 | 233 | Dispatcher.get_instance().bot.send_message( 234 | chat_id=user.chat_id, 235 | text=f'Ваш депозит был увеличен на {instance.amount} ETH.' 236 | ) 237 | 238 | 239 | class Withdrawal(AscensionModel): 240 | user = ForeignKeyField(User, on_delete='CASCADE', related_name='withdrawals') 241 | approved = BooleanField(default=False) 242 | amount = peewee.FloatField() 243 | created_at = DateTimeField(default=datetime.datetime.now()) 244 | 245 | 246 | @pre_save(sender=Withdrawal) 247 | def on_save_handler(model_class, instance, created): 248 | if created: 249 | user = instance.user 250 | if len(user.withdrawals.where(Withdrawal.approved == False)) or user.balance < instance.amount: 251 | raise PermissionError() 252 | 253 | 254 | @post_save(sender=Withdrawal) 255 | def on_save_handler(model_class, instance, created): 256 | user = instance.user 257 | 258 | if created: 259 | user.balance -= instance.amount 260 | user.save() 261 | elif instance.approved: 262 | Dispatcher.get_instance().bot.send_message( 263 | chat_id=user.chat_id, 264 | text=f'Ваш перевод на сумму {instance.amount} ETH был подтвержден. ' 265 | f'Средства будут переведены в кратчайшие сроки.' 266 | ) 267 | -------------------------------------------------------------------------------- /mq_bot.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import telegram 3 | from telegram.ext import messagequeue as mq 4 | 5 | 6 | class MQBot(telegram.bot.Bot): 7 | 8 | def __init__(self, *args, is_queued_def=True, mqueue=None, **kwargs): 9 | super(MQBot, self).__init__(*args, **kwargs) 10 | self._is_messages_queued_default = is_queued_def 11 | self._msg_queue = mqueue or mq.MessageQueue(all_burst_limit=25, all_time_limit_ms=1017) 12 | 13 | def __del__(self): 14 | try: 15 | self._msg_queue.stop() 16 | except: 17 | pass 18 | super(MQBot, self).__del__() 19 | 20 | @mq.queuedmessage 21 | def send_message(self, *args, **kwargs): 22 | if 'text' in kwargs: 23 | text = kwargs['text'] 24 | # print('Bytes: ' + str(sys.getsizeof(text))) 25 | return super(MQBot, self).send_message(*args, **kwargs) 26 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | attrdict>=2.0.0 2 | certifi>=2018.4.16 3 | chardet>=3.0.4 4 | click>=6.7 5 | cytoolz>=0.9.0.1 6 | eth-abi>=1.1.1 7 | eth-account>=0.2.3 8 | eth-hash>=0.1.4 9 | eth-keyfile>=0.5.1 10 | eth-keys>=0.2.0b3 11 | eth-rlp>=0.1.2 12 | eth-utils>=1.0.3 13 | Flask>=1.0.2 14 | Flask-BasicAuth>=0.2.0 15 | future>=0.16.0 16 | hexbytes>=0.1.0 17 | idna>=2.7 18 | itsdangerous>=0.24 19 | Jinja2>=2.10 20 | lru-dict>=1.1.6 21 | MarkupSafe>=1.0 22 | parsimonious>=0.8.0 23 | peewee>=3.6.4 24 | pycryptodome>=3.6.4 25 | python-telegram-bot>=10.1.0 26 | requests>=2.19.1 27 | rlp>=1.0.1 28 | six>=1.11.0 29 | toolz>=0.9.0 30 | ujson>=1.35 31 | urllib3>=1.23 32 | web3>=4.4.1 33 | websockets>=5.0.1 34 | Werkzeug>=0.14.1 35 | WTForms>=2.2.1 36 | XlsxWriter>=1.0.5 -------------------------------------------------------------------------------- /static/css/admin.css: -------------------------------------------------------------------------------- 1 | ul { 2 | list-style-type: none; 3 | margin: 0; 4 | padding: 0; 5 | overflow: hidden; 6 | background-color: #333; 7 | } 8 | 9 | li { 10 | float: left; 11 | } 12 | 13 | li a { 14 | display: block; 15 | color: white; 16 | text-align: center; 17 | padding: 14px 16px; 18 | text-decoration: none; 19 | } 20 | 21 | li a:hover:not(.active) { 22 | background-color: #111; 23 | } 24 | 25 | .active { 26 | background-color: #4CAF50; 27 | } 28 | -------------------------------------------------------------------------------- /tariffs.py: -------------------------------------------------------------------------------- 1 | 2 | _MINIMAL_ETH_DEPOSIT = 0.0025 3 | _MINIMAL_ETH_WITHDRAW = 0.0125 4 | 5 | 6 | _TARIFF_REWARDS = { 7 | 0: 0, 8 | 1: 0.006, 9 | 2: 0.008, 10 | 3: 0.01 11 | } 12 | 13 | _PRECISION = '.0001' 14 | 15 | _TARIFF_DEPOSIT = { 16 | 0: 0, 17 | 1: 0.0025, 18 | 2: 1, 19 | 3: 3, 20 | } 21 | 22 | _REFERRAL_LEVELS_INCOME = [0.1, 0.05, 0.01] 23 | 24 | GOLD_TARIFF_INDEX = 3 25 | SILVER_TARIFF_INDEX = 2 26 | BRONZE_TARIFF_INDEX = 1 27 | NO_TARIFF_INDEX = 0 28 | 29 | 30 | def tariff_reward(tariff_id): 31 | return _TARIFF_REWARDS[tariff_id] 32 | 33 | 34 | def tariff_deposit(tariff_id): 35 | return _TARIFF_DEPOSIT[tariff_id] 36 | 37 | 38 | def get_referral_levels_percentage(): 39 | return _REFERRAL_LEVELS_INCOME 40 | 41 | 42 | def eth_minimal_deposit(): 43 | return _MINIMAL_ETH_DEPOSIT 44 | 45 | 46 | def minimal_eth_withdraw(): 47 | return _MINIMAL_ETH_WITHDRAW 48 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |6 | 7 | From 8 | 9 | | 10 |11 | 12 | Amount 13 | 14 | | 15 |16 | 17 | Date and time 18 | 19 | | 20 |||
{{ top_up.from_wallet }} | 24 |{{ top_up.amount }} | 25 |{{ top_up.created_at.strftime('%Y %B %d %H:%M') }} | 26 |27 | 28 | 29 | 30 | | 31 |32 | 33 | | 34 |