mirror of
https://github.com/TitanEmbeds/Titan.git
synced 2024-11-14 18:11:23 +01:00
Implement Patreon token syncer
This commit is contained in:
parent
2e71200843
commit
6d2d4dd19c
@ -11,3 +11,4 @@ kombu
|
|||||||
redis
|
redis
|
||||||
aioredis
|
aioredis
|
||||||
Flask-Babel
|
Flask-Babel
|
||||||
|
patreon
|
33
webapp/alembic/versions/16b4fdbbe155_added_patreon_table.py
Normal file
33
webapp/alembic/versions/16b4fdbbe155_added_patreon_table.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
"""Added patreon table
|
||||||
|
|
||||||
|
Revision ID: 16b4fdbbe155
|
||||||
|
Revises: 7d6484faaccd
|
||||||
|
Create Date: 2017-11-21 03:16:42.629612
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '16b4fdbbe155'
|
||||||
|
down_revision = '7d6484faaccd'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('patreon',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('user_id', sa.String(length=255), nullable=False),
|
||||||
|
sa.Column('total_synced', sa.Integer(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('patreon')
|
||||||
|
# ### end Alembic commands ###
|
@ -13,6 +13,10 @@ config = {
|
|||||||
'recaptcha-site-key': "reCAPTCHA v2 Site Key",
|
'recaptcha-site-key': "reCAPTCHA v2 Site Key",
|
||||||
'recaptcha-secret-key': "reCAPTCHA v2 Secret Key",
|
'recaptcha-secret-key': "reCAPTCHA v2 Secret Key",
|
||||||
|
|
||||||
|
# Patreon
|
||||||
|
'patreon-client-id': "Patreon client id",
|
||||||
|
'patreon-client-secret': "Patreon client secret",
|
||||||
|
|
||||||
'app-location': "/var/www/Titan/webapp/",
|
'app-location': "/var/www/Titan/webapp/",
|
||||||
'app-secret': "Type something random here, go wild.",
|
'app-secret': "Type something random here, go wild.",
|
||||||
|
|
||||||
|
@ -3,12 +3,13 @@ from flask import current_app as app
|
|||||||
from flask_socketio import emit
|
from flask_socketio import emit
|
||||||
from config import config
|
from config import config
|
||||||
from titanembeds.decorators import discord_users_only
|
from titanembeds.decorators import discord_users_only
|
||||||
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, Cosmetics, UserCSS, set_titan_token, get_titan_token
|
from titanembeds.database import db, Guilds, UnauthenticatedUsers, UnauthenticatedBans, Cosmetics, UserCSS, Patreon, set_titan_token, get_titan_token
|
||||||
from titanembeds.oauth import authorize_url, token_url, make_authenticated_session, get_current_authenticated_user, get_user_managed_servers, check_user_can_administrate_guild, check_user_permission, generate_avatar_url, generate_guild_icon_url, generate_bot_invite_url
|
from titanembeds.oauth import authorize_url, token_url, make_authenticated_session, get_current_authenticated_user, get_user_managed_servers, check_user_can_administrate_guild, check_user_permission, generate_avatar_url, generate_guild_icon_url, generate_bot_invite_url
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
import paypalrestsdk
|
import paypalrestsdk
|
||||||
import json
|
import json
|
||||||
|
import patreon
|
||||||
|
|
||||||
user = Blueprint("user", __name__)
|
user = Blueprint("user", __name__)
|
||||||
|
|
||||||
@ -473,3 +474,90 @@ def donate_patch():
|
|||||||
db.session.add(entry)
|
db.session.add(entry)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return ('', 204)
|
return ('', 204)
|
||||||
|
|
||||||
|
@user.route("/patreon")
|
||||||
|
@discord_users_only()
|
||||||
|
def patreon_landing():
|
||||||
|
return render_template("patreon.html.j2", pclient_id=config["patreon-client-id"], state="initial")
|
||||||
|
|
||||||
|
@user.route("/patreon/callback")
|
||||||
|
@discord_users_only()
|
||||||
|
def patreon_callback():
|
||||||
|
patreon_oauth_client = patreon.OAuth(config["patreon-client-id"], config["patreon-client-secret"])
|
||||||
|
tokens = patreon_oauth_client.get_tokens(request.args.get("code"), url_for("user.patreon_callback", _external=True))
|
||||||
|
if "error" in tokens:
|
||||||
|
if "patreon" in session:
|
||||||
|
del session["patreon"]
|
||||||
|
return redirect(url_for("user.patreon_landing"))
|
||||||
|
session["patreon"] = tokens
|
||||||
|
return redirect(url_for("user.patreon_sync_get"))
|
||||||
|
|
||||||
|
def format_patreon_user(user):
|
||||||
|
pledges = []
|
||||||
|
for pledge in user.relationship('pledges'):
|
||||||
|
pledges.append({
|
||||||
|
"id": pledge.id(),
|
||||||
|
"attributes": pledge.attributes(),
|
||||||
|
})
|
||||||
|
usrobj = {
|
||||||
|
"id": user.id(),
|
||||||
|
"attributes": user.attributes(),
|
||||||
|
"pledges": pledges,
|
||||||
|
"titan": {
|
||||||
|
"eligible_tokens": 0,
|
||||||
|
"total_cents_synced": 0,
|
||||||
|
"total_cents_pledged": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if usrobj["pledges"]:
|
||||||
|
usrobj["titan"]["total_cents_pledged"] = usrobj["pledges"][0]["total_historical_amount_cents"]
|
||||||
|
dbpatreon = db.session.query(Patreon).filter(Patreon.user_id == user.id()).first()
|
||||||
|
if dbpatreon:
|
||||||
|
usrobj["titan"]["total_cents_synced"] = dbpatreon.total_synced
|
||||||
|
usrobj["titan"]["eligible_tokens"] = usrobj["titan"]["total_cents_pledged"] - usrobj["titan"]["total_cents_synced"]
|
||||||
|
return usrobj
|
||||||
|
|
||||||
|
@user.route("/patreon/sync", methods=["GET"])
|
||||||
|
@discord_users_only()
|
||||||
|
def patreon_sync_get():
|
||||||
|
if "patreon" not in session:
|
||||||
|
return redirect(url_for("user.patreon_landing"))
|
||||||
|
api_client = patreon.API(session["patreon"]["access_token"])
|
||||||
|
user_response = api_client.fetch_user(None, {
|
||||||
|
'pledge': ["amount_cents", "total_historical_amount_cents", "declined_since", "created_at", "pledge_cap_cents", "patron_pays_fees", "outstanding_payment_amount_cents"]
|
||||||
|
})
|
||||||
|
user = user_response.data()
|
||||||
|
if not (user):
|
||||||
|
del session["patreon"]
|
||||||
|
return redirect(url_for("user.patreon_landing"))
|
||||||
|
return render_template("patreon.html.j2", state="prepare", user=format_patreon_user(user))
|
||||||
|
|
||||||
|
@user.route("/patreon/sync", methods=["POST"])
|
||||||
|
@discord_users_only()
|
||||||
|
def patreon_sync_post():
|
||||||
|
if "patreon" not in session:
|
||||||
|
abort(401)
|
||||||
|
api_client = patreon.API(session["patreon"]["access_token"])
|
||||||
|
user_response = api_client.fetch_user(None, {
|
||||||
|
'pledge': ["amount_cents", "total_historical_amount_cents", "declined_since", "created_at", "pledge_cap_cents", "patron_pays_fees", "outstanding_payment_amount_cents"]
|
||||||
|
})
|
||||||
|
user = user_response.data()
|
||||||
|
if not (user):
|
||||||
|
abort(403)
|
||||||
|
usr = format_patreon_user(user)
|
||||||
|
if usr["titan"]["eligible_tokens"] <= 0:
|
||||||
|
return ('', 402)
|
||||||
|
dbpatreon = db.session.query(Patreon).filter(Patreon.user_id == usr["id"]).first()
|
||||||
|
if not dbpatreon:
|
||||||
|
dbpatreon = Patreon(usr["id"])
|
||||||
|
dbpatreon.total_synced = usr["titan"]["total_cents_pledged"]
|
||||||
|
db.session.add(dbpatreon)
|
||||||
|
db.session.commit()
|
||||||
|
set_titan_token(session["user_id"], usr["titan"]["eligible_tokens"], "PATREON {} [{}]".format(usr["attributes"]["full_name"], usr["id"]))
|
||||||
|
session["tokens"] = get_titan_token(session["user_id"])
|
||||||
|
return ('', 204)
|
||||||
|
|
||||||
|
@user.route("/patreon/thanks")
|
||||||
|
@discord_users_only()
|
||||||
|
def patreon_thanks():
|
||||||
|
return render_template("patreon.html.j2", state="thanks")
|
@ -14,6 +14,7 @@ from .user_css import UserCSS
|
|||||||
from .administrators import Administrators, get_administrators_list
|
from .administrators import Administrators, get_administrators_list
|
||||||
from .titan_tokens import TitanTokens, get_titan_token
|
from .titan_tokens import TitanTokens, get_titan_token
|
||||||
from .token_transactions import TokenTransactions
|
from .token_transactions import TokenTransactions
|
||||||
|
from .patreon import Patreon
|
||||||
|
|
||||||
def set_titan_token(user_id, amt_change, action):
|
def set_titan_token(user_id, amt_change, action):
|
||||||
token_count = get_titan_token(user_id)
|
token_count = get_titan_token(user_id)
|
||||||
|
14
webapp/titanembeds/database/patreon.py
Normal file
14
webapp/titanembeds/database/patreon.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from titanembeds.database import db
|
||||||
|
|
||||||
|
class Patreon(db.Model):
|
||||||
|
__tablename__ = "patreon"
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
user_id = db.Column(db.String(255), nullable=False) # User ID from patreon
|
||||||
|
total_synced = db.Column(db.Integer, nullable=False) # Total cents synced on our end
|
||||||
|
|
||||||
|
def __init__(self, user_id, total_synced=0):
|
||||||
|
self.user_id = user_id
|
||||||
|
self.total_synced = total_synced
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Patreon {0} {1} {2}>'.format(self.id, self.user_id, self.total_synced)
|
20
webapp/titanembeds/static/js/patreon.js
Normal file
20
webapp/titanembeds/static/js/patreon.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* global $, Materialize */
|
||||||
|
(function () {
|
||||||
|
function post() {
|
||||||
|
var funct = $.ajax({
|
||||||
|
dataType: "json",
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
return funct.promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#syncbtn").click(function () {
|
||||||
|
var formPost = post();
|
||||||
|
formPost.done(function (data) {
|
||||||
|
window.location.href = "thanks";
|
||||||
|
});
|
||||||
|
formPost.fail(function (data) {
|
||||||
|
Materialize.toast('Failed to sync Patreon....', 10000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
@ -6,12 +6,7 @@
|
|||||||
<p class="flow-text">Contributing to the Titan project has never been so easy! Donate to support our project development and hosting.</p>
|
<p class="flow-text">Contributing to the Titan project has never been so easy! Donate to support our project development and hosting.</p>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12 m8">
|
||||||
<div class="card-panel indigo lighten-5 z-depth-3 hoverable black-text">
|
|
||||||
<a id="patreonbtn" class="waves-effect waves-light btn btn-large center_content" href="https://www.patreon.com/TitanEmbeds" target="_blank">Donate monthly on our Patreon!</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col s12">
|
|
||||||
<div class="card-panel indigo lighten-5 z-depth-3 hoverable black-text">
|
<div class="card-panel indigo lighten-5 z-depth-3 hoverable black-text">
|
||||||
<h4>The Name-Your-Price Tool</h4>
|
<h4>The Name-Your-Price Tool</h4>
|
||||||
<p class="flow-text">Donate to receive <strong>Titan Tokens™</strong> (to be spent on donator features below) and a <strong>supporter role</strong> on our support server.</p>
|
<p class="flow-text">Donate to receive <strong>Titan Tokens™</strong> (to be spent on donator features below) and a <strong>supporter role</strong> on our support server.</p>
|
||||||
@ -22,6 +17,16 @@
|
|||||||
<a class="waves-effect waves-light btn" id="donate-btn">Donate</a>
|
<a class="waves-effect waves-light btn" id="donate-btn">Donate</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col s12 m4">
|
||||||
|
<div class="col s12">
|
||||||
|
<div class="card-panel indigo lighten-5 z-depth-3 hoverable black-text">
|
||||||
|
<h4>Patreon</h4>
|
||||||
|
<p>Would you like to donate monthly instead of one-time? How about using a different payment method other than PayPal? Patreon, is the answer!</p>
|
||||||
|
<a id="patreonbtn" class="waves-effect waves-light btn btn-large center_content" href="https://www.patreon.com/TitanEmbeds" target="_blank">Visit Patreon</a> <br>
|
||||||
|
<a id="patreonsyncbtn" class="waves-effect waves-light btn btn-large center_content" href="{{ url_for("user.patreon_landing") }}">Sync Pledges</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
87
webapp/titanembeds/templates/patreon.html.j2
Normal file
87
webapp/titanembeds/templates/patreon.html.j2
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{% extends 'site_layout.html.j2' %}
|
||||||
|
{% set title="Patreon Portal" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Ready to sync your Patreon with Titan Embeds?</h1>
|
||||||
|
<p class="flow-text">Keeping track of Titan Tokens from your patreon pledges has never been so easy!</p>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12">
|
||||||
|
{% if state %}
|
||||||
|
|
||||||
|
{% if state == "initial" %}
|
||||||
|
<div class="card-panel indigo lighten-5 z-depth-3 hoverable">
|
||||||
|
<span class="black-text center-align">
|
||||||
|
<h5>Once you are ready to sync your Patreon pledges with Titan, hit the button to begin!</h5>
|
||||||
|
<br>
|
||||||
|
<a href="{{ "https://www.patreon.com/oauth2/authorize?response_type=code&client_id={}&redirect_uri={}".format(pclient_id, url_for("user.patreon_callback", _external=True))|e }}" class="waves-effect waves-light btn btn-large center_content">Authenticate w/ Patreon!</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if state == "prepare" %}
|
||||||
|
<div class="card-panel indigo lighten-5 z-depth-3 hoverable">
|
||||||
|
<span class="black-text center-align">
|
||||||
|
<h5>Just making sure this is correct...</h5>
|
||||||
|
<p>If any of these information is incorrect, you may be using an incorrect account for either services before syncing.</p>
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col s12 m6">
|
||||||
|
<h4>Discord</h4>
|
||||||
|
<img src="{{ session["avatar"] }}" class="circle syncavatar">
|
||||||
|
<p class="flow-text">{{ session["username"] }}#{{ session["discriminator"] }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col s12 m6">
|
||||||
|
<h4>Patreon</h4>
|
||||||
|
<img src="{{ user["attributes"]["thumb_url"] }}" class="circle syncavatar">
|
||||||
|
<p class="flow-text">{{ user["attributes"]["full_name"] }}</p>
|
||||||
|
<p class="flow-text">{{ user["attributes"]["email"] }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="col s12">
|
||||||
|
<p class="flow-text">Notices:</p>
|
||||||
|
<ul class="browser-default">
|
||||||
|
{% if user["attributes"]["discord_id"] is none %}
|
||||||
|
<li class="warning">You have not linked a Discord account with your Patreon. Proceed with caution as the account you're trying to sync may be different.</li>
|
||||||
|
{% elif user["attributes"]["discord_id"] != session["user_id"] %}
|
||||||
|
<li class="warning">Patreon has reported that you've linked a different Discord account to their services. Proceed with caution as the account you're trying to sync may be different.</li>
|
||||||
|
{% endif %}
|
||||||
|
<li>Titan Embeds will only sync pledges that have been successfully paid. If you have pledged more than what it currently shows here, please wait till the payment goes through at the beginning of next month.</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<p class="flow-text"><strong>{{ user["titan"]["eligible_tokens"] }} tokens avaliable for syncing.</strong><br>Out of {{ "$%.2f"|format(user["titan"]["total_cents_pledged"]/100) }} dollar total pledged, {{ "$%.2f"|format(user["titan"]["total_cents_synced"]/100) }} has been claimed from the Patreon account already.</p>
|
||||||
|
<a id="syncbtn" class="waves-effect waves-light btn btn-large center_content" {% if user["titan"]["eligible_tokens"] <= 0 %}disabled{% endif %}>Sync</a>
|
||||||
|
<p>*If there are any discrepancies, please <a href="https://discord.io/titan" target="_blank">contact us</a>!</p>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if state == "thanks" %}
|
||||||
|
<div class="card-panel indigo lighten-5 z-depth-3 hoverable">
|
||||||
|
<span class="black-text center-align">
|
||||||
|
<h5>Thanks for syncing your Patreon!</h5>
|
||||||
|
<p>You now have {{ session["tokens"] }} Titan Tokens!</p>
|
||||||
|
<br>
|
||||||
|
<a href="{{ url_for("user.donate_get") }}" class="waves-effect waves-light btn btn-large center_content">Return to Store</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block additional_head_elements %}
|
||||||
|
<style>
|
||||||
|
.syncavatar {
|
||||||
|
max-width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.warning {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
{% block script %}
|
||||||
|
<script type="text/javascript" src="{{ url_for('static', filename='js/patreon.js') }}"></script>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user