commit
a154488711
7 changed files with 314 additions and 157 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
env/
|
||||
*.ini
|
||||
__pycache__/
|
26
README.md
26
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.
|
||||
|
|
145
main.py
145
main.py
|
@ -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()
|
23
requirements.txt
Normal file
23
requirements.txt
Normal file
|
@ -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
|
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
127
src/cli.py
Normal file
127
src/cli.py
Normal file
|
@ -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()
|
147
src/main.py
Normal file
147
src/main.py
Normal file
|
@ -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()
|
Loading…
Reference in a new issue