Compare commits
7 Commits
b8fb30a2ae
...
924c4c547f
| Author | SHA1 | Date | |
|---|---|---|---|
| 924c4c547f | |||
| d7fdc1e183 | |||
| bd6f94a976 | |||
| 32596ab4e5 | |||
| 6f4c6b33f2 | |||
| ecdd399e27 | |||
| 3bbfc140ef |
@ -26,10 +26,15 @@ def create_app(test_config: None = None) -> Flask:
|
|||||||
app.config.from_pyfile('redeems.py', silent=True)
|
app.config.from_pyfile('redeems.py', silent=True)
|
||||||
|
|
||||||
# set up polls if they're enabled
|
# set up polls if they're enabled
|
||||||
if app.config['POLLS']:
|
if app.config['POLLS_ENABLED']:
|
||||||
app.config.from_object('tlapbot.defaults.default_polls')
|
app.config.from_object('tlapbot.defaults.default_polls')
|
||||||
app.config.from_pyfile('polls.py', silent=True)
|
app.config.from_pyfile('polls.py', silent=True)
|
||||||
|
|
||||||
|
for poll in app.config['POLLS']:
|
||||||
|
if ' ' in poll:
|
||||||
|
app.logger.warning(f"Poll '{poll}' has spaces in its name.")
|
||||||
|
app.logger.warning("Poll with spaces are impossible to redeem.")
|
||||||
|
|
||||||
# Make logging work for gunicorn-ran instances of tlapbot.
|
# Make logging work for gunicorn-ran instances of tlapbot.
|
||||||
if app.config['GUNICORN']:
|
if app.config['GUNICORN']:
|
||||||
gunicorn_logger = logging.getLogger('gunicorn.error')
|
gunicorn_logger = logging.getLogger('gunicorn.error')
|
||||||
@ -62,6 +67,8 @@ def create_app(test_config: None = None) -> Flask:
|
|||||||
app.cli.add_command(db.refresh_milestones_command)
|
app.cli.add_command(db.refresh_milestones_command)
|
||||||
app.cli.add_command(db.reset_milestone_command)
|
app.cli.add_command(db.reset_milestone_command)
|
||||||
app.cli.add_command(db.hard_reset_milestone_command)
|
app.cli.add_command(db.hard_reset_milestone_command)
|
||||||
|
app.cli.add_command(db.refresh_polls_command)
|
||||||
|
app.cli.add_command(db.reset_poll_command)
|
||||||
|
|
||||||
# scheduler job for giving points to users
|
# scheduler job for giving points to users
|
||||||
def proxy_job() -> None:
|
def proxy_job() -> None:
|
||||||
|
|||||||
@ -18,7 +18,7 @@ def get_db() -> sqlite3.Connection:
|
|||||||
return g.db
|
return g.db
|
||||||
|
|
||||||
|
|
||||||
def close_db() -> None:
|
def close_db(*_args) -> None:
|
||||||
db: sqlite3.Connection = g.pop('db', None)
|
db: sqlite3.Connection = g.pop('db', None)
|
||||||
|
|
||||||
if db is not None:
|
if db is not None:
|
||||||
@ -92,12 +92,24 @@ def refresh_counters() -> bool:
|
|||||||
return insert_counters(db)
|
return insert_counters(db)
|
||||||
|
|
||||||
|
|
||||||
|
def populate_poll_options(db: sqlite3.Connection, poll: str) -> bool:
|
||||||
|
for option in current_app.config['POLLS'][poll]['options']:
|
||||||
|
db.execute(
|
||||||
|
"INSERT INTO polls(poll_name, points, option) VALUES(?, ?, ?)",
|
||||||
|
(poll, 0, option)
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def refresh_polls() -> bool:
|
def refresh_polls() -> bool:
|
||||||
db = get_db()
|
db = get_db()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
db.execute("DELETE FROM polls")
|
db.execute("DELETE FROM polls")
|
||||||
db.commit()
|
db.commit()
|
||||||
|
for poll in current_app.config['POLLS']:
|
||||||
|
populate_poll_options(db, poll)
|
||||||
|
db.commit()
|
||||||
except sqlite3.Error as e:
|
except sqlite3.Error as e:
|
||||||
print("Error occurred deleting old counters:", e.args[0])
|
print("Error occurred deleting old counters:", e.args[0])
|
||||||
return False
|
return False
|
||||||
@ -105,16 +117,19 @@ def refresh_polls() -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def reset_poll(poll: str) -> bool:
|
def reset_poll(poll: str) -> bool:
|
||||||
|
if poll not in current_app.config['POLLS']:
|
||||||
|
print(f"Failed resetting poll, {poll} not in polls file.")
|
||||||
|
return False
|
||||||
try:
|
try:
|
||||||
db = get_db()
|
db = get_db()
|
||||||
db.execute(
|
db.execute(
|
||||||
"UPDATE polls SET points = 0 WHERE poll_name = ?",
|
"DELETE FROM polls WHERE poll_name = ?",
|
||||||
(poll,)
|
(poll,)
|
||||||
)
|
)
|
||||||
db.commit()
|
populate_poll_options(db, poll)
|
||||||
return True
|
return True
|
||||||
except sqlite3.Error as e:
|
except sqlite3.Error as e:
|
||||||
current_app.logger.error(f"Error occurred adding a milestone: {e.args[0]}")
|
current_app.logger.error(f"Error occurred resetting poll: {e.args[0]}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -257,8 +272,8 @@ def refresh_polls_command() -> None:
|
|||||||
click.echo('Refreshed polls.')
|
click.echo('Refreshed polls.')
|
||||||
|
|
||||||
|
|
||||||
@click.command('reset-polls')
|
@click.command('reset-poll')
|
||||||
@click.argument('milestone')
|
@click.argument('poll')
|
||||||
def reset_poll_command(poll: str) -> None:
|
def reset_poll_command(poll: str) -> None:
|
||||||
"""Resets polls progress back to zero."""
|
"""Resets polls progress back to zero."""
|
||||||
if reset_poll(poll):
|
if reset_poll(poll):
|
||||||
|
|||||||
@ -8,4 +8,4 @@ LIST_REDEEMS=False
|
|||||||
ACTIVE_CATEGORIES=[]
|
ACTIVE_CATEGORIES=[]
|
||||||
GUNICORN=False
|
GUNICORN=False
|
||||||
PREFIX='!'
|
PREFIX='!'
|
||||||
POLLS=True
|
POLLS_ENABLED=True
|
||||||
@ -6,9 +6,10 @@ from tlapbot.redeems.polls import poll_option_exists, vote_in_poll
|
|||||||
|
|
||||||
|
|
||||||
def handle_poll_vote(message: str, user_id: str) -> None:
|
def handle_poll_vote(message: str, user_id: str) -> None:
|
||||||
split_message = message[5:].split(maxsplit=1)
|
split_message = message[5:].split(maxsplit=2)
|
||||||
if len(split_message < 3):
|
if len(split_message) < 3:
|
||||||
send_chat("Can't vote for poll, not enough arguments in message.")
|
send_chat("Can't vote for poll, not enough arguments in message.")
|
||||||
|
return
|
||||||
poll_name = split_message[0]
|
poll_name = split_message[0]
|
||||||
option = split_message[1]
|
option = split_message[1]
|
||||||
poll_points = split_message[2]
|
poll_points = split_message[2]
|
||||||
@ -24,8 +25,10 @@ def handle_poll_vote(message: str, user_id: str) -> None:
|
|||||||
if not user_points:
|
if not user_points:
|
||||||
send_chat(f"Can't vote in {poll_name} poll, failed to read users' points.")
|
send_chat(f"Can't vote in {poll_name} poll, failed to read users' points.")
|
||||||
return
|
return
|
||||||
|
poll_points = int(poll_points)
|
||||||
if user_points < poll_points:
|
if user_points < poll_points:
|
||||||
send_chat(f"Can't vote in {poll_name} poll, you're voting with more points than you have.")
|
send_chat(f"Can't vote in {poll_name} poll, you're voting with more points than you have.")
|
||||||
|
return
|
||||||
if (vote_in_poll(db, poll_name, option, poll_points) and
|
if (vote_in_poll(db, poll_name, option, poll_points) and
|
||||||
use_points(db, user_id, poll_points)):
|
use_points(db, user_id, poll_points)):
|
||||||
send_chat(f"{poll_points} donated to {option} in poll {poll_name}")
|
send_chat(f"{poll_points} points donated to {option} in poll {poll_name}")
|
||||||
@ -2,7 +2,7 @@ from flask import current_app
|
|||||||
from sqlite3 import Error, Connection
|
from sqlite3 import Error, Connection
|
||||||
from typing import Tuple, Any
|
from typing import Tuple, Any
|
||||||
from tlapbot.owncast_helpers import use_points
|
from tlapbot.owncast_helpers import use_points
|
||||||
from tlapbot.redeems import is_redeem_active
|
from tlapbot.redeems.redeems import is_redeem_active
|
||||||
|
|
||||||
|
|
||||||
# TODO: add a milestone_exists check?
|
# TODO: add a milestone_exists check?
|
||||||
|
|||||||
@ -2,6 +2,43 @@ from flask import current_app
|
|||||||
from sqlite3 import Error, Connection
|
from sqlite3 import Error, Connection
|
||||||
|
|
||||||
|
|
||||||
|
def get_poll_votes(db: Connection, poll_name: str) -> dict[str, int] | None:
|
||||||
|
"""Get all poll votes and point values for a given `poll_name`"""
|
||||||
|
try:
|
||||||
|
options = {}
|
||||||
|
cursor = db.execute(
|
||||||
|
"SELECT option, points from polls WHERE poll_name = ?",
|
||||||
|
(poll_name,)
|
||||||
|
)
|
||||||
|
for row in cursor:
|
||||||
|
options[row[0]] = row[1]
|
||||||
|
return options
|
||||||
|
except Error as e:
|
||||||
|
current_app.logger.error(f"Error occurred getting poll votes: {e.args[0]}")
|
||||||
|
current_app.logger.error(f"For poll {poll_name}")
|
||||||
|
|
||||||
|
|
||||||
|
def all_active_polls(db: Connection) -> dict[str, dict[str, int]] | None:
|
||||||
|
"""Returns a dict where the keys are poll names, values is a dict of options and points.
|
||||||
|
Returns None if polls aren't enabled."""
|
||||||
|
if not current_app.config['POLLS_ENABLED']:
|
||||||
|
return None
|
||||||
|
polls = current_app.config['POLLS']
|
||||||
|
poll_votes = {}
|
||||||
|
for poll_name in polls.keys():
|
||||||
|
poll_votes[poll_name] = get_poll_votes(db, poll_name)
|
||||||
|
return poll_votes
|
||||||
|
|
||||||
|
|
||||||
|
def max_poll_votes(poll_dict: dict[str, int]) -> int:
|
||||||
|
"""Returns the maximum number of poll votes/points. Input is the output of `get_poll_votes`"""
|
||||||
|
highest_points = 0
|
||||||
|
for _, points in poll_dict.items():
|
||||||
|
if points > highest_points:
|
||||||
|
highest_points = points
|
||||||
|
return highest_points
|
||||||
|
|
||||||
|
|
||||||
def poll_option_exists(db: Connection, poll_name: str, option: str) -> bool | None:
|
def poll_option_exists(db: Connection, poll_name: str, option: str) -> bool | None:
|
||||||
"""Returns None only if error was logged."""
|
"""Returns None only if error was logged."""
|
||||||
try:
|
try:
|
||||||
@ -28,11 +65,11 @@ def vote_in_poll(db: Connection, poll_name: str, option: str, points: int) -> bo
|
|||||||
if poll_option_exists(db, poll_name, option):
|
if poll_option_exists(db, poll_name, option):
|
||||||
try:
|
try:
|
||||||
cursor = db.execute(
|
cursor = db.execute(
|
||||||
"UPDATE polls SET progress = progress + ? WHERE poll_name = ? and option = ?",
|
"UPDATE polls SET points = points + ? WHERE poll_name = ? and option = ?",
|
||||||
(points, poll_name, option)
|
(points, poll_name, option)
|
||||||
)
|
)
|
||||||
db.commit()
|
db.commit()
|
||||||
return True
|
return True
|
||||||
except Error as e:
|
except Error as e:
|
||||||
current_app.logger.error(f"Error occurred updating milestone: {e.args[0]}")
|
current_app.logger.error(f"Error occurred updating poll: {e.args[0]}")
|
||||||
return False
|
return False
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
DROP TABLE IF EXISTS counters;
|
DROP TABLE IF EXISTS counters;
|
||||||
DROP TABLE IF EXISTS redeem_queue;
|
DROP TABLE IF EXISTS redeem_queue;
|
||||||
DROP TABLE IF EXISTS milestones;
|
DROP TABLE IF EXISTS milestones;
|
||||||
|
DROP TABLE IF EXISTS polls;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS points (
|
CREATE TABLE IF NOT EXISTS points (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
@ -34,5 +35,5 @@ CREATE TABLE polls (
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
points INTEGER NOT NULL,
|
points INTEGER NOT NULL,
|
||||||
option TEXT NOT NULL,
|
option TEXT NOT NULL,
|
||||||
poll_name TEXT NOT NULL,
|
poll_name TEXT NOT NULL
|
||||||
)
|
);
|
||||||
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h3>Redeems Dashboard</h3>
|
<h3>Redeems Dashboard</h3>
|
||||||
{% if (username and users ) %}
|
{% if (username and users) %}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -47,6 +47,31 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if not passive %}
|
{% if not passive %}
|
||||||
|
{% if polls %}
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th colspan="2">Active polls</th>
|
||||||
|
<th colspan="2">Points</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for poll_name, poll_dict in polls.items() %}
|
||||||
|
{% for option in poll_dict.keys() %}
|
||||||
|
{% set max_vote = max_poll_votes(poll_dict) %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ poll_name }} </td>
|
||||||
|
<td> {{ option }} </td>
|
||||||
|
<td> {{ poll_dict[option] }} </td>
|
||||||
|
<td> <progress id="file" max={{ max_vote }} value={{ poll_dict[option] }}></progress></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
{% if counters %}
|
{% if counters %}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ from tlapbot.db import get_db
|
|||||||
from tlapbot.redeems.redeems import all_active_redeems, pretty_redeem_queue
|
from tlapbot.redeems.redeems import all_active_redeems, pretty_redeem_queue
|
||||||
from tlapbot.redeems.counters import all_active_counters
|
from tlapbot.redeems.counters import all_active_counters
|
||||||
from tlapbot.redeems.milestones import all_active_milestones
|
from tlapbot.redeems.milestones import all_active_milestones
|
||||||
|
from tlapbot.redeems.polls import all_active_polls, max_poll_votes
|
||||||
from tlapbot.owncast_helpers import read_all_users_with_username
|
from tlapbot.owncast_helpers import read_all_users_with_username
|
||||||
from datetime import timezone
|
from datetime import timezone
|
||||||
|
|
||||||
@ -22,7 +23,9 @@ def dashboard() -> str:
|
|||||||
queue=pretty_redeem_queue(db),
|
queue=pretty_redeem_queue(db),
|
||||||
counters=all_active_counters(db),
|
counters=all_active_counters(db),
|
||||||
milestones=all_active_milestones(db),
|
milestones=all_active_milestones(db),
|
||||||
|
polls=all_active_polls(db),
|
||||||
redeems=all_active_redeems(),
|
redeems=all_active_redeems(),
|
||||||
|
max_poll_votes=max_poll_votes,
|
||||||
prefix=current_app.config['PREFIX'],
|
prefix=current_app.config['PREFIX'],
|
||||||
passive=current_app.config['PASSIVE'],
|
passive=current_app.config['PASSIVE'],
|
||||||
username=username,
|
username=username,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user