removed cli and added filtering

This commit is contained in:
mcinj 2022-04-23 15:00:22 -04:00
parent 9e481fc2e3
commit 1cbe40c8c8
9 changed files with 149 additions and 167 deletions

1
.dockerignore Normal file
View file

@ -0,0 +1 @@
src/config.ini

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
env/ env/
*.ini *.ini
__pycache__/ __pycache__/
.idea .idea
config

View file

@ -14,4 +14,6 @@ COPY requirements.txt .
RUN pip3 install -r requirements.txt RUN pip3 install -r requirements.txt
COPY ./src/* /app/ COPY ./src/* /app/
VOLUME /config
CMD ["python", "run.py"] CMD ["python", "run.py"]

View file

@ -1,22 +1,13 @@
![](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/)
### Features ### Features
- Automatically enters giveaways. - Automatically enters giveaways.
- Undetectable. - Undetectable.
- Сonvenient user interface.
- Сonfigurable. - Сonfigurable.
- Sleeps to restock the points. - Sleeps to restock the points.
- Can run 24/7. - Can run 24/7.
### 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.
4. Start the bot and follow instructions.
### Run from sources ### Run from sources
```bash ```bash
python -m venv env python -m venv env
@ -27,7 +18,7 @@ python src/cli.py
### Docker ### Docker
#### Run it using a hub.docker.com image #### Run it
```bash ```bash
# Run the container # Run the container
docker run --name steamgifts -d -it mcinj/docker-steamgifts-bot:latest docker run --name steamgifts -d -it mcinj/docker-steamgifts-bot:latest

View file

@ -1,127 +0,0 @@
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()

11
src/config.ini.example Normal file
View file

@ -0,0 +1,11 @@
[DEFAULT]
cookie = PHPSESSIONCOOKIEGOESHERE
# gift_types options: All, Wishlist, Recommended, Copies, DLC, New
gift_types = All
pinned = true
# minimum number of points in your account before entering into giveaways
minimum_points = 50
# max number of entries in a giveaway for it to be considered
max_entries = 900
# time left in minutes of a giveaway for it to be considered
max_time_left = 180

13
src/log.py Normal file
View file

@ -0,0 +1,13 @@
import logging
import sys
Log_Format = "%(levelname)s %(asctime)s - %(message)s"
logging.basicConfig(stream = sys.stdout,
filemode = "w",
format = Log_Format,
level = logging.INFO)
def get_logger(name):
return logging.getLogger(name)

View file

@ -1,27 +1,27 @@
import sys
import configparser
import requests
import json import json
import threading import re
from random import randint
from time import sleep
import requests
from bs4 import BeautifulSoup
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
from urllib3.util import Retry 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 import log
logger = log.get_logger(__name__)
class SteamGifts: class SteamGifts:
def __init__(self, cookie, gifts_type, pinned, min_points): def __init__(self, cookie, gifts_type, pinned, min_points, max_entries, max_time_left):
self.cookie = { self.cookie = {
'PHPSESSID': cookie 'PHPSESSID': cookie
} }
self.gifts_type = gifts_type self.gifts_type = gifts_type
self.pinned = pinned self.pinned = pinned
self.min_points = int(min_points) self.min_points = int(min_points)
self.max_entries = int(max_entries)
self.max_time_left = int(max_time_left)
self.base = "https://www.steamgifts.com" self.base = "https://www.steamgifts.com"
self.session = requests.Session() self.session = requests.Session()
@ -66,15 +66,36 @@ class SteamGifts:
self.xsrf_token = soup.find('input', {'name': 'xsrf_token'})['value'] self.xsrf_token = soup.find('input', {'name': 'xsrf_token'})['value']
self.points = int(soup.find('span', {'class': 'nav__points'}).text) # storage points self.points = int(soup.find('span', {'class': 'nav__points'}).text) # storage points
except TypeError: except TypeError:
log("⛔ Cookie is not valid.", "red") logger.error("⛔ Cookie is not valid.")
sleep(10) sleep(10)
exit() exit()
def determine_time_in_minutes(self, string_time):
if not string_time:
logger.error(f"Could not determine time from string {string_time}")
return None
match = re.search('(?P<number>[0-9]+) (?P<time_unit>(hour|day|minute|second))', string_time)
if match:
number = int(match.group('number'))
time_unit = match.group('time_unit')
if time_unit == 'hour':
return number * 60
elif time_unit == 'day':
return number * 24 * 60
elif time_unit == 'minute':
return number
elif time_unit == 'second':
return 1
else:
return None
else:
return None
def get_game_content(self, page=1): def get_game_content(self, page=1):
n = page n = page
while True: while True:
txt = "⚙️ Retrieving games from %d page." % n txt = "⚙️ Retrieving games from %d page." % n
log(txt, "magenta") logger.info(txt)
filtered_url = self.filter_url[self.gifts_type] % n filtered_url = self.filter_url[self.gifts_type] % n
paginated_url = f"{self.base}/giveaways/{filtered_url}" paginated_url = f"{self.base}/giveaways/{filtered_url}"
@ -86,7 +107,7 @@ class SteamGifts:
if not len(game_list): if not len(game_list):
random_seconds = randint(900, 1400) random_seconds = randint(900, 1400)
txt = f"We have run out of gifts to consider. Trying again in {random_seconds} seconds." txt = f"We have run out of gifts to consider. Trying again in {random_seconds} seconds."
log(txt, "yellow") logger.info(txt)
sleep(random_seconds) sleep(random_seconds)
self.start() self.start()
continue continue
@ -98,7 +119,7 @@ class SteamGifts:
if self.points == 0 or self.points < self.min_points: if self.points == 0 or self.points < self.min_points:
random_seconds = randint(900, 1400) random_seconds = randint(900, 1400)
txt = f"🛋️ Sleeping {random_seconds} seconds to get more points. We have {self.points} points, but we need {self.min_points} to start." txt = f"🛋️ Sleeping {random_seconds} seconds to get more points. We have {self.points} points, but we need {self.min_points} to start."
log(txt, "yellow") logger.info(txt)
sleep(random_seconds) sleep(random_seconds)
self.start() self.start()
break break
@ -112,29 +133,38 @@ class SteamGifts:
continue continue
times = item.select('div span[data-timestamp]') times = item.select('div span[data-timestamp]')
game_remaining = times[0].text game_remaining = times[0].text
game_remaining_in_minutes = self.determine_time_in_minutes(game_remaining)
game_created = times[1].text game_created = times[1].text
game_entries = item.select('div.giveaway__links span')[0].text game_created_in_minutes = self.determine_time_in_minutes(game_created)
game_entries = int(item.select('div.giveaway__links span')[0].text.split(' ')[0].replace(',' ,''))
txt = f"{game_name} {game_cost} - {game_entries} - Created {game_created} ago with {game_remaining} remaining." txt = f"{game_name} {game_cost} - {game_entries} - Created {game_created} ago with {game_remaining} remaining."
log(txt, 'grey') logger.debug(txt)
if self.points - int(game_cost) < 0: if self.points - int(game_cost) < 0:
txt = f"⛔ Not enough points to enter: {game_name}" txt = f"⛔ Not enough points to enter: {game_name}"
log(txt, "red") logger.info(txt)
continue continue
if self.max_time_left > game_remaining_in_minutes:
elif self.points - int(game_cost) >= 0: txt = f"Game {game_name} has {game_remaining_in_minutes} left and is above your cutoff of {self.max_time_left} minutes."
logger.info(txt)
continue
if self.max_entries > game_entries:
txt = f"Game {game_name} has {game_entries} entries is above your cutoff of {self.max_entries} entries."
logger.info(txt)
continue
# defensive move
if self.points - int(game_cost) >= 0:
res = self.entry_gift(game_id) res = self.entry_gift(game_id)
if res: if res:
self.points -= int(game_cost) self.points -= int(game_cost)
txt = f"🎉 One more game! Has just entered {game_name}" txt = f"🎉 One more game! Has just entered {game_name}"
log(txt, "green") logger.info(txt)
sleep(randint(3, 7)) sleep(randint(3, 7))
n = n+1 n = n+1
logger.info("🛋️ List of games is ended. Waiting 2 mins to update...")
log("🛋️ List of games is ended. Waiting 2 mins to update...", "yellow")
sleep(120) sleep(120)
self.start() self.start()
@ -151,6 +181,6 @@ class SteamGifts:
if self.points > 0: if self.points > 0:
txt = "🤖 Hoho! I am back! You have %d points. Lets hack." % self.points txt = "🤖 Hoho! I am back! You have %d points. Lets hack." % self.points
log(txt, "blue") logger.info(txt)
self.get_game_content() self.get_game_content()

View file

@ -1,21 +1,81 @@
import configparser import configparser
from configparser import ConfigParser
import log
logger = log.get_logger(__name__)
config = configparser.ConfigParser() config = configparser.ConfigParser()
class MyException(Exception):
pass
def value_range(min, max):
return [str(x) for x in [*range(min, max + 1)]]
class MyConfig(ConfigParser):
def __init__(self, config_file):
super(MyConfig, self).__init__()
self.read(config_file)
self.validate_config()
def validate_config(self):
required_values = {
'DEFAULT': {
'gift_types': ('All', 'Wishlist', 'Recommended', 'Copies', 'DLC', 'New'),
'pinned': ('true', 'false'),
'minimum_points': '%s' % (value_range(0,400)),
'max_entries': '%s' % (value_range(0,10000)),
'max_time_left': '%s' % (value_range(0,21600))
}
}
for section, keys in required_values.items():
if section not in self:
raise MyException(
'Missing section %s in the config file' % section)
for key, values in keys.items():
if key not in self[section] or self[section][key] == '':
raise MyException((
'Missing value for %s under section %s in ' +
'the config file') % (key, section))
if values:
if self[section][key] not in values:
raise MyException((
'Invalid value for %s under section %s in ' +
'the config file') % (key, section))
def run(): def run():
from main import SteamGifts as SG from main import SteamGifts as SG
config.read('config.ini') file_name = '../config/config.ini'
try:
with open(file_name) as f:
config.read_file(f)
MyConfig(file_name)
except IOError:
txt = f"{file_name} doesn't exist. Rename {file_name}.example to {file_name} and fill out."
logger.warning(txt)
exit(-1)
except MyException as e:
logger.error(e)
exit(-1)
config.read(file_name)
cookie = config['DEFAULT'].get('cookie') cookie = config['DEFAULT'].get('cookie')
pinned_games = config['DEFAULT'].getboolean('pinned') pinned_games = config['DEFAULT'].getboolean('pinned')
gift_types = config['DEFAULT'].get('gift_types') gift_types = config['DEFAULT'].get('gift_types')
minimum_points = config['DEFAULT'].getint('minimum_points')
max_entries = config['DEFAULT'].getint('max_entries')
max_time_left = config['DEFAULT'].getint('max_time_left')
min_points = config['DEFAULT'].getint('minimum_points') s = SG(cookie, gift_types, pinned_games, minimum_points, max_entries, max_time_left)
s = SG(cookie, gift_types, pinned_games, min_points)
s.start() s.start()