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
|
### About
|
||||||
The bot is specially designed for [SteamGifts.com](https://www.steamgifts.com/)
|
The bot is specially designed for [SteamGifts.com](https://www.steamgifts.com/)
|
||||||
|
|
||||||
## TODO
|
|
||||||
* Add GUI
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- Automatically enters giveaways.
|
- Automatically enters giveaways.
|
||||||
- Undetectable.
|
- Undetectable.
|
||||||
|
- Сonvenient user interface.
|
||||||
|
- Сonfigurable.
|
||||||
- Sleeps to restock the points.
|
- Sleeps to restock the points.
|
||||||
- Can run 24/7.
|
- Can run 24/7.
|
||||||
|
|
||||||
### Instructions
|
### How to run
|
||||||
1. Download the latest version: https://github.com/stilManiac/steamgifts-bot/releases
|
1. Download the latest version: https://github.com/stilManiac/steamgifts-bot/releases
|
||||||
2. Sign in on [SteamGifts.com](https://www.steamgifts.com/) by Steam.
|
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.
|
3. Find `PHPSESSID` cookie in your browser.
|
||||||
4. Make sure your cookie is right.
|
4. Start the bot and follow instructions.
|
||||||
5. Start the bot.
|
|
||||||
|
|
||||||
### Commands
|
### Run from sources
|
||||||
`!sleep [arg]` - change a sleeping interval in sec. Default and recommended is 900 sec (15 min.).
|
```bash
|
||||||
`!page` - set a final websites page bot reaches
|
python -m venv env
|
||||||
|
source env/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python src/cli.py
|
||||||
|
```
|
||||||
|
|
||||||
### Help
|
### 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