From 6a5e679aecfc37ff17794ebbf816480da25b1e73 Mon Sep 17 00:00:00 2001
From: mcinj <98779161+mcinj@users.noreply.github.com>
Date: Fri, 20 May 2022 23:42:42 -0400
Subject: [PATCH 01/16] fix ref
---
src/bot/templates/configuration.html | 3 ++-
src/bot/templates/log.html | 3 ++-
src/bot/webserver_thread.py | 15 +++++++++++----
3 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/src/bot/templates/configuration.html b/src/bot/templates/configuration.html
index db2c119..7268e62 100644
--- a/src/bot/templates/configuration.html
+++ b/src/bot/templates/configuration.html
@@ -11,7 +11,8 @@
diff --git a/src/bot/templates/log.html b/src/bot/templates/log.html
index 80b7b71..9b486de 100644
--- a/src/bot/templates/log.html
+++ b/src/bot/templates/log.html
@@ -11,7 +11,8 @@
diff --git a/src/bot/webserver_thread.py b/src/bot/webserver_thread.py
index c62ebf0..6f6aeba 100644
--- a/src/bot/webserver_thread.py
+++ b/src/bot/webserver_thread.py
@@ -1,3 +1,4 @@
+import os
import threading
from threading import Thread
@@ -38,13 +39,19 @@ class WebServerThread(threading.Thread):
@app.route(f"{self.app_root}")
def config():
- with open('../config/config.ini', 'r') as f:
+ with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/config.ini", 'r') as f:
content = f.read()
return render_template('configuration.html', content=content)
- @app.route(f"{self.app_root}log")
- def logs():
- with open('../config/info.log', 'r') as f:
+ @app.route(f"{self.app_root}log_info")
+ def log_info():
+ with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/info.log", 'r') as f:
+ content = f.read()
+ return render_template('log.html', content=content)
+
+ @app.route(f"{self.app_root}log_debug")
+ def log_debug():
+ with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/debug.log", 'r') as f:
content = f.read()
return render_template('log.html', content=content)
From 9f2b2e7be2f066b4d6c0e1d899b51e524bfd57a6 Mon Sep 17 00:00:00 2001
From: mcinj <98779161+mcinj@users.noreply.github.com>
Date: Sat, 21 May 2022 13:12:04 -0400
Subject: [PATCH 02/16] removed unused column
---
.dockerignore | 1 -
.gitignore | 1 -
alembic.ini | 102 ++++++++++++++++++
...0_25_41-15c028536ef5_won_column_removed.py | 31 ++++++
src/bot/database.py | 2 -
src/bot/models.py | 1 -
6 files changed, 133 insertions(+), 5 deletions(-)
create mode 100644 alembic.ini
create mode 100644 src/alembic/versions/2022_05_21-10_25_41-15c028536ef5_won_column_removed.py
diff --git a/.dockerignore b/.dockerignore
index fb2bb68..c56db2a 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -2,5 +2,4 @@ config/config.ini
config/debug.log
config/sqlite.db
config/info.log
-alembic.ini
README.ini
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index e292dc2..6dc7536 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
env/
venv/
-*.ini
__pycache__/
.idea
config/config.ini
diff --git a/alembic.ini b/alembic.ini
new file mode 100644
index 0000000..06ec989
--- /dev/null
+++ b/alembic.ini
@@ -0,0 +1,102 @@
+# A generic, single database configuration.
+
+[alembic]
+# path to migration scripts
+script_location = src/alembic
+
+# template used to generate migration files
+file_template = %%(year)d_%%(month).2d_%%(day).2d-%%(hour).2d_%%(minute).2d_%%(second).2d-%%(rev)s_%%(slug)s
+
+# sys.path path, will be prepended to sys.path if present.
+# defaults to the current working directory.
+prepend_sys_path = .
+
+# timezone to use when rendering the date within the migration file
+# as well as the filename.
+# If specified, requires the python-dateutil library that can be
+# installed by adding `alembic[tz]` to the pip requirements
+# string value is passed to dateutil.tz.gettz()
+# leave blank for localtime
+# timezone =
+
+# max length of characters to apply to the
+# "slug" field
+# truncate_slug_length = 40
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+# set to 'true' to allow .pyc and .pyo files without
+# a source .py file to be detected as revisions in the
+# versions/ directory
+# sourceless = false
+
+# version location specification; This defaults
+# to alembic/versions. When using multiple version
+# directories, initial revisions must be specified with --version-path.
+# The path separator used here should be the separator specified by "version_path_separator" below.
+# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions:src/alembic/versions
+
+# version path separator; As mentioned above, this is the character used to split
+# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
+# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
+# Valid values for version_path_separator are:
+#
+# version_path_separator = :
+# version_path_separator = ;
+# version_path_separator = space
+# version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
+
+# the output encoding used when revision files
+# are written from script.py.mako
+# output_encoding = utf-8
+
+sqlalchemy.url = sqlite:///config/sqlite.db
+
+
+[post_write_hooks]
+# post_write_hooks defines scripts or Python functions that are run
+# on newly generated revision scripts. See the documentation for further
+# detail and examples
+
+# format using "black" - use the console_scripts runner, against the "black" entrypoint
+# hooks = black
+# black.type = console_scripts
+# black.entrypoint = black
+# black.options = -l 79 REVISION_SCRIPT_FILENAME
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S
diff --git a/src/alembic/versions/2022_05_21-10_25_41-15c028536ef5_won_column_removed.py b/src/alembic/versions/2022_05_21-10_25_41-15c028536ef5_won_column_removed.py
new file mode 100644
index 0000000..7abd9af
--- /dev/null
+++ b/src/alembic/versions/2022_05_21-10_25_41-15c028536ef5_won_column_removed.py
@@ -0,0 +1,31 @@
+"""won column removed
+
+Revision ID: 15c028536ef5
+Revises: 1da33402b659
+Create Date: 2022-05-21 10:25:41.647723
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = '15c028536ef5'
+down_revision = '1da33402b659'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.drop_column('giveaway', 'won')
+
+
+def downgrade():
+ # add columns as nullable as existing records don't have that column value set
+ op.add_column('giveaway', sa.Column('won', sa.Boolean(), nullable=True))
+ # set value on new columns for all records
+ with op.get_context().autocommit_block():
+ op.execute("UPDATE giveaway SET won=false WHERE won is null;")
+ # SQLite doesn't support ALTERs so alembic uses a batch mode
+ # Set columns to non-nullable now that all records have a value
+ with op.batch_alter_table('giveaway') as batch_op:
+ batch_op.alter_column('won', nullable=False)
diff --git a/src/bot/database.py b/src/bot/database.py
index 849fcad..ec6beea 100644
--- a/src/bot/database.py
+++ b/src/bot/database.py
@@ -124,7 +124,6 @@ class GiveawayHelper:
copies=giveaway.copies,
contributor_level=giveaway.contributor_level,
entered=entered,
- won=False,
game_entries=giveaway.game_entries)
session.add(g)
session.commit()
@@ -147,7 +146,6 @@ class GiveawayHelper:
copies=giveaway.copies,
contributor_level=giveaway.contributor_level,
entered=entered,
- won=False,
game_entries=giveaway.game_entries)
session.merge(g)
session.commit()
\ No newline at end of file
diff --git a/src/bot/models.py b/src/bot/models.py
index f9174f8..c16b9f2 100644
--- a/src/bot/models.py
+++ b/src/bot/models.py
@@ -42,7 +42,6 @@ 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())
From 7c6493cea87586f3ac92c8479765f3dd89b248b2 Mon Sep 17 00:00:00 2001
From: mcinj <98779161+mcinj@users.noreply.github.com>
Date: Sun, 22 May 2022 14:20:44 -0400
Subject: [PATCH 03/16] re-org and db views
---
.gitignore | 2 +-
main.py | 2 +-
src/bot/database.py | 25 +++++---
src/{bot => }/run.py | 18 +++---
src/{bot => web}/static/css/main.css | 0
src/{bot => web}/templates/configuration.html | 4 +-
src/web/templates/giveaways.html | 63 +++++++++++++++++++
src/{bot => web}/templates/log.html | 4 +-
src/web/templates/notifications.html | 51 +++++++++++++++
src/{bot => web}/webserver_thread.py | 18 ++++--
10 files changed, 160 insertions(+), 27 deletions(-)
rename src/{bot => }/run.py (86%)
rename src/{bot => web}/static/css/main.css (100%)
rename src/{bot => web}/templates/configuration.html (74%)
create mode 100644 src/web/templates/giveaways.html
rename src/{bot => web}/templates/log.html (74%)
create mode 100644 src/web/templates/notifications.html
rename src/{bot => web}/webserver_thread.py (76%)
diff --git a/.gitignore b/.gitignore
index 6dc7536..8304bd8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,5 @@ __pycache__/
.idea
config/config.ini
config/*.log*
-config/sqlite.db
+config/sqlite*
.DS_STORE
\ No newline at end of file
diff --git a/main.py b/main.py
index 3131670..d19fa56 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,4 @@
-from src.bot import run
+from src import run
if __name__ == '__main__':
diff --git a/src/bot/database.py b/src/bot/database.py
index ec6beea..e3fc60e 100644
--- a/src/bot/database.py
+++ b/src/bot/database.py
@@ -6,7 +6,7 @@ from alembic import command
from alembic.config import Config
from dateutil import tz
from sqlalchemy import func
-from sqlalchemy.orm import Session
+from sqlalchemy.orm import Session, joinedload
from sqlalchemy_utils import database_exists
from .log import get_logger
@@ -17,11 +17,7 @@ engine = sqlalchemy.create_engine(f"{os.getenv('BOT_DB_URL', 'sqlite:///./config
engine.connect()
-def create_engine(db_url: str):
- return engine
-
-
-def run_migrations(script_location: str, db_url: str) -> None:
+def run_db_migrations(script_location: str, db_url: str) -> None:
logger.debug('Running DB migrations in %r on %r', script_location, db_url)
alembic_cfg = Config()
alembic_cfg.set_main_option('script_location', script_location)
@@ -30,10 +26,9 @@ def run_migrations(script_location: str, db_url: str) -> None:
if not database_exists(db_url):
logger.debug(f"'{db_url}' does not exist. Running normal migration to create db and tables." )
command.upgrade(alembic_cfg, 'head')
- if database_exists(db_url):
- logger.debug(f"'{db_url} exists.")
- e = create_engine(db_url)
- insp = sqlalchemy.inspect(e)
+ elif database_exists(db_url):
+ logger.debug(f"'{db_url}' exists.")
+ insp = sqlalchemy.inspect(engine)
alembic_version_table_name = 'alembic_version'
has_alembic_table = insp.has_table(alembic_version_table_name)
if has_alembic_table:
@@ -49,6 +44,11 @@ def run_migrations(script_location: str, db_url: str) -> None:
class NotificationHelper:
+ @classmethod
+ def get(cls):
+ with Session(engine) as session:
+ return session.query(TableNotification).order_by(TableNotification.created_at.desc()).all()
+
@classmethod
def insert(cls, type_of_error, message, medium, success):
with Session(engine) as session:
@@ -89,6 +89,11 @@ class NotificationHelper:
class GiveawayHelper:
+ @classmethod
+ def get(cls):
+ with Session(engine) as session:
+ return session.query(TableGiveaway).options(joinedload('steam_item')).order_by(TableGiveaway.giveaway_ended_at.desc()).all()
+
@classmethod
def unix_timestamp_to_utc_datetime(cls, timestamp):
return datetime.utcfromtimestamp(timestamp)
diff --git a/src/bot/run.py b/src/run.py
similarity index 86%
rename from src/bot/run.py
rename to src/run.py
index c5993fb..2375878 100644
--- a/src/bot/run.py
+++ b/src/run.py
@@ -1,13 +1,13 @@
import os
from time import sleep
-from .log import get_logger
-from .config_reader import ConfigReader, ConfigException
-from .enter_giveaways import SteamGiftsException
-from .giveaway_thread import GiveawayThread
-from .notification import Notification
-from .database import run_migrations, create_engine
-from .webserver_thread import WebServerThread
+from src.bot.log import get_logger
+from src.bot.config_reader import ConfigReader, ConfigException
+from src.bot.enter_giveaways import SteamGiftsException
+from src.bot.giveaway_thread import GiveawayThread
+from src.bot.notification import Notification
+from src.bot.database import run_db_migrations
+from src.web.webserver_thread import WebServerThread
logger = get_logger(__name__)
config_file_name = f"{os.getenv('BOT_CONFIG_DIR', './config')}/config.ini"
@@ -73,9 +73,9 @@ def entry():
|___/
-------------------------------------------------------------------------------------
""")
- run_migrations(alembic_migration_files, db_url)
- create_engine(db_url)
+ run_db_migrations(alembic_migration_files, db_url)
run()
+
if __name__ == '__main__':
entry()
diff --git a/src/bot/static/css/main.css b/src/web/static/css/main.css
similarity index 100%
rename from src/bot/static/css/main.css
rename to src/web/static/css/main.css
diff --git a/src/bot/templates/configuration.html b/src/web/templates/configuration.html
similarity index 74%
rename from src/bot/templates/configuration.html
rename to src/web/templates/configuration.html
index 7268e62..670abb9 100644
--- a/src/bot/templates/configuration.html
+++ b/src/web/templates/configuration.html
@@ -1,7 +1,7 @@
- Steamgifts Bot Configuration
+ {{name}} Steamgifts Bot Configuration
@@ -13,6 +13,8 @@
Config
Info Logs
Debug Logs
+ Giveaways
+ Notifications
diff --git a/src/web/templates/giveaways.html b/src/web/templates/giveaways.html
new file mode 100644
index 0000000..e35376d
--- /dev/null
+++ b/src/web/templates/giveaways.html
@@ -0,0 +1,63 @@
+
+
+
+ {{name}} Steamgifts Bot DB View
+
+
+
+
+
+
+
+
+ Giveaway ID |
+ Steam ID |
+ Giveaway URI |
+ Name |
+ Steam URL |
+ User |
+ Giveaway Created At |
+ Giveaway Ended At |
+ Cost |
+ Copies |
+ Contributor Level |
+ Entered |
+
+
+
+ {% for row in content %}
+
+ {{row.giveaway_id}} |
+ {{row.steam_id}} |
+ {{row.giveaway_uri}} |
+ {{row.steam_item.game_name}} |
+ {{row.steam_item.steam_url}} |
+ {{row.user}} |
+ {{row.giveaway_created_at}} |
+ {{row.giveaway_ended_at}} |
+ {{row.cost}} |
+ {{row.copies}} |
+ {{row.contributor_level}} |
+ {{row.entered}} |
+
+ {% endfor %}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/bot/templates/log.html b/src/web/templates/log.html
similarity index 74%
rename from src/bot/templates/log.html
rename to src/web/templates/log.html
index 9b486de..8a7408f 100644
--- a/src/bot/templates/log.html
+++ b/src/web/templates/log.html
@@ -1,7 +1,7 @@
- Steamgifts Bot Logs
+ {{name}} Steamgifts Bot {{log_type}} Logs
@@ -13,6 +13,8 @@
Config
Info Logs
Debug Logs
+ Giveaways
+ Notifications
diff --git a/src/web/templates/notifications.html b/src/web/templates/notifications.html
new file mode 100644
index 0000000..b11fe88
--- /dev/null
+++ b/src/web/templates/notifications.html
@@ -0,0 +1,51 @@
+
+
+
+ {{name}} Steamgifts Bot DB View
+
+
+
+
+
+
+
+
+ ID |
+ Medium |
+ Type |
+ Success |
+ Created At |
+ Message |
+
+
+
+ {% for row in content %}
+
+ {{row.id}} |
+ {{row.medium}} |
+ {{row.type}} |
+ {{row.success}} |
+ {{row.created_at}} |
+ {{row.message}} |
+
+ {% endfor %}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/bot/webserver_thread.py b/src/web/webserver_thread.py
similarity index 76%
rename from src/bot/webserver_thread.py
rename to src/web/webserver_thread.py
index 6f6aeba..4456801 100644
--- a/src/bot/webserver_thread.py
+++ b/src/web/webserver_thread.py
@@ -4,7 +4,8 @@ from threading import Thread
from flask_basicauth import BasicAuth
-from .log import get_logger
+from src.bot.database import NotificationHelper, GiveawayHelper
+from src.bot.log import get_logger
logger = get_logger(__name__)
@@ -15,6 +16,7 @@ class WebServerThread(threading.Thread):
Thread.__init__(self)
self.exc = None
self.config = config
+ self.name = config['NOTIFICATIONS'].get('notification.prefix')
self.host = config['WEB'].get('web.host')
self.port = config['WEB'].getint('web.port')
self.ssl = config['WEB'].getboolean('web.ssl')
@@ -41,24 +43,32 @@ class WebServerThread(threading.Thread):
def config():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/config.ini", 'r') as f:
content = f.read()
- return render_template('configuration.html', content=content)
+ return render_template('configuration.html', name=self.name, content=content)
@app.route(f"{self.app_root}log_info")
def log_info():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/info.log", 'r') as f:
content = f.read()
- return render_template('log.html', content=content)
+ return render_template('log.html', name=self.name, log_type='info', content=content)
@app.route(f"{self.app_root}log_debug")
def log_debug():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/debug.log", 'r') as f:
content = f.read()
- return render_template('log.html', content=content)
+ return render_template('log.html', name=self.name, log_type='debug', content=content)
@app.route(f"{self.app_root}alive")
def alive():
return 'OK'
+ @app.route(f"{self.app_root}notifications")
+ def db_notifications():
+ return render_template('notifications.html', content=NotificationHelper.get())
+
+ @app.route(f"{self.app_root}giveaways")
+ def db_giveaways():
+ return render_template('giveaways.html', content=GiveawayHelper.get())
+
if self.enabled:
logger.info("Webserver Enabled. Running")
if self.ssl:
From e7a4e90988da6ab63a7334cccd109a63faa5dbee Mon Sep 17 00:00:00 2001
From: mcinj <98779161+mcinj@users.noreply.github.com>
Date: Tue, 24 May 2022 08:27:54 -0400
Subject: [PATCH 04/16] prettify (but still ugly) the giveaway table
---
requirements.txt | 5 +++--
src/bot/database.py | 12 +++++++++++-
src/web/static/css/main.css | 30 ++++++++++++++++++++++++++++++
src/web/templates/giveaways.html | 16 ++++++++++++++--
src/web/webserver_thread.py | 17 +++++++++--------
5 files changed, 67 insertions(+), 13 deletions(-)
diff --git a/requirements.txt b/requirements.txt
index 47e10c5..517508f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,8 +3,9 @@ beautifulsoup4==4.11.1
urllib3==1.26.9
sqlalchemy==1.4.36
sqlalchemy_utils==0.38.2
+paginate-sqlalchemy==0.3.1
+alembic==1.7.7
python-dateutil==2.8.2
Flask==2.1.2
Flask-BasicAuth==0.2.0
-pyopenssl==22.0.0
-alembic==1.7.7
\ No newline at end of file
+pyopenssl==22.0.0
\ No newline at end of file
diff --git a/src/bot/database.py b/src/bot/database.py
index e3fc60e..913986a 100644
--- a/src/bot/database.py
+++ b/src/bot/database.py
@@ -1,6 +1,7 @@
import os
from datetime import datetime, timedelta
+import paginate_sqlalchemy
import sqlalchemy
from alembic import command
from alembic.config import Config
@@ -92,7 +93,16 @@ class GiveawayHelper:
@classmethod
def get(cls):
with Session(engine) as session:
- return session.query(TableGiveaway).options(joinedload('steam_item')).order_by(TableGiveaway.giveaway_ended_at.desc()).all()
+ return session.query(TableGiveaway).options(joinedload('steam_item'))\
+ .order_by(TableGiveaway.giveaway_ended_at.desc()).all()
+
+ @classmethod
+ def paginate(cls, page=1):
+ with Session(engine) as session:
+ paginated_giveaways = paginate_sqlalchemy.SqlalchemyOrmPage(session.query(TableGiveaway)
+ .options(joinedload('steam_item'))
+ .order_by(TableGiveaway.giveaway_ended_at.desc()), page=page)
+ return paginated_giveaways
@classmethod
def unix_timestamp_to_utc_datetime(cls, timestamp):
diff --git a/src/web/static/css/main.css b/src/web/static/css/main.css
index 9613db1..8936f21 100644
--- a/src/web/static/css/main.css
+++ b/src/web/static/css/main.css
@@ -73,4 +73,34 @@ h3 {
.menu li a {
color: #444;
text-decoration: none;
+}
+.styled-table {
+ border-collapse: collapse;
+ margin: 25px 0;
+ font-size: 0.9em;
+ font-family: sans-serif;
+ min-width: 400px;
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
+}
+.styled-table thead tr {
+ background-color: #009879;
+ color: #ffffff;
+ text-align: left;
+}
+.styled-table th,
+.styled-table td {
+ padding: 12px 15px;
+}
+.styled-table tbody tr {
+ border-bottom: 1px solid #dddddd;
+}
+.styled-table tbody tr:nth-of-type(even) {
+ background-color: #f3f3f3;
+}
+.styled-table tbody tr:last-of-type {
+ border-bottom: 2px solid #009879;
+}
+.styled-table tbody tr.active-row {
+ font-weight: bold;
+ color: #009879;
}
\ No newline at end of file
diff --git a/src/web/templates/giveaways.html b/src/web/templates/giveaways.html
index e35376d..d33163c 100644
--- a/src/web/templates/giveaways.html
+++ b/src/web/templates/giveaways.html
@@ -22,7 +22,19 @@
-
+
+
Giveaway ID |
@@ -40,7 +52,7 @@
- {% for row in content %}
+ {% for row in content.items %}
{{row.giveaway_id}} |
{{row.steam_id}} |
diff --git a/src/web/webserver_thread.py b/src/web/webserver_thread.py
index 4456801..9cf74b8 100644
--- a/src/web/webserver_thread.py
+++ b/src/web/webserver_thread.py
@@ -16,7 +16,7 @@ class WebServerThread(threading.Thread):
Thread.__init__(self)
self.exc = None
self.config = config
- self.name = config['NOTIFICATIONS'].get('notification.prefix')
+ self.prefix = config['NOTIFICATIONS'].get('notification.prefix')
self.host = config['WEB'].get('web.host')
self.port = config['WEB'].getint('web.port')
self.ssl = config['WEB'].getboolean('web.ssl')
@@ -43,19 +43,19 @@ class WebServerThread(threading.Thread):
def config():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/config.ini", 'r') as f:
content = f.read()
- return render_template('configuration.html', name=self.name, content=content)
+ return render_template('configuration.html', name=self.prefix, content=content)
@app.route(f"{self.app_root}log_info")
def log_info():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/info.log", 'r') as f:
content = f.read()
- return render_template('log.html', name=self.name, log_type='info', content=content)
+ return render_template('log.html', name=self.prefix, log_type='info', content=content)
@app.route(f"{self.app_root}log_debug")
def log_debug():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/debug.log", 'r') as f:
content = f.read()
- return render_template('log.html', name=self.name, log_type='debug', content=content)
+ return render_template('log.html', name=self.prefix, log_type='debug', content=content)
@app.route(f"{self.app_root}alive")
def alive():
@@ -63,11 +63,12 @@ class WebServerThread(threading.Thread):
@app.route(f"{self.app_root}notifications")
def db_notifications():
- return render_template('notifications.html', content=NotificationHelper.get())
+ return render_template('notifications.html', name=self.prefix, content=NotificationHelper.get())
- @app.route(f"{self.app_root}giveaways")
- def db_giveaways():
- return render_template('giveaways.html', content=GiveawayHelper.get())
+ @app.route(f"{self.app_root}giveaways", methods=['GET'], defaults={"page": 1})
+ @app.route(f"{self.app_root}giveaways/", methods=['GET'])
+ def db_giveaways(page):
+ return render_template('giveaways.html', name=self.prefix, content=GiveawayHelper.paginate(page=page))
if self.enabled:
logger.info("Webserver Enabled. Running")
From 7a8d2fe79302aebccabffe4ad8389ca450e9d8a6 Mon Sep 17 00:00:00 2001
From: mcinj <98779161+mcinj@users.noreply.github.com>
Date: Tue, 24 May 2022 09:15:25 -0400
Subject: [PATCH 05/16] stateful notifications
---
...31_25-ff0a728ba3da_add_games_won_column.py | 27 +++++++++++++++++++
src/bot/database.py | 7 ++---
src/bot/enter_giveaways.py | 16 ++++++++---
src/bot/models.py | 1 +
src/bot/notification.py | 16 +++++------
5 files changed, 52 insertions(+), 15 deletions(-)
create mode 100644 src/alembic/versions/2022_05_24-08_31_25-ff0a728ba3da_add_games_won_column.py
diff --git a/src/alembic/versions/2022_05_24-08_31_25-ff0a728ba3da_add_games_won_column.py b/src/alembic/versions/2022_05_24-08_31_25-ff0a728ba3da_add_games_won_column.py
new file mode 100644
index 0000000..e6c0c79
--- /dev/null
+++ b/src/alembic/versions/2022_05_24-08_31_25-ff0a728ba3da_add_games_won_column.py
@@ -0,0 +1,27 @@
+"""add games_won column
+
+Revision ID: ff0a728ba3da
+Revises: 15c028536ef5
+Create Date: 2022-05-24 08:31:25.684099
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = 'ff0a728ba3da'
+down_revision = '15c028536ef5'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.add_column('notification', sa.Column('games_won', sa.Integer(), nullable=True))
+ # set a default for previous won notifications
+ with op.get_context().autocommit_block():
+ op.execute("UPDATE notification SET games_won=1 WHERE type='won';")
+
+
+def downgrade():
+ op.drop_column('notification', 'games_won')
diff --git a/src/bot/database.py b/src/bot/database.py
index 913986a..ff42668 100644
--- a/src/bot/database.py
+++ b/src/bot/database.py
@@ -51,9 +51,10 @@ class NotificationHelper:
return session.query(TableNotification).order_by(TableNotification.created_at.desc()).all()
@classmethod
- def insert(cls, type_of_error, message, medium, success):
+ def insert(cls, type_of_error, message, medium, success, number_won):
with Session(engine) as session:
- n = TableNotification(type=type_of_error, message=message, medium=medium, success=success)
+ n = TableNotification(type=type_of_error, message=message, medium=medium, success=success,
+ games_won=number_won)
session.add(n)
session.commit()
@@ -65,7 +66,7 @@ class NotificationHelper:
within_3_days = session.query(TableNotification) \
.filter(func.DATE(TableNotification.created_at) >= (datetime.utcnow().date() - timedelta(days=1))) \
.filter(func.DATE(TableNotification.created_at) <= (datetime.utcnow().date() + timedelta(days=1))) \
- .filter_by(type='won').all()
+ .filter_by(type='won').order_by(TableNotification.created_at.asc()).all()
actual = []
for r in within_3_days:
if r.created_at.replace(tzinfo=tz.tzutc()).astimezone(tz.tzlocal()).date() == datetime.now(
diff --git a/src/bot/enter_giveaways.py b/src/bot/enter_giveaways.py
index bcd25c8..9455c9b 100644
--- a/src/bot/enter_giveaways.py
+++ b/src/bot/enter_giveaways.py
@@ -89,14 +89,22 @@ class EnterGiveaways:
won = soup.select("a[title='Giveaways Won'] div")
if won:
- number_won = soup.select_one("a[title='Giveaways Won'] div").text
+ number_won = int(soup.select_one("a[title='Giveaways Won'] div").text)
won_notifications = NotificationHelper.get_won_notifications_today()
if won_notifications and len(won_notifications) >= 1:
- logger.info("🆒️ Win(s) detected, but we have already notified that there are won games waiting "
- "to be received. Doing nothing.")
+ if number_won == won_notifications[-1].games_won:
+ logger.info("🆒️ Win(s) detected, but we have already notified that there are won games waiting "
+ "to be received. Doing nothing.")
+ elif number_won > won_notifications[-1].games_won:
+ logger.info("🔥🔥 MORE win(s) detected. Notifying again.")
+ self.notification.send_won(f"You won ANOTHER game. You now have {number_won} game(s) "
+ f"waiting to be claimed.", number_won)
+ else: # we have less games waiting to be claimed than notified, meaning some have been claimed
+ logger.info("🆒️ Win(s) detected, but we have already notified that there are won games waiting "
+ "to be received. Some have been claimed. Doing nothing.")
else:
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.")
+ self.notification.send_won(f"WINNER! You have {number_won} game(s) waiting to be claimed.", number_won)
else:
logger.debug('No wins detected. Doing nothing.')
diff --git a/src/bot/models.py b/src/bot/models.py
index c16b9f2..23272be 100644
--- a/src/bot/models.py
+++ b/src/bot/models.py
@@ -13,6 +13,7 @@ class TableNotification(Base):
message = Column(String(300), nullable=False)
medium = Column(String(50), nullable=False)
success = Column(Boolean, nullable=False)
+ games_won = Column(Integer, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
diff --git a/src/bot/notification.py b/src/bot/notification.py
index 7a3089b..14ef359 100644
--- a/src/bot/notification.py
+++ b/src/bot/notification.py
@@ -15,11 +15,11 @@ class Notification:
self.pushover_user_key = None
self.message_prefix = f"{message_prefix}: "
- def send_won(self, message):
- self.__send('won', message)
+ def send_won(self, message, number_won):
+ self.__send('won', message, number_won)
def send_error(self, message):
- self.__send('error', message)
+ self.__send('error', message, number_won=None)
def enable_pushover(self, token, user_key):
logger.debug("Enabling pushover notifications.")
@@ -27,13 +27,13 @@ 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}")
+ def __send(self, type_of_error, message, number_won=None):
+ logger.debug(f"Attempting to notify: '{message}'. Won: {number_won}")
if self.pushover:
logger.debug("Pushover enabled. Sending message.")
- self.__pushover(type_of_error, message)
+ self.__pushover(type_of_error, message, number_won)
- def __pushover(self, type_of_error, message):
+ def __pushover(self, type_of_error, message, number_won=None):
conn = http.client.HTTPSConnection("api.pushover.net:443")
conn.request("POST", "/1/messages.json",
urllib.parse.urlencode({
@@ -48,4 +48,4 @@ class Notification:
else:
logger.error(f"Pushover notification failed. Code {response.getcode()}: {response.read().decode()}")
success = False
- NotificationHelper.insert(type_of_error, f"{message}", 'pushover', success)
+ NotificationHelper.insert(type_of_error, f"{message}", 'pushover', success, number_won)
From f3e6c165f4e248976396be1d3b22f02efba3e8f3 Mon Sep 17 00:00:00 2001
From: mcinj <98779161+mcinj@users.noreply.github.com>
Date: Thu, 26 May 2022 17:03:58 -0400
Subject: [PATCH 06/16] clean up html template
---
src/web/templates/base.html | 26 ++++++++++++++++
src/web/templates/configuration.html | 32 ++++----------------
src/web/templates/giveaways.html | 44 +++++++---------------------
src/web/templates/log.html | 32 ++++----------------
src/web/templates/notifications.html | 34 ++++-----------------
src/web/webserver_thread.py | 16 +++++-----
6 files changed, 63 insertions(+), 121 deletions(-)
create mode 100644 src/web/templates/base.html
diff --git a/src/web/templates/base.html b/src/web/templates/base.html
new file mode 100644
index 0000000..0992170
--- /dev/null
+++ b/src/web/templates/base.html
@@ -0,0 +1,26 @@
+
+
+
+ {% block title %} {% endblock %}
+
+
+
+
+
+
Steamgifts Bot
+
+
+
+
+ {% block content %} {% endblock %}
+
+
+
\ No newline at end of file
diff --git a/src/web/templates/configuration.html b/src/web/templates/configuration.html
index 670abb9..0e9979f 100644
--- a/src/web/templates/configuration.html
+++ b/src/web/templates/configuration.html
@@ -1,26 +1,6 @@
-
-
-
- {{name}} Steamgifts Bot Configuration
-
-
-
-
-
-
Steamgifts Bot
-
-
-
-
-
-
\ No newline at end of file
+{% extends 'base.html' %}
+
+{% block title %} {{name}} Steamgifts Bot Configuration {% endblock %}
+{% block content %}
+ {{data}}
+{% endblock %}
\ No newline at end of file
diff --git a/src/web/templates/giveaways.html b/src/web/templates/giveaways.html
index d33163c..b3e5f64 100644
--- a/src/web/templates/giveaways.html
+++ b/src/web/templates/giveaways.html
@@ -1,35 +1,15 @@
-
-
-
- {{name}} Steamgifts Bot DB View
-
-
-
-
-
-
- {% for row in content.items %}
+ {% for row in data.items %}
{{row.giveaway_id}} |
{{row.steam_id}} |
@@ -70,6 +50,4 @@
{% endfor %}
-
-
-
\ No newline at end of file
+{% endblock %}
\ No newline at end of file
diff --git a/src/web/templates/log.html b/src/web/templates/log.html
index 8a7408f..70960cf 100644
--- a/src/web/templates/log.html
+++ b/src/web/templates/log.html
@@ -1,26 +1,6 @@
-
-
-
- {{name}} Steamgifts Bot {{log_type}} Logs
-
-
-
-
-
-
Steamgifts Bot
-
-
-
-
-
-
\ No newline at end of file
+{% extends 'base.html' %}
+
+{% block title %} {{name}} Steamgifts Bot {{log_type}} Logs{% endblock %}
+{% block content %}
+ {{data}}
+{% endblock %}
\ No newline at end of file
diff --git a/src/web/templates/notifications.html b/src/web/templates/notifications.html
index b11fe88..6a9b518 100644
--- a/src/web/templates/notifications.html
+++ b/src/web/templates/notifications.html
@@ -1,28 +1,8 @@
-
-
-
- {{name}} Steamgifts Bot DB View
-
-
-
-
-
-
+{% extends 'base.html' %}
+
+{% block title %} {{name}} Steamgifts Bot Notifications {% endblock %}
+{% block content %}
+
ID |
@@ -46,6 +26,4 @@
{% endfor %}
-
-
-
\ No newline at end of file
+{% endblock %}
\ No newline at end of file
diff --git a/src/web/webserver_thread.py b/src/web/webserver_thread.py
index 9cf74b8..f517722 100644
--- a/src/web/webserver_thread.py
+++ b/src/web/webserver_thread.py
@@ -42,20 +42,20 @@ class WebServerThread(threading.Thread):
@app.route(f"{self.app_root}")
def config():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/config.ini", 'r') as f:
- content = f.read()
- return render_template('configuration.html', name=self.prefix, content=content)
+ data = f.read()
+ return render_template('configuration.html', name=self.prefix, data=data)
@app.route(f"{self.app_root}log_info")
def log_info():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/info.log", 'r') as f:
- content = f.read()
- return render_template('log.html', name=self.prefix, log_type='info', content=content)
+ data = f.read()
+ return render_template('log.html', name=self.prefix, log_type='info', data=data)
@app.route(f"{self.app_root}log_debug")
def log_debug():
with open(f"{os.getenv('BOT_CONFIG_DIR', './config')}/debug.log", 'r') as f:
- content = f.read()
- return render_template('log.html', name=self.prefix, log_type='debug', content=content)
+ data = f.read()
+ return render_template('log.html', name=self.prefix, log_type='debug', data=data)
@app.route(f"{self.app_root}alive")
def alive():
@@ -63,12 +63,12 @@ class WebServerThread(threading.Thread):
@app.route(f"{self.app_root}notifications")
def db_notifications():
- return render_template('notifications.html', name=self.prefix, content=NotificationHelper.get())
+ return render_template('notifications.html', name=self.prefix, data=NotificationHelper.get())
@app.route(f"{self.app_root}giveaways", methods=['GET'], defaults={"page": 1})
@app.route(f"{self.app_root}giveaways/", methods=['GET'])
def db_giveaways(page):
- return render_template('giveaways.html', name=self.prefix, content=GiveawayHelper.paginate(page=page))
+ return render_template('giveaways.html', name=self.prefix, data=GiveawayHelper.paginate(page=page))
if self.enabled:
logger.info("Webserver Enabled. Running")
From 8010bbd750fc8c7f9a326a1715d921bf494189f1 Mon Sep 17 00:00:00 2001
From: mcinj <98779161+mcinj@users.noreply.github.com>
Date: Wed, 1 Jun 2022 07:58:06 -0400
Subject: [PATCH 07/16] stats
---
src/bot/database.py | 21 ++++++++++++++++-----
src/web/templates/base.html | 1 +
src/web/templates/stats.html | 9 +++++++++
src/web/webserver_thread.py | 6 ++++++
4 files changed, 32 insertions(+), 5 deletions(-)
create mode 100644 src/web/templates/stats.html
diff --git a/src/bot/database.py b/src/bot/database.py
index ff42668..65ebbe0 100644
--- a/src/bot/database.py
+++ b/src/bot/database.py
@@ -6,7 +6,7 @@ import sqlalchemy
from alembic import command
from alembic.config import Config
from dateutil import tz
-from sqlalchemy import func
+from sqlalchemy import func, select
from sqlalchemy.orm import Session, joinedload
from sqlalchemy_utils import database_exists
@@ -25,7 +25,7 @@ def run_db_migrations(script_location: str, db_url: str) -> None:
alembic_cfg.set_main_option('sqlalchemy.url', db_url)
if not database_exists(db_url):
- logger.debug(f"'{db_url}' does not exist. Running normal migration to create db and tables." )
+ logger.debug(f"'{db_url}' does not exist. Running normal migration to create db and tables.")
command.upgrade(alembic_cfg, 'head')
elif database_exists(db_url):
logger.debug(f"'{db_url}' exists.")
@@ -94,7 +94,7 @@ class GiveawayHelper:
@classmethod
def get(cls):
with Session(engine) as session:
- return session.query(TableGiveaway).options(joinedload('steam_item'))\
+ return session.query(TableGiveaway).options(joinedload('steam_item')) \
.order_by(TableGiveaway.giveaway_ended_at.desc()).all()
@classmethod
@@ -102,9 +102,20 @@ class GiveawayHelper:
with Session(engine) as session:
paginated_giveaways = paginate_sqlalchemy.SqlalchemyOrmPage(session.query(TableGiveaway)
.options(joinedload('steam_item'))
- .order_by(TableGiveaway.giveaway_ended_at.desc()), page=page)
+ .order_by(
+ TableGiveaway.giveaway_ended_at.desc()), page=page)
return paginated_giveaways
+ @classmethod
+ def total_giveaways(cls):
+ with Session(engine) as session:
+ return session.execute(select(func.count(TableGiveaway.giveaway_id))).scalar_one()
+
+ @classmethod
+ def total_entered(cls):
+ with Session(engine) as session:
+ return session.query(TableGiveaway).filter_by(entered=True).count()
+
@classmethod
def unix_timestamp_to_utc_datetime(cls, timestamp):
return datetime.utcfromtimestamp(timestamp)
@@ -164,4 +175,4 @@ class GiveawayHelper:
entered=entered,
game_entries=giveaway.game_entries)
session.merge(g)
- session.commit()
\ No newline at end of file
+ session.commit()
diff --git a/src/web/templates/base.html b/src/web/templates/base.html
index 0992170..6922545 100644
--- a/src/web/templates/base.html
+++ b/src/web/templates/base.html
@@ -11,6 +11,7 @@