Spaces:
Paused
Paused
| from aiohttp import ( | |
| ClientResponseError, | |
| ClientSession, | |
| ClientTimeout, | |
| BasicAuth | |
| ) | |
| from aiohttp_socks import ProxyConnector | |
| from base64 import urlsafe_b64decode | |
| from datetime import datetime | |
| from colorama import * | |
| import asyncio, time, json, pytz, re, os | |
| wib = pytz.timezone('Asia/Jakarta') | |
| class Interlink: | |
| def __init__(self) -> None: | |
| self.HEADERS = { | |
| "Accept-Encoding": "*/*", | |
| "User-Agent": "okhttp/4.12.0", | |
| "Accept-Encoding": "gzip" | |
| } | |
| self.BASE_API = "https://prod.interlinklabs.ai/api/v1" | |
| self.proxies = [] | |
| self.proxy_index = 0 | |
| self.account_proxies = {} | |
| self.access_tokens = {} | |
| def clear_terminal(self): | |
| os.system('cls' if os.name == 'nt' else 'clear') | |
| def log(self, message): | |
| print( | |
| f"{Fore.CYAN + Style.BRIGHT}[ {datetime.now().astimezone(wib).strftime('%x %X %Z')} ]{Style.RESET_ALL}" | |
| f"{Fore.WHITE + Style.BRIGHT} | {Style.RESET_ALL}{message}", | |
| flush=True | |
| ) | |
| def welcome(self): | |
| print( | |
| f""" | |
| {Fore.GREEN + Style.BRIGHT}Auto Claim {Fore.BLUE + Style.BRIGHT}Interlink - BOT | |
| """ | |
| f""" | |
| {Fore.GREEN + Style.BRIGHT}Rey? {Fore.YELLOW + Style.BRIGHT}<INI WATERMARK> | |
| """ | |
| ) | |
| def format_seconds(self, seconds): | |
| hours, remainder = divmod(seconds, 3600) | |
| minutes, seconds = divmod(remainder, 60) | |
| return f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}" | |
| def load_accounts(self): | |
| filename = "tokens.json" | |
| env_tokens = os.getenv("TOKENS_JSON") | |
| if env_tokens: | |
| try: | |
| data = json.loads(env_tokens) | |
| if isinstance(data, list): | |
| self.log( | |
| f"{Fore.GREEN + Style.BRIGHT}Loaded accounts from environment variable TOKENS_JSON.{Style.RESET_ALL}" | |
| ) | |
| return data | |
| self.log( | |
| f"{Fore.RED + Style.BRIGHT}TOKENS_JSON is not a JSON array. No accounts loaded.{Style.RESET_ALL}" | |
| ) | |
| return [] | |
| except json.JSONDecodeError as exc: | |
| self.log( | |
| f"{Fore.RED + Style.BRIGHT}Failed to parse TOKENS_JSON: {exc}.{Style.RESET_ALL}" | |
| ) | |
| return [] | |
| try: | |
| if not os.path.exists(filename): | |
| self.log(f"{Fore.RED}File {filename} Not Found.{Style.RESET_ALL}") | |
| return [] | |
| with open(filename, 'r') as file: | |
| data = json.load(file) | |
| if isinstance(data, list): | |
| return data | |
| self.log( | |
| f"{Fore.RED + Style.BRIGHT}{filename} does not contain a JSON array. No accounts loaded.{Style.RESET_ALL}" | |
| ) | |
| return [] | |
| except json.JSONDecodeError as exc: | |
| self.log( | |
| f"{Fore.RED + Style.BRIGHT}Failed to parse {filename}: {exc}.{Style.RESET_ALL}" | |
| ) | |
| return [] | |
| async def load_proxies(self): | |
| filename = "proxy.txt" | |
| try: | |
| env_proxies = os.getenv("PROXIES") | |
| if env_proxies: | |
| self.proxies = self.parse_env_list(env_proxies) | |
| source = "environment variable PROXIES" | |
| else: | |
| if not os.path.exists(filename): | |
| self.log(f"{Fore.RED + Style.BRIGHT}File {filename} Not Found.{Style.RESET_ALL}") | |
| return | |
| with open(filename, 'r') as f: | |
| self.proxies = [line.strip() for line in f.read().splitlines() if line.strip()] | |
| source = filename | |
| if not self.proxies: | |
| self.log(f"{Fore.RED + Style.BRIGHT}No Proxies Found.{Style.RESET_ALL}") | |
| return | |
| self.log( | |
| f"{Fore.GREEN + Style.BRIGHT}Proxies Total : {Style.RESET_ALL}" | |
| f"{Fore.WHITE + Style.BRIGHT}{len(self.proxies)}{Style.RESET_ALL}" | |
| f"{Fore.GREEN + Style.BRIGHT} (from {source}){Style.RESET_ALL}" | |
| ) | |
| except Exception as e: | |
| self.log(f"{Fore.RED + Style.BRIGHT}Failed To Load Proxies: {e}{Style.RESET_ALL}") | |
| self.proxies = [] | |
| def check_proxy_schemes(self, proxies): | |
| schemes = ["http://", "https://", "socks4://", "socks5://"] | |
| if any(proxies.startswith(scheme) for scheme in schemes): | |
| return proxies | |
| return f"http://{proxies}" | |
| def get_next_proxy_for_account(self, account): | |
| if account not in self.account_proxies: | |
| if not self.proxies: | |
| return None | |
| proxy = self.check_proxy_schemes(self.proxies[self.proxy_index]) | |
| self.account_proxies[account] = proxy | |
| self.proxy_index = (self.proxy_index + 1) % len(self.proxies) | |
| return self.account_proxies[account] | |
| def rotate_proxy_for_account(self, account): | |
| if not self.proxies: | |
| return None | |
| proxy = self.check_proxy_schemes(self.proxies[self.proxy_index]) | |
| self.account_proxies[account] = proxy | |
| self.proxy_index = (self.proxy_index + 1) % len(self.proxies) | |
| return proxy | |
| def build_proxy_config(self, proxy=None): | |
| if not proxy: | |
| return None, None, None | |
| if proxy.startswith("socks"): | |
| connector = ProxyConnector.from_url(proxy) | |
| return connector, None, None | |
| elif proxy.startswith("http"): | |
| match = re.match(r"http://(.*?):(.*?)@(.*)", proxy) | |
| if match: | |
| username, password, host_port = match.groups() | |
| clean_url = f"http://{host_port}" | |
| auth = BasicAuth(username, password) | |
| return None, clean_url, auth | |
| else: | |
| return None, proxy, None | |
| raise Exception("Unsupported Proxy Type.") | |
| def decode_token(self, token: str): | |
| try: | |
| header, payload, signature = token.split(".") | |
| decoded_payload = urlsafe_b64decode(payload + "==").decode("utf-8") | |
| parsed_payload = json.loads(decoded_payload) | |
| exp_time = parsed_payload["exp"] | |
| return exp_time | |
| except Exception as e: | |
| return None | |
| def mask_account(self, account): | |
| if "@" in account: | |
| local, domain = account.split('@', 1) | |
| mask_account = local[:3] + '*' * 3 + local[-3:] | |
| return f"{mask_account}@{domain}" | |
| def parse_env_list(self, raw: str): | |
| items = [] | |
| if not raw: | |
| return items | |
| normalized = raw.replace("\r\n", "\n") | |
| for line in normalized.split("\n"): | |
| for part in line.split(","): | |
| candidate = part.strip() | |
| if candidate: | |
| items.append(candidate) | |
| return items | |
| def get_env_bool(self, name: str, default=None): | |
| value = os.getenv(name) | |
| if value is None: | |
| return default | |
| value = value.strip().lower() | |
| if value in {"1", "true", "yes", "y"}: | |
| return True | |
| if value in {"0", "false", "no", "n"}: | |
| return False | |
| return default | |
| def print_question(self): | |
| while True: | |
| try: | |
| print(f"{Fore.WHITE + Style.BRIGHT}1. Run With Proxy{Style.RESET_ALL}") | |
| print(f"{Fore.WHITE + Style.BRIGHT}2. Run Without Proxy{Style.RESET_ALL}") | |
| proxy_choice = int(input(f"{Fore.BLUE + Style.BRIGHT}Choose [1/2] -> {Style.RESET_ALL}").strip()) | |
| if proxy_choice in [1, 2]: | |
| proxy_type = ( | |
| "With" if proxy_choice == 1 else | |
| "Without" | |
| ) | |
| print(f"{Fore.GREEN + Style.BRIGHT}Run {proxy_type} Proxy Selected.{Style.RESET_ALL}") | |
| break | |
| else: | |
| print(f"{Fore.RED + Style.BRIGHT}Please enter either 1 or 2.{Style.RESET_ALL}") | |
| except ValueError: | |
| print(f"{Fore.RED + Style.BRIGHT}Invalid input. Enter a number (1 or 2).{Style.RESET_ALL}") | |
| rotate_proxy = False | |
| if proxy_choice == 1: | |
| while True: | |
| rotate_proxy = input(f"{Fore.BLUE + Style.BRIGHT}Rotate Invalid Proxy? [y/n] -> {Style.RESET_ALL}").strip() | |
| if rotate_proxy in ["y", "n"]: | |
| rotate_proxy = rotate_proxy == "y" | |
| break | |
| else: | |
| print(f"{Fore.RED + Style.BRIGHT}Invalid input. Enter 'y' or 'n'.{Style.RESET_ALL}") | |
| return proxy_choice, rotate_proxy | |
| async def check_connection(self, proxy_url=None): | |
| connector, proxy, proxy_auth = self.build_proxy_config(proxy_url) | |
| try: | |
| async with ClientSession(connector=connector, timeout=ClientTimeout(total=10)) as session: | |
| async with session.get(url="https://api.ipify.org?format=json", proxy=proxy, proxy_auth=proxy_auth) as response: | |
| response.raise_for_status() | |
| return True | |
| except (Exception, ClientResponseError) as e: | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Status :{Style.RESET_ALL}" | |
| f"{Fore.RED+Style.BRIGHT} Connection Not 200 OK {Style.RESET_ALL}" | |
| f"{Fore.MAGENTA+Style.BRIGHT}-{Style.RESET_ALL}" | |
| f"{Fore.YELLOW+Style.BRIGHT} {str(e)} {Style.RESET_ALL}" | |
| ) | |
| return None | |
| async def token_balance(self, email: str, proxy_url=None, retries=5): | |
| url = f"{self.BASE_API}/token/get-token" | |
| headers = { | |
| **self.HEADERS, | |
| "Authorization": f"Bearer {self.access_tokens[email]}" | |
| } | |
| await asyncio.sleep(3) | |
| for attempt in range(retries): | |
| connector, proxy, proxy_auth = self.build_proxy_config(proxy_url) | |
| try: | |
| async with ClientSession(connector=connector, timeout=ClientTimeout(total=60)) as session: | |
| async with session.get(url=url, headers=headers, proxy=proxy, proxy_auth=proxy_auth, ssl=False) as response: | |
| response.raise_for_status() | |
| return await response.json() | |
| except (Exception, ClientResponseError) as e: | |
| if attempt < retries - 1: | |
| await asyncio.sleep(5) | |
| continue | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Balance:{Style.RESET_ALL}" | |
| f"{Fore.RED+Style.BRIGHT} GET Token Earned Failed {Style.RESET_ALL}" | |
| f"{Fore.MAGENTA+Style.BRIGHT}-{Style.RESET_ALL}" | |
| f"{Fore.YELLOW+Style.BRIGHT} {str(e)} {Style.RESET_ALL}" | |
| ) | |
| return None | |
| async def claimable_check(self, email: str, proxy_url=None, retries=5): | |
| url = f"{self.BASE_API}/token/check-is-claimable" | |
| headers = { | |
| **self.HEADERS, | |
| "Authorization": f"Bearer {self.access_tokens[email]}" | |
| } | |
| await asyncio.sleep(3) | |
| for attempt in range(retries): | |
| connector, proxy, proxy_auth = self.build_proxy_config(proxy_url) | |
| try: | |
| async with ClientSession(connector=connector, timeout=ClientTimeout(total=60)) as session: | |
| async with session.get(url=url, headers=headers, proxy=proxy, proxy_auth=proxy_auth, ssl=False) as response: | |
| response.raise_for_status() | |
| return await response.json() | |
| except (Exception, ClientResponseError) as e: | |
| if attempt < retries - 1: | |
| await asyncio.sleep(5) | |
| continue | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Mining :{Style.RESET_ALL}" | |
| f"{Fore.RED+Style.BRIGHT} GET Status Failed {Style.RESET_ALL}" | |
| f"{Fore.MAGENTA+Style.BRIGHT}-{Style.RESET_ALL}" | |
| f"{Fore.YELLOW+Style.BRIGHT} {str(e)} {Style.RESET_ALL}" | |
| ) | |
| return None | |
| async def claim_airdrop(self, email: str, proxy_url=None, retries=1): | |
| url = f"{self.BASE_API}/token/claim-airdrop" | |
| headers = { | |
| **self.HEADERS, | |
| "Authorization": f"Bearer {self.access_tokens[email]}", | |
| "Content-Length": "2", | |
| "Content-Type": "application/json" | |
| } | |
| await asyncio.sleep(3) | |
| for attempt in range(retries): | |
| connector, proxy, proxy_auth = self.build_proxy_config(proxy_url) | |
| try: | |
| async with ClientSession(connector=connector, timeout=ClientTimeout(total=60)) as session: | |
| async with session.post(url=url, headers=headers, json={}, proxy=proxy, proxy_auth=proxy_auth, ssl=False) as response: | |
| response.raise_for_status() | |
| return await response.json() | |
| except (Exception, ClientResponseError) as e: | |
| if attempt < retries - 1: | |
| await asyncio.sleep(5) | |
| continue | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Mining :{Style.RESET_ALL}" | |
| f"{Fore.RED+Style.BRIGHT} Not Claimed {Style.RESET_ALL}" | |
| f"{Fore.MAGENTA+Style.BRIGHT}-{Style.RESET_ALL}" | |
| f"{Fore.YELLOW+Style.BRIGHT} {str(e)} {Style.RESET_ALL}" | |
| ) | |
| return None | |
| async def process_check_connection(self, email: str, use_proxy: bool, rotate_proxy: bool): | |
| while True: | |
| proxy = self.get_next_proxy_for_account(email) if use_proxy else None | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Proxy :{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} {proxy if proxy else 'No Proxy'} {Style.RESET_ALL}" | |
| ) | |
| is_valid = await self.check_connection(proxy) | |
| if is_valid: return True | |
| if rotate_proxy: | |
| proxy = self.rotate_proxy_for_account(email) | |
| await asyncio.sleep(1) | |
| continue | |
| return False | |
| async def process_accounts(self, email: str, use_proxy: bool, rotate_proxy: bool): | |
| is_valid = await self.process_check_connection(email, use_proxy, rotate_proxy) | |
| if not is_valid: return | |
| proxy = self.get_next_proxy_for_account(email) if use_proxy else None | |
| balance = await self.token_balance(email, proxy) | |
| if balance: | |
| token_balance = balance.get("data", {}).get("interlinkTokenAmount", 0) | |
| silver_balance = balance.get("data", {}).get("interlinkSilverTokenAmount", 0) | |
| gold_balance = balance.get("data", {}).get("interlinkGoldTokenAmount", 0) | |
| diamond_balance = balance.get("data", {}).get("interlinkDiamondTokenAmount", 0) | |
| self.log(f"{Fore.CYAN+Style.BRIGHT}Balance:{Style.RESET_ALL}") | |
| self.log( | |
| f"{Fore.MAGENTA+Style.BRIGHT} ● {Style.RESET_ALL}" | |
| f"{Fore.BLUE+Style.BRIGHT}Interlink:{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} {token_balance} {Style.RESET_ALL}" | |
| ) | |
| self.log( | |
| f"{Fore.MAGENTA+Style.BRIGHT} ● {Style.RESET_ALL}" | |
| f"{Fore.BLUE+Style.BRIGHT}Silver :{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} {silver_balance} {Style.RESET_ALL}" | |
| ) | |
| self.log( | |
| f"{Fore.MAGENTA+Style.BRIGHT} ● {Style.RESET_ALL}" | |
| f"{Fore.BLUE+Style.BRIGHT}Gold :{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} {gold_balance} {Style.RESET_ALL}" | |
| ) | |
| self.log( | |
| f"{Fore.MAGENTA+Style.BRIGHT} ● {Style.RESET_ALL}" | |
| f"{Fore.BLUE+Style.BRIGHT}Diamond :{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} {diamond_balance} {Style.RESET_ALL}" | |
| ) | |
| claimable = await self.claimable_check(email, proxy) | |
| if claimable: | |
| is_claimable = claimable.get("data", {}).get("isClaimable", False) | |
| if is_claimable: | |
| claim = await self.claim_airdrop(email, proxy) | |
| if claim: | |
| reward = claim.get("data") or "N/A" | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Mining :{Style.RESET_ALL}" | |
| f"{Fore.GREEN+Style.BRIGHT} Claimed Successfully {Style.RESET_ALL}" | |
| f"{Fore.MAGENTA+Style.BRIGHT}-{Style.RESET_ALL}" | |
| f"{Fore.CYAN+Style.BRIGHT} Reward: {Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT}{reward}{Style.RESET_ALL}" | |
| ) | |
| else: | |
| next_frame_ts = claimable.get("data", {}).get("nextFrame", 0) / 1000 | |
| next_frame_wib = datetime.fromtimestamp(next_frame_ts).astimezone(wib).strftime('%x %X %Z') | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Mining :{Style.RESET_ALL}" | |
| f"{Fore.YELLOW+Style.BRIGHT} Already Claimed {Style.RESET_ALL}" | |
| f"{Fore.MAGENTA+Style.BRIGHT}-{Style.RESET_ALL}" | |
| f"{Fore.CYAN+Style.BRIGHT} Next Claim at: {Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT}{next_frame_wib}{Style.RESET_ALL}" | |
| ) | |
| async def main(self): | |
| try: | |
| accounts = self.load_accounts() | |
| if not accounts: | |
| self.log(f"{Fore.RED}No Accounts Loaded.{Style.RESET_ALL}") | |
| return | |
| env_use_proxy = self.get_env_bool("USE_PROXY") | |
| env_rotate_proxy = self.get_env_bool("ROTATE_PROXY") | |
| if env_use_proxy is None: | |
| proxy_choice, rotate_proxy = self.print_question() | |
| use_proxy = proxy_choice == 1 | |
| else: | |
| use_proxy = bool(env_use_proxy) | |
| rotate_proxy = use_proxy and ( | |
| env_rotate_proxy if env_rotate_proxy is not None else True | |
| ) | |
| mode_text = "With Proxy" if use_proxy else "Without Proxy" | |
| source_text = "environment variables" | |
| self.log( | |
| f"{Fore.GREEN + Style.BRIGHT}Run {mode_text} Selected via {source_text}.{Style.RESET_ALL}" | |
| ) | |
| if not use_proxy: | |
| rotate_proxy = False | |
| elif rotate_proxy: | |
| self.log( | |
| f"{Fore.GREEN + Style.BRIGHT}Invalid proxy auto-rotation enabled.{Style.RESET_ALL}" | |
| ) | |
| else: | |
| self.log( | |
| f"{Fore.YELLOW + Style.BRIGHT}Invalid proxy auto-rotation disabled.{Style.RESET_ALL}" | |
| ) | |
| while True: | |
| self.clear_terminal() | |
| self.welcome() | |
| self.log( | |
| f"{Fore.GREEN + Style.BRIGHT}Account's Total: {Style.RESET_ALL}" | |
| f"{Fore.WHITE + Style.BRIGHT}{len(accounts)}{Style.RESET_ALL}" | |
| ) | |
| if use_proxy: | |
| await self.load_proxies() | |
| separator = "=" * 27 | |
| for idx, account in enumerate(accounts, start=1): | |
| if account: | |
| email = account["email"] | |
| token = account["token"] | |
| self.log( | |
| f"{Fore.CYAN + Style.BRIGHT}{separator}[{Style.RESET_ALL}" | |
| f"{Fore.WHITE + Style.BRIGHT} {idx} {Style.RESET_ALL}" | |
| f"{Fore.CYAN + Style.BRIGHT}Of{Style.RESET_ALL}" | |
| f"{Fore.WHITE + Style.BRIGHT} {len(accounts)} {Style.RESET_ALL}" | |
| f"{Fore.CYAN + Style.BRIGHT}]{separator}{Style.RESET_ALL}" | |
| ) | |
| if not "@" in email or not token: | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Status :{Style.RESET_ALL}" | |
| f"{Fore.RED+Style.BRIGHT} Invalid Account Data {Style.RESET_ALL}" | |
| ) | |
| continue | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Account:{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} {self.mask_account(email)} {Style.RESET_ALL}" | |
| ) | |
| exp_time = self.decode_token(token) | |
| if not exp_time: | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Status :{Style.RESET_ALL}" | |
| f"{Fore.RED+Style.BRIGHT} Invalid Token {Style.RESET_ALL}" | |
| ) | |
| continue | |
| if int(time.time()) > exp_time: | |
| self.log( | |
| f"{Fore.CYAN+Style.BRIGHT}Status :{Style.RESET_ALL}" | |
| f"{Fore.RED+Style.BRIGHT} Token Already Expired {Style.RESET_ALL}" | |
| ) | |
| continue | |
| self.access_tokens[email] = token | |
| await self.process_accounts(email, use_proxy, rotate_proxy) | |
| await asyncio.sleep(3) | |
| self.log(f"{Fore.CYAN + Style.BRIGHT}={Style.RESET_ALL}"*65) | |
| seconds = 4 * 60 * 60 | |
| while seconds > 0: | |
| formatted_time = self.format_seconds(seconds) | |
| print( | |
| f"{Fore.CYAN+Style.BRIGHT}[ Wait for{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} {formatted_time} {Style.RESET_ALL}" | |
| f"{Fore.CYAN+Style.BRIGHT}... ]{Style.RESET_ALL}" | |
| f"{Fore.WHITE+Style.BRIGHT} | {Style.RESET_ALL}" | |
| f"{Fore.BLUE+Style.BRIGHT}All Accounts Have Been Processed...{Style.RESET_ALL}", | |
| end="\r" | |
| ) | |
| await asyncio.sleep(1) | |
| seconds -= 1 | |
| except Exception as e: | |
| self.log(f"{Fore.RED+Style.BRIGHT}Error: {e}{Style.RESET_ALL}") | |
| raise e | |
| if __name__ == "__main__": | |
| try: | |
| bot = Interlink() | |
| asyncio.run(bot.main()) | |
| except KeyboardInterrupt: | |
| print( | |
| f"{Fore.CYAN + Style.BRIGHT}[ {datetime.now().astimezone(wib).strftime('%x %X %Z')} ]{Style.RESET_ALL}" | |
| f"{Fore.WHITE + Style.BRIGHT} | {Style.RESET_ALL}" | |
| f"{Fore.RED + Style.BRIGHT}[ EXIT ] Interlink - BOT{Style.RESET_ALL} " | |
| ) |