diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..30ddd3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +env/ +*.ini +__pycache__/ \ No newline at end of file diff --git a/README.md b/README.md index 5c89068..e24df00 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,29 @@ -![](https://i.imgur.com/dKA3udT.png) +![](https://i.imgur.com/oCob3wQ.gif) ### About The bot is specially designed for [SteamGifts.com](https://www.steamgifts.com/) -## TODO -* Add GUI - ### Features - Automatically enters giveaways. - Undetectable. +- Сonvenient user interface. +- Сonfigurable. - Sleeps to restock the points. - Can run 24/7. -### Instructions +### How to run 1. Download the latest version: https://github.com/stilManiac/steamgifts-bot/releases 2. Sign in on [SteamGifts.com](https://www.steamgifts.com/) by Steam. -3. Find `PHPSESSID` cookie in your browser and write it in `cookie.txt` file. -4. Make sure your cookie is right. -5. Start the bot. +3. Find `PHPSESSID` cookie in your browser. +4. Start the bot and follow instructions. -### Commands -`!sleep [arg]` - change a sleeping interval in sec. Default and recommended is 900 sec (15 min.). -`!page` - set a final websites page bot reaches +### Run from sources +```bash +python -m venv env +source env/bin/activate +pip install -r requirements.txt +python src/cli.py +``` ### Help -Please leave your feedback and bugs in `Issues` page. Thank you! +Please leave your feedback and bugs in `Issues` page. diff --git a/main.py b/main.py deleted file mode 100644 index 307159e..0000000 --- a/main.py +++ /dev/null @@ -1,145 +0,0 @@ -import sys -import requests -from bs4 import BeautifulSoup -import json -import time -import threading -from requests import RequestException - -try: - file = open('cookie.txt', 'r') - cook = file.readline() - if len(cook) == 0: - print('There is no cookie in cookie.txt file') - time.sleep(30) - sys.exit(0) -except FileNotFoundError: - print('Cant find cookie.txt file') - time.sleep(30) - sys.exit(0) - -timeout = 900 -pages = 1 - - -def get_soup_from_page(url): - global cookies - - cookies = {'PHPSESSID': cook} - r = requests.get(url, cookies=cookies) - soup = BeautifulSoup(r.text, 'html.parser') - - return soup - -def get_page(): - global xsrf_token, points - - try: - soup = get_soup_from_page('https://www.steamgifts.com') - - xsrf_token = soup.find('input', {'name': 'xsrf_token'})['value'] - points = soup.find('span', {'class': 'nav__points'}).text # storage points - except RequestException: - print('Cant connect to the site') - print('Waiting 2 minutes and reconnect...') - time.sleep(120) - get_page() - except TypeError: - print('Cant recognize your cookie value.') - time.sleep(30) - sys.exit(0) - - -# get codes of the games -def get_games(): - global game_name - global pages - - n = 1 - while n <= pages: - print('Proccessing games from %d page.' % n) - - soup = get_soup_from_page('https://www.steamgifts.com/giveaways/search?page=' + str(n)) - - try: - gifts_list = soup.find_all(lambda tag: tag.name == 'div' and tag.get('class') == ['giveaway__row-inner-wrap']) - - for item in gifts_list: - if int(points) == 0: - print('> Sleeping to get 6 points') - time.sleep(timeout) - get_games() - break - - game_cost = item.find_all('span', {'class': 'giveaway__heading__thin'}) - - last_div = None - for last_div in game_cost: - pass - if last_div: - game_cost = last_div.getText().replace('(', '').replace(')', '').replace('P', '') - - game_name = item.find('a', {'class': 'giveaway__heading__name'}).text.encode('utf-8') - - if int(points) - int(game_cost) < 0: - print('Not enough points to enter: ' + game_name) - continue - elif int(points) - int(game_cost) > 0: - entry_gift(item.find('a', {'class': 'giveaway__heading__name'})['href'].split('/')[2]) - - n = n+1 - except AttributeError as e: - break - - print('List of games is ended. Waiting 2 min to update...') - time.sleep(120) - get_page() - get_games() - - -def entry_gift(code): - payload = {'xsrf_token': xsrf_token, 'do': 'entry_insert', 'code': code} - entry = requests.post('https://www.steamgifts.com/ajax.php', data=payload, cookies=cookies) - json_data = json.loads(entry.text) - - get_page() - # print(json_data) - - # updating points after entered a giveaway - if json_data['type'] == 'success': - print('> Bot has entered giveaway: ' + game_name) - time.sleep(5) - - -def inputs_data(): - global timeout - global pages - - while 1: - cmd = input().split() - if cmd == '!help': - print(' [ HELP BOX ]') - print('!sleep [arg]\t- change a sleeping interval in sec (default is 900 sec)') - print('!page [arg]\t- set a final page') - if len(cmd) == 1: - print('!help to see available commands') - if cmd[0] == '!sleep': - try: - timeout = int(cmd[1]) - print('Successfuly set interval to ' + (timeout)) - except ValueError: - print('Expect a digit!') - - elif cmd[0] == '!page': - try: - pages = int(cmd[1]) - print('Successfuly set final page to ' + str(pages)) - except ValueError: - print('Expect a digit!') - - -if __name__ == '__main__': - thread = threading.Thread(target=inputs_data) - thread.start() - get_page() - get_games() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..53dd215 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,23 @@ +astroid==2.4.2 +beautifulsoup4==4.9.1 +certifi==2020.6.20 +chardet==3.0.4 +idna==2.9 +isort==4.3.21 +lazy-object-proxy==1.4.3 +mccabe==0.6.1 +prompt-toolkit==1.0.14 +pyconfigstore3==1.0.1 +pyfiglet==0.8.post1 +Pygments==2.6.1 +PyInquirer==1.0.3 +pylint==2.5.3 +regex==2020.6.8 +requests==2.24.0 +six==1.15.0 +soupsieve==2.0.1 +termcolor==1.1.0 +toml==0.10.1 +urllib3==1.25.9 +wcwidth==0.2.5 +wrapt==1.12.1 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cli.py b/src/cli.py new file mode 100644 index 0000000..bbd7f3d --- /dev/null +++ b/src/cli.py @@ -0,0 +1,127 @@ +import six +import configparser +import re + +from pyfiglet import figlet_format +from pyconfigstore import ConfigStore +from PyInquirer import (Token, ValidationError, Validator, print_json, prompt, + style_from_dict) +from prompt_toolkit import document + +try: + import colorama + colorama.init() +except ImportError: + colorama = None + +try: + from termcolor import colored +except ImportError: + colored = None + +config = configparser.ConfigParser() + +style = style_from_dict({ + Token.QuestionMark: '#fac731 bold', + Token.Answer: '#4688f1 bold', + Token.Selected: '#0abf5b', # default + Token.Pointer: '#673ab7 bold', +}) + +def log(string, color, font="slant", figlet=False): + if colored: + if not figlet: + six.print_(colored(string, color)) + else: + six.print_(colored(figlet_format( + string, font=font), color)) + else: + six.print_(string) + + +class PointValidator(Validator): + def validate(self, document: document.Document): + value = document.text + try: + value = int(value) + except Exception: + raise ValidationError(message = 'Value should be greater than 0', cursor_position = len(document.text)) + + if value <= 0: + raise ValidationError(message = 'Value should be greater than 0', cursor_position = len(document.text)) + return True + + +def ask(type, name, message, validate=None, choices=[]): + questions = [ + { + 'type': type, + 'name': name, + 'message': message, + 'validate': validate, + }, + ] + if choices: + questions[0].update({ + 'choices': choices, + }) + answers = prompt(questions, style=style) + return answers + + +def run(): + from main import SteamGifts as SG + + def askCookie(): + cookie = ask(type='input', + name='cookie', + message='Enter PHPSESSID cookie (Only needed to provide once):') + config['DEFAULT']['cookie'] = cookie['cookie'] + + with open('config.ini', 'w') as configfile: + config.write(configfile) + return cookie['cookie'] + + log("SteamGifts Bot", color="blue", figlet=True) + log("Welcome to SteamGifts Bot!", "green") + log("Created by: github.com/stilManiac", "white") + + config.read('config.ini') + if not config['DEFAULT'].get('cookie'): + cookie = askCookie() + else: + re_enter_cookie = ask(type='confirm', + name='reenter', + message='Do you want to enter new cookie?')['reenter'] + if re_enter_cookie: + cookie = askCookie() + else: + cookie = config['DEFAULT'].get('cookie') + + pinned_games = ask(type='confirm', + name='pinned', + message='Should bot enter pinned games?')['pinned'] + + gift_type = ask(type='list', + name='gift_type', + message='Select type:', + choices=[ + 'All', + 'Wishlist', + 'Recommended', + 'Copies', + 'DLC', + 'New' + ])['gift_type'] + + min_points = ask(type='input', + name='min_points', + message='Enter minimum points to start working (bot will try to enter giveaways until minimum value is reached):', + validate=PointValidator)['min_points'] + + s = SG(cookie, gift_type, pinned_games, min_points) + s.start() + + +if __name__ == '__main__': + run() \ No newline at end of file diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..4e0bcea --- /dev/null +++ b/src/main.py @@ -0,0 +1,147 @@ +import sys +import configparser +import requests +import json +import threading + +from requests.adapters import HTTPAdapter +from urllib3.util import Retry +from time import sleep +from random import randint +from requests import RequestException +from bs4 import BeautifulSoup + +from cli import log + + +class SteamGifts: + def __init__(self, cookie, gifts_type, pinned, min_points): + self.cookie = { + 'PHPSESSID': cookie + } + self.gifts_type = gifts_type + self.pinned = pinned + self.min_points = int(min_points) + + self.base = "https://www.steamgifts.com" + self.session = requests.Session() + + self.filter_url = { + 'All': "search?page=%d", + 'Wishlist': "search?page=%d&type=wishlist", + 'Recommended': "search?page=%d&type=recommended", + 'Copies': "search?page=%d©_min=2", + 'DLC': "search?page=%d&dlc=true", + 'New': "search?page=%d&type=new" + } + + def requests_retry_session( + self, + retries=5, + backoff_factor=0.3 + ): + session = self.session or requests.Session() + retry = Retry( + total=retries, + read=retries, + connect=retries, + backoff_factor=backoff_factor, + status_forcelist=(500, 502, 504), + ) + adapter = HTTPAdapter(max_retries=retry) + session.mount('http://', adapter) + session.mount('https://', adapter) + return session + + def get_soup_from_page(self, url): + r = self.requests_retry_session().get(url) + r = requests.get(url, cookies=self.cookie) + soup = BeautifulSoup(r.text, 'html.parser') + return soup + + def update_info(self): + soup = self.get_soup_from_page(self.base) + + try: + self.xsrf_token = soup.find('input', {'name': 'xsrf_token'})['value'] + self.points = int(soup.find('span', {'class': 'nav__points'}).text) # storage points + except TypeError: + log("⛔ Cookie is not valid.", "red") + sleep(10) + exit() + + def get_game_content(self, page=1): + n = page + while True: + txt = "⚙️ Retrieving games from %d page." % n + log(txt, "magenta") + + filtered_url = self.filter_url[self.gifts_type] % n + paginated_url = f"{self.base}/giveaways/{filtered_url}" + + soup = self.get_soup_from_page(paginated_url) + + game_list = soup.find_all('div', {'class': 'giveaway__row-inner-wrap'}) + + if not len(game_list): + log("⛔ Page is empty. Please, select another type.", "red") + sleep(10) + exit() + + for item in game_list: + if len(item.get('class', [])) == 2 and not self.pinned: + continue + + if self.points == 0 or self.points < self.min_points: + txt = f"🛋️ Sleeping to get 6 points. We have {self.points} points, but we need {self.min_points} to start." + log(txt, "yellow") + sleep(900) + self.start() + break + + game_cost = item.find_all('span', {'class': 'giveaway__heading__thin'})[-1] + + if game_cost: + game_cost = game_cost.getText().replace('(', '').replace(')', '').replace('P', '') + else: + continue + + game_name = item.find('a', {'class': 'giveaway__heading__name'}).text + + if self.points - int(game_cost) < 0: + txt = f"⛔ Not enough points to enter: {game_name}" + log(txt, "red") + continue + + elif self.points - int(game_cost) >= 0: + game_id = item.find('a', {'class': 'giveaway__heading__name'})['href'].split('/')[2] + res = self.entry_gift(game_id) + if res: + self.points -= int(game_cost) + txt = f"🎉 One more game! Has just entered {game_name}" + log(txt, "green") + sleep(randint(3, 7)) + + n = n+1 + + + log("🛋️ List of games is ended. Waiting 2 mins to update...", "yellow") + sleep(120) + self.start() + + def entry_gift(self, game_id): + payload = {'xsrf_token': self.xsrf_token, 'do': 'entry_insert', 'code': game_id} + entry = requests.post('https://www.steamgifts.com/ajax.php', data=payload, cookies=self.cookie) + json_data = json.loads(entry.text) + + if json_data['type'] == 'success': + return True + + def start(self): + self.update_info() + + if self.points > 0: + txt = "🤖 Hoho! I am back! You have %d points. Lets hack." % self.points + log(txt, "blue") + + self.get_game_content()