Merge pull request #3 from SleepyLili/improved-help
Improved help function
This commit is contained in:
commit
0182d323d2
2
setup.py
2
setup.py
|
@ -2,7 +2,7 @@ from setuptools import find_packages, setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='tlapbot',
|
name='tlapbot',
|
||||||
version='0.6.0',
|
version='0.6.1',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
|
|
|
@ -21,8 +21,8 @@ def create_app(test_config=None):
|
||||||
)
|
)
|
||||||
app.config.from_object('tlapbot.default_config')
|
app.config.from_object('tlapbot.default_config')
|
||||||
app.config.from_object('tlapbot.default_redeems')
|
app.config.from_object('tlapbot.default_redeems')
|
||||||
app.config.from_pyfile('config.py')
|
app.config.from_pyfile('config.py', silent=True)
|
||||||
app.config.from_pyfile('redeems.py')
|
app.config.from_pyfile('redeems.py', silent=True)
|
||||||
|
|
||||||
# prepare webhooks and redeem dashboard blueprints
|
# prepare webhooks and redeem dashboard blueprints
|
||||||
from . import owncast_webhooks
|
from . import owncast_webhooks
|
||||||
|
@ -30,14 +30,12 @@ def create_app(test_config=None):
|
||||||
app.register_blueprint(owncast_webhooks.bp)
|
app.register_blueprint(owncast_webhooks.bp)
|
||||||
app.register_blueprint(owncast_redeem_dashboard.bp)
|
app.register_blueprint(owncast_redeem_dashboard.bp)
|
||||||
|
|
||||||
# add db initialization CLI command
|
# add db CLI commands
|
||||||
from . import db
|
from . import db
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
app.cli.add_command(db.clear_queue_command)
|
||||||
# add clear queue CLI command
|
app.cli.add_command(db.refresh_counters_command)
|
||||||
from . import owncast_helpers
|
|
||||||
app.cli.add_command(owncast_helpers.clear_queue_command)
|
|
||||||
|
|
||||||
# scheduler job for giving points to users
|
# scheduler job for giving points to users
|
||||||
def proxy_job():
|
def proxy_job():
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
|
|
|
@ -27,7 +27,7 @@ def insert_counters(db):
|
||||||
for redeem, redeem_info in current_app.config['REDEEMS'].items():
|
for redeem, redeem_info in current_app.config['REDEEMS'].items():
|
||||||
if redeem_info["type"] == "counter":
|
if redeem_info["type"] == "counter":
|
||||||
try:
|
try:
|
||||||
cursor = db.execute(
|
db.execute(
|
||||||
"INSERT INTO counters(name, count) VALUES(?, 0)",
|
"INSERT INTO counters(name, count) VALUES(?, 0)",
|
||||||
(redeem,)
|
(redeem,)
|
||||||
)
|
)
|
||||||
|
@ -45,6 +45,42 @@ def init_db():
|
||||||
insert_counters(db)
|
insert_counters(db)
|
||||||
|
|
||||||
|
|
||||||
|
def clear_redeem_queue():
|
||||||
|
db = get_db()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cursor = db.execute(
|
||||||
|
"DELETE FROM redeem_queue"
|
||||||
|
)
|
||||||
|
cursor.execute(
|
||||||
|
"""UPDATE counters SET count = 0"""
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
except Error as e:
|
||||||
|
print("Error occured deleting redeem queue:", e.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_counters():
|
||||||
|
db = get_db()
|
||||||
|
|
||||||
|
try:
|
||||||
|
db.execute("DELETE FROM counters")
|
||||||
|
db.commit()
|
||||||
|
except Error as e:
|
||||||
|
print("Error occured deleting old counters:", e.args[0])
|
||||||
|
|
||||||
|
for redeem, redeem_info in current_app.config['REDEEMS'].items():
|
||||||
|
if redeem_info["type"] == "counter":
|
||||||
|
try:
|
||||||
|
cursor = db.execute(
|
||||||
|
"INSERT INTO counters(name, count) VALUES(?, 0)",
|
||||||
|
(redeem,)
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
except Error as e:
|
||||||
|
print("Failed inserting counters to db:", e.args[0])
|
||||||
|
|
||||||
|
|
||||||
@click.command('init-db')
|
@click.command('init-db')
|
||||||
@with_appcontext
|
@with_appcontext
|
||||||
def init_db_command():
|
def init_db_command():
|
||||||
|
@ -53,6 +89,22 @@ def init_db_command():
|
||||||
click.echo('Initialized the database.')
|
click.echo('Initialized the database.')
|
||||||
|
|
||||||
|
|
||||||
|
@click.command('clear-queue')
|
||||||
|
@with_appcontext
|
||||||
|
def clear_queue_command():
|
||||||
|
"""Remove all redeems from the redeem queue."""
|
||||||
|
clear_redeem_queue()
|
||||||
|
click.echo('Cleared redeem queue.')
|
||||||
|
|
||||||
|
|
||||||
|
@click.command('refresh-counters')
|
||||||
|
@with_appcontext
|
||||||
|
def refresh_counters_command():
|
||||||
|
"""Refresh counters from current config file. (Remove old ones, add new ones.)"""
|
||||||
|
refresh_counters()
|
||||||
|
click.echo('Counters refreshed.')
|
||||||
|
|
||||||
|
|
||||||
def init_app(app):
|
def init_app(app):
|
||||||
app.teardown_appcontext(close_db)
|
app.teardown_appcontext(close_db)
|
||||||
app.cli.add_command(init_db_command)
|
app.cli.add_command(init_db_command)
|
||||||
|
|
|
@ -2,4 +2,5 @@ SECRET_KEY='dev'
|
||||||
OWNCAST_ACCESS_TOKEN=''
|
OWNCAST_ACCESS_TOKEN=''
|
||||||
OWNCAST_INSTANCE_URL='http://localhost:8080'
|
OWNCAST_INSTANCE_URL='http://localhost:8080'
|
||||||
POINTS_CYCLE_TIME=600
|
POINTS_CYCLE_TIME=600
|
||||||
POINTS_AMOUNT_GIVEN=10
|
POINTS_AMOUNT_GIVEN=10
|
||||||
|
LIST_REDEEMS=False
|
|
@ -1,6 +1,6 @@
|
||||||
REDEEMS={
|
REDEEMS={
|
||||||
"hydrate": {"price": 60, "type": "list"},
|
"hydrate": {"price": 60, "type": "list"},
|
||||||
"lurk": {"price": 1, "type": "counter"},
|
"lurk": {"price": 1, "type": "counter", "info": "Let us know you're going to lurk."},
|
||||||
"react": {"price": 200, "type": "note"},
|
"react": {"price": 200, "type": "note", "info": "Attach link to a video for me to react to."},
|
||||||
"request": {"price": 100, "type": "note"}
|
"request": {"price": 100, "type": "note", "info": "Request a level, gamemode, skin, etc."}
|
||||||
}
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
from flask import current_app
|
||||||
|
from tlapbot.owncast_helpers 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.\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:\n")
|
||||||
|
for redeem, redeem_info in current_app.config['REDEEMS'].items():
|
||||||
|
if 'info' in redeem_info:
|
||||||
|
message.append(f"!{redeem} for {redeem_info['price']} points. {redeem_info['info']}\n")
|
||||||
|
else:
|
||||||
|
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))
|
|
@ -1,7 +1,6 @@
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
import requests
|
import requests
|
||||||
from sqlite3 import Error
|
from sqlite3 import Error
|
||||||
import click
|
|
||||||
from flask.cli import with_appcontext
|
from flask.cli import with_appcontext
|
||||||
from tlapbot.db import get_db
|
from tlapbot.db import get_db
|
||||||
|
|
||||||
|
@ -160,17 +159,6 @@ def add_to_redeem_queue(db, user_id, redeem_name, note=None):
|
||||||
print("To user:", user_id, " with redeem:", redeem_name, "with note:", note)
|
print("To user:", user_id, " with redeem:", redeem_name, "with note:", note)
|
||||||
|
|
||||||
|
|
||||||
def clear_redeem_queue(db):
|
|
||||||
try:
|
|
||||||
cursor = db.execute(
|
|
||||||
"DELETE FROM redeem_queue"
|
|
||||||
)
|
|
||||||
cursor.execute(
|
|
||||||
"""UPDATE counters SET count = 0"""
|
|
||||||
)
|
|
||||||
db.commit()
|
|
||||||
except Error as e:
|
|
||||||
print("Error occured deleting redeem queue:", e.args[0])
|
|
||||||
|
|
||||||
|
|
||||||
def all_counters(db):
|
def all_counters(db):
|
||||||
|
@ -217,11 +205,3 @@ def remove_duplicate_usernames(db, user_id, username):
|
||||||
db.commit()
|
db.commit()
|
||||||
except Error as e:
|
except Error as e:
|
||||||
print("Error occured removing duplicate usernames:", e.args[0])
|
print("Error occured removing duplicate usernames:", e.args[0])
|
||||||
|
|
||||||
|
|
||||||
@click.command('clear-queue')
|
|
||||||
@with_appcontext
|
|
||||||
def clear_queue_command():
|
|
||||||
"""Remove all redeems from the redeem queue."""
|
|
||||||
clear_redeem_queue(get_db())
|
|
||||||
click.echo('Cleared redeem queue.')
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from flask import render_template, Blueprint, request
|
from flask import render_template, Blueprint, request, current_app
|
||||||
from tlapbot.db import get_db
|
from tlapbot.db import get_db
|
||||||
from tlapbot.owncast_helpers import (pretty_redeem_queue, all_counters,
|
from tlapbot.owncast_helpers import (pretty_redeem_queue, all_counters,
|
||||||
read_all_users_with_username)
|
read_all_users_with_username)
|
||||||
|
@ -12,6 +12,7 @@ def dashboard():
|
||||||
db = get_db()
|
db = get_db()
|
||||||
queue = pretty_redeem_queue(db)
|
queue = pretty_redeem_queue(db)
|
||||||
counters = all_counters(db)
|
counters = all_counters(db)
|
||||||
|
redeems = current_app.config['REDEEMS']
|
||||||
username = request.args.get("username")
|
username = request.args.get("username")
|
||||||
if username is not None:
|
if username is not None:
|
||||||
users = read_all_users_with_username(db, username)
|
users = read_all_users_with_username(db, username)
|
||||||
|
@ -21,6 +22,7 @@ def dashboard():
|
||||||
return render_template('dashboard.html',
|
return render_template('dashboard.html',
|
||||||
queue=queue,
|
queue=queue,
|
||||||
counters=counters,
|
counters=counters,
|
||||||
|
redeems=redeems,
|
||||||
username=username,
|
username=username,
|
||||||
users=users,
|
users=users,
|
||||||
utc_timezone=utc_timezone)
|
utc_timezone=utc_timezone)
|
||||||
|
|
|
@ -3,8 +3,10 @@ from sqlite3 import Error
|
||||||
from tlapbot.db import get_db
|
from tlapbot.db import get_db
|
||||||
from tlapbot.owncast_helpers import (add_user_to_database, change_display_name,
|
from tlapbot.owncast_helpers import (add_user_to_database, change_display_name,
|
||||||
user_exists, send_chat, read_users_points, remove_duplicate_usernames)
|
user_exists, send_chat, read_users_points, remove_duplicate_usernames)
|
||||||
|
from tlapbot.help_message import send_help
|
||||||
from tlapbot.redeems_handler import handle_redeem
|
from tlapbot.redeems_handler import handle_redeem
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint('owncast_webhooks', __name__)
|
bp = Blueprint('owncast_webhooks', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,15 +33,7 @@ def owncast_webhook():
|
||||||
print(f'New chat message from {display_name}:')
|
print(f'New chat message from {display_name}:')
|
||||||
print(f'{data["eventData"]["body"]}')
|
print(f'{data["eventData"]["body"]}')
|
||||||
if "!help" in data["eventData"]["body"]:
|
if "!help" in data["eventData"]["body"]:
|
||||||
message = """Tlapbot commands:
|
send_help()
|
||||||
!help to see this help message.
|
|
||||||
!points to see your points.
|
|
||||||
!name_update to force name update if tlapbot didn't catch it.
|
|
||||||
Tlapbot redeems:\n"""
|
|
||||||
for redeem, redeem_info in current_app.config['REDEEMS'].items():
|
|
||||||
message += (f"!{redeem} for {redeem_info['price']} points.\n")
|
|
||||||
# TODO: also make this customizable
|
|
||||||
send_chat(message)
|
|
||||||
elif "!points" in data["eventData"]["body"]:
|
elif "!points" in data["eventData"]["body"]:
|
||||||
if not user_exists(db, user_id):
|
if not user_exists(db, user_id):
|
||||||
add_user_to_database(db, user_id, display_name)
|
add_user_to_database(db, user_id, display_name)
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
function openTab(event, tabName) {
|
||||||
|
var i, tabcontent, tablinks;
|
||||||
|
|
||||||
|
// Get all elements with class="tabcontent" and hide them
|
||||||
|
tabcontent = document.getElementsByClassName("tabcontent");
|
||||||
|
for (i = 0; i < tabcontent.length; i++) {
|
||||||
|
tabcontent[i].style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all elements with class="tablinks" and remove the class "active"
|
||||||
|
tablinks = document.getElementsByClassName("tablinks");
|
||||||
|
for (i = 0; i < tablinks.length; i++) {
|
||||||
|
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the current tab, and add an "active" class to the button that opened the tab
|
||||||
|
document.getElementById(tabName).style.display = "block";
|
||||||
|
evt.currentTarget.className += " active";
|
||||||
|
}
|
|
@ -38,6 +38,51 @@ th:last-child {
|
||||||
padding-right: 0
|
padding-right: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing:-.1rem;
|
||||||
|
margin-bottom:2.0rem;
|
||||||
|
margin-top:1.5rem
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size:4.6rem;
|
||||||
|
line-height:1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size:3.6rem;
|
||||||
|
line-height:1.25
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size:2.8rem;
|
||||||
|
line-height:1.3
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-size:2.2rem;
|
||||||
|
letter-spacing:-.08rem;
|
||||||
|
line-height:1.35
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
font-size:1.8rem;
|
||||||
|
letter-spacing:-.05rem;
|
||||||
|
line-height:1.5
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
font-size:1.6rem;
|
||||||
|
letter-spacing:0;
|
||||||
|
line-height:1.4
|
||||||
|
}
|
||||||
|
|
||||||
/* General style */
|
/* General style */
|
||||||
h1{font-size: 3.6rem; line-height: 1.25}
|
h1{font-size: 3.6rem; line-height: 1.25}
|
||||||
h2{font-size: 2.8rem; line-height: 1.3}
|
h2{font-size: 2.8rem; line-height: 1.3}
|
||||||
|
@ -56,4 +101,39 @@ pre{padding: 1em;}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
width: auto;
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the buttons that are used to open the tab content */
|
||||||
|
.tab button {
|
||||||
|
background-color: inherit;
|
||||||
|
float: left;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 14px 16px;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Change background color of buttons on hover */
|
||||||
|
.tab button:hover {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create an active/current tablink class */
|
||||||
|
.tab button.active {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style the tab content */
|
||||||
|
.tabcontent {
|
||||||
|
display: none;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-top: none;
|
||||||
|
}
|
|
@ -4,60 +4,116 @@
|
||||||
<head>
|
<head>
|
||||||
<title>Redeems Dashboard</title>
|
<title>Redeems Dashboard</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<div id="script">
|
||||||
{% if (username and users ) %}
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Points balance:</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{% for user in users %}
|
|
||||||
<tbody>
|
|
||||||
<td> {{ user[0] }} </td>
|
|
||||||
<td> {{ user[1] }} </td>
|
|
||||||
</tbody>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if counters %}
|
<script src="/static/dashboard.js"></script>
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Counters</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{% for counter in counters %}
|
|
||||||
<tbody>
|
|
||||||
<td> {{ counter[0] }} </td>
|
|
||||||
<td> {{ counter[1] }} </td>
|
|
||||||
</tbody>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if queue %}
|
<div class="tab">
|
||||||
<table>
|
<button class="tablinks" onclick="openTab(event, 'dashboard')", id="defaultOpen">Tlapbot dashboard</button>
|
||||||
<thead>
|
<button class="tablinks" onclick="openTab(event, 'redeems-list')">Redeems help</button>
|
||||||
<tr>
|
</div>
|
||||||
<th>time</th>
|
|
||||||
<th>redeem</th>
|
<div id='dashboard' class="tabcontent">
|
||||||
<th>redeemer</th>
|
<body>
|
||||||
<th>note</th>
|
<h3>Redeems Dashboard</h3>
|
||||||
</tr>
|
{% if (username and users ) %}
|
||||||
</thead>
|
<table>
|
||||||
{% for row in queue %}
|
<thead>
|
||||||
<tbody>
|
<tr>
|
||||||
<td>{{ row[0].replace(tzinfo=utc_timezone).astimezone().strftime("%H:%M") }}</td>
|
<th>Your points balance</th>
|
||||||
<td>{{ row[1] }}</td>
|
</tr>
|
||||||
<td>{{ row[3] }}</td>
|
</thead>
|
||||||
{% if row[2] %}
|
{% for user in users %}
|
||||||
<td>{{ row[2] }}</td>
|
<tbody>
|
||||||
|
<td> {{ user[0] }} </td>
|
||||||
|
<td> {{ user[1] }} </td>
|
||||||
|
</tbody>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tbody>
|
|
||||||
{% endfor %}
|
{% if counters %}
|
||||||
</table>
|
<table>
|
||||||
{% endif %}
|
<thead>
|
||||||
</body>
|
<tr>
|
||||||
|
<th>Active counters</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for counter in counters %}
|
||||||
|
<tbody>
|
||||||
|
<td> {{ counter[0] }} </td>
|
||||||
|
<td> {{ counter[1] }} </td>
|
||||||
|
</tbody>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if queue %}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Recent redeems</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Redeem</th>
|
||||||
|
<th>Redeemer</th>
|
||||||
|
<th>Note</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for row in queue %}
|
||||||
|
<tbody>
|
||||||
|
<td>{{ row[0].replace(tzinfo=utc_timezone).astimezone().strftime("%H:%M") }}</td>
|
||||||
|
<td>{{ row[1] }}</td>
|
||||||
|
<td>{{ row[3] }}</td>
|
||||||
|
{% if row[2] %}
|
||||||
|
<td>{{ row[2] }}</td>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id='redeems-list' class="tabcontent">
|
||||||
|
<h3>Currently active redeems</h3>
|
||||||
|
<p>If you have enough points, you can redeem those redeems by typing the command in chat.</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Counter</strong> redeems add +1 to their counter.</li>
|
||||||
|
<li><strong>List</strong> redeems get added to the list of recent redeems (without a note).</li>
|
||||||
|
<li><strong>Note</strong> redeems require you to send a message together with the redeem.</li>
|
||||||
|
</ul>
|
||||||
|
<body>
|
||||||
|
{% if redeems %}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Redeem</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for redeem, redeem_info in redeems.items() %}
|
||||||
|
<tbody>
|
||||||
|
<td>!{{ redeem }}</td>
|
||||||
|
<td>{{ redeem_info["price"] }}</td>
|
||||||
|
<td>{{ redeem_info["type"] }}</td>
|
||||||
|
{% if redeem_info["info"] %}
|
||||||
|
<td>{{ redeem_info["info"] }}</td>
|
||||||
|
{% endif %}
|
||||||
|
</tbody>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("defaultOpen").click();
|
||||||
|
</script>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue