├── utils ├── core │ ├── __init__.py │ ├── file_manager.py │ ├── logger.py │ └── telegram.py ├── starter.py └── beer.py ├── requirements.txt ├── data └── config.py ├── main.py └── README.md /utils/core/__init__.py: -------------------------------------------------------------------------------- 1 | from .logger import logger 2 | from .file_manager import get_all_lines, load_from_json, save_to_json, save_list_to_file 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyrogram==2.0.106 2 | tgcrypto==1.2.5 3 | loguru==0.7.2 4 | aiohttp==3.9.5 5 | fake-useragent==1.5.1 6 | pandas==2.2.2 7 | aiohttp_socks==0.8.4 -------------------------------------------------------------------------------- /data/config.py: -------------------------------------------------------------------------------- 1 | # api id, hash 2 | API_ID = 1488 3 | API_HASH = 'abcde1488' 4 | 5 | # pints for requests 6 | PINTS = [1.2, 1.3] 7 | 8 | DELAYS = { 9 | 'ACCOUNT': [5, 15], # delay between connections to accounts (the more accounts, the longer the delay) 10 | 'POUR': [0.9, 1], # delay between pour beers 11 | } 12 | 13 | PROXY_TYPES = { 14 | "TG": "socks5", # proxy type for tg client. "socks4", "socks5" and "http" are supported 15 | "REQUESTS": "socks5" # proxy type for requests. "http" for https and http proxys, "socks5" for socks5 proxy. 16 | } 17 | 18 | # session folder (do not change) 19 | WORKDIR = "sessions/" 20 | 21 | # timeout in seconds for checking accounts on valid 22 | TIMEOUT = 30 23 | -------------------------------------------------------------------------------- /utils/core/file_manager.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def get_all_lines(filepath: str): 5 | with open(filepath, 'r') as file: 6 | lines = file.readlines() 7 | 8 | if not lines: 9 | return [] 10 | 11 | return [line.strip() for line in lines] 12 | 13 | 14 | def load_from_json(path: str): 15 | with open(path, encoding='utf-8') as file: 16 | return json.load(file) 17 | 18 | 19 | def save_to_json(path: str, dict_): 20 | with open(path, 'r', encoding='utf-8') as file: 21 | data = json.load(file) 22 | 23 | data.append(dict_) 24 | with open(path, 'w', encoding='utf-8') as file: 25 | json.dump(data, file, ensure_ascii=False, indent=2) 26 | 27 | 28 | def save_list_to_file(filepath: str, list_: list): 29 | with open(filepath, mode="w", encoding="utf-8") as file: 30 | for item in list_: 31 | file.write(f"{item['session_name']}.session\n") 32 | -------------------------------------------------------------------------------- /utils/core/logger.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | from loguru import logger 4 | 5 | 6 | def formatter(record, format_string): 7 | return format_string + record["extra"].get("end", "\n") + "{exception}" 8 | 9 | 10 | def clean_brackets(raw_str): 11 | return re.sub(r'<.*?>', '', raw_str) 12 | 13 | 14 | def logging_setup(): 15 | format_info = "{time:HH:mm:ss.SS} | {level} | {message}" 16 | format_error = "{time:HH:mm:ss.SS} | {level} | {name}:{function}:{line} | {message}" 17 | logger_path = r"logs/out.log" 18 | 19 | logger.remove() 20 | 21 | logger.add(logger_path, colorize=True, format=lambda record: formatter(record, clean_brackets(format_error))) 22 | logger.add(sys.stdout, colorize=True, format=lambda record: formatter(record, format_info), level="INFO") 23 | 24 | 25 | logging_setup() 26 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from utils.core.telegram import Accounts 2 | from utils.starter import start, stats 3 | import asyncio 4 | import os 5 | 6 | 7 | async def main(): 8 | print("Soft's author: https://t.me/ApeCryptor\n") 9 | action = int(input("Select action:\n1. Start soft\n2. Get statistics\n3. Create sessions\n\n> ")) 10 | 11 | if not os.path.exists('sessions'): os.mkdir('sessions') 12 | if not os.path.exists('sessions/accounts.json'): 13 | with open("sessions/accounts.json", 'w') as f: 14 | f.write("[]") 15 | 16 | if action == 3: 17 | await Accounts().create_sessions() 18 | 19 | if action == 2: 20 | await stats() 21 | 22 | if action == 1: 23 | accounts = await Accounts().get_accounts() 24 | 25 | tasks = [] 26 | for thread, account in enumerate(accounts): 27 | session_name, phone_number, proxy = account.values() 28 | tasks.append(asyncio.create_task(start(session_name=session_name, phone_number=phone_number, thread=thread, proxy=proxy))) 29 | 30 | await asyncio.gather(*tasks) 31 | 32 | 33 | if __name__ == '__main__': 34 | asyncio.get_event_loop().run_until_complete(main()) 35 | -------------------------------------------------------------------------------- /utils/starter.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | from utils.beer import BeerTap 4 | from utils.core import logger 5 | import datetime 6 | import pandas as pd 7 | from utils.core.telegram import Accounts 8 | from aiohttp.client_exceptions import ContentTypeError 9 | import asyncio 10 | from data import config 11 | 12 | 13 | async def start(thread: int, session_name: str, phone_number: str, proxy: [str, None]): 14 | beer = BeerTap(session_name=session_name, phone_number=phone_number, thread=thread, proxy=proxy) 15 | account = session_name + '.session' 16 | 17 | if await beer.login(): 18 | logger.success(f"Thread {thread} | {account} | Login") 19 | while True: 20 | try: 21 | await asyncio.sleep(random.uniform(*config.DELAYS['POUR'])) 22 | 23 | new_balance = await beer.battery_taps(random.uniform(*config.PINTS)) 24 | logger.success(f"Thread {thread} | {account} | Poured beer! Balance: {new_balance}") 25 | 26 | except ContentTypeError as e: 27 | logger.error(f"Thread {thread} | {account} | Error: {e}") 28 | await asyncio.sleep(12) 29 | 30 | except Exception as e: 31 | logger.error(f"Thread {thread} | {account} | Error: {e}") 32 | await asyncio.sleep(5) 33 | 34 | 35 | 36 | await beer.logout() 37 | 38 | 39 | async def stats(): 40 | accounts = await Accounts().get_accounts() 41 | 42 | tasks = [] 43 | for thread, account in enumerate(accounts): 44 | session_name, phone_number, proxy = account.values() 45 | tasks.append(asyncio.create_task(BeerTap(session_name=session_name, phone_number=phone_number, thread=thread, proxy=proxy).stats())) 46 | 47 | data = await asyncio.gather(*tasks) 48 | 49 | path = f"statistics/statistics_{datetime.datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.csv" 50 | columns = ['Phone number', 'Name', 'Balance', 'Leaderboard', 'Referrals reward', 'Referrals', 'Referral link', 'Proxy (login:password@ip:port)'] 51 | 52 | if not os.path.exists('statistics'): os.mkdir('statistics') 53 | df = pd.DataFrame(data, columns=columns) 54 | df.to_csv(path, index=False, encoding='utf-8-sig') 55 | 56 | logger.success(f"Saved statistics to {path}") 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BeerTap-bot 2 | clicker for [https://t.me/BeerCoinTap_Bot](https://t.me/BeerCoinTap_Bot/beertapapp?startapp=_6008239182) 3 | 4 | More crypto themes and softs in telegram: [ApeCryptor](https://t.me/+_xCNXumUNWJkYjAy "ApeCryptor") 🦧 5 | Additional soft information: https://t.me/ApeCryptorSoft/96 6 | 7 | ## Functionality 8 | | Functional | Supported | 9 | |----------------------------------------------------------------|:---------:| 10 | | Multithreading | ✅ | 11 | | Binding a proxy to a session | ✅ | 12 | | Auto-pou | ✅ | 13 | | Random sleep time between accounts; pours | ✅ | 14 | | Support pyrogram .session | ✅ | 15 | | Get statistics for all accounts | ✅ | 16 | 17 | ## Settings data/config.py 18 | | Setting | Description | 19 | |------------------------------|------------------------------------------------------------------------------------------------| 20 | | **API_ID / API_HASH** | Platform data from which to launch a Telegram session | 21 | | **DELAYS-ACCOUNT** | Delay between connections to accounts (the more accounts, the longer the delay) | 22 | | **DELAYS-POUR** | Delay between pour beers | 23 | | **PINTS** | pints for requests | 24 | | **PROXY_TYPES-TG** | Proxy type for telegram session | 25 | | **PROXY_TYPES-REQUESTS** | Proxy type for requests | 26 | | **WORKDIR** | directory with session | 27 | | **TIMEOUT** | timeout in seconds for checking accounts on valid | 28 | 29 | ## Requirements 30 | - Python 3.9 (you can install it [here](https://www.python.org/downloads/release/python-390/)) 31 | - Telegram API_ID and API_HASH (you can get them [here](https://my.telegram.org/auth)) 32 | 33 | 1. Install the required dependencies: 34 | ```bash 35 | pip install -r requirements.txt 36 | ``` 37 | 38 | ## Usage 39 | 1. Run the bot: 40 | ```bash 41 | python main.py 42 | ``` 43 | -------------------------------------------------------------------------------- /utils/beer.py: -------------------------------------------------------------------------------- 1 | import random 2 | from utils.core import logger 3 | from pyrogram import Client 4 | from pyrogram.raw.functions.messages import RequestAppWebView 5 | from pyrogram.raw.types import InputBotAppShortName 6 | import asyncio 7 | from urllib.parse import unquote, quote 8 | from data import config 9 | import aiohttp 10 | from fake_useragent import UserAgent 11 | from aiohttp_socks import ProxyConnector 12 | 13 | 14 | class BeerTap: 15 | def __init__(self, thread: int, session_name: str, phone_number: str, proxy: [str, None]): 16 | self.account = session_name + '.session' 17 | self.thread = thread 18 | self.tg_init_data = None 19 | self.proxy = f"{config.PROXY_TYPES['REQUESTS']}://{proxy}" if proxy is not None else None 20 | connector = ProxyConnector.from_url(self.proxy) if proxy else aiohttp.TCPConnector(verify_ssl=False) 21 | 22 | if proxy: 23 | proxy = { 24 | "scheme": config.PROXY_TYPES['TG'], 25 | "hostname": proxy.split(":")[1].split("@")[1], 26 | "port": int(proxy.split(":")[2]), 27 | "username": proxy.split(":")[0], 28 | "password": proxy.split(":")[1].split("@")[0] 29 | } 30 | 31 | self.client = Client( 32 | name=session_name, 33 | api_id=config.API_ID, 34 | api_hash=config.API_HASH, 35 | workdir=config.WORKDIR, 36 | proxy=proxy, 37 | lang_code='ru' 38 | ) 39 | 40 | headers = {'User-Agent': UserAgent(os='android').random} 41 | self.session = aiohttp.ClientSession(headers=headers, trust_env=True, connector=connector) 42 | 43 | async def stats(self): 44 | await asyncio.sleep(random.uniform(*config.DELAYS['ACCOUNT'])) 45 | await self.login() 46 | 47 | r = await (await self.session.get( 48 | f"https://beer-tg-prod.onrender.com/user?tgInitData={self.tg_init_data}&startParam=_6008239182")).json() 49 | balance = round(r.get('user').get('balance').get('lastBoonAmount'), 4) 50 | referrals_reward = round( 51 | r.get('user').get('ref').get('refState')[0]['earned'] + r.get('user').get('ref').get('refState')[1][ 52 | 'earned'], 4) 53 | referrals = r.get('user').get('ref').get('refState')[0]['amount'] 54 | referral_link = f"https://t.me/BeerCoinTap_Bot/beertapapp?startapp=_{r.get('user').get('_id')}" 55 | 56 | r = await (await self.session.get( 57 | f"https://beer-tg-prod.onrender.com/game/uiLeaderboard?tgInitData={self.tg_init_data}&startParam=_6008239182")).json() 58 | leaderboard = r.get("user").get('position') 59 | 60 | await self.logout() 61 | 62 | await self.client.connect() 63 | me = await self.client.get_me() 64 | phone_number, name = "'" + me.phone_number, f"{me.first_name} {me.last_name if me.last_name is not None else ''}" 65 | await self.client.disconnect() 66 | 67 | proxy = self.proxy.replace('http://', "") if self.proxy is not None else '-' 68 | return [phone_number, name, str(balance), str(leaderboard), str(referrals_reward), str(referrals), 69 | referral_link, proxy] 70 | 71 | async def battery_taps(self, liters): 72 | json_data = {"liters": liters} 73 | url = f'https://beer-tg-prod.onrender.com/game/batteryTaps?tgInitData={self.tg_init_data}' 74 | 75 | resp = await self.session.post(url, json=json_data) 76 | return (await resp.json()).get('balance').get('lastBoonAmount') 77 | 78 | async def logout(self): 79 | await self.session.close() 80 | 81 | async def login(self): 82 | await asyncio.sleep(random.uniform(*config.DELAYS['ACCOUNT'])) 83 | query = await self.get_tg_web_data() 84 | 85 | if query is None: 86 | logger.error(f"Thread {self.thread} | {self.account} | Session {self.account} invalid") 87 | await self.logout() 88 | return None 89 | 90 | self.tg_init_data = query 91 | 92 | await self.session.get( 93 | f'https://beer-tg-prod.onrender.com/user?tgInitData={self.tg_init_data}&startParam=_6008239182') 94 | return True 95 | 96 | async def get_tg_web_data(self): 97 | try: 98 | await self.client.connect() 99 | 100 | web_view = await self.client.invoke(RequestAppWebView( 101 | peer=await self.client.resolve_peer('BeerCoinTap_Bot'), 102 | app=InputBotAppShortName(bot_id=await self.client.resolve_peer('BeerCoinTap_Bot'), 103 | short_name="Beertapapp"), 104 | platform='android', 105 | write_allowed=True, 106 | start_param=f"_6008239182" 107 | )) 108 | await self.client.disconnect() 109 | auth_url = web_view.url 110 | 111 | return quote(string=unquote(string=auth_url.split('tgWebAppData=')[1].split('&tgWebAppVersion')[0])) 112 | 113 | except: 114 | return None -------------------------------------------------------------------------------- /utils/core/telegram.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import os 3 | from data import config 4 | from pyrogram import Client 5 | from utils.core import logger, load_from_json, save_list_to_file, save_to_json 6 | 7 | 8 | class Accounts: 9 | def __init__(self): 10 | self.workdir = config.WORKDIR 11 | self.api_id = config.API_ID 12 | self.api_hash = config.API_HASH 13 | 14 | @staticmethod 15 | def get_available_accounts(sessions: list): 16 | accounts_from_json = load_from_json('sessions/accounts.json') 17 | 18 | if not accounts_from_json: 19 | raise ValueError("Have not account's in sessions/accounts.json") 20 | 21 | available_accounts = [] 22 | for session in sessions: 23 | for saved_account in accounts_from_json: 24 | if saved_account['session_name'] == session: 25 | available_accounts.append(saved_account) 26 | break 27 | 28 | return available_accounts 29 | 30 | def pars_sessions(self): 31 | sessions = [] 32 | for file in os.listdir(self.workdir): 33 | if file.endswith(".session"): 34 | sessions.append(file.replace(".session", "")) 35 | 36 | logger.info(f"Searched sessions: {len(sessions)}.") 37 | return sessions 38 | 39 | async def check_valid_account(self, account: dict): 40 | session_name, phone_number, proxy = account.values() 41 | 42 | try: 43 | proxy_dict = { 44 | "scheme": config.PROXY_TYPES['TG'], 45 | "hostname": proxy.split(":")[1].split("@")[1], 46 | "port": int(proxy.split(":")[2]), 47 | "username": proxy.split(":")[0], 48 | "password": proxy.split(":")[1].split("@")[0] 49 | } if proxy else None 50 | 51 | client = Client(name=session_name, api_id=self.api_id, api_hash=self.api_hash, workdir=self.workdir, 52 | proxy=proxy_dict) 53 | 54 | connect = await asyncio.wait_for(client.connect(), timeout=config.TIMEOUT) 55 | if connect: 56 | await client.get_me() 57 | await client.disconnect() 58 | return account 59 | else: 60 | await client.disconnect() 61 | except: 62 | pass 63 | 64 | async def check_valid_accounts(self, accounts: list): 65 | logger.info(f"Checking accounts for valid...") 66 | 67 | tasks = [] 68 | for account in accounts: 69 | tasks.append(asyncio.create_task(self.check_valid_account(account))) 70 | 71 | v_accounts = await asyncio.gather(*tasks) 72 | 73 | valid_accounts = [account for account, is_valid in zip(accounts, v_accounts) if is_valid] 74 | invalid_accounts = [account for account, is_valid in zip(accounts, v_accounts) if not is_valid] 75 | logger.success(f"Valid accounts: {len(valid_accounts)}; Invalid: {len(invalid_accounts)}") 76 | 77 | return valid_accounts, invalid_accounts 78 | 79 | async def get_accounts(self): 80 | sessions = self.pars_sessions() 81 | available_accounts = self.get_available_accounts(sessions) 82 | 83 | if not available_accounts: 84 | raise ValueError("Have not available accounts!") 85 | else: 86 | logger.success(f"Search available accounts: {len(available_accounts)}.") 87 | 88 | valid_accounts, invalid_accounts = await self.check_valid_accounts(available_accounts) 89 | 90 | if invalid_accounts: 91 | save_list_to_file(f"{config.WORKDIR}invalid_accounts.txt", invalid_accounts) 92 | logger.info(f"Saved {len(invalid_accounts)} invalid account(s) in {config.WORKDIR}invalid_accounts.txt") 93 | 94 | if not valid_accounts: 95 | raise ValueError("Have not valid sessions") 96 | else: 97 | return valid_accounts 98 | 99 | async def create_sessions(self): 100 | while True: 101 | session_name = input('\nInput the name of the session (press Enter to exit): ') 102 | if not session_name: return 103 | 104 | proxy = input("Input the proxy in the format login:password@ip:port (press Enter to use without proxy): ") 105 | if proxy: 106 | client_proxy = { 107 | "scheme": config.PROXY_TYPES['TG'], 108 | "hostname": proxy.split(":")[1].split("@")[1], 109 | "port": int(proxy.split(":")[2]), 110 | "username": proxy.split(":")[0], 111 | "password": proxy.split(":")[1].split("@")[0] 112 | } 113 | else: 114 | client_proxy, proxy = None, None 115 | 116 | phone_number = (input("Input the phone number of the account: ")).replace(' ', '') 117 | phone_number = '+' + phone_number if not phone_number.startswith('+') else phone_number 118 | 119 | client = Client( 120 | api_id=self.api_id, 121 | api_hash=self.api_hash, 122 | name=session_name, 123 | workdir=self.workdir, 124 | phone_number=phone_number, 125 | proxy=client_proxy, 126 | lang_code='ru' 127 | ) 128 | 129 | async with client: 130 | me = await client.get_me() 131 | 132 | save_to_json(f'{config.WORKDIR}accounts.json', dict_={ 133 | "session_name": session_name, 134 | "phone_number": phone_number, 135 | "proxy": proxy 136 | }) 137 | logger.success(f'Added a account {me.username} ({me.first_name}) | {me.phone_number}') 138 | --------------------------------------------------------------------------------