Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acbc93bedd |
@@ -329,8 +329,7 @@ REDEEMS={
|
||||
```
|
||||
#### File format
|
||||
`redeems.py` is a config file with just a `REDEEMS` key, that assigns a dictionary of redeems to it.
|
||||
Each dictionary entry is a redeem, and the dictionary keys are strings that decide the chat command for the redeem.
|
||||
The redeem names shouldn't have spaces in them.
|
||||
Each dictionary entry is a redeem, and the dictionary keys are strings that decides the chat command for the redeem.
|
||||
The value is another dictionary that needs to have an entry for `"type"` and
|
||||
an entry for `"price"` for non-milestones or `"goal"` for milestones.
|
||||
Optionally, each redeem can also have `"info"` and `"category"` entries.
|
||||
|
||||
@@ -2,7 +2,7 @@ from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name='tlapbot',
|
||||
version='1.2.2',
|
||||
version='1.2.1',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
|
||||
+9
-7
@@ -2,9 +2,12 @@ import os
|
||||
import logging
|
||||
from flask import Flask
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from datetime import datetime
|
||||
from tlapbot.db import get_db
|
||||
from tlapbot.owncast_requests import is_stream_live, give_points_to_chat
|
||||
|
||||
from tlapbot.redeems import remove_inactive_redeems
|
||||
from tlapbot.helpers import (get_last_online_time, delete_last_online_time,
|
||||
save_last_online_time)
|
||||
|
||||
def create_app(test_config=None):
|
||||
app = Flask(__name__, instance_relative_config=True)
|
||||
@@ -36,12 +39,6 @@ def create_app(test_config=None):
|
||||
raise RuntimeError("Prefix is >1 character. "
|
||||
"Change your config to set 1-character prefix.")
|
||||
|
||||
# Check for spaces in redeems (they won't work)
|
||||
for redeem in app.config['REDEEMS']:
|
||||
if ' ' in redeem:
|
||||
app.logger.warning(f"Redeem '{redeem}' has spaces in its name.")
|
||||
app.logger.warning("Redeems with spaces are impossible to redeem.")
|
||||
|
||||
# prepare webhooks and redeem dashboard blueprints
|
||||
from . import owncast_webhooks
|
||||
from . import tlapbot_dashboard
|
||||
@@ -62,9 +59,14 @@ def create_app(test_config=None):
|
||||
def proxy_job():
|
||||
with app.app_context():
|
||||
if is_stream_live():
|
||||
if get_last_online_time:
|
||||
delete_last_online_time()
|
||||
app.logger.info("Stream is LIVE. Giving points to chat.")
|
||||
give_points_to_chat(get_db())
|
||||
else:
|
||||
if not get_last_online_time:
|
||||
# TODO: error state
|
||||
save_last_online_time(get_db(), datetime.now(), False)
|
||||
app.logger.info("Stream is NOT LIVE. (Not giving points to chat.)")
|
||||
|
||||
# start scheduler that will give points to users
|
||||
|
||||
+2
-2
@@ -6,7 +6,6 @@ from flask.cli import with_appcontext
|
||||
|
||||
from tlapbot.redeems import milestone_complete
|
||||
|
||||
|
||||
def get_db():
|
||||
if 'db' not in g:
|
||||
g.db = sqlite3.connect(
|
||||
@@ -128,7 +127,7 @@ def refresh_milestones():
|
||||
|
||||
|
||||
def reset_milestone(milestone):
|
||||
if milestone not in current_app.config['REDEEMS']:
|
||||
if not milestone in current_app.config['REDEEMS']:
|
||||
print(f"Failed resetting milestone, {milestone} not in redeems file.")
|
||||
return False
|
||||
try:
|
||||
@@ -148,6 +147,7 @@ def reset_milestone(milestone):
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@click.command('init-db')
|
||||
@with_appcontext
|
||||
def init_db_command():
|
||||
|
||||
+9
-16
@@ -4,27 +4,20 @@ from tlapbot.owncast_requests import send_chat
|
||||
|
||||
def send_help():
|
||||
message = []
|
||||
message.append("Tlapbot gives you points for being in chat, and then allows you to spend those points. <br>")
|
||||
message.append(f"People connected to chat receive {current_app.config['POINTS_AMOUNT_GIVEN']} points every {current_app.config['POINTS_CYCLE_TIME']} seconds. <br>")
|
||||
message.append("You can see your points and recent redeems in the Tlapbot dashboard. Look for a button to click under the stream window. <br>")
|
||||
message.append("""Tlapbot commands: <br>
|
||||
!help to see this help message. <br>
|
||||
!points to see your points. <br>"""
|
||||
message.append("Tlapbot gives you points for being in chat, and then allows you to spend those points.\n")
|
||||
message.append(f"People connected to chat receive {current_app.config['POINTS_AMOUNT_GIVEN']} points every {current_app.config['POINTS_CYCLE_TIME']} seconds.\n")
|
||||
message.append("You can see your points and recent redeems in the Tlapbot dashboard. Look for a button to click under the stream window.\n")
|
||||
message.append("""Tlapbot commands:
|
||||
!help to see this help message.
|
||||
!points to see your points.\n"""
|
||||
)
|
||||
if current_app.config['LIST_REDEEMS']:
|
||||
message.append("Active redeems: <br>")
|
||||
message.append("Active redeems:\n")
|
||||
for redeem, redeem_info in current_app.config['REDEEMS'].items():
|
||||
if redeem_info.get('category', None):
|
||||
if not set(redeem_info['category']).intersection(set(current_app.config['ACTIVE_CATEGORIES'])):
|
||||
continue
|
||||
if 'type' in redeem_info and redeem_info['type'] == 'milestone':
|
||||
message.append(f"!{redeem} milestone with goal of {redeem_info['goal']}.")
|
||||
else:
|
||||
message.append(f"!{redeem} for {redeem_info['price']} points.")
|
||||
if 'info' in redeem_info:
|
||||
message.append(f" {redeem_info['info']} <br>")
|
||||
message.append(f"!{redeem} for {redeem_info['price']} points. {redeem_info['info']}\n")
|
||||
else:
|
||||
message.append("<br>")
|
||||
message.append(f"!{redeem} for {redeem_info['price']} points.\n")
|
||||
else:
|
||||
message.append("Check the dashboard for a list of currently active redeems.")
|
||||
send_chat(''.join(message))
|
||||
|
||||
@@ -2,7 +2,6 @@ from flask import current_app
|
||||
from sqlite3 import Error
|
||||
from re import sub
|
||||
|
||||
|
||||
# # # db stuff # # #
|
||||
def read_users_points(db, user_id):
|
||||
"""Errors out if user doesn't exist."""
|
||||
@@ -96,9 +95,40 @@ def add_user_to_database(db, user_id, display_name):
|
||||
current_app.logger.error(f"To user id: {user_id}, with display name: {display_name}")
|
||||
|
||||
|
||||
def change_display_name(db, user_id, new_name):
|
||||
def save_last_online_time(db, timestamp, from_owncast):
|
||||
try:
|
||||
db.execute(
|
||||
"INSERT OVERWRITE last_online_time(id, last_online_time, from_owncast)",
|
||||
(1, timestamp, from_owncast)
|
||||
)
|
||||
db.commit()
|
||||
except Error as e:
|
||||
current_app.logger.error(f"Error occured saving last online time: {e.args[0]}")
|
||||
current_app.logger.error(f"Timestamp: {timestamp}, from_owncast: {from_owncast}")
|
||||
|
||||
|
||||
def get_last_online_time(db):
|
||||
try:
|
||||
cursor = db.execute(
|
||||
"SELECT last_online_time FROM last_online_time WHERE id = 1"
|
||||
)
|
||||
last_online_time = cursor.fetchone()
|
||||
return last_online_time
|
||||
except Error as e:
|
||||
current_app.logger.error(f"Error occured reading last online time: {e.args[0]}")
|
||||
|
||||
|
||||
def delete_last_online_time(db):
|
||||
try:
|
||||
db.execute("DELETE FROM last_online_time")
|
||||
db.commit()
|
||||
except Error as e:
|
||||
current_app.logger.error(f"Error occured deleting last online time: {e.args[0]}")
|
||||
|
||||
|
||||
def change_display_name(db, user_id, new_name):
|
||||
try:
|
||||
cursor = db.execute(
|
||||
"UPDATE points SET name = ? WHERE id = ?",
|
||||
(new_name, user_id)
|
||||
)
|
||||
@@ -110,7 +140,7 @@ def change_display_name(db, user_id, new_name):
|
||||
|
||||
def remove_duplicate_usernames(db, user_id, username):
|
||||
try:
|
||||
db.execute(
|
||||
cursor = db.execute(
|
||||
"""UPDATE points
|
||||
SET name = NULL
|
||||
WHERE name = ? AND NOT id = ?""",
|
||||
@@ -122,7 +152,6 @@ def remove_duplicate_usernames(db, user_id, username):
|
||||
|
||||
|
||||
# # # misc. stuff # # #
|
||||
# This is now unused since rawBody attribute of the webhook now returns cleaned-up emotes.
|
||||
def remove_emoji(message):
|
||||
return sub(
|
||||
r'<img class="emoji" alt="(:.*?:)" title=":.*?:" src="/img/emoji/.*?">',
|
||||
|
||||
@@ -45,3 +45,4 @@ def send_chat(message):
|
||||
current_app.logger.error(f"Check owncast instance url and access key.")
|
||||
return
|
||||
return r.json()
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from flask import Flask, request, json, Blueprint, current_app
|
||||
from tlapbot.db import get_db
|
||||
from datetime import datetime
|
||||
from tlapbot.db import get_db, refresh_counters, clear_redeem_queue
|
||||
from tlapbot.owncast_requests import send_chat
|
||||
from tlapbot.owncast_helpers import (add_user_to_database, change_display_name,
|
||||
read_users_points, remove_duplicate_usernames)
|
||||
read_users_points, remove_duplicate_usernames, get_last_online_time, delete_last_online_time)
|
||||
from tlapbot.help_message import send_help
|
||||
from tlapbot.redeems_handler import handle_redeem
|
||||
|
||||
# might need datetime timestamp
|
||||
|
||||
bp = Blueprint('owncast_webhooks', __name__)
|
||||
|
||||
@@ -15,6 +17,22 @@ def owncast_webhook():
|
||||
data = request.json
|
||||
db = get_db()
|
||||
|
||||
if data["type"] == "STREAM_STARTED":
|
||||
# TODO: make this a function, import here and in init
|
||||
delete_last_online_time(db)
|
||||
last_online = get_last_online_time(db)
|
||||
if last_online and current_app.config['AUTO_REFRESH']:
|
||||
time_difference = datetime.now() - last_online
|
||||
if time_difference.seconds//60 > current_app.config['RECONNECT_TIME']:
|
||||
if refresh_counters() and clear_redeem_queue():
|
||||
current_app.logger.debug(f'Counters refreshed, redeem queue cleared.')
|
||||
else:
|
||||
current_app.logger.error(
|
||||
f'Error occured when automatically clearing queue and resetting counters.'
|
||||
)
|
||||
elif data["type"] == "STREAM_STOPPED":
|
||||
save_last_online_time(db, datetime.now(), True)
|
||||
|
||||
# Make sure user is in db before doing anything else.
|
||||
if data["type"] in ["CHAT", "NAME_CHANGED", "USER_JOINED"]:
|
||||
user_id = data["eventData"]["user"]["id"]
|
||||
@@ -36,22 +54,22 @@ def owncast_webhook():
|
||||
user_id = data["eventData"]["user"]["id"]
|
||||
display_name = data["eventData"]["user"]["displayName"]
|
||||
current_app.logger.debug(f'New chat message from {display_name}:')
|
||||
current_app.logger.debug(f'{data["eventData"]["rawBody"]}')
|
||||
if data["eventData"]["rawBody"].startswith(f"{prefix}help"):
|
||||
current_app.logger.debug(f'{data["eventData"]["body"]}')
|
||||
if data["eventData"]["body"].startswith(f"{prefix}help"):
|
||||
send_help()
|
||||
elif data["eventData"]["rawBody"].startswith(f"{prefix}points"):
|
||||
elif data["eventData"]["body"].startswith(f"{prefix}points"):
|
||||
points = read_users_points(db, user_id)
|
||||
if points is None:
|
||||
send_chat("Error reading points.")
|
||||
else:
|
||||
send_chat(f"{display_name}'s points: {points}")
|
||||
elif data["eventData"]["rawBody"].startswith(f"{prefix}name_update"):
|
||||
elif data["eventData"]["body"].startswith(f"{prefix}name_update"):
|
||||
# Forces name update in case bot didn't catch the NAME_CHANGE
|
||||
# event. Also removes saved usernames from users with same name
|
||||
# if user is authenticated.
|
||||
change_display_name(db, user_id, display_name)
|
||||
if data["eventData"]["user"]["authenticated"]:
|
||||
remove_duplicate_usernames(db, user_id, display_name)
|
||||
elif data["eventData"]["rawBody"].startswith(prefix):
|
||||
handle_redeem(data["eventData"]["rawBody"], user_id)
|
||||
elif data["eventData"]["body"].startswith(prefix):
|
||||
handle_redeem(data["eventData"]["body"], user_id)
|
||||
return data
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ def counter_exists(db, counter_name):
|
||||
def add_to_counter(db, counter_name):
|
||||
if counter_exists(db, counter_name):
|
||||
try:
|
||||
db.execute(
|
||||
cursor = db.execute(
|
||||
"UPDATE counters SET count = count + 1 WHERE name = ?",
|
||||
(counter_name,)
|
||||
)
|
||||
@@ -37,7 +37,7 @@ def add_to_counter(db, counter_name):
|
||||
|
||||
def add_to_redeem_queue(db, user_id, redeem_name, note=None):
|
||||
try:
|
||||
db.execute(
|
||||
cursor = db.execute(
|
||||
"INSERT INTO redeem_queue(redeem, redeemer_id, note) VALUES(?, ?, ?)",
|
||||
(redeem_name, user_id, note)
|
||||
)
|
||||
@@ -159,7 +159,7 @@ def all_active_milestones(db):
|
||||
return all_active_milestones
|
||||
|
||||
|
||||
def all_active_redeems():
|
||||
def all_active_redeems(db):
|
||||
redeems = current_app.config['REDEEMS']
|
||||
all_active_redeems = {}
|
||||
for redeem_name, redeem_dict in redeems.items():
|
||||
|
||||
@@ -3,7 +3,7 @@ from tlapbot.db import get_db
|
||||
from tlapbot.owncast_requests import send_chat
|
||||
from tlapbot.redeems import (add_to_redeem_queue, add_to_counter, add_to_milestone,
|
||||
check_apply_milestone_completion, milestone_complete, is_redeem_active)
|
||||
from tlapbot.owncast_helpers import use_points, read_users_points
|
||||
from tlapbot.owncast_helpers import use_points, read_users_points, remove_emoji
|
||||
|
||||
|
||||
def handle_redeem(message, user_id):
|
||||
@@ -32,11 +32,9 @@ def handle_redeem(message, user_id):
|
||||
elif not note:
|
||||
send_chat(f"Cannot redeem {redeem}, no amount of points specified.")
|
||||
elif not note.isdigit():
|
||||
send_chat(f"Cannot redeem {redeem}, amount of points is not a positive integer.")
|
||||
send_chat(f"Cannot redeem {redeem}, amount of points is not an integer.")
|
||||
elif int(note) > points:
|
||||
send_chat(f"Can't redeem {redeem}, you're donating more points than you have.")
|
||||
elif int(note) == 0:
|
||||
send_chat(f"Can't donate zero points.")
|
||||
elif add_to_milestone(db, user_id, redeem, int(note)):
|
||||
send_chat(f"Succesfully donated to {redeem} milestone!")
|
||||
if check_apply_milestone_completion(db, redeem):
|
||||
@@ -66,7 +64,7 @@ def handle_redeem(message, user_id):
|
||||
if not note:
|
||||
send_chat(f"Cannot redeem {redeem}, no note included.")
|
||||
return
|
||||
if (add_to_redeem_queue(db, user_id, redeem, note) and
|
||||
if (add_to_redeem_queue(db, user_id, redeem, remove_emoji(note)) and
|
||||
use_points(db, user_id, price)):
|
||||
send_chat(f"{redeem} redeemed for {price} points.")
|
||||
else:
|
||||
|
||||
@@ -30,3 +30,8 @@ CREATE TABLE redeem_queue (
|
||||
note TEXT,
|
||||
FOREIGN KEY (redeemer_id) REFERENCES points (id)
|
||||
);
|
||||
|
||||
CREATE TABLE online_time(
|
||||
id INTEGER PRIMARY KEY,
|
||||
online_time TIMESTAMP NOT NULL
|
||||
);
|
||||
@@ -2,7 +2,7 @@ from flask import render_template, Blueprint, request, current_app
|
||||
from tlapbot.db import get_db
|
||||
from tlapbot.redeems import all_active_counters, all_active_milestones, all_active_redeems, pretty_redeem_queue
|
||||
from tlapbot.owncast_helpers import read_all_users_with_username
|
||||
from datetime import timezone
|
||||
from datetime import datetime, timezone
|
||||
|
||||
bp = Blueprint('redeem_dashboard', __name__)
|
||||
|
||||
@@ -20,7 +20,7 @@ def dashboard():
|
||||
queue=pretty_redeem_queue(db),
|
||||
counters=all_active_counters(db),
|
||||
milestones=all_active_milestones(db),
|
||||
redeems=all_active_redeems(),
|
||||
redeems=all_active_redeems(db),
|
||||
prefix=current_app.config['PREFIX'],
|
||||
passive=current_app.config['PASSIVE'],
|
||||
username=username,
|
||||
|
||||
Reference in New Issue
Block a user