on-win notification
- added notifications when a won giveaway is detected - a notification will only be sent once a day
This commit is contained in:
parent
405dd11a9b
commit
3d3752de22
6 changed files with 99 additions and 50 deletions
|
@ -2,5 +2,4 @@ requests==2.27.1
|
||||||
beautifulsoup4==4.11.1
|
beautifulsoup4==4.11.1
|
||||||
urllib3==1.26.9
|
urllib3==1.26.9
|
||||||
sqlalchemy==1.4.36
|
sqlalchemy==1.4.36
|
||||||
sqlalchemy_utils==0.38.2
|
sqlalchemy_utils==0.38.2
|
||||||
pytz==2022.1
|
|
|
@ -1,17 +1,15 @@
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
|
||||||
from random import randint
|
from random import randint
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from requests.adapters import HTTPAdapter
|
from requests.adapters import HTTPAdapter
|
||||||
from sqlalchemy.orm import Session
|
|
||||||
from urllib3.util import Retry
|
from urllib3.util import Retry
|
||||||
|
|
||||||
import log
|
import log
|
||||||
from giveaway import Giveaway
|
from giveaway import Giveaway
|
||||||
from tables import engine, TableGiveaway
|
from tables import TableNotification, TableGiveaway
|
||||||
|
|
||||||
logger = log.get_logger(__name__)
|
logger = log.get_logger(__name__)
|
||||||
|
|
||||||
|
@ -69,7 +67,7 @@ class SteamGifts:
|
||||||
return session
|
return session
|
||||||
|
|
||||||
def get_soup_from_page(self, url):
|
def get_soup_from_page(self, url):
|
||||||
r = self.requests_retry_session().get(url)
|
self.requests_retry_session().get(url)
|
||||||
r = requests.get(url, cookies=self.cookie)
|
r = requests.get(url, cookies=self.cookie)
|
||||||
soup = BeautifulSoup(r.text, 'html.parser')
|
soup = BeautifulSoup(r.text, 'html.parser')
|
||||||
return soup
|
return soup
|
||||||
|
@ -85,6 +83,20 @@ class SteamGifts:
|
||||||
logger.error("⛔ Cookie is not valid.")
|
logger.error("⛔ Cookie is not valid.")
|
||||||
raise SteamGiftsException("Cookie is not valid.")
|
raise SteamGiftsException("Cookie is not valid.")
|
||||||
|
|
||||||
|
won = soup.select("a[title='Giveaways Won'] div.fade_infinite")
|
||||||
|
if won:
|
||||||
|
number_won = soup.select_one("a[title='Giveaways Won'] div.fade_infinite").text
|
||||||
|
won_notifications = TableNotification.get_won_notifications_today()
|
||||||
|
if won_notifications and len(won_notifications) >= 1:
|
||||||
|
logger.debug("Win(s) detected, but we have already notified that there are won games waiting "
|
||||||
|
"to be received. Doing nothing.")
|
||||||
|
else:
|
||||||
|
logger.debug("Win(s) detected. Going to send a notification.")
|
||||||
|
logger.info(f"WINNER! You have {number_won} game(s) waiting to be claimed.")
|
||||||
|
self.notification.send_won(f"WINNER! You have {number_won} game(s) waiting to be claimed.")
|
||||||
|
else:
|
||||||
|
logger.debug('No wins detected. Doing nothing.')
|
||||||
|
|
||||||
def should_we_enter_giveaway(self, giveaway):
|
def should_we_enter_giveaway(self, giveaway):
|
||||||
if giveaway.time_remaining_in_minutes is None:
|
if giveaway.time_remaining_in_minutes is None:
|
||||||
return False
|
return False
|
||||||
|
@ -139,25 +151,6 @@ class SteamGifts:
|
||||||
logger.error(f"Failed entering giveaway {giveaway.giveaway_game_id}")
|
logger.error(f"Failed entering giveaway {giveaway.giveaway_game_id}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def create_or_update_giveaway(self, giveaway, entered):
|
|
||||||
g = TableGiveaway(
|
|
||||||
steam_app_id=giveaway.steam_app_id,
|
|
||||||
steam_url=giveaway.steam_url,
|
|
||||||
game_name=giveaway.game_name,
|
|
||||||
giveaway_game_id=giveaway.giveaway_game_id,
|
|
||||||
giveaway_uri=giveaway.giveaway_uri,
|
|
||||||
user=giveaway.user,
|
|
||||||
giveaway_created_at=TableGiveaway.unix_timestamp_to_utc_datetime(giveaway.time_created_timestamp),
|
|
||||||
giveaway_ended_at=TableGiveaway.unix_timestamp_to_utc_datetime(giveaway.time_remaining_timestamp),
|
|
||||||
cost=giveaway.cost,
|
|
||||||
copies=giveaway.copies,
|
|
||||||
contributor_level=giveaway.contributor_level,
|
|
||||||
entered=entered,
|
|
||||||
game_entries=giveaway.game_entries)
|
|
||||||
with Session(engine) as session:
|
|
||||||
session.merge(g)
|
|
||||||
session.commit()
|
|
||||||
|
|
||||||
def evaluate_giveaways(self, page=1):
|
def evaluate_giveaways(self, page=1):
|
||||||
n = page
|
n = page
|
||||||
run = True
|
run = True
|
||||||
|
@ -180,7 +173,6 @@ class SteamGifts:
|
||||||
if not len(unentered_game_list) or (all_games_list_count == pinned_giveaway_count):
|
if not len(unentered_game_list) or (all_games_list_count == pinned_giveaway_count):
|
||||||
txt = f"We have run out of gifts to consider."
|
txt = f"We have run out of gifts to consider."
|
||||||
logger.info(txt)
|
logger.info(txt)
|
||||||
run = False
|
|
||||||
break
|
break
|
||||||
|
|
||||||
for item in unentered_game_list:
|
for item in unentered_game_list:
|
||||||
|
@ -200,15 +192,15 @@ class SteamGifts:
|
||||||
if if_enter_giveaway:
|
if if_enter_giveaway:
|
||||||
res = self.enter_giveaway(giveaway)
|
res = self.enter_giveaway(giveaway)
|
||||||
if res:
|
if res:
|
||||||
self.create_or_update_giveaway(giveaway, True)
|
TableGiveaway.upsert_giveaway(giveaway, True)
|
||||||
self.points -= int(giveaway.cost)
|
self.points -= int(giveaway.cost)
|
||||||
txt = f"🎉 One more game! Has just entered {giveaway.game_name}"
|
txt = f"🎉 One more game! Has just entered {giveaway.game_name}"
|
||||||
logger.info(txt)
|
logger.info(txt)
|
||||||
sleep(randint(4, 15))
|
sleep(randint(4, 15))
|
||||||
else:
|
else:
|
||||||
self.create_or_update_giveaway(giveaway, False)
|
TableGiveaway.upsert_giveaway(giveaway, False)
|
||||||
else:
|
else:
|
||||||
self.create_or_update_giveaway(giveaway, False)
|
TableGiveaway.upsert_giveaway(giveaway, False)
|
||||||
# if we are on any filter type except New and we get to a giveaway that exceeds our
|
# if we are on any filter type except New and we get to a giveaway that exceeds our
|
||||||
# max time left amount, then we don't need to continue to look at giveaways as any
|
# max time left amount, then we don't need to continue to look at giveaways as any
|
||||||
# after this point will also exceed the max time left
|
# after this point will also exceed the max time left
|
||||||
|
|
|
@ -5,7 +5,7 @@ from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
log_format = "%(levelname)s %(asctime)s - %(message)s"
|
log_format = "%(levelname)s %(asctime)s - %(message)s"
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
handlers=[RotatingFileHandler('../config/debug.log', maxBytes=100000, backupCount=10)],
|
handlers=[RotatingFileHandler('../config/debug.log', maxBytes=500000, backupCount=10)],
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
format=log_format)
|
format=log_format)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ stream.setLevel(logging.INFO)
|
||||||
stream_format = logging.Formatter(log_format)
|
stream_format = logging.Formatter(log_format)
|
||||||
stream.setFormatter(stream_format)
|
stream.setFormatter(stream_format)
|
||||||
|
|
||||||
|
|
||||||
def get_logger(name):
|
def get_logger(name):
|
||||||
l = logging.getLogger(name)
|
l = logging.getLogger(name)
|
||||||
l.addHandler(stream)
|
l.addHandler(stream)
|
||||||
|
|
|
@ -17,11 +17,11 @@ class Notification:
|
||||||
self.pushover_user_key = None
|
self.pushover_user_key = None
|
||||||
self.message_prefix = "SG-bot: "
|
self.message_prefix = "SG-bot: "
|
||||||
|
|
||||||
def send(self, type_of_error, message):
|
def send_won(self, message):
|
||||||
logger.debug(f"Attempting to notify: {message}")
|
self.__send('won', message)
|
||||||
if self.pushover:
|
|
||||||
logger.debug("Pushover enabled. Sending message.")
|
def send_error(self, message):
|
||||||
self.__pushover(type_of_error, message)
|
self.__send('error', message)
|
||||||
|
|
||||||
def enable_pushover(self, token, user_key):
|
def enable_pushover(self, token, user_key):
|
||||||
logger.debug("Enabling pushover notifications.")
|
logger.debug("Enabling pushover notifications.")
|
||||||
|
@ -29,6 +29,12 @@ class Notification:
|
||||||
self.pushover_token = token
|
self.pushover_token = token
|
||||||
self.pushover_user_key = user_key
|
self.pushover_user_key = user_key
|
||||||
|
|
||||||
|
def __send(self, type_of_error, message):
|
||||||
|
logger.debug(f"Attempting to notify: {message}")
|
||||||
|
if self.pushover:
|
||||||
|
logger.debug("Pushover enabled. Sending message.")
|
||||||
|
self.__pushover(type_of_error, message)
|
||||||
|
|
||||||
def __pushover(self, type_of_error, message):
|
def __pushover(self, type_of_error, message):
|
||||||
conn = http.client.HTTPSConnection("api.pushover.net:443")
|
conn = http.client.HTTPSConnection("api.pushover.net:443")
|
||||||
conn.request("POST", "/1/messages.json",
|
conn.request("POST", "/1/messages.json",
|
||||||
|
@ -47,5 +53,3 @@ class Notification:
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
session.add(n)
|
session.add(n)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,15 +68,15 @@ def run():
|
||||||
logger.info(f"Going to sleep for {random_seconds / 60} minutes.")
|
logger.info(f"Going to sleep for {random_seconds / 60} minutes.")
|
||||||
sleep(random_seconds)
|
sleep(random_seconds)
|
||||||
except SteamGiftsException as e:
|
except SteamGiftsException as e:
|
||||||
notification.send('error', e)
|
notification.send_error(e)
|
||||||
sleep(5)
|
sleep(5)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
notification.send('error', "Something happened and the bot had to quit!")
|
notification.send_error("Something happened and the bot had to quit!")
|
||||||
sleep(5)
|
sleep(5)
|
||||||
exit(-1)
|
exit(-1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
run()
|
run()
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from sqlalchemy import create_engine, Integer, String, Column, Table, \
|
|
||||||
MetaData, DateTime, Numeric, Enum, Boolean, TIMESTAMP, func
|
|
||||||
from sqlalchemy_utils import database_exists, create_database
|
|
||||||
from sqlalchemy.orm import registry
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pytz
|
|
||||||
|
from sqlalchemy import create_engine, Integer, String, Column, DateTime, Boolean, func, ForeignKey
|
||||||
|
from sqlalchemy.orm import registry, relationship, Session
|
||||||
|
from sqlalchemy_utils import database_exists, create_database
|
||||||
|
|
||||||
mapper_registry = registry()
|
mapper_registry = registry()
|
||||||
mapper_registry.metadata
|
mapper_registry.metadata
|
||||||
|
@ -19,16 +18,34 @@ class TableNotification(Base):
|
||||||
medium = Column(String(50), nullable=False)
|
medium = Column(String(50), nullable=False)
|
||||||
success = Column(Boolean, nullable=False)
|
success = Column(Boolean, nullable=False)
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||||
|
|
||||||
__mapper_args__ = {"eager_defaults": True}
|
__mapper_args__ = {"eager_defaults": True}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_won_notifications_today(cls):
|
||||||
|
with Session(engine) as session:
|
||||||
|
return session.query(TableNotification)\
|
||||||
|
.filter(func.DATE(TableNotification.created_at) == datetime.utcnow().date())\
|
||||||
|
.filter_by(type='won')\
|
||||||
|
.all()
|
||||||
|
|
||||||
|
|
||||||
|
class TableSteamItem(Base):
|
||||||
|
__tablename__ = 'steam_item'
|
||||||
|
steam_id = Column(String(15), primary_key=True, nullable=False)
|
||||||
|
game_name = Column(String(200), nullable=False)
|
||||||
|
steam_url = Column(String(100), nullable=False)
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||||
|
|
||||||
|
giveaways = relationship("TableGiveaway", back_populates="steam_item")
|
||||||
|
|
||||||
|
|
||||||
class TableGiveaway(Base):
|
class TableGiveaway(Base):
|
||||||
__tablename__ = 'giveaway'
|
__tablename__ = 'giveaway'
|
||||||
steam_app_id = Column(String(15), primary_key=True, nullable=False)
|
giveaway_id = Column(String(10), primary_key=True, nullable=False)
|
||||||
giveaway_game_id = Column(String(10), primary_key=True, nullable=False)
|
steam_id = Column(Integer, ForeignKey('steam_item.steam_id'), primary_key=True)
|
||||||
steam_url = Column(String(100), nullable=False)
|
|
||||||
game_name = Column(String(200), nullable=False)
|
|
||||||
giveaway_uri = Column(String(200), nullable=False)
|
giveaway_uri = Column(String(200), nullable=False)
|
||||||
user = Column(String(40), nullable=False)
|
user = Column(String(40), nullable=False)
|
||||||
giveaway_created_at = Column(DateTime(timezone=True), nullable=False)
|
giveaway_created_at = Column(DateTime(timezone=True), nullable=False)
|
||||||
|
@ -37,16 +54,52 @@ class TableGiveaway(Base):
|
||||||
copies = Column(Integer(), nullable=False)
|
copies = Column(Integer(), nullable=False)
|
||||||
contributor_level = Column(Integer(), nullable=False)
|
contributor_level = Column(Integer(), nullable=False)
|
||||||
entered = Column(Boolean(), nullable=False)
|
entered = Column(Boolean(), nullable=False)
|
||||||
|
won = Column(Boolean(), nullable=False)
|
||||||
game_entries = Column(Integer(), nullable=False)
|
game_entries = Column(Integer(), nullable=False)
|
||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||||
|
|
||||||
|
steam_item = relationship("TableSteamItem", back_populates="giveaways")
|
||||||
|
|
||||||
__mapper_args__ = {"eager_defaults": True}
|
__mapper_args__ = {"eager_defaults": True}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unix_timestamp_to_utc_datetime(cls, timestamp):
|
def unix_timestamp_to_utc_datetime(cls, timestamp):
|
||||||
return datetime.utcfromtimestamp(timestamp)
|
return datetime.utcfromtimestamp(timestamp)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def upsert_giveaway(cls, giveaway, entered):
|
||||||
|
with Session(engine) as session:
|
||||||
|
result = session.query(TableGiveaway).filter_by(giveaway_id=giveaway.giveaway_game_id,
|
||||||
|
steam_id=giveaway.steam_app_id).all()
|
||||||
|
if result:
|
||||||
|
steam_id = result[0].steam_id
|
||||||
|
else:
|
||||||
|
item = TableSteamItem(
|
||||||
|
steam_id=giveaway.steam_app_id,
|
||||||
|
steam_url=giveaway.steam_url,
|
||||||
|
game_name=giveaway.game_name)
|
||||||
|
session.merge(item)
|
||||||
|
session.flush()
|
||||||
|
steam_id = item.steam_id
|
||||||
|
|
||||||
|
g = TableGiveaway(
|
||||||
|
giveaway_id=giveaway.giveaway_game_id,
|
||||||
|
steam_id=steam_id,
|
||||||
|
giveaway_uri=giveaway.giveaway_uri,
|
||||||
|
user=giveaway.user,
|
||||||
|
giveaway_created_at=TableGiveaway.unix_timestamp_to_utc_datetime(giveaway.time_created_timestamp),
|
||||||
|
giveaway_ended_at=TableGiveaway.unix_timestamp_to_utc_datetime(giveaway.time_remaining_timestamp),
|
||||||
|
cost=giveaway.cost,
|
||||||
|
copies=giveaway.copies,
|
||||||
|
contributor_level=giveaway.contributor_level,
|
||||||
|
entered=entered,
|
||||||
|
won=False,
|
||||||
|
game_entries=giveaway.game_entries)
|
||||||
|
|
||||||
|
session.merge(g)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
if not database_exists(engine.url):
|
if not database_exists(engine.url):
|
||||||
create_database(engine.url)
|
create_database(engine.url)
|
||||||
|
@ -55,4 +108,4 @@ if not database_exists(engine.url):
|
||||||
Base.metadata.create_all(engine)
|
Base.metadata.create_all(engine)
|
||||||
else:
|
else:
|
||||||
# Connect the database if exists.
|
# Connect the database if exists.
|
||||||
engine.connect()
|
engine.connect()
|
||||||
|
|
Loading…
Reference in a new issue