You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

253 lines
6.3 KiB

from flask import Flask, request, render_template, send_from_directory, abort, redirect
from flask_sqlalchemy import SQLAlchemy
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from mastodon import Mastodon
import time, re, random, string, datetime, hashlib
from models import db, User, Post, Comment, Attention
from utils import require_token, map_post, map_comment, check_attention
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///hole.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['JSON_AS_ASCII'] = False
app.config.from_pyfile('config.py')
db.init_app(app)
with app.app_context():
db.create_all()
CS_LOGIN_URL = Mastodon(api_base_url=app.config['MASTODON_URL']) \
.auth_request_url(
client_id = app.config['CLIENT_ID'],
redirect_uris = app.config['REDIRECT_URI'],
scopes = ['read:accounts']
)
PER_PAGE = 25
def hash_name(name):
return hashlib.sha256((app.config['SALT'] + name).encode('utf-8')).hexdigest()
@app.route('/_login')
def login():
provider = request.args.get('p')
if(provider == 'cs'):
return redirect(CS_LOGIN_URL)
abort(404)
@app.route('/_auth')
def auth():
# Currently, only for closed.social
code = request.args.get('code')
client = Mastodon(
client_id = app.config['CLIENT_ID'],
client_secret = app.config['CLIENT_SECRET'],
api_base_url = app.config['MASTODON_URL']
)
token = client.log_in(
code=code,
redirect_uri=app.config['REDIRECT_URI'],
scopes=['read:accounts']
)
info = client.account_verify_credentials()
name = 'cs_' + str(info.id)
u = v = User.query.filter_by(name=name).first()
if not u:
u = User(name=name)
db.session.add(u)
if not v or False: #TODO: reset token
u.token = ''.join(random.choices(string.ascii_letters + string.digits, k=16))
db.session.commit()
return redirect('/?token='+ u.token)
@app.route('/_api/v1/getlist')
def get_list():
u = require_token()
p = request.args.get('p')
p = int(p) if p and p.isdigit() else -1
posts = Post.query.filter_by(deleted=False).order_by(db.desc('timestamp')).paginate(page=p, per_page=PER_PAGE)
data =list(map(map_post, posts.items, [hash_name(u.name)] * len(posts.items)))
return {
'code': 0,
'count': len(data),
'data': data
}
@app.route('/_api/v1/getone')
def get_one():
u = require_token()
pid = request.args.get('pid')
pid = int(pid) if pid and pid.isdigit() else -1
post = Post.query.get(pid)
if not post: abort(404)
if post.deleted: abort(451)
data = map_post(post, name_hash=hash_name(u.name))
return {
'code': 0,
'data': data
}
@app.route('/_api/v1/dopost', methods=['POST'])
def do_post():
u = require_token()
content = request.form.get('text')
content = content.strip() if content else None
post_type = request.form.get('type')
cw = request.form.get('cw')
cw = cw.strip() if cw else None
print(content, post_type, cw)
if not content or len(content) > 4096: abort(422)
if cw and len(cw)>32: abort(422)
p = Post(
name_hash = hash_name(u.name),
content = content,
post_type = post_type,
cw = cw or None,
timestamp = int(time.time()),
likenum = 1,
comments = []
)
if post_type == 'text':
pass
elif post_type == 'image':
# TODO
p.file_url = 'foo bar'
else:
abort(422)
db.session.add(p)
db.session.commit()
db.session.add(Attention(name_hash=hash_name(u.name), pid=p.id))
db.session.commit()
return {
'code': 0,
'date': p.id
}
@app.route('/_api/v1/getcomment')
def get_comment():
u = require_token()
pid = request.args.get('pid')
if pid and pid.isdigit():
p = int(pid)
else:
abort(422)
post = Post.query.get(pid)
if not post: abort(404)
data = map_comment(post)
return {
'code': 0,
'attention': check_attention(hash_name(u.name), pid),
'data': data
}
@app.route('/_api/v1/docomment', methods=['POST'])
def do_comment():
u = require_token()
pid = request.form.get('pid')
if pid and pid.isdigit():
p = int(pid)
else:
abort(422)
post = Post.query.get(pid)
if not post: abort(404)
content = request.form.get('text')
content = content.strip() if content else None
if not content or len(content) > 4096: abort(422)
c = Comment(
name_hash = hash_name(u.name),
content = content,
timestamp = int(time.time())
)
post.comments.append(c)
db.session.commit()
return {
'code': 0,
'data': pid
}
@app.route('/_api/v1/attention', methods=['POST'])
def attention():
u = require_token()
s = request.form.get('switch')
if s not in ['0', '1']: abort(422)
pid = request.form.get('pid')
if pid and pid.isdigit():
p = int(pid)
else:
abort(422)
post = Post.query.get(pid)
if not post: abort(404)
at = Attention.query.filter_by(name_hash=hash_name(u.name), pid=pid).first()
if not at:
at = Attention(name_hash=hash_name(u.name), pid=pid, disabled=True)
db.session.add(at)
if(at.disabled != (s == '0')):
at.disabled = (s == '0')
post.likenum += 1 - 2 * int(s == '0');
db.session.commit()
return {'code': 0}
@app.route('/_api/v1/getattention')
def get_attention():
u = require_token()
ats = Attention.query.filter_by(name_hash=hash_name(u.name), disabled=False)
posts = [Post.query.get(at.pid) for at in ats.all()]
print(posts)
data = [ map_post(post, hash_name(u.name), 10)
for post in posts[::-1]
if post and not post.deleted
]
return {
'code': 0,
'count': len(data),
'data': data
}
if __name__ == '__main__':
app.run(debug=True)