华清大学特普通奖学金初选报名系统
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.

184 lines
5.1 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. from flask import Flask, request, render_template, send_from_directory, abort, redirect, session
  2. from flask_sqlalchemy import SQLAlchemy
  3. from flask_limiter import Limiter
  4. from flask_limiter.util import get_remote_address
  5. from mastodon import Mastodon
  6. import re
  7. import random
  8. from datetime import datetime
  9. from dateutil.tz import tzlocal
  10. import html2text
  11. from config import C
  12. app = Flask(__name__)
  13. app.config.from_object('config.C')
  14. app.secret_key = C.session_key
  15. th = Mastodon(
  16. access_token = C.token,
  17. api_base_url = 'https://' + C.domain
  18. )
  19. limiter = Limiter(
  20. app,
  21. key_func=get_remote_address,
  22. default_limits=["50 / minute"],
  23. )
  24. h2t = html2text.HTML2Text()
  25. h2t.ignore_links = True
  26. db = SQLAlchemy(app)
  27. class Candidate(db.Model):
  28. id = db.Column(db.Integer, primary_key=True)
  29. content = db.Column(db.String(4000))
  30. private = db.Column(db.String(1000))
  31. url = db.Column(db.String(50))
  32. time = db.Column(db.DateTime)
  33. toot = db.Column(db.BigInteger)
  34. likeNum = db.Column(db.Integer, default=0)
  35. class Like(db.Model):
  36. id = db.Column(db.Integer, primary_key=True)
  37. cid = db.Column(db.Integer)
  38. uid = db.Column(db.Integer)
  39. db.create_all()
  40. @app.route('/img/<path:path>')
  41. def send_img(path):
  42. return send_from_directory('static/img', path)
  43. @app.route('/ordinary/set_session')
  44. @limiter.limit("3 / hour; 1 / 5 minute")
  45. def set_session():
  46. if 'uid' not in session:
  47. session['uid'] = random.randint(0, 2000000000)
  48. return redirect('.')
  49. @app.route('/ordinary/')
  50. def can_list():
  51. key = request.args.get('key')
  52. sort_by = request.args.get('sort_by', 'time')
  53. if 'uid' not in session:
  54. return redirect('set_session')
  55. uid = session['uid']
  56. q = Candidate.query
  57. q = q.order_by(db.desc('likeNum')) if sort_by=='likeNum' else q.order_by(db.desc('id'))
  58. pag = q.paginate(max_per_page=100)
  59. def check_like(c):
  60. c.liked = 'liked' if Like.query.filter_by(uid=uid, cid=c.id).count() else 'like'
  61. return c
  62. pag.items = map(check_like, pag.items)
  63. vs = [{
  64. 'name': name,
  65. 'ques': ques,
  66. 'hint': hint
  67. } for name, ques, hint, ans in C.verify
  68. ]
  69. return render_template('list.html', pagination=pag, vs=vs, showPrivate=(key==C.key), sort_by=sort_by, key=key)
  70. @app.route('/ordinary/new', methods=['POST'])
  71. @limiter.limit("5 / hour; 1 / 2 second")
  72. def new_one():
  73. content = request.form.get('text')
  74. private = request.form.get('privateText')
  75. url = request.form.get('url')
  76. for name, ques, hint, ans in C.verify:
  77. if request.form.get(name) != ans:
  78. return '''<html>
  79. <head>
  80. <meta charset='UTF-8'>
  81. <meta name='viewport' content='width=device-width initial-scale=1'>
  82. <title></title>
  83. </head>
  84. <body>
  85. <h1></h1>
  86. <a href="##" onclick="window.history.back()">退</a>
  87. </body>
  88. </html>
  89. ''', 401
  90. if not content or len(content)>4000: abort(422)
  91. if private and len(private)>1000: abort(422)
  92. if url and not re.match('https://(cloud\.tsinghua\.edu\.cn/f/[0-9a-z]+/(\?dl=1)?|closed\.social/safeShare/\d([a-zA-Z]+)?)', url): abort(422)
  93. if not Candidate.query.filter_by(content=content).first():
  94. toot = th.status_post(
  95. f"有新的自荐报名(大家可以直接在此处评论):\n\n{content}",
  96. visibility=C.visibility
  97. )
  98. c = Candidate(
  99. content=content,
  100. private=private,
  101. url=url,
  102. toot=toot.id,
  103. time=datetime.now()
  104. )
  105. db.session.add(c)
  106. db.session.commit()
  107. return redirect(".")
  108. @limiter.limit("100 / hour; 2 / second")
  109. @app.route('/ordinary/<int:toot>/comments')
  110. def get_comments(toot):
  111. c = Candidate.query.filter_by(toot=toot).first()
  112. if not c:
  113. abort(404)
  114. context = th.status_context(toot)
  115. replies = [
  116. {
  117. 'disp': (t.account.display_name or t.account.acct),
  118. 'url': t.account.url,
  119. 'content': h2t.handle(t.content).replace(C.bot_name,'').strip(),
  120. 'time': str(t.created_at)
  121. }
  122. for t in context.descendants
  123. ]
  124. d = list(filter(
  125. lambda r: r['content'] == '删除' and r['url'].split('/@')[1] in C.admins,
  126. replies
  127. ))
  128. if d:
  129. db.session.delete(c)
  130. db.session.commit()
  131. th.status_delete(toot)
  132. return '该内容已被删除', 404
  133. return {'replies': replies}
  134. @limiter.limit("100 / hour")
  135. @app.route('/ordinary/<int:toot>/like', methods=['POST'])
  136. def like(toot):
  137. c = Candidate.query.filter_by(toot=toot).first()
  138. if not c:
  139. abort(404)
  140. uid = session['uid']
  141. if not uid: abort(401)
  142. if Like.query.filter_by(uid=uid, cid=c.id).first():
  143. return '点赞过了', 403
  144. l = Like(uid=uid, cid=c.id)
  145. c.likeNum += 1
  146. db.session.add(l)
  147. db.session.commit()
  148. return str(c.likeNum)
  149. if __name__ == '__main__':
  150. app.run(debug=True)