From 3d3752de22727f02fc7c2c8045903e77e6ac8c5d Mon Sep 17 00:00:00 2001 From: mcinj <98779161+mcinj@users.noreply.github.com> Date: Sun, 8 May 2022 21:55:09 -0400 Subject: [PATCH] on-win notification - added notifications when a won giveaway is detected - a notification will only be sent once a day --- requirements.txt | 3 +- src/SteamGifts.py | 46 ++++++++++++---------------- src/log.py | 3 +- src/notification.py | 18 ++++++----- src/run.py | 6 ++-- src/tables.py | 73 ++++++++++++++++++++++++++++++++++++++------- 6 files changed, 99 insertions(+), 50 deletions(-) diff --git a/requirements.txt b/requirements.txt index f19567d..3374fd5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,5 +2,4 @@ requests==2.27.1 beautifulsoup4==4.11.1 urllib3==1.26.9 sqlalchemy==1.4.36 -sqlalchemy_utils==0.38.2 -pytz==2022.1 \ No newline at end of file +sqlalchemy_utils==0.38.2 \ No newline at end of file diff --git a/src/SteamGifts.py b/src/SteamGifts.py index 93f08c6..b3b7eb1 100644 --- a/src/SteamGifts.py +++ b/src/SteamGifts.py @@ -1,17 +1,15 @@ import json -from datetime import datetime from random import randint from time import sleep import requests from bs4 import BeautifulSoup from requests.adapters import HTTPAdapter -from sqlalchemy.orm import Session from urllib3.util import Retry import log from giveaway import Giveaway -from tables import engine, TableGiveaway +from tables import TableNotification, TableGiveaway logger = log.get_logger(__name__) @@ -69,7 +67,7 @@ class SteamGifts: return session 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) soup = BeautifulSoup(r.text, 'html.parser') return soup @@ -85,6 +83,20 @@ class SteamGifts: logger.error("⛔ 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): if giveaway.time_remaining_in_minutes is None: return False @@ -139,25 +151,6 @@ class SteamGifts: logger.error(f"Failed entering giveaway {giveaway.giveaway_game_id}") 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): n = page run = True @@ -180,7 +173,6 @@ class SteamGifts: if not len(unentered_game_list) or (all_games_list_count == pinned_giveaway_count): txt = f"We have run out of gifts to consider." logger.info(txt) - run = False break for item in unentered_game_list: @@ -200,15 +192,15 @@ class SteamGifts: if if_enter_giveaway: res = self.enter_giveaway(giveaway) if res: - self.create_or_update_giveaway(giveaway, True) + TableGiveaway.upsert_giveaway(giveaway, True) self.points -= int(giveaway.cost) txt = f"🎉 One more game! Has just entered {giveaway.game_name}" logger.info(txt) sleep(randint(4, 15)) else: - self.create_or_update_giveaway(giveaway, False) + TableGiveaway.upsert_giveaway(giveaway, False) 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 # 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 diff --git a/src/log.py b/src/log.py index 2427a8c..ff345ae 100644 --- a/src/log.py +++ b/src/log.py @@ -5,7 +5,7 @@ from logging.handlers import RotatingFileHandler log_format = "%(levelname)s %(asctime)s - %(message)s" logging.basicConfig( - handlers=[RotatingFileHandler('../config/debug.log', maxBytes=100000, backupCount=10)], + handlers=[RotatingFileHandler('../config/debug.log', maxBytes=500000, backupCount=10)], level=logging.DEBUG, format=log_format) @@ -14,6 +14,7 @@ stream.setLevel(logging.INFO) stream_format = logging.Formatter(log_format) stream.setFormatter(stream_format) + def get_logger(name): l = logging.getLogger(name) l.addHandler(stream) diff --git a/src/notification.py b/src/notification.py index 00232be..e3381ff 100644 --- a/src/notification.py +++ b/src/notification.py @@ -17,11 +17,11 @@ class Notification: self.pushover_user_key = None self.message_prefix = "SG-bot: " - 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 send_won(self, message): + self.__send('won', message) + + def send_error(self, message): + self.__send('error', message) def enable_pushover(self, token, user_key): logger.debug("Enabling pushover notifications.") @@ -29,6 +29,12 @@ class Notification: self.pushover_token = token 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): conn = http.client.HTTPSConnection("api.pushover.net:443") conn.request("POST", "/1/messages.json", @@ -47,5 +53,3 @@ class Notification: with Session(engine) as session: session.add(n) session.commit() - - diff --git a/src/run.py b/src/run.py index 57d03d2..b5701e8 100644 --- a/src/run.py +++ b/src/run.py @@ -68,15 +68,15 @@ def run(): logger.info(f"Going to sleep for {random_seconds / 60} minutes.") sleep(random_seconds) except SteamGiftsException as e: - notification.send('error', e) + notification.send_error(e) sleep(5) exit(-1) except Exception as 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) exit(-1) if __name__ == '__main__': - run() \ No newline at end of file + run() diff --git a/src/tables.py b/src/tables.py index 6de1426..d9bb9f1 100644 --- a/src/tables.py +++ b/src/tables.py @@ -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 -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.metadata @@ -19,16 +18,34 @@ class TableNotification(Base): medium = Column(String(50), nullable=False) success = Column(Boolean, 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()) __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): __tablename__ = 'giveaway' - steam_app_id = Column(String(15), primary_key=True, nullable=False) - giveaway_game_id = Column(String(10), primary_key=True, nullable=False) - steam_url = Column(String(100), nullable=False) - game_name = Column(String(200), nullable=False) + giveaway_id = Column(String(10), primary_key=True, nullable=False) + steam_id = Column(Integer, ForeignKey('steam_item.steam_id'), primary_key=True) giveaway_uri = Column(String(200), nullable=False) user = Column(String(40), nullable=False) giveaway_created_at = Column(DateTime(timezone=True), nullable=False) @@ -37,16 +54,52 @@ class TableGiveaway(Base): copies = Column(Integer(), nullable=False) contributor_level = Column(Integer(), nullable=False) entered = Column(Boolean(), nullable=False) + won = Column(Boolean(), nullable=False) game_entries = Column(Integer(), 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()) + steam_item = relationship("TableSteamItem", back_populates="giveaways") + __mapper_args__ = {"eager_defaults": True} @classmethod def unix_timestamp_to_utc_datetime(cls, 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): create_database(engine.url) @@ -55,4 +108,4 @@ if not database_exists(engine.url): Base.metadata.create_all(engine) else: # Connect the database if exists. - engine.connect() \ No newline at end of file + engine.connect()