Big rework of Flask and database with new login system
14
app/__init__.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from flask import Flask
|
||||
from config import Config
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from flask_login import LoginManager
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object(Config)
|
||||
db = SQLAlchemy(app)
|
||||
migrate = Migrate(app, db)
|
||||
login = LoginManager(app)
|
||||
login.login_view = "login"
|
||||
|
||||
from app import routes, models
|
28
app/forms.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, BooleanField, SubmitField
|
||||
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
|
||||
from app.models import User
|
||||
|
||||
class LoginForm(FlaskForm):
|
||||
username = StringField("Username", validators=[DataRequired()])
|
||||
password = PasswordField("Password", validators=[DataRequired()])
|
||||
remember_me = BooleanField("Remember Me")
|
||||
submit = SubmitField("Sign In")
|
||||
|
||||
class RegistrationForm(FlaskForm):
|
||||
username = StringField("Username", validators=[DataRequired()])
|
||||
email = StringField("Email", validators=[DataRequired(), Email()])
|
||||
password = PasswordField("Password", validators=[DataRequired()])
|
||||
password2 = PasswordField(
|
||||
"Repeat Password", validators=[DataRequired(), EqualTo("password")])
|
||||
submit = SubmitField("Register")
|
||||
|
||||
def validate_username(self, username):
|
||||
user = User.query.filter_by(username=username.data).first()
|
||||
if user is not None:
|
||||
raise ValidationError("Please use a different username.")
|
||||
|
||||
def validate_email(self, email):
|
||||
user = User.query.filter_by(email=email.data).first()
|
||||
if user is not None:
|
||||
raise ValidationError("Please use a different email address.")
|
36
app/models.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from app import db
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from flask_login import UserMixin
|
||||
from app import login
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True, unique=True)
|
||||
username = db.Column(db.String(64), index=True, unique=True)
|
||||
email = db.Column(db.String(120), index=True, unique=True)
|
||||
password_hash = db.Column(db.String(128))
|
||||
twitter_api = db.relationship("TwitterAPI", backref="user", lazy="dynamic")
|
||||
trello_api = db.relationship('TrelloAPI', backref="user", lazy="dynamic")
|
||||
|
||||
def set_password(self, password):
|
||||
self.password_hash = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password_hash, password)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User {self.username}>"
|
||||
|
||||
class TwitterAPI(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True, unique=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), unique=True)
|
||||
access_token = db.Column(db.String(50), unique=True)
|
||||
access_token_secret = db.Column(db.String(45), unique=True)
|
||||
|
||||
class TrelloAPI(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True, unique=True)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), unique=True)
|
||||
token = db.Column(db.String(64), unique=True)
|
||||
|
||||
@login.user_loader
|
||||
def load_user(id):
|
||||
return User.query.get(int(id))
|
165
app/routes.py
Normal file
|
@ -0,0 +1,165 @@
|
|||
from app import app, db
|
||||
from flask import Flask, request, redirect, session, render_template, flash, url_for
|
||||
from flask_login import current_user, login_user, logout_user, login_required
|
||||
from app.forms import LoginForm, RegistrationForm
|
||||
from app.models import User, TwitterAPI, TrelloAPI
|
||||
from werkzeug.urls import url_parse
|
||||
import twitter_credentials, tweepy, tw
|
||||
import trello_credentials, trello, tr
|
||||
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for("home"))
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = User.query.filter_by(username=form.username.data).first()
|
||||
if user is None or not user.check_password(form.password.data):
|
||||
flash("Invalid username or password")
|
||||
return redirect(url_for("login"))
|
||||
login_user(user, remember=form.remember_me.data)
|
||||
next_page = request.args.get("next")
|
||||
if not next_page or url_parse(next_page).netloc != "":
|
||||
next_page = url_for("home")
|
||||
return redirect(next_page)
|
||||
return render_template("login.html", form=form)
|
||||
|
||||
@app.route("/register", methods=["GET", "POST"])
|
||||
def register():
|
||||
if current_user.is_authenticated:
|
||||
return redirect(url_for("index"))
|
||||
form = RegistrationForm()
|
||||
if form.validate_on_submit():
|
||||
user = User(username=form.username.data, email=form.email.data)
|
||||
user.set_password(form.password.data)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
flash("Congratulations, you are now a registered user!")
|
||||
return redirect(url_for("login"))
|
||||
return render_template("register.html", form=form)
|
||||
|
||||
@app.route("/logout")
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for("home"))
|
||||
|
||||
@app.route("/twlogin")
|
||||
@login_required
|
||||
def twlogin():
|
||||
auth = tweepy.OAuthHandler(twitter_credentials.consumer_key, twitter_credentials.consumer_secret_key, "https://cyberplanificateur.flifloo.fr/twlogin")
|
||||
if request.args.get("oauth_token") and request.args.get("oauth_verifier"):
|
||||
auth.request_token = {"oauth_token" : request.args.get("oauth_token"), "oauth_token_secret" : request.args.get("oauth_verifier")}
|
||||
try:
|
||||
auth.get_access_token(request.args.get("oauth_verifier"))
|
||||
except:
|
||||
return "Error ! Failed to get access token"
|
||||
else:
|
||||
twapi = TwitterAPI(access_token = auth.access_token, access_token_secret = auth.access_token_secret, user = current_user)
|
||||
db.session.add(twapi)
|
||||
db.session.commit()
|
||||
elif not TwitterAPI.query.filter_by(user=current_user).first():
|
||||
return redirect(auth.get_authorization_url())
|
||||
return redirect(url_for("settings"))
|
||||
|
||||
@app.route("/twlogout")
|
||||
@login_required
|
||||
def twlogout():
|
||||
twapi = TwitterAPI.query.filter_by(user=current_user).first()
|
||||
if twapi:
|
||||
db.session.delete(twapi)
|
||||
db.session.commit()
|
||||
return redirect(url_for("settings"))
|
||||
|
||||
@app.route("/trlogin")
|
||||
@login_required
|
||||
def trlogin():
|
||||
return redirect(f"https://trello.com/1/authorize?expiration=never&name=Cyberplanificateur&scope=read,write&response_type=token&key={trello_credentials.api_key}&return_url=https://cyberplanificateur.flifloo.fr/settings")
|
||||
|
||||
@app.route("/trlogout")
|
||||
@login_required
|
||||
def trlogout():
|
||||
trapi = TrelloAPI.query.filter_by(user=current_user).first()
|
||||
if trapi:
|
||||
db.session.delete(trapi)
|
||||
db.session.commit()
|
||||
return redirect(url_for("settings"))
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return render_template("index.html")
|
||||
|
||||
@app.route("/settings", methods = ["POST", "GET"])
|
||||
@login_required
|
||||
def settings():
|
||||
trloginfail = False
|
||||
if "trtoken" in request.form:
|
||||
try:
|
||||
trello.TrelloClient(api_key = trello_credentials.api_key, token = request.form["trtoken"]).list_boards()
|
||||
except:
|
||||
trloginfail = True
|
||||
else:
|
||||
trapi = TrelloAPI(token = request.form["trtoken"], user = current_user)
|
||||
db.session.add(trapi)
|
||||
db.session.commit()
|
||||
return render_template("settings.html", twlogin = TwitterAPI.query.filter_by(user=current_user).first(), trlogin = TrelloAPI.query.filter_by(user=current_user).first(), trloginfail = trloginfail)
|
||||
|
||||
@app.route("/dashboard", methods = ["POST", "GET"])
|
||||
def dashboard():
|
||||
if not tw.is_login(session):
|
||||
return redirect("/")
|
||||
|
||||
twapi = tw.api_login(session)
|
||||
if request.args.get("twrm"):
|
||||
database.twrm(request.args.get("twrm"))
|
||||
if request.args.get("delet"):
|
||||
twapi.destroy_status(request.args.get["twrm"])
|
||||
elif "tweet" in request.form and "slots" in request.form and "keywords" in request.form:
|
||||
try:
|
||||
slots = int(request.form["slots"])
|
||||
tweet = int(request.form["tweet"])
|
||||
except:
|
||||
formerror = True
|
||||
else:
|
||||
database.twadd(twapi.me().id, tweet, slots, str(request.form["keywords"].split(",")))
|
||||
formerror = False
|
||||
|
||||
tweets = list()
|
||||
idtake = list()
|
||||
for t in database.twlist(twapi.me().id):
|
||||
tw = twapi.get_status(t["id"])
|
||||
keywords = "|"
|
||||
for te in eval(t["keywords"]):
|
||||
keywords += f" {te} |"
|
||||
tweets.append({"text": tw.text, "id": tw.id, "slots": f"{t['slots']}/{t['maxslots']}", "keywords": keywords})
|
||||
idtake.append(tw.id)
|
||||
|
||||
timeline = list()
|
||||
for t in twapi.user_timeline():
|
||||
if not t.in_reply_to_status_id and not t.retweeted and t.id not in idtake:
|
||||
timeline.append({"text": t.text, "id": t.id})
|
||||
|
||||
if tr.is_login(session):
|
||||
trapi = trello.TrelloClient(api_key = trello_credentials.api_key, token = database.trtoken(twapi.me().id))
|
||||
boards = list()
|
||||
for b in trapi.list_boards():
|
||||
boards.append(b.name)
|
||||
|
||||
|
||||
return render_template("dashboard.html", login = True, tweets = tweets, timeline = timeline, boards = boards, columns = None)
|
||||
|
||||
@app.route("/twtoken")
|
||||
def twtoken():
|
||||
return f"{session['access_token']} -- {session['access_secret_token']}"
|
||||
|
||||
@app.route("/twpost")
|
||||
def twpost():
|
||||
if tw.is_login(session):
|
||||
api = twapi_login(session)
|
||||
api.update_status("bloup")
|
||||
return "Send !"
|
||||
else:
|
||||
return "Not login !"
|
||||
|
||||
@app.route("/test")
|
||||
def test():
|
||||
return render_template("elements.html")
|
Before Width: | Height: | Size: 401 B After Width: | Height: | Size: 401 B |
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 434 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
96
app/templates/dashboard.html
Normal file
|
@ -0,0 +1,96 @@
|
|||
{% extends "template.html" %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Main -->
|
||||
<article id="main">
|
||||
<header>
|
||||
<h2>Dashboard</h2>
|
||||
<p>Your personal dashboard</p>
|
||||
</header>
|
||||
<section class="wrapper style5">
|
||||
<div class="inner">
|
||||
|
||||
<h3>Current automatized tweets</h3>
|
||||
<hr />
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="align-center">Tweet</th>
|
||||
<th class="align-center">Slots</th>
|
||||
<th class="align-center">Keywords</th>
|
||||
<th class="align-center">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for t in tweets %}
|
||||
<tr>
|
||||
<td><blockquote>{{ t['text'] }}</blockquote></td>
|
||||
<td class="align-center">{{ t['slots'] }}</td>
|
||||
<td class="align-center">{{ t['keywords'] }}</td>
|
||||
<td class="align-right"><a href="https://cyberplanificateur.flifloo.fr/dashboard?twrm={{ t['id']}}" class="button primary">Disable</a>
|
||||
<a href="https://cyberplanificateur.flifloo.fr/dashboard?twrm={{ t['id']}}&delet=True" class="button">Delet</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr />
|
||||
|
||||
<h3>Add an automatized tweet</h3>
|
||||
<hr />
|
||||
<form action = "https://cyberplanificateur.flifloo.fr/dashboard" method = "POST">
|
||||
<section>
|
||||
<div class="row">
|
||||
<div class="col-6 col-12-medium">
|
||||
{% for t in timeline %}
|
||||
<div class="col-4 col-12-small">
|
||||
<input type="radio" id="{{ t['id'] }}" name="tweet" value="{{ t['id'] }}">
|
||||
<label for="{{ t['id'] }}">{{ t['text'] }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col-6 col-12-medium">
|
||||
<input type="text" name="slots" placeholder="Slots number" />
|
||||
<input type="text" name="keywords" placeholder="Keywords (separate with a comma)" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<input type="submit" value="Connect" class="button primary">
|
||||
</form>
|
||||
<hr />
|
||||
|
||||
<h3>Trello board</h3>
|
||||
<hr />
|
||||
<section>
|
||||
<div class="row">
|
||||
<div class="col-6 col-12-medium">
|
||||
<h4>Choose a board</h4>
|
||||
<form action="https://cyberplanificateur.flifloo.fr/dashboard" method="POST">
|
||||
{% for b in boards %}
|
||||
<div class="col-4 col-12-small">
|
||||
<input type="radio" id="{{ b['id'] }}" name="board" value="{{ b['id'] }}">
|
||||
<label for="{{ b['id'] }}">{{ b['text'] }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<input type="submit" value="Select" class="button primary">
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-6 col-12-medium">
|
||||
<h4>Choose a column</h4>
|
||||
<form action="https://cyberplanificateur.flifloo.fr/dashboard" method="POST">
|
||||
{% for c in columns %}
|
||||
<div class="col-4 col-12-small">
|
||||
<input type="radio" id="{{ c['id'] }}" name="column" value="{{ c['id'] }}">
|
||||
<label for="{{ c['id'] }}">{{ c['text'] }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<input type="submit" value="Select" class="button primary">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<hr />
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
|
@ -7,10 +7,10 @@
|
|||
<h2>Cyberplanificateur</h2>
|
||||
<p>Manage your commissions was never so easy with with the Cyberplanificateur</p>
|
||||
<ul class="actions special">
|
||||
{% if login %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<li><a href="{{ url_for('dashboard') }}" class="button primary">Dashboard</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('twlogin') }}" class="button primary">Login !</a></li>
|
||||
<li><a href="{{ url_for('login') }}" class="button primary">Login !</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
31
app/templates/login.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% extends "template.html" %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Main -->
|
||||
<article id="main">
|
||||
<header>
|
||||
<h2>Sign In</h2>
|
||||
</header>
|
||||
<section class="wrapper style5">
|
||||
<div class="inner">
|
||||
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.username.label }}<br>
|
||||
{{ form.username(size=32) }}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password.label }}<br>
|
||||
{{ form.password(size=32) }}
|
||||
</p>
|
||||
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
</form>
|
||||
<p>New User? <a href="{{ url_for('register') }}">Click to Register!</a></p>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
49
app/templates/register.html
Normal file
|
@ -0,0 +1,49 @@
|
|||
{% extends "template.html" %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Main -->
|
||||
<article id="main">
|
||||
<header>
|
||||
<h2>Register</h2>
|
||||
</header>
|
||||
<section class="wrapper style5">
|
||||
<div class="inner">
|
||||
|
||||
<form action="" method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
<p>
|
||||
{{ form.username.label }}<br>
|
||||
{{ form.username(size=32) }}<br>
|
||||
{% for error in form.username.errors %}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.email.label }}<br>
|
||||
{{ form.email(size=64) }}<br>
|
||||
{% for error in form.email.errors %}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password.label }}<br>
|
||||
{{ form.password(size=32) }}<br>
|
||||
{% for error in form.password.errors %}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
<p>
|
||||
{{ form.password2.label }}<br>
|
||||
{{ form.password2(size=32) }}<br>
|
||||
{% for error in form.password2.errors %}
|
||||
<span style="color: red;">[{{ error }}]</span>
|
||||
{% endfor %}
|
||||
</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
|
@ -11,16 +11,21 @@
|
|||
<div class="inner">
|
||||
|
||||
<h3>Connexions</h3>
|
||||
{% if trloginfail and not trlogin %}
|
||||
<h4><font color="red">Invalid Trello token !</font></h4>
|
||||
<p>Twitter :
|
||||
{% if twlogin %}
|
||||
<font color="green">Connected</font></p>
|
||||
<a href="{{ url_for('twlogout') }}" class="button">Disconnect</a>
|
||||
{% else %}
|
||||
<font color="red">Disconnected</font></p>
|
||||
<a href="{{ url_for('twlogin') }}" class="button primary">Connect</a>
|
||||
{% endif %}
|
||||
<p>Trello :
|
||||
{% if trlogin %}
|
||||
<font color="green">Connected</font></p>
|
||||
<a href="{{ url_for('trlogout')}}" class="button">Disconnect</a>
|
||||
<a href="{{ url_for('trlogout') }}" class="button">Disconnect</a>
|
||||
{% else %}
|
||||
<font color="red">Disconnected</font></p>
|
||||
<p><a href="https://trello.com/1/authorize?expiration=never&name=Cyberplanificateur&scope=read,write&response_type=token&key=4b9aa97a8ec693574d819aeaf9e0eefa&return_url=https://cyberplanificateur.flifloo.fr/settings">Click here</a> and give the token bellow</p>
|
||||
<p><a href="{{ url_for('trlogin') }}">Click here</a> and give the token bellow</p>
|
||||
<form action = "https://cyberplanificateur.flifloo.fr/settings" method = "POST">
|
||||
<input type="text" name="trtoken" placeholder="Trello token" />
|
||||
<input type="submit" value="Connect" class="button primary">
|
|
@ -27,12 +27,12 @@
|
|||
<div id="menu">
|
||||
<ul>
|
||||
<li><a href="{{ url_for('home') }}">Home</a></li>
|
||||
{% if login %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<li><a href="{{ url_for('dashboard') }}">Dashboard</a></li>
|
||||
<li><a href="{{ url_for('settings')}}">Settings</a></li>
|
||||
<li><a href="{{ url_for('twlogout')}}">Log Out</a></li>
|
||||
<li><a href="{{ url_for('logout')}}">Log Out</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('twlogin')}}">Log In</a></li>
|
||||
<li><a href="{{ url_for('login')}}">Log In</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
7
config.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
import os
|
||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
class Config(object):
|
||||
SECRET_KEY = "*i9uld6u@t!kxl9%o+byxqf14&a&&@y@q=l$!lg4m%b-a*^o(a"
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///" + os.path.join(basedir, "database.db")
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
9
cyberplanificateur.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from app import app
|
||||
from app.models import User, TwitterAPI, TrelloAPI
|
||||
|
||||
@app.shell_context_processor
|
||||
def make_shell_context():
|
||||
return {"db": db, "User": User, "TwitterAPI": TwitterAPI, "TrelloAPI": TrelloAPI}
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, port=5000, host="0.0.0.0")
|
30
database.py
|
@ -7,7 +7,7 @@ def tradd(id, token):
|
|||
for u in dbc.execute("SELECT * FROM trello WHERE id=?", (id,)):
|
||||
sucess = False
|
||||
if sucess:
|
||||
dbc.execute("INSERT INTO trello (id, token) VALUES (?, ?)", (id, token))
|
||||
dbc.execute("INSERT INTO trello (id, token) VALUES (?, ?)", (id, token,))
|
||||
dbc.close()
|
||||
db.commit()
|
||||
return sucess
|
||||
|
@ -28,6 +28,31 @@ def trtoken(id):
|
|||
dbc.close()
|
||||
return token
|
||||
|
||||
def twadd(user, id, maxslots, keywords):
|
||||
with sqlite3.connect('database.db') as db:
|
||||
dbc = db.cursor()
|
||||
dbc.execute("INSERT INTO tweets (user, id, slots, maxslots, keywords) VALUES (?, ?, ?, ?, ?)", (user, id, 0, maxslots, keywords,))
|
||||
dbc.close()
|
||||
db.commit()
|
||||
|
||||
def twrm(id):
|
||||
with sqlite3.connect('database.db') as db:
|
||||
dbc = db.cursor()
|
||||
dbc.execute("DELETE FROM tweets WHERE id=?", (id,))
|
||||
dbc.close()
|
||||
db.commit()
|
||||
|
||||
def twlist(user):
|
||||
with sqlite3.connect('database.db') as db:
|
||||
dbc = db.cursor()
|
||||
tweets = list()
|
||||
for u in dbc.execute("SELECT * FROM tweets WHERE user=?", (user,)):
|
||||
tweets.append({"id": u[1], "slots": u[2], "maxslots": u[3], "keywords": u[4]})
|
||||
dbc.close()
|
||||
db.commit()
|
||||
return tweets
|
||||
|
||||
|
||||
with sqlite3.connect('database.db') as db:
|
||||
Table = False
|
||||
dbc = db.cursor()
|
||||
|
@ -36,7 +61,8 @@ with sqlite3.connect('database.db') as db:
|
|||
for t in dbc.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='trello';"):
|
||||
Table = True
|
||||
if not Table:
|
||||
dbc.execute('''CREATE TABLE trello (id text, token text)''')
|
||||
dbc.execute('''CREATE TABLE trello (id int, token text)''')
|
||||
dbc.execute('''CREATE TABLE tweets (user int, id int, slots int, maxslots int, keywords text)''')
|
||||
dbc.close()
|
||||
db.commit()
|
||||
#register("flifloo", "flifloo@gmail.com", "owo")
|
||||
|
|
99
site.py
|
@ -1,99 +0,0 @@
|
|||
from flask import Flask, request, redirect, session, render_template
|
||||
import twitter_credentials, tweepy, database, trello_credentials, trello
|
||||
|
||||
app = Flask(__name__)
|
||||
app.secret_key = "*i9uld6u@t!kxl9%o+byxqf14&a&&@y@q=l$!lg4m%b-a*^o(a"
|
||||
|
||||
def is_twkeys(session):
|
||||
try:
|
||||
session["access_token"]
|
||||
session["access_secret_token"]
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def twapi_login(session):
|
||||
auth = tweepy.OAuthHandler(twitter_credentials.consumer_key, twitter_credentials.consumer_secret_key)
|
||||
auth.set_access_token(session["access_token"], session["access_secret_token"])
|
||||
return tweepy.API(auth)
|
||||
|
||||
def is_twlogin(session):
|
||||
return is_twkeys(session) and twapi_login(session).verify_credentials()
|
||||
|
||||
def is_trlogin(session):
|
||||
return is_twkeys(session) and database.trtoken(twapi_login(session).me().id)
|
||||
|
||||
@app.route("/twlogin")
|
||||
def twlogin():
|
||||
auth = tweepy.OAuthHandler(twitter_credentials.consumer_key, twitter_credentials.consumer_secret_key, "https://cyberplanificateur.flifloo.fr/twlogin")
|
||||
if request.args.get("oauth_token") and request.args.get("oauth_verifier"):
|
||||
auth.request_token = {"oauth_token" : request.args.get("oauth_token"), "oauth_token_secret" : request.args.get("oauth_verifier")}
|
||||
try:
|
||||
auth.get_access_token(request.args.get("oauth_verifier"))
|
||||
except:
|
||||
return "Error ! Failed to get access token"
|
||||
else:
|
||||
session["access_token"] = auth.access_token
|
||||
session["access_secret_token"] = auth.access_token_secret
|
||||
elif not is_twlogin(session):
|
||||
return redirect(auth.get_authorization_url())
|
||||
return redirect("/")
|
||||
|
||||
@app.route("/twlogout")
|
||||
def twlogout():
|
||||
if is_twkeys(session):
|
||||
session.pop("access_token", None)
|
||||
session.pop("access_secret_token", None)
|
||||
return redirect("/")
|
||||
|
||||
|
||||
@app.route("/trlogout")
|
||||
def trlogout():
|
||||
if not is_twlogin(session):
|
||||
return redirect("/")
|
||||
if is_trlogin(session):
|
||||
database.trrm(twapi_login(session).me().id)
|
||||
return redirect("/settings")
|
||||
|
||||
@app.route("/")
|
||||
def home():
|
||||
return render_template("index.html", login = is_twlogin(session))
|
||||
|
||||
@app.route("/settings", methods = ['POST', 'GET'])
|
||||
def settings():
|
||||
if not is_twlogin(session):
|
||||
return redirect("/")
|
||||
|
||||
trloginfail = False
|
||||
if "trtoken" in request.form:
|
||||
try:
|
||||
trello.TrelloClient(api_key = trello_credentials.api_key, token = request.form["trtoken"]).list_boards()
|
||||
except:
|
||||
trloginfail = True
|
||||
else:
|
||||
database.tradd(twapi_login(session).me().id, request.form["trtoken"])
|
||||
return render_template("settings.html", login = True, trlogin = is_trlogin(session), trloginfail = trloginfail)
|
||||
|
||||
@app.route("/dashboard")
|
||||
def dashboard():
|
||||
if not is_twlogin(session):
|
||||
return redirect("/")
|
||||
|
||||
return render_template("dashboard.html", login = True, tweets = ["test1", "test2", "test3"], timeline = [{"id": 1, "text": "test1"}, {"id": 2, "text": "test2"}, {"id": 3, "text": "test3"}, {"id": 4, "text": "test4"}])
|
||||
|
||||
@app.route("/twpost")
|
||||
def twpost():
|
||||
if is_twlogin(session):
|
||||
api = twapi_login(session)
|
||||
api.update_status("bloup")
|
||||
return "Send !"
|
||||
else:
|
||||
return "Not login !"
|
||||
|
||||
@app.route("/test")
|
||||
def test():
|
||||
return render_template("elements.html")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug=True, port=5000, host="0.0.0.0")
|
|
@ -1,64 +0,0 @@
|
|||
{% extends "template.html" %}
|
||||
{% block content %}
|
||||
|
||||
<!-- Main -->
|
||||
<article id="main">
|
||||
<header>
|
||||
<h2>Dashboard</h2>
|
||||
<p>Your personal dashboard</p>
|
||||
</header>
|
||||
<section class="wrapper style5">
|
||||
<div class="inner">
|
||||
|
||||
<h3>Current automatized tweets</h3>
|
||||
<hr />
|
||||
<table>
|
||||
{% for t in tweets %}
|
||||
<tr>
|
||||
<td><blockquote>{{ t }}</blockquote></td>
|
||||
<td><a href="#" class="button primary">Disable</a>
|
||||
<a href="#" class="button">Delet</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<hr />
|
||||
|
||||
<h3>Add an automatized tweet</h3>
|
||||
<hr />
|
||||
<section>
|
||||
<div class="row">
|
||||
<div class="col-6 col-12-medium">
|
||||
{% for t in timeline %}
|
||||
<div class="col-4 col-12-small">
|
||||
<input type="radio" id="{{ t['id'] }}" name="tweet">
|
||||
<label for="{{ t['id'] }}">{{ t['text'] }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col-6 col-12-medium">
|
||||
<input type="text" name="slots" placeholder="Slots number" />
|
||||
<input type="text" name="keywords" placeholder="Keywords (separate with a comma)" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<input type="submit" value="Connect" class="button primary">
|
||||
<hr />
|
||||
|
||||
<h3>Trello board</h3>
|
||||
<hr />
|
||||
<section>
|
||||
<div class="row">
|
||||
<div class="col-6 col-12-medium">
|
||||
<h4>Choose a board</h4>
|
||||
</div>
|
||||
<div class="col-6 col-12-medium">
|
||||
<h4>Choose a columns</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<hr />
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
5
tr.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
import trello
|
||||
import tw
|
||||
|
||||
def is_trlogin(session):
|
||||
return is_twkeys(session) and database.trtoken(tw.api_login(session).me().id)
|
18
tw.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
import tweepy, twitter_credentials
|
||||
|
||||
def api_login(session):
|
||||
auth = tweepy.OAuthHandler(twitter_credentials.consumer_key, twitter_credentials.consumer_secret_key)
|
||||
auth.set_access_token(session["access_token"], session["access_secret_token"])
|
||||
return tweepy.API(auth)
|
||||
|
||||
def is_keys(session):
|
||||
try:
|
||||
session["access_token"]
|
||||
session["access_secret_token"]
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def is_login(session):
|
||||
return is_keys(session) and api_login(session).verify_credentials()
|