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

109 lines
3.0 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. from flask import Flask, request, render_template, send_from_directory, abort, redirect
  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. from datetime import datetime
  8. from dateutil.tz import tzlocal
  9. import html2text
  10. from config import C
  11. app = Flask(__name__)
  12. app.config.from_object('config.C')
  13. th = Mastodon(
  14. access_token = C.token,
  15. api_base_url = 'https://' + C.domain
  16. )
  17. limiter = Limiter(
  18. app,
  19. key_func=get_remote_address,
  20. default_limits=["50 / minute"],
  21. )
  22. h2t = html2text.HTML2Text()
  23. h2t.ignore_links = True
  24. db = SQLAlchemy(app)
  25. class Candidate(db.Model):
  26. id = db.Column(db.Integer, primary_key=True)
  27. content = db.Column(db.String(400))
  28. url = db.Column(db.String(50))
  29. time = db.Column(db.DateTime)
  30. toot = db.Column(db.BigInteger)
  31. db.create_all()
  32. @app.route('/ordinary/')
  33. def list():
  34. pag = Candidate.query.order_by(db.desc('id')).paginate(max_per_page=100)
  35. vs = [{
  36. 'name': name,
  37. 'ques': ques,
  38. 'hint': hint
  39. } for name, ques, hint, ans in C.verify
  40. ]
  41. return render_template('list.html', pagination=pag, vs=vs)
  42. @app.route('/ordinary/new', methods=['POST'])
  43. @limiter.limit("5 / hour; 1 / 2 second")
  44. def new_one():
  45. content = request.form.get('text')
  46. url = request.form.get('url')
  47. for name, ques, hint, ans in C.verify:
  48. if request.form.get(name) != ans: abort(401)
  49. if not content or len(content)>400: abort(422)
  50. 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)
  51. if not Candidate.query.filter_by(content=content).first():
  52. toot = th.status_post(
  53. f"叮~ 有新的自荐报名(大家可以直接在此处评论):\n\n{content}",
  54. visibility='unlisted'
  55. )
  56. c = Candidate(content=content, url=url, toot=toot.id, time = datetime.now())
  57. db.session.add(c)
  58. db.session.commit()
  59. return redirect(".")
  60. @limiter.limit("100 / hour; 2 / second")
  61. @app.route('/ordinary/<int:toot>')
  62. def get_replies(toot):
  63. c = Candidate.query.filter_by(toot=toot).first()
  64. if not c:
  65. abort(404)
  66. context = th.status_context(toot)
  67. replies = [
  68. {
  69. 'disp': (t.account.display_name or t.account.acct),
  70. 'url': t.account.url,
  71. 'content': h2t.handle(t.content).replace(C.bot_name,'').strip(),
  72. 'time': str(t.created_at)
  73. }
  74. for t in context.descendants
  75. ]
  76. d = list(filter(
  77. lambda r: r['content'] == '删除' and r['url'].split('/@')[1] in C.admins,
  78. replies
  79. ))
  80. if d:
  81. db.session.delete(c)
  82. db.session.commit()
  83. th.status_delete(toot)
  84. return '该内容已被删除', 404
  85. return {'replies': replies}
  86. if __name__ == '__main__':
  87. app.run(debug=True)